summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schauenberg <d@unwiredcouch.com>2021-01-07 09:26:12 +0100
committerDaniel Schauenberg <d@unwiredcouch.com>2021-01-07 09:27:09 +0100
commite50d6e907d6bebec70271e9bcca50459dad88548 (patch)
tree57bef79bd565b536404761031eea39ebc90babdd
parentd98533a4d677cfc5bf1a9a77ff9cdcc4e2543862 (diff)
parentf2a9569d23af039bd69d2dd3adb1251f7da044e3 (diff)
downloadchef-e50d6e907d6bebec70271e9bcca50459dad88548.tar.gz
Merge remote-tracking branch 'origin/master' into mrtazz/pkgng-exit-code-fix
Signed-off-by: Daniel Schauenberg <d@unwiredcouch.com>
-rw-r--r--.bldr.toml6
-rw-r--r--.buildkite/hooks/pre-command38
-rw-r--r--.bundle/config2
-rw-r--r--.dockerignore2
-rw-r--r--.expeditor/build.docker.yml1
-rw-r--r--.expeditor/build.habitat.yml2
-rw-r--r--.expeditor/config.yml210
-rw-r--r--.expeditor/habitat-test.pipeline.yml62
-rw-r--r--.expeditor/release.omnibus.yml69
-rwxr-xr-x.expeditor/scripts/bk_container_prep.sh27
-rwxr-xr-x.expeditor/scripts/bk_linux_exec.sh43
-rw-r--r--.expeditor/scripts/bk_run_choco.ps19
-rw-r--r--.expeditor/scripts/bk_win_functional.ps133
-rw-r--r--.expeditor/scripts/bk_win_integration.ps112
-rw-r--r--.expeditor/scripts/bk_win_prep.ps115
-rw-r--r--.expeditor/scripts/bk_win_unit.ps19
-rw-r--r--.expeditor/scripts/ensure-minimum-viable-hab.ps18
-rwxr-xr-x.expeditor/scripts/install-hab.sh24
-rw-r--r--.expeditor/scripts/verify-plan.ps137
-rwxr-xr-x.expeditor/scripts/verify-plan.sh38
-rw-r--r--.expeditor/update_dep.sh43
-rwxr-xr-x.expeditor/update_dockerfile.sh14
-rwxr-xr-x.expeditor/update_version.sh25
-rw-r--r--.expeditor/verify.pipeline.yml617
-rw-r--r--.gitattributes4
-rw-r--r--.github/CODEOWNERS5
-rw-r--r--.github/ISSUE_TEMPLATE.md35
-rw-r--r--.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md43
-rw-r--r--.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md40
-rw-r--r--.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md17
-rw-r--r--.github/ISSUE_TEMPLATE/RESOURCE_REQUEST.md26
-rw-r--r--.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md12
-rw-r--r--.github/lock.yml1
-rw-r--r--.gitignore54
-rw-r--r--.mailmap4
-rw-r--r--.rspec2
-rw-r--r--.rubocop.yml26
-rw-r--r--.travis.yml288
-rw-r--r--CBGB.md40
-rw-r--r--CBGB.toml96
-rw-r--r--CHANGELOG.md2824
-rw-r--r--CHEF_MVPS.md95
-rw-r--r--CLA_ARCHIVE.md2705
-rw-r--r--CODE_OF_CONDUCT.md1
-rw-r--r--CONTRIBUTING.md136
-rw-r--r--DOC_CHANGES.md9
-rw-r--r--Dockerfile27
-rw-r--r--Gemfile109
-rw-r--r--Gemfile.lock816
-rw-r--r--MAINTAINERS.md291
-rw-r--r--MAINTAINERS.toml419
-rw-r--r--README.md283
-rw-r--r--RELEASE_NOTES.md5586
-rw-r--r--ROADMAP.md11
-rw-r--r--Rakefile112
-rw-r--r--VERSION2
-rw-r--r--acceptance/.gitignore3
-rw-r--r--acceptance/.shared/kitchen_acceptance/.kitchen.digitalocean.yml33
-rw-r--r--acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml280
-rw-r--r--acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml59
-rw-r--r--acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb68
-rw-r--r--acceptance/.shared/kitchen_acceptance/metadata.rb1
-rw-r--r--acceptance/Gemfile13
-rw-r--r--acceptance/Gemfile.lock247
-rw-r--r--acceptance/README.md132
-rw-r--r--acceptance/basics/.acceptance/acceptance-cookbook/.gitignore2
-rw-r--r--acceptance/basics/.acceptance/acceptance-cookbook/metadata.rb3
-rw-r--r--acceptance/basics/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/basics/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/basics/.acceptance/acceptance-cookbook/recipes/verify.rb1
-rw-r--r--acceptance/basics/.kitchen.yml4
-rw-r--r--acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb19
-rw-r--r--acceptance/basics/test/integration/chef-current-install/serverspec/spec_helper.rb6
-rw-r--r--acceptance/basics/test/integration/helpers/serverspec/Gemfile8
-rw-r--r--acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore2
-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/.gitignore16
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/Berksfile3
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb85
-rw-r--r--acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile3
-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.rb38
-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.rb212
-rw-r--r--acceptance/data-collector/test/integration/helpers/serverspec/Gemfile8
-rw-r--r--acceptance/fips/.acceptance/acceptance-cookbook/.gitignore2
-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/Gemfile9
-rw-r--r--acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb52
-rw-r--r--acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile7
-rw-r--r--acceptance/fips/test/integration/fips-unit-functional/serverspec/fips-unit-functional_spec.rb56
-rw-r--r--acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore2
-rw-r--r--acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb1
-rw-r--r--acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb61
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/.gitignore2
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/cookbook_kitchen.rb43
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb45
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/metadata.rb3
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/verify.rb1
-rw-r--r--acceptance/top-cookbooks/.gitignore1
-rw-r--r--acceptance/top-cookbooks/.kitchen.chocolatey.yml6
-rw-r--r--acceptance/top-cookbooks/.kitchen.docker.yml9
-rw-r--r--acceptance/top-cookbooks/.kitchen.git.yml11
-rw-r--r--acceptance/top-cookbooks/.kitchen.iis.yml4
-rw-r--r--acceptance/top-cookbooks/.kitchen.learn-the-basics-rhel.yml7
-rw-r--r--acceptance/top-cookbooks/.kitchen.learn-the-basics-ubuntu.yml7
-rw-r--r--acceptance/top-cookbooks/.kitchen.learn-the-basics-windows.yml7
-rw-r--r--acceptance/top-cookbooks/.kitchen.powershell.yml4
-rw-r--r--acceptance/top-cookbooks/.kitchen.sql_server.yml5
-rw-r--r--acceptance/top-cookbooks/.kitchen.winbox.yml8
-rw-r--r--acceptance/top-cookbooks/.kitchen.windows.yml38
-rw-r--r--acceptance/trivial/.acceptance/acceptance-cookbook/.gitignore2
-rw-r--r--acceptance/trivial/.acceptance/acceptance-cookbook/metadata.rb2
-rw-r--r--acceptance/trivial/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/trivial/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/trivial/.acceptance/acceptance-cookbook/recipes/verify.rb1
-rw-r--r--acceptance/trivial/.kitchen.yml7
-rw-r--r--acceptance/trivial/test/integration/chef-current-install/inspec/chef_client_spec.rb5
m---------acceptance/vendor/bundle/bundler/gems/chef-acceptance-47e931cec1000
m---------acceptance/vendor/bundle/bundler/gems/chef-acceptance-e92ddae46d210
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/.gitignore2
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/metadata.rb2
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb1
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb1
-rw-r--r--acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb1
-rw-r--r--acceptance/windows-service/.kitchen.yml7
-rw-r--r--acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb58
-rw-r--r--appveyor.yml39
-rw-r--r--azure-pipelines.yml91
-rwxr-xr-xbin/chef-apply25
-rwxr-xr-xbin/chef-client26
-rwxr-xr-xbin/chef-service-manager38
-rwxr-xr-xbin/chef-shell34
-rwxr-xr-xbin/chef-solo25
-rwxr-xr-xbin/chef-windows-service35
-rwxr-xr-xbin/knife5
-rw-r--r--chef-bin/Gemfile8
-rw-r--r--chef-bin/LICENSE201
-rw-r--r--chef-bin/Rakefile17
-rwxr-xr-xchef-bin/bin/chef-apply24
-rwxr-xr-xchef-bin/bin/chef-client25
-rwxr-xr-xchef-bin/bin/chef-resource-inspector26
-rwxr-xr-xchef-bin/bin/chef-service-manager38
-rwxr-xr-xchef-bin/bin/chef-shell31
-rwxr-xr-xchef-bin/bin/chef-solo24
-rwxr-xr-xchef-bin/bin/chef-windows-service34
-rw-r--r--chef-bin/chef-bin.gemspec25
-rw-r--r--chef-bin/lib/chef-bin.rb20
-rw-r--r--chef-bin/lib/chef-bin/version.rb34
-rw-r--r--chef-config/.gitignore9
-rw-r--r--chef-config/.rspec2
-rw-r--r--chef-config/.travis.yml31
-rw-r--r--chef-config/Gemfile8
-rw-r--r--chef-config/README.md4
-rw-r--r--chef-config/Rakefile9
-rw-r--r--chef-config/chef-config.gemspec29
-rw-r--r--chef-config/lib/chef-config.rb2
-rw-r--r--chef-config/lib/chef-config/config.rb591
-rw-r--r--chef-config/lib/chef-config/exceptions.rb7
-rw-r--r--chef-config/lib/chef-config/fips.rb8
-rw-r--r--chef-config/lib/chef-config/logger.rb28
-rw-r--r--chef-config/lib/chef-config/mixin/credentials.rb102
-rw-r--r--chef-config/lib/chef-config/mixin/dot_d.rb30
-rw-r--r--chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb20
-rw-r--r--chef-config/lib/chef-config/mixin/train_transport.rb141
-rw-r--r--chef-config/lib/chef-config/package_task.rb272
-rw-r--r--chef-config/lib/chef-config/path_helper.rb197
-rw-r--r--chef-config/lib/chef-config/version.rb21
-rw-r--r--chef-config/lib/chef-config/windows.rb12
-rw-r--r--chef-config/lib/chef-config/workstation_config_loader.rb143
-rw-r--r--chef-config/spec/spec_helper.rb6
-rw-r--r--chef-config/spec/unit/config_spec.rb489
-rw-r--r--chef-config/spec/unit/fips_spec.rb12
-rw-r--r--chef-config/spec/unit/path_helper_spec.rb163
-rw-r--r--chef-config/spec/unit/workstation_config_loader_spec.rb287
-rw-r--r--chef-universal-mingw32.gemspec21
-rw-r--r--chef-utils/.rspec2
-rw-r--r--chef-utils/Gemfile10
-rw-r--r--chef-utils/LICENSE201
-rw-r--r--chef-utils/README.md259
-rw-r--r--chef-utils/Rakefile15
-rw-r--r--chef-utils/chef-utils.gemspec46
-rw-r--r--chef-utils/lib/chef-utils.rb53
-rw-r--r--chef-utils/lib/chef-utils/dist.rb98
-rw-r--r--chef-utils/lib/chef-utils/dsl/architecture.rb150
-rw-r--r--chef-utils/lib/chef-utils/dsl/cloud.rb144
-rw-r--r--chef-utils/lib/chef-utils/dsl/default_paths.rb60
-rw-r--r--chef-utils/lib/chef-utils/dsl/introspection.rb123
-rw-r--r--chef-utils/lib/chef-utils/dsl/os.rb58
-rw-r--r--chef-utils/lib/chef-utils/dsl/path_sanity.rb39
-rw-r--r--chef-utils/lib/chef-utils/dsl/platform.rb372
-rw-r--r--chef-utils/lib/chef-utils/dsl/platform_family.rb344
-rw-r--r--chef-utils/lib/chef-utils/dsl/platform_version.rb41
-rw-r--r--chef-utils/lib/chef-utils/dsl/service.rb112
-rw-r--r--chef-utils/lib/chef-utils/dsl/train_helpers.rb87
-rw-r--r--chef-utils/lib/chef-utils/dsl/virtualization.rb250
-rw-r--r--chef-utils/lib/chef-utils/dsl/which.rb123
-rw-r--r--chef-utils/lib/chef-utils/dsl/windows.rb86
-rw-r--r--chef-utils/lib/chef-utils/internal.rb114
-rw-r--r--chef-utils/lib/chef-utils/mash.rb240
-rw-r--r--chef-utils/lib/chef-utils/version.rb20
-rw-r--r--chef-utils/lib/chef-utils/version_string.rb160
-rw-r--r--chef-utils/spec/spec_helper.rb100
-rw-r--r--chef-utils/spec/unit/dsl/architecture_spec.rb151
-rw-r--r--chef-utils/spec/unit/dsl/cloud_spec.rb89
-rw-r--r--chef-utils/spec/unit/dsl/dsl_spec.rb34
-rw-r--r--chef-utils/spec/unit/dsl/introspection_spec.rb189
-rw-r--r--chef-utils/spec/unit/dsl/os_spec.rb175
-rw-r--r--chef-utils/spec/unit/dsl/path_sanity_spec.rb86
-rw-r--r--chef-utils/spec/unit/dsl/platform_family_spec.rb223
-rw-r--r--chef-utils/spec/unit/dsl/platform_spec.rb238
-rw-r--r--chef-utils/spec/unit/dsl/service_spec.rb117
-rw-r--r--chef-utils/spec/unit/dsl/virtualization_spec.rb75
-rw-r--r--chef-utils/spec/unit/dsl/which_spec.rb171
-rw-r--r--chef-utils/spec/unit/dsl/windows_spec.rb84
-rw-r--r--chef-utils/spec/unit/mash_spec.rb51
-rw-r--r--chef.gemspec74
-rwxr-xr-xci/bundle_install.sh9
-rwxr-xr-xci/dependency_update.sh9
-rwxr-xr-xci/verify-chef.bat67
-rwxr-xr-xci/verify-chef.sh125
-rwxr-xr-xci/version_bump.sh9
-rwxr-xr-xci/version_show.sh3
-rw-r--r--cspell.json1925
-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.psm1327
-rw-r--r--distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dllbin0 -> 171008 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/Chef.PowerShell.dllbin0 -> 6144 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/Ijwhost.dllbin0 -> 319880 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/Newtonsoft.Json.dllbin0 -> 664576 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/concrt140.dllbin0 -> 309128 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/host/fxr/5.0.0/hostfxr.dllbin0 -> 499072 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/msvcp140.dllbin0 -> 585096 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/msvcp140_1.dllbin0 -> 23928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/msvcp140_2.dllbin0 -> 186248 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/msvcp140_atomic_wait.dllbin0 -> 41336 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/msvcp140_codecvt_ids.dllbin0 -> 20360 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dllbin0 -> 98304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dllbin0 -> 5632 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdbbin0 -> 11716 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json9
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dllbin0 -> 319880 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dllbin0 -> 406016 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dllbin0 -> 357752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dllbin0 -> 993160 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dllbin0 -> 5437320 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dllbin0 -> 2398080 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.amd64.dllbin0 -> 1495800 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dllbin0 -> 17920 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dllbin0 -> 142336 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dllbin0 -> 114688 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json2576
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dllbin0 -> 220552 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dllbin0 -> 572288 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dllbin0 -> 808840 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dllbin0 -> 336264 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dllbin0 -> 182664 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dllbin0 -> 154504 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dllbin0 -> 228744 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dllbin0 -> 1194888 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dllbin0 -> 17288 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dllbin0 -> 286600 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dllbin0 -> 143744 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dllbin0 -> 22920 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dllbin0 -> 20872 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dllbin0 -> 84352 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dllbin0 -> 52104 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dllbin0 -> 224768 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dllbin0 -> 47616 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dllbin0 -> 693680 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dllbin0 -> 101256 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dllbin0 -> 14208 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dllbin0 -> 183176 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dllbin0 -> 191368 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dllbin0 -> 665992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dllbin0 -> 99200 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dllbin0 -> 93056 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dllbin0 -> 287112 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dllbin0 -> 175496 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dllbin0 -> 16256 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dllbin0 -> 36744 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dllbin0 -> 62856 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dllbin0 -> 701824 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dllbin0 -> 16776 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dllbin0 -> 381832 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dllbin0 -> 18816 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Console.dllbin0 -> 152968 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Core.dllbin0 -> 23944 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dllbin0 -> 2889088 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.dllbin0 -> 25992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dllbin0 -> 185736 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dllbin0 -> 130440 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dllbin0 -> 30600 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dllbin0 -> 271240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dllbin0 -> 34696 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dllbin0 -> 58760 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dllbin0 -> 126344 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dllbin0 -> 369544 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dllbin0 -> 437128 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dllbin0 -> 124296 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dllbin0 -> 20352 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dllbin0 -> 193416 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dllbin0 -> 71560 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dllbin0 -> 36744 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dllbin0 -> 245640 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dllbin0 -> 89992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dllbin0 -> 40328 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dllbin0 -> 70536 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dllbin0 -> 220032 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dllbin0 -> 80776 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dllbin0 -> 64896 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dllbin0 -> 135560 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dllbin0 -> 5272968 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dllbin0 -> 1291648 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dllbin0 -> 188296 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dllbin0 -> 419208 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dllbin0 -> 7389576 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.dllbin0 -> 294784 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dllbin0 -> 213384 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dllbin0 -> 51080 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dllbin0 -> 1821056 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dllbin0 -> 634760 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dllbin0 -> 544648 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dllbin0 -> 85384 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dllbin0 -> 148864 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dllbin0 -> 75144 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dllbin0 -> 211336 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dllbin0 -> 344960 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dllbin0 -> 678792 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dllbin0 -> 34696 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dllbin0 -> 546696 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dllbin0 -> 161160 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dllbin0 -> 59784 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dllbin0 -> 27528 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dllbin0 -> 62344 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dllbin0 -> 151936 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.dllbin0 -> 16776 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dllbin0 -> 89992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dllbin0 -> 9389960 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dllbin0 -> 2068360 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dllbin0 -> 242568 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dllbin0 -> 397704 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dllbin0 -> 8422792 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dllbin0 -> 70536 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dllbin0 -> 1064840 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dllbin0 -> 32136 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dllbin0 -> 14208 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dllbin0 -> 43400 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dllbin0 -> 19848 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dllbin0 -> 18816 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dllbin0 -> 17288 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dllbin0 -> 28040 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dllbin0 -> 48520 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dllbin0 -> 201096 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dllbin0 -> 317832 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dllbin0 -> 27016 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dllbin0 -> 16256 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dllbin0 -> 42368 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dllbin0 -> 212360 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dllbin0 -> 92040 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dllbin0 -> 743816 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dllbin0 -> 453504 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dllbin0 -> 183176 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dllbin0 -> 93064 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dllbin0 -> 34696 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dllbin0 -> 275840 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dllbin0 -> 112512 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dllbin0 -> 33152 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dllbin0 -> 450944 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dllbin0 -> 98184 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dllbin0 -> 146824 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dllbin0 -> 14208 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.dllbin0 -> 17800 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dllbin0 -> 16264 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dllbin0 -> 61832 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dllbin0 -> 864136 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dllbin0 -> 92552 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dllbin0 -> 876936 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dllbin0 -> 508808 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dllbin0 -> 43400 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dllbin0 -> 115584 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dllbin0 -> 473992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dllbin0 -> 107904 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dllbin0 -> 77704 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dllbin0 -> 345992 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dllbin0 -> 50568 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dllbin0 -> 42376 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dllbin0 -> 21896 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dllbin0 -> 16776 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dllbin0 -> 17280 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dllbin0 -> 24456 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.dllbin0 -> 54664 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dllbin0 -> 12440 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dllbin0 -> 15512 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dllbin0 -> 12440 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dllbin0 -> 12952 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dllbin0 -> 14488 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dllbin0 -> 11920 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dllbin0 -> 12952 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dllbin0 -> 14488 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dllbin0 -> 11416 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dllbin0 -> 12440 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dllbin0 -> 13768 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dllbin0 -> 12432 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dllbin0 -> 12952 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dllbin0 -> 12440 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dllbin0 -> 12952 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dllbin0 -> 15816 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dllbin0 -> 13768 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dllbin0 -> 12952 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dllbin0 -> 12464 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dllbin0 -> 21144 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dllbin0 -> 20120 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dllbin0 -> 64664 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dllbin0 -> 12976 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dllbin0 -> 16536 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dllbin0 -> 17864 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dllbin0 -> 18376 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dllbin0 -> 14280 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dllbin0 -> 747912 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dllbin0 -> 262536 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrjit.dllbin0 -> 1324424 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/coreclr.dllbin0 -> 5155720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/createdump.exebin0 -> 54728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 329608 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dllbin0 -> 37760 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dllbin0 -> 116104 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 350080 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 342904 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dllbin0 -> 38776 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 350080 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dllbin0 -> 499072 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dllbin0 -> 506248 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 349064 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 384392 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dllbin0 -> 41856 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 353672 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dllbin0 -> 39816 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mi.dllbin0 -> 123904 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/miutils.dllbin0 -> 239616 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dllbin0 -> 1047928 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_amd64_amd64_5.0.20.51904.dllbin0 -> 1048456 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dllbin0 -> 1082240 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dllbin0 -> 57216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dllbin0 -> 142216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/netstandard.dllbin0 -> 114048 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 353672 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 337280 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dllbin0 -> 38792 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dllbin0 -> 159616 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 462728 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dllbin0 -> 46984 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 334216 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dllbin0 -> 38264 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dllbin0 -> 1035720 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 297864 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dllbin0 -> 36232 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 298888 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dllbin0 -> 36224 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/vccorlib140.dllbin0 -> 330120 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/vcruntime140.dllbin0 -> 94088 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/vcruntime140_1.dllbin0 -> 36744 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Chef.PowerShell.dllbin0 -> 6656 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dllbin0 -> 145920 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Ijwhost.dllbin0 -> 244616 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Newtonsoft.Json.dllbin0 -> 664576 bytes
-rw-r--r--distro/ruby_bin_folder/x86/concrt140.dllbin0 -> 244088 bytes
-rw-r--r--distro/ruby_bin_folder/x86/host/fxr/5.0.0/hostfxr.dllbin0 -> 389512 bytes
-rw-r--r--distro/ruby_bin_folder/x86/msvcp140.dllbin0 -> 450952 bytes
-rw-r--r--distro/ruby_bin_folder/x86/msvcp140_1.dllbin0 -> 21376 bytes
-rw-r--r--distro/ruby_bin_folder/x86/msvcp140_2.dllbin0 -> 166776 bytes
-rw-r--r--distro/ruby_bin_folder/x86/msvcp140_atomic_wait.dllbin0 -> 39296 bytes
-rw-r--r--distro/ruby_bin_folder/x86/msvcp140_codecvt_ids.dllbin0 -> 18808 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/API-MS-Win-core-xstate-l2-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dllbin0 -> 83456 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dllbin0 -> 6144 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdbbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json9
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dllbin0 -> 244616 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dllbin0 -> 406016 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dllbin0 -> 357752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dllbin0 -> 900488 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dllbin0 -> 5437320 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dllbin0 -> 2398080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.x86.dllbin0 -> 1188080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dllbin0 -> 14336 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dllbin0 -> 140288 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dllbin0 -> 114688 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json2579
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dllbin0 -> 220552 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dllbin0 -> 572288 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dllbin0 -> 808840 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dllbin0 -> 336264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dllbin0 -> 182664 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dllbin0 -> 154504 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dllbin0 -> 228744 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dllbin0 -> 1112448 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dllbin0 -> 17288 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dllbin0 -> 286600 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dllbin0 -> 143744 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dllbin0 -> 21896 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dllbin0 -> 20872 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dllbin0 -> 77192 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dllbin0 -> 52104 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dllbin0 -> 224768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dllbin0 -> 47616 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dllbin0 -> 693680 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dllbin0 -> 100744 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dllbin0 -> 183176 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dllbin0 -> 172936 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dllbin0 -> 580488 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dllbin0 -> 86920 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dllbin0 -> 82816 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dllbin0 -> 254344 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dllbin0 -> 158600 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dllbin0 -> 16264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dllbin0 -> 34184 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dllbin0 -> 57224 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dllbin0 -> 625544 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dllbin0 -> 16776 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dllbin0 -> 381832 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dllbin0 -> 18824 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Console.dllbin0 -> 138624 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Core.dllbin0 -> 23936 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dllbin0 -> 2617216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.dllbin0 -> 25992 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dllbin0 -> 165768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dllbin0 -> 130440 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dllbin0 -> 28552 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dllbin0 -> 242568 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dllbin0 -> 32136 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dllbin0 -> 52104 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dllbin0 -> 107912 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dllbin0 -> 369544 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dllbin0 -> 437128 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dllbin0 -> 114560 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dllbin0 -> 20360 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dllbin0 -> 170888 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dllbin0 -> 64904 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dllbin0 -> 33160 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dllbin0 -> 222600 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dllbin0 -> 81800 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dllbin0 -> 37768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dllbin0 -> 64904 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dllbin0 -> 194952 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dllbin0 -> 72072 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dllbin0 -> 60808 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dllbin0 -> 15232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dllbin0 -> 122760 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dllbin0 -> 4622216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dllbin0 -> 1119112 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dllbin0 -> 172936 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dllbin0 -> 371592 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dllbin0 -> 7389576 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.dllbin0 -> 294784 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dllbin0 -> 195976 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dllbin0 -> 46984 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dllbin0 -> 1606536 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dllbin0 -> 557960 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dllbin0 -> 479112 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dllbin0 -> 77704 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dllbin0 -> 132992 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dllbin0 -> 67456 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dllbin0 -> 189824 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dllbin0 -> 307080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dllbin0 -> 591752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dllbin0 -> 32648 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dllbin0 -> 483208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dllbin0 -> 140680 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dllbin0 -> 54664 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dllbin0 -> 25480 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dllbin0 -> 57224 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dllbin0 -> 134016 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.dllbin0 -> 16768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dllbin0 -> 81800 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dllbin0 -> 8576392 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dllbin0 -> 1859968 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dllbin0 -> 230792 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dllbin0 -> 350088 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dllbin0 -> 7571336 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dllbin0 -> 63880 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dllbin0 -> 982408 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dllbin0 -> 30088 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dllbin0 -> 14200 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dllbin0 -> 40832 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dllbin0 -> 18824 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dllbin0 -> 18312 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dllbin0 -> 17288 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dllbin0 -> 25992 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dllbin0 -> 45960 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dllbin0 -> 191880 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dllbin0 -> 290184 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dllbin0 -> 25992 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dllbin0 -> 16264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dllbin0 -> 42368 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dllbin0 -> 191360 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dllbin0 -> 83336 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dllbin0 -> 616320 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dllbin0 -> 383880 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dllbin0 -> 163720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dllbin0 -> 84872 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dllbin0 -> 33672 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dllbin0 -> 275840 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dllbin0 -> 101256 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dllbin0 -> 33152 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dllbin0 -> 395144 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dllbin0 -> 98184 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dllbin0 -> 132488 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.dllbin0 -> 17800 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dllbin0 -> 16264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dllbin0 -> 61832 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dllbin0 -> 849800 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dllbin0 -> 86920 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dllbin0 -> 795528 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dllbin0 -> 467336 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dllbin0 -> 43400 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dllbin0 -> 105864 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dllbin0 -> 419712 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dllbin0 -> 99200 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dllbin0 -> 14712 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dllbin0 -> 14216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dllbin0 -> 73088 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dllbin0 -> 307080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dllbin0 -> 15744 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dllbin0 -> 47496 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.dllbin0 -> 14720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dllbin0 -> 42376 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dllbin0 -> 21896 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dllbin0 -> 16264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dllbin0 -> 14728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dllbin0 -> 15240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dllbin0 -> 17288 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dllbin0 -> 24456 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.dllbin0 -> 54656 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dllbin0 -> 15752 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dllbin0 -> 11928 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dllbin0 -> 11200 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dllbin0 -> 15000 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dllbin0 -> 11440 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dllbin0 -> 13768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dllbin0 -> 12432 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dllbin0 -> 13760 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dllbin0 -> 10688 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dllbin0 -> 11416 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dllbin0 -> 13488 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dllbin0 -> 11920 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dllbin0 -> 12440 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dllbin0 -> 11712 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dllbin0 -> 11208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dllbin0 -> 15304 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dllbin0 -> 11720 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dllbin0 -> 13248 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dllbin0 -> 11712 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dllbin0 -> 21960 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dllbin0 -> 19400 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dllbin0 -> 66200 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dllbin0 -> 12232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dllbin0 -> 15816 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dllbin0 -> 17352 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dllbin0 -> 18072 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dllbin0 -> 13768 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dllbin0 -> 11712 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dllbin0 -> 719240 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dllbin0 -> 262536 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrjit.dllbin0 -> 1119112 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/coreclr.dllbin0 -> 4224904 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/createdump.exebin0 -> 43976 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 329608 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dllbin0 -> 37760 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dllbin0 -> 106888 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 350080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 342904 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dllbin0 -> 38776 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 350080 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dllbin0 -> 389512 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dllbin0 -> 391048 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 349064 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 384392 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dllbin0 -> 41856 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 353672 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dllbin0 -> 39816 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mi.dllbin0 -> 102400 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/miutils.dllbin0 -> 190464 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dllbin0 -> 988536 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_x86_x86_5.0.20.51904.dllbin0 -> 989056 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dllbin0 -> 988032 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dllbin0 -> 57224 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dllbin0 -> 142208 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/netstandard.dllbin0 -> 114056 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 353672 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dllbin0 -> 39304 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 337280 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dllbin0 -> 38792 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dllbin0 -> 127360 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 462728 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dllbin0 -> 46984 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 334216 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dllbin0 -> 38264 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dllbin0 -> 1170880 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 297864 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dllbin0 -> 36232 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dllbin0 -> 298888 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dllbin0 -> 36224 bytes
-rw-r--r--distro/ruby_bin_folder/x86/vccorlib140.dllbin0 -> 267656 bytes
-rw-r--r--distro/ruby_bin_folder/x86/vcruntime140.dllbin0 -> 76152 bytes
-rw-r--r--distro/templates/powershell/chef/chef.psm1.erb459
-rw-r--r--dobi.yaml21
-rw-r--r--docs/dev/README.md31
-rw-r--r--docs/dev/design_documents/action_collection.md106
-rw-r--r--docs/dev/design_documents/bootstrap_with_train.md150
-rw-r--r--docs/dev/design_documents/client_exit_codes.md62
-rw-r--r--docs/dev/design_documents/client_release_cadence.md15
-rw-r--r--docs/dev/design_documents/cookbook_root_aliases.md53
-rw-r--r--docs/dev/design_documents/data_collector.md568
-rw-r--r--docs/dev/design_documents/deprecations_in_resources.md68
-rw-r--r--docs/dev/design_documents/event_handler_recipe_dsl.md67
-rw-r--r--docs/dev/design_documents/gem_installation_via_metadata.md30
-rw-r--r--docs/dev/design_documents/how_chef_is_tested_and_built.md39
-rw-r--r--docs/dev/design_documents/ohai_cookbook_segment.md36
-rw-r--r--docs/dev/design_documents/resource_before_notifications.md77
-rw-r--r--docs/dev/design_documents/resource_file_content_verification.md110
-rwxr-xr-xdocs/dev/design_documents/resource_guard_interpreters.md534
-rw-r--r--docs/dev/design_documents/resource_load_and_converge_methods.md235
-rw-r--r--docs/dev/design_documents/resource_property_validation_messaging.md29
-rw-r--r--docs/dev/design_documents/self_documenting_resources.md80
-rw-r--r--docs/dev/design_documents/server_enforced_recipes.md101
-rw-r--r--docs/dev/how_to/adding_new_platforms_to_infra.md57
-rw-r--r--docs/dev/how_to/branching_and_backporting.md21
-rw-r--r--docs/dev/how_to/building_and_installing.md46
-rw-r--r--docs/dev/how_to/bumping_minor_or_major_versions.md16
-rw-r--r--docs/dev/how_to/bumping_the_major_version.md61
-rw-r--r--docs/dev/how_to/releasing_chef_infra.md69
-rw-r--r--docs/dev/how_to/running_adhoc_builds.md45
-rw-r--r--docs/dev/how_to/updating_dependencies.md13
-rw-r--r--docs/dev/how_to/upgrading_from_chef_12.md239
-rw-r--r--docs/dev/license_acceptance.md13
-rw-r--r--docs/dev/policy/release_and_support_schedule.md77
-rw-r--r--ext/win32-eventlog/Rakefile36
-rw-r--r--ext/win32-eventlog/chef-log.man56
-rw-r--r--ext/win32-eventlog/chef-log.man.erb56
-rw-r--r--habitat/config/client.rb19
-rw-r--r--habitat/default.toml42
-rw-r--r--habitat/hooks/init5
-rw-r--r--habitat/hooks/run16
-rw-r--r--habitat/plan.ps1151
-rw-r--r--habitat/plan.sh145
-rw-r--r--habitat/tests/spec.ps125
-rw-r--r--habitat/tests/test.pester.ps168
-rw-r--r--habitat/tests/test.ps123
-rwxr-xr-xhabitat/tests/test.sh37
-rw-r--r--kitchen-tests/.kitchen.travis.yml118
-rw-r--r--kitchen-tests/.kitchen.yml37
-rw-r--r--kitchen-tests/Berksfile9
-rw-r--r--kitchen-tests/Berksfile.lock108
-rw-r--r--kitchen-tests/Gemfile18
-rw-r--r--kitchen-tests/Gemfile.lock245
-rw-r--r--kitchen-tests/README.md102
-rw-r--r--kitchen-tests/cookbooks/audit_test/.gitignore15
-rw-r--r--kitchen-tests/cookbooks/audit_test/.kitchen.yml16
-rw-r--r--kitchen-tests/cookbooks/audit_test/Berksfile3
-rw-r--r--kitchen-tests/cookbooks/audit_test/Berksfile.lock7
-rw-r--r--kitchen-tests/cookbooks/audit_test/README.md12
-rw-r--r--kitchen-tests/cookbooks/audit_test/chefignore95
-rw-r--r--kitchen-tests/cookbooks/audit_test/metadata.rb7
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/default.rb26
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/error_duplicate_control_groups.rb17
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/error_no_block.rb7
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/error_orphan_control.rb13
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/failed_specs.rb14
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/serverspec_collision.rb31
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/serverspec_support.rb37
-rw-r--r--kitchen-tests/cookbooks/audit_test/recipes/with_include_recipe.rb16
-rw-r--r--kitchen-tests/cookbooks/base/Berksfile5
-rw-r--r--kitchen-tests/cookbooks/base/README.md3
-rw-r--r--kitchen-tests/cookbooks/base/attributes/default.rb87
-rw-r--r--kitchen-tests/cookbooks/base/libraries/chef-sugar.rb4
-rw-r--r--kitchen-tests/cookbooks/base/metadata.rb24
-rw-r--r--kitchen-tests/cookbooks/base/recipes/default.rb54
-rw-r--r--kitchen-tests/cookbooks/base/recipes/packages.rb17
-rw-r--r--kitchen-tests/cookbooks/base/recipes/tests.rb21
-rw-r--r--kitchen-tests/cookbooks/end_to_end/README.md3
-rw-r--r--kitchen-tests/cookbooks/end_to_end/attributes/default.rb80
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/GlobalSignRootCA.pem21
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/base64-cert2.cer24
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/der-cert1.cerbin0 -> 969 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.cerbin0 -> 792 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pfxbin0 -> 2582 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pvkbin0 -> 1212 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test_cert.crtbin0 -> 889 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test_der.derbin0 -> 889 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/certs/test_p7b.p7b22
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/io.chef.testing.fake.plist25
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/tourism.tar.gzbin0 -> 2253 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/tourism.tar.xzbin0 -> 2128 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/files/tourism.zipbin0 -> 2680 bytes
-rw-r--r--kitchen-tests/cookbooks/end_to_end/metadata.rb27
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_alternatives.rb51
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_apt.rb24
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_chef-vault.rb43
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_config.rb11
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_trusted_certificate.rb30
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_cron.rb67
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_dmg_package.rb21
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_ifconfig.rb15
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_launchd.rb13
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_macos_userdefaults.rb75
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb20
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_ohai_hint.rb13
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_openssl.rb258
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_packages.rb24
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_snap.rb14
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_sudo.rb63
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_sysctl.rb21
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_tests.rb30
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_yum.rb16
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb13
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/default.rb10
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/linux.rb130
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/macos.rb91
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/windows.rb136
-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/mysql.json5
-rw-r--r--kitchen-tests/data_bags/passwords/webapp.json4
-rw-r--r--kitchen-tests/kitchen.azure.yml39
-rw-r--r--kitchen-tests/kitchen.yml153
-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/end-to-end/_chef_client_config.rb11
-rw-r--r--kitchen-tests/test/integration/webapp/default_spec.rb118
m---------kitchen-tests/vendor/bundle/bundler/gems/kitchen-ec2-fec3f199a6460
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/acl_entry.rb5
-rw-r--r--lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb20
-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.rb31
-rw-r--r--lib/chef/action_collection.rb275
-rw-r--r--lib/chef/api_client.rb75
-rw-r--r--lib/chef/api_client/registration.rb39
-rw-r--r--lib/chef/api_client_v1.rb100
-rw-r--r--lib/chef/application.rb308
-rw-r--r--lib/chef/application/apply.rb191
-rw-r--r--lib/chef/application/base.rb455
-rw-r--r--lib/chef/application/client.rb488
-rw-r--r--lib/chef/application/exit_code.rb128
-rw-r--r--lib/chef/application/knife.rb182
-rw-r--r--lib/chef/application/solo.rb342
-rw-r--r--lib/chef/application/windows_service.rb208
-rw-r--r--lib/chef/application/windows_service_manager.rb97
-rw-r--r--lib/chef/applications.rb8
-rw-r--r--lib/chef/attribute_allowlist.rb86
-rw-r--r--lib/chef/attribute_blocklist.rb81
-rw-r--r--lib/chef/audit/audit_event_proxy.rb93
-rw-r--r--lib/chef/audit/audit_reporter.rb176
-rw-r--r--lib/chef/audit/control_group_data.rb139
-rw-r--r--lib/chef/audit/logger.rb36
-rw-r--r--lib/chef/audit/rspec_formatter.rb37
-rw-r--r--lib/chef/audit/runner.rb196
-rw-r--r--lib/chef/chef_class.rb58
-rw-r--r--lib/chef/chef_fs.rb4
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb182
-rw-r--r--lib/chef/chef_fs/command_line.rb83
-rw-r--r--lib/chef/chef_fs/config.rb21
-rw-r--r--lib/chef/chef_fs/data_handler/acl_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/client_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/client_key_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/container_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/cookbook_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb13
-rw-r--r--lib/chef/chef_fs/data_handler/data_handler_base.rb22
-rw-r--r--lib/chef/chef_fs/data_handler/environment_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/group_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/node_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/organization_data_handler.rb9
-rw-r--r--lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/organization_members_data_handler.rb2
-rw-r--r--lib/chef/chef_fs/data_handler/policy_data_handler.rb8
-rw-r--r--lib/chef/chef_fs/data_handler/policy_group_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/data_handler/role_data_handler.rb6
-rw-r--r--lib/chef/chef_fs/data_handler/user_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/file_pattern.rb34
-rw-r--r--lib/chef/chef_fs/file_system.rb17
-rw-r--r--lib/chef/chef_fs/file_system/base_fs_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/base_fs_object.rb27
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/acl_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/acl_entry.rb28
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/acls_dir.rb18
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/chef_server_root_dir.rb58
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_artifact_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir.rb22
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb83
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb32
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbook_subdir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbooks_acl_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb45
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb17
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/data_bags_dir.rb26
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/environments_dir.rb14
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb32
-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.rb24
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb24
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policies_acl_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policies_dir.rb69
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb19
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/policy_groups_dir.rb10
-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.rb69
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb86
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir.rb20
-rw-r--r--lib/chef/chef_fs/file_system/exceptions.rb2
-rw-r--r--lib/chef/chef_fs/file_system/memory/memory_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/memory/memory_file.rb4
-rw-r--r--lib/chef/chef_fs/file_system/memory/memory_root.rb2
-rw-r--r--lib/chef/chef_fs/file_system/multiplexed_dir.rb30
-rw-r--r--lib/chef/chef_fs/file_system/nonexistent_fs_object.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/acl.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/acls_dir.rb14
-rw-r--r--lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/base_file.rb12
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_artifact_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir.rb63
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb45
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb100
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir.rb9
-rw-r--r--lib/chef/chef_fs/file_system/repository/client.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/client_key.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/client_keys_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/clients_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/container.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/containers_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/cookbook_artifacts_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb10
-rw-r--r--lib/chef/chef_fs/file_system/repository/data_bag.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/data_bag_item.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/data_bags_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/directory.rb20
-rw-r--r--lib/chef/chef_fs/file_system/repository/environment.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/environments_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/file_system_entry.rb26
-rw-r--r--lib/chef/chef_fs/file_system/repository/group.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/groups_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/node.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/nodes_dir.rb30
-rw-r--r--lib/chef/chef_fs/file_system/repository/policies_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/policy.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/policy_group.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/role.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/roles_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/user.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/users_dir.rb8
-rw-r--r--lib/chef/chef_fs/file_system/repository/versioned_cookbooks_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system_cache.rb9
-rw-r--r--lib/chef/chef_fs/knife.rb31
-rw-r--r--lib/chef/chef_fs/parallelizer.rb25
-rw-r--r--lib/chef/chef_fs/parallelizer/parallel_enumerable.rb9
-rw-r--r--lib/chef/chef_fs/path_utils.rb25
-rw-r--r--lib/chef/client.rb503
-rw-r--r--lib/chef/compliance/default_attributes.rb93
-rw-r--r--lib/chef/compliance/fetcher/automate.rb69
-rw-r--r--lib/chef/compliance/fetcher/chef_server.rb134
-rw-r--r--lib/chef/compliance/reporter/automate.rb201
-rw-r--r--lib/chef/compliance/reporter/chef_server_automate.rb94
-rw-r--r--lib/chef/compliance/reporter/compliance_enforcer.rb20
-rw-r--r--lib/chef/compliance/reporter/json_file.rb19
-rw-r--r--lib/chef/compliance/runner.rb266
-rw-r--r--lib/chef/config.rb14
-rw-r--r--lib/chef/config_fetcher.rb20
-rw-r--r--lib/chef/constants.rb2
-rw-r--r--lib/chef/cookbook/chefignore.rb38
-rw-r--r--lib/chef/cookbook/cookbook_collection.rb13
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb290
-rw-r--r--lib/chef/cookbook/file_system_file_vendor.rb23
-rw-r--r--lib/chef/cookbook/file_vendor.rb4
-rw-r--r--lib/chef/cookbook/gem_installer.rb44
-rw-r--r--lib/chef/cookbook/manifest_v0.rb74
-rw-r--r--lib/chef/cookbook/manifest_v2.rb45
-rw-r--r--lib/chef/cookbook/metadata.rb444
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb26
-rw-r--r--lib/chef/cookbook/synchronizer.rb107
-rw-r--r--lib/chef/cookbook/syntax_check.rb40
-rw-r--r--lib/chef/cookbook_loader.rb249
-rw-r--r--lib/chef/cookbook_manifest.rb187
-rw-r--r--lib/chef/cookbook_site_streaming_uploader.rb61
-rw-r--r--lib/chef/cookbook_uploader.rb49
-rw-r--r--lib/chef/cookbook_version.rb351
-rw-r--r--lib/chef/daemon.rb8
-rw-r--r--lib/chef/data_bag.rb62
-rw-r--r--lib/chef/data_bag_item.rb72
-rw-r--r--lib/chef/data_collector.rb511
-rw-r--r--lib/chef/data_collector/config_validation.rb140
-rw-r--r--lib/chef/data_collector/error_handlers.rb116
-rw-r--r--lib/chef/data_collector/message_helpers.rb50
-rw-r--r--lib/chef/data_collector/messages.rb96
-rw-r--r--lib/chef/data_collector/messages/helpers.rb161
-rw-r--r--lib/chef/data_collector/resource_report.rb95
-rw-r--r--lib/chef/data_collector/run_end_message.rb191
-rw-r--r--lib/chef/data_collector/run_start_message.rb60
-rw-r--r--lib/chef/decorator.rb13
-rw-r--r--lib/chef/decorator/lazy.rb4
-rw-r--r--lib/chef/decorator/lazy_array.rb8
-rw-r--r--lib/chef/decorator/unchain.rb16
-rw-r--r--lib/chef/delayed_evaluator.rb2
-rw-r--r--lib/chef/deprecated.rb262
-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.rb12
-rw-r--r--lib/chef/digester.rb15
-rw-r--r--lib/chef/dsl.rb12
-rw-r--r--lib/chef/dsl/audit.rb51
-rw-r--r--lib/chef/dsl/chef_provisioning.rb57
-rw-r--r--lib/chef/dsl/chef_vault.rb84
-rw-r--r--lib/chef/dsl/cheffish.rb2
-rw-r--r--lib/chef/dsl/core.rb52
-rw-r--r--lib/chef/dsl/data_query.rb42
-rw-r--r--lib/chef/dsl/declare_resource.rb98
-rw-r--r--lib/chef/dsl/definitions.rb2
-rw-r--r--lib/chef/dsl/include_attribute.rb12
-rw-r--r--lib/chef/dsl/include_recipe.rb14
-rw-r--r--lib/chef/dsl/method_missing.rb75
-rw-r--r--lib/chef/dsl/platform_introspection.rb103
-rw-r--r--lib/chef/dsl/powershell.rb4
-rw-r--r--lib/chef/dsl/reboot_pending.rb25
-rw-r--r--lib/chef/dsl/recipe.rb56
-rw-r--r--lib/chef/dsl/registry_helper.rb2
-rw-r--r--lib/chef/dsl/resources.rb27
-rw-r--r--lib/chef/dsl/universal.rb28
-rw-r--r--lib/chef/encrypted_data_bag_item.rb35
-rw-r--r--lib/chef/encrypted_data_bag_item/assertions.rb8
-rw-r--r--lib/chef/encrypted_data_bag_item/check_encrypted.rb7
-rw-r--r--lib/chef/encrypted_data_bag_item/decryption_failure.rb2
-rw-r--r--lib/chef/encrypted_data_bag_item/decryptor.rb97
-rw-r--r--lib/chef/encrypted_data_bag_item/encryptor.rb23
-rw-r--r--lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb2
-rw-r--r--lib/chef/encrypted_data_bag_item/unsupported_cipher.rb2
-rw-r--r--lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb2
-rw-r--r--lib/chef/environment.rb93
-rw-r--r--lib/chef/event_dispatch/base.rb292
-rw-r--r--lib/chef/event_dispatch/dispatcher.rb66
-rw-r--r--lib/chef/event_dispatch/dsl.rb10
-rw-r--r--lib/chef/event_loggers/base.rb7
-rw-r--r--lib/chef/event_loggers/windows_eventlog.rb55
-rw-r--r--lib/chef/exceptions.rb114
-rw-r--r--lib/chef/file_access_control.rb10
-rw-r--r--lib/chef/file_access_control/unix.rb81
-rw-r--r--lib/chef/file_access_control/windows.rb54
-rw-r--r--lib/chef/file_cache.rb49
-rw-r--r--lib/chef/file_content_management/content_base.rb6
-rw-r--r--lib/chef/file_content_management/deploy.rb14
-rw-r--r--lib/chef/file_content_management/deploy/cp.rb6
-rw-r--r--lib/chef/file_content_management/deploy/mv_unix.rb19
-rw-r--r--lib/chef/file_content_management/deploy/mv_windows.rb10
-rw-r--r--lib/chef/file_content_management/tempfile.rb28
-rw-r--r--lib/chef/formatters/base.rb38
-rw-r--r--lib/chef/formatters/doc.rb156
-rw-r--r--lib/chef/formatters/error_description.rb23
-rw-r--r--lib/chef/formatters/error_inspectors.rb14
-rw-r--r--lib/chef/formatters/error_inspectors/api_error_formatting.rb139
-rw-r--r--lib/chef/formatters/error_inspectors/compile_error_inspector.rb14
-rw-r--r--lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb35
-rw-r--r--lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb6
-rw-r--r--lib/chef/formatters/error_inspectors/node_load_error_inspector.rb45
-rw-r--r--lib/chef/formatters/error_inspectors/registration_error_inspector.rb113
-rw-r--r--lib/chef/formatters/error_inspectors/resource_failure_inspector.rb25
-rw-r--r--lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb53
-rw-r--r--lib/chef/formatters/error_mapper.rb14
-rw-r--r--lib/chef/formatters/indentable_output_stream.rb45
-rw-r--r--lib/chef/formatters/minimal.rb76
-rw-r--r--lib/chef/guard_interpreter.rb6
-rw-r--r--lib/chef/guard_interpreter/default_guard_interpreter.rb17
-rw-r--r--lib/chef/guard_interpreter/resource_guard_interpreter.rb77
-rw-r--r--lib/chef/handler.rb17
-rw-r--r--lib/chef/handler/error_report.rb6
-rw-r--r--lib/chef/handler/json_file.rb9
-rw-r--r--lib/chef/http.rb203
-rw-r--r--lib/chef/http/api_versions.rb55
-rw-r--r--lib/chef/http/auth_credentials.rb23
-rw-r--r--lib/chef/http/authenticator.rb52
-rw-r--r--lib/chef/http/basic_client.rb47
-rw-r--r--lib/chef/http/cookie_jar.rb4
-rw-r--r--lib/chef/http/cookie_manager.rb6
-rw-r--r--lib/chef/http/decompressor.rb21
-rw-r--r--lib/chef/http/http_request.rb41
-rw-r--r--lib/chef/http/json_input.rb10
-rw-r--r--lib/chef/http/json_output.rb16
-rw-r--r--lib/chef/http/json_to_model_output.rb6
-rw-r--r--lib/chef/http/remote_request_id.rb7
-rw-r--r--lib/chef/http/simple.rb12
-rw-r--r--lib/chef/http/simple_json.rb12
-rw-r--r--lib/chef/http/socketless_chef_zero_client.rb32
-rw-r--r--lib/chef/http/ssl_policies.rb74
-rw-r--r--lib/chef/http/validate_content_length.rb24
-rw-r--r--lib/chef/json_compat.rb129
-rw-r--r--lib/chef/key.rb59
-rw-r--r--lib/chef/knife.rb289
-rw-r--r--lib/chef/knife/acl_add.rb57
-rw-r--r--lib/chef/knife/acl_base.rb183
-rw-r--r--lib/chef/knife/acl_bulk_add.rb78
-rw-r--r--lib/chef/knife/acl_bulk_remove.rb83
-rw-r--r--lib/chef/knife/acl_remove.rb62
-rw-r--r--lib/chef/knife/acl_show.rb56
-rw-r--r--lib/chef/knife/bootstrap.rb1173
-rw-r--r--lib/chef/knife/bootstrap/chef_vault_handler.rb33
-rw-r--r--lib/chef/knife/bootstrap/client_builder.rb60
-rw-r--r--lib/chef/knife/bootstrap/templates/README.md4
-rw-r--r--lib/chef/knife/bootstrap/templates/chef-full.erb54
-rw-r--r--lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb278
-rw-r--r--lib/chef/knife/bootstrap/train_connector.rb336
-rw-r--r--lib/chef/knife/client_bulk_delete.rb16
-rw-r--r--lib/chef/knife/client_create.rb44
-rw-r--r--lib/chef/knife/client_delete.rb34
-rw-r--r--lib/chef/knife/client_edit.rb9
-rw-r--r--lib/chef/knife/client_key_create.rb12
-rw-r--r--lib/chef/knife/client_key_delete.rb8
-rw-r--r--lib/chef/knife/client_key_edit.rb10
-rw-r--r--lib/chef/knife/client_key_list.rb10
-rw-r--r--lib/chef/knife/client_key_show.rb8
-rw-r--r--lib/chef/knife/client_list.rb13
-rw-r--r--lib/chef/knife/client_reregister.rb15
-rw-r--r--lib/chef/knife/client_show.rb7
-rw-r--r--lib/chef/knife/config_get.rb39
-rw-r--r--lib/chef/knife/config_get_profile.rb37
-rw-r--r--lib/chef/knife/config_list.rb139
-rw-r--r--lib/chef/knife/config_list_profiles.rb37
-rw-r--r--lib/chef/knife/config_show.rb127
-rw-r--r--lib/chef/knife/config_use.rb61
-rw-r--r--lib/chef/knife/config_use_profile.rb47
-rw-r--r--lib/chef/knife/configure.rb113
-rw-r--r--lib/chef/knife/configure_client.rb6
-rw-r--r--lib/chef/knife/cookbook_bulk_delete.rb10
-rw-r--r--lib/chef/knife/cookbook_create.rb462
-rw-r--r--lib/chef/knife/cookbook_delete.rb20
-rw-r--r--lib/chef/knife/cookbook_download.rb41
-rw-r--r--lib/chef/knife/cookbook_list.rb16
-rw-r--r--lib/chef/knife/cookbook_metadata.rb29
-rw-r--r--lib/chef/knife/cookbook_metadata_from_file.rb15
-rw-r--r--lib/chef/knife/cookbook_show.rb54
-rw-r--r--lib/chef/knife/cookbook_site_download.rb116
-rw-r--r--lib/chef/knife/cookbook_site_install.rb196
-rw-r--r--lib/chef/knife/cookbook_site_list.rb65
-rw-r--r--lib/chef/knife/cookbook_site_search.rb53
-rw-r--r--lib/chef/knife/cookbook_site_share.rb170
-rw-r--r--lib/chef/knife/cookbook_site_show.rb66
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb63
-rw-r--r--lib/chef/knife/cookbook_site_vendor.rb46
-rw-r--r--lib/chef/knife/cookbook_test.rb95
-rw-r--r--lib/chef/knife/cookbook_upload.rb250
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb183
-rw-r--r--lib/chef/knife/core/cookbook_scm_repo.rb18
-rw-r--r--lib/chef/knife/core/custom_manifest_loader.rb69
-rw-r--r--lib/chef/knife/core/formatting_options.rb49
-rw-r--r--lib/chef/knife/core/gem_glob_loader.rb20
-rw-r--r--lib/chef/knife/core/generic_presenter.rb76
-rw-r--r--lib/chef/knife/core/hashed_command_loader.rb15
-rw-r--r--lib/chef/knife/core/node_editor.rb6
-rw-r--r--lib/chef/knife/core/node_presenter.rb110
-rw-r--r--lib/chef/knife/core/object_loader.rb10
-rw-r--r--lib/chef/knife/core/status_presenter.rb98
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb73
-rw-r--r--lib/chef/knife/core/text_formatter.rb10
-rw-r--r--lib/chef/knife/core/ui.rb136
-rw-r--r--lib/chef/knife/core/windows_bootstrap_context.rb406
-rw-r--r--lib/chef/knife/data_bag_create.rb21
-rw-r--r--lib/chef/knife/data_bag_delete.rb6
-rw-r--r--lib/chef/knife/data_bag_edit.rb14
-rw-r--r--lib/chef/knife/data_bag_from_file.rb27
-rw-r--r--lib/chef/knife/data_bag_list.rb12
-rw-r--r--lib/chef/knife/data_bag_secret_options.rb80
-rw-r--r--lib/chef/knife/data_bag_show.rb18
-rw-r--r--lib/chef/knife/delete.rb78
-rw-r--r--lib/chef/knife/deps.rb118
-rw-r--r--lib/chef/knife/diff.rb49
-rw-r--r--lib/chef/knife/download.rb66
-rw-r--r--lib/chef/knife/edit.rb34
-rw-r--r--lib/chef/knife/environment_compare.rb29
-rw-r--r--lib/chef/knife/environment_create.rb13
-rw-r--r--lib/chef/knife/environment_delete.rb7
-rw-r--r--lib/chef/knife/environment_edit.rb7
-rw-r--r--lib/chef/knife/environment_from_file.rb14
-rw-r--r--lib/chef/knife/environment_list.rb13
-rw-r--r--lib/chef/knife/environment_show.rb7
-rw-r--r--lib/chef/knife/exec.rb46
-rw-r--r--lib/chef/knife/group_add.rb55
-rw-r--r--lib/chef/knife/group_create.rb49
-rw-r--r--lib/chef/knife/group_destroy.rb53
-rw-r--r--lib/chef/knife/group_list.rb43
-rw-r--r--lib/chef/knife/group_remove.rb56
-rw-r--r--lib/chef/knife/group_show.rb49
-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/key_create.rb18
-rw-r--r--lib/chef/knife/key_create_base.rb26
-rw-r--r--lib/chef/knife/key_delete.rb4
-rw-r--r--lib/chef/knife/key_edit.rb20
-rw-r--r--lib/chef/knife/key_edit_base.rb32
-rw-r--r--lib/chef/knife/key_list.rb20
-rw-r--r--lib/chef/knife/key_list_base.rb20
-rw-r--r--lib/chef/knife/key_show.rb8
-rw-r--r--lib/chef/knife/list.rb83
-rw-r--r--lib/chef/knife/node_bulk_delete.rb11
-rw-r--r--lib/chef/knife/node_create.rb8
-rw-r--r--lib/chef/knife/node_delete.rb20
-rw-r--r--lib/chef/knife/node_edit.rb20
-rw-r--r--lib/chef/knife/node_environment_set.rb7
-rw-r--r--lib/chef/knife/node_from_file.rb10
-rw-r--r--lib/chef/knife/node_list.rb14
-rw-r--r--lib/chef/knife/node_policy_set.rb79
-rw-r--r--lib/chef/knife/node_run_list_add.rb28
-rw-r--r--lib/chef/knife/node_run_list_remove.rb18
-rw-r--r--lib/chef/knife/node_run_list_set.rb12
-rw-r--r--lib/chef/knife/node_show.rb31
-rw-r--r--lib/chef/knife/null.rb6
-rw-r--r--lib/chef/knife/osc_user_create.rb97
-rw-r--r--lib/chef/knife/osc_user_delete.rb51
-rw-r--r--lib/chef/knife/osc_user_edit.rb58
-rw-r--r--lib/chef/knife/osc_user_list.rb47
-rw-r--r--lib/chef/knife/osc_user_reregister.rb64
-rw-r--r--lib/chef/knife/osc_user_show.rb54
-rw-r--r--lib/chef/knife/raw.rb76
-rw-r--r--lib/chef/knife/recipe_list.rb4
-rw-r--r--lib/chef/knife/rehash.rb33
-rw-r--r--lib/chef/knife/role_bulk_delete.rb11
-rw-r--r--lib/chef/knife/role_create.rb14
-rw-r--r--lib/chef/knife/role_delete.rb8
-rw-r--r--lib/chef/knife/role_edit.rb8
-rw-r--r--lib/chef/knife/role_env_run_list_add.rb23
-rw-r--r--lib/chef/knife/role_env_run_list_clear.rb8
-rw-r--r--lib/chef/knife/role_env_run_list_remove.rb14
-rw-r--r--lib/chef/knife/role_env_run_list_replace.rb11
-rw-r--r--lib/chef/knife/role_env_run_list_set.rb14
-rw-r--r--lib/chef/knife/role_from_file.rb10
-rw-r--r--lib/chef/knife/role_list.rb14
-rw-r--r--lib/chef/knife/role_run_list_add.rb23
-rw-r--r--lib/chef/knife/role_run_list_clear.rb8
-rw-r--r--lib/chef/knife/role_run_list_remove.rb13
-rw-r--r--lib/chef/knife/role_run_list_replace.rb11
-rw-r--r--lib/chef/knife/role_run_list_set.rb13
-rw-r--r--lib/chef/knife/role_show.rb9
-rw-r--r--lib/chef/knife/search.rb107
-rw-r--r--lib/chef/knife/serve.rb33
-rw-r--r--lib/chef/knife/show.rb30
-rw-r--r--lib/chef/knife/ssh.rb427
-rw-r--r--lib/chef/knife/ssl_check.rb97
-rw-r--r--lib/chef/knife/ssl_fetch.rb48
-rw-r--r--lib/chef/knife/status.rb59
-rw-r--r--lib/chef/knife/supermarket_download.rb102
-rw-r--r--lib/chef/knife/supermarket_install.rb173
-rw-r--r--lib/chef/knife/supermarket_list.rb57
-rw-r--r--lib/chef/knife/supermarket_search.rb36
-rw-r--r--lib/chef/knife/supermarket_share.rb147
-rw-r--r--lib/chef/knife/supermarket_show.rb47
-rw-r--r--lib/chef/knife/supermarket_unshare.rb44
-rw-r--r--lib/chef/knife/tag_create.rb6
-rw-r--r--lib/chef/knife/tag_delete.rb8
-rw-r--r--lib/chef/knife/tag_list.rb4
-rw-r--r--lib/chef/knife/upload.rb72
-rw-r--r--lib/chef/knife/user_create.rb133
-rw-r--r--lib/chef/knife/user_delete.rb59
-rw-r--r--lib/chef/knife/user_dissociate.rb42
-rw-r--r--lib/chef/knife/user_edit.rb49
-rw-r--r--lib/chef/knife/user_invite_add.rb43
-rw-r--r--lib/chef/knife/user_invite_list.rb34
-rw-r--r--lib/chef/knife/user_invite_rescind.rb63
-rw-r--r--lib/chef/knife/user_key_create.rb10
-rw-r--r--lib/chef/knife/user_key_delete.rb8
-rw-r--r--lib/chef/knife/user_key_edit.rb10
-rw-r--r--lib/chef/knife/user_key_list.rb10
-rw-r--r--lib/chef/knife/user_key_show.rb8
-rw-r--r--lib/chef/knife/user_list.rb15
-rw-r--r--lib/chef/knife/user_reregister.rb58
-rw-r--r--lib/chef/knife/user_show.rb38
-rw-r--r--lib/chef/knife/xargs.rb138
-rw-r--r--lib/chef/knife/yaml_convert.rb91
-rw-r--r--lib/chef/local_mode.rb19
-rw-r--r--lib/chef/log.rb35
-rw-r--r--lib/chef/log/syslog.rb11
-rw-r--r--lib/chef/log/winevt.rb54
-rw-r--r--lib/chef/mash.rb247
-rw-r--r--lib/chef/mixin/api_version_request_handling.rb28
-rw-r--r--lib/chef/mixin/checksum.rb10
-rw-r--r--lib/chef/mixin/chef_utils_wiring.rb40
-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/convert_to_class_name.rb59
-rw-r--r--lib/chef/mixin/create_path.rb30
-rw-r--r--lib/chef/mixin/deep_merge.rb71
-rw-r--r--lib/chef/mixin/default_paths.rb32
-rw-r--r--lib/chef/mixin/deprecation.rb48
-rw-r--r--lib/chef/mixin/enforce_ownership_and_permissions.rb4
-rw-r--r--lib/chef/mixin/file_class.rb6
-rw-r--r--lib/chef/mixin/from_file.rb15
-rw-r--r--lib/chef/mixin/get_source_from_package.rb11
-rw-r--r--lib/chef/mixin/homebrew_user.rb26
-rw-r--r--lib/chef/mixin/language.rb48
-rw-r--r--lib/chef/mixin/language_include_attribute.rb34
-rw-r--r--lib/chef/mixin/language_include_recipe.rb31
-rw-r--r--lib/chef/mixin/lazy_module_include.rb2
-rw-r--r--lib/chef/mixin/notifying_block.rb18
-rw-r--r--lib/chef/mixin/openssl_helper.rb448
-rw-r--r--lib/chef/mixin/params_validate.rb99
-rw-r--r--lib/chef/mixin/path_sanity.rb47
-rw-r--r--lib/chef/mixin/powershell_exec.rb128
-rw-r--r--lib/chef/mixin/powershell_out.rb29
-rw-r--r--lib/chef/mixin/powershell_type_coercions.rb44
-rw-r--r--lib/chef/mixin/properties.rb104
-rw-r--r--lib/chef/mixin/provides.rb7
-rw-r--r--lib/chef/mixin/proxified_socket.rb2
-rw-r--r--lib/chef/mixin/recipe_definition_dsl_core.rb35
-rw-r--r--lib/chef/mixin/securable.rb54
-rw-r--r--lib/chef/mixin/shell_out.rb118
-rw-r--r--lib/chef/mixin/subclass_directive.rb2
-rw-r--r--lib/chef/mixin/template.rb41
-rw-r--r--lib/chef/mixin/unformatter.rb8
-rw-r--r--lib/chef/mixin/uris.rb18
-rw-r--r--lib/chef/mixin/user_context.rb55
-rw-r--r--lib/chef/mixin/versioned_api.rb83
-rw-r--r--lib/chef/mixin/which.rb30
-rw-r--r--lib/chef/mixin/why_run.rb19
-rw-r--r--lib/chef/mixin/wide_string.rb32
-rw-r--r--lib/chef/mixin/windows_architecture_helper.rb28
-rw-r--r--lib/chef/mixin/windows_env_helper.rb22
-rw-r--r--lib/chef/mixin/xml_escape.rb30
-rw-r--r--lib/chef/mixins.rb26
-rw-r--r--lib/chef/monkey_patches/net-ssh-multi.rb141
-rw-r--r--lib/chef/monkey_patches/net_http.rb60
-rw-r--r--lib/chef/monkey_patches/webrick-utils.rb32
-rw-r--r--lib/chef/monkey_patches/win32/registry.rb34
-rw-r--r--lib/chef/monologger.rb90
-rw-r--r--lib/chef/nil_argument.rb3
-rw-r--r--lib/chef/node.rb312
-rw-r--r--lib/chef/node/attribute.rb761
-rw-r--r--lib/chef/node/attribute_collections.rb140
-rw-r--r--lib/chef/node/common_api.rb34
-rw-r--r--lib/chef/node/immutable_collections.rb220
-rw-r--r--lib/chef/node/mixin/deep_merge_cache.rb61
-rw-r--r--lib/chef/node/mixin/immutablize_array.rb184
-rw-r--r--lib/chef/node/mixin/immutablize_hash.rb171
-rw-r--r--lib/chef/node/mixin/mashy_array.rb68
-rw-r--r--lib/chef/node/mixin/state_tracking.rb96
-rw-r--r--lib/chef/node_map.rb283
-rw-r--r--lib/chef/null_logger.rb29
-rw-r--r--lib/chef/org.rb53
-rw-r--r--lib/chef/platform.rb6
-rw-r--r--lib/chef/platform/handler_map.rb40
-rw-r--r--lib/chef/platform/priority_map.rb12
-rw-r--r--lib/chef/platform/provider_handler_map.rb8
-rw-r--r--lib/chef/platform/provider_mapping.rb176
-rw-r--r--lib/chef/platform/provider_priority_map.rb4
-rw-r--r--lib/chef/platform/query_helpers.rb67
-rw-r--r--lib/chef/platform/rebooter.rb26
-rw-r--r--lib/chef/platform/resource_handler_map.rb8
-rw-r--r--lib/chef/platform/resource_priority_map.rb4
-rw-r--r--lib/chef/platform/service_helpers.rb119
-rw-r--r--lib/chef/policy_builder.rb8
-rw-r--r--lib/chef/policy_builder/dynamic.rb21
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb111
-rw-r--r--lib/chef/policy_builder/policyfile.rb111
-rw-r--r--lib/chef/powershell.rb79
-rw-r--r--lib/chef/property.rb338
-rw-r--r--lib/chef/provider.rb362
-rw-r--r--lib/chef/provider/apt_repository.rb253
-rw-r--r--lib/chef/provider/apt_update.rb87
-rw-r--r--lib/chef/provider/batch.rb19
-rw-r--r--lib/chef/provider/breakpoint.rb38
-rw-r--r--lib/chef/provider/cookbook_file.rb17
-rw-r--r--lib/chef/provider/cookbook_file/content.rb8
-rw-r--r--lib/chef/provider/cron.rb217
-rw-r--r--lib/chef/provider/cron/aix.rb15
-rw-r--r--lib/chef/provider/cron/solaris.rb4
-rw-r--r--lib/chef/provider/cron/unix.rb19
-rw-r--r--lib/chef/provider/deploy.rb476
-rw-r--r--lib/chef/provider/deploy/revision.rb109
-rw-r--r--lib/chef/provider/deploy/timestamped.rb34
-rw-r--r--lib/chef/provider/directory.rb88
-rw-r--r--lib/chef/provider/dsc_resource.rb91
-rw-r--r--lib/chef/provider/dsc_script.rb74
-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.rb54
-rw-r--r--lib/chef/provider/file.rb181
-rw-r--r--lib/chef/provider/file/content.rb6
-rw-r--r--lib/chef/provider/git.rb279
-rw-r--r--lib/chef/provider/group.rb119
-rw-r--r--lib/chef/provider/group/aix.rb35
-rw-r--r--lib/chef/provider/group/dscl.rb102
-rw-r--r--lib/chef/provider/group/gpasswd.rb18
-rw-r--r--lib/chef/provider/group/groupadd.rb67
-rw-r--r--lib/chef/provider/group/groupmod.rb59
-rw-r--r--lib/chef/provider/group/pw.rb61
-rw-r--r--lib/chef/provider/group/solaris.rb62
-rw-r--r--lib/chef/provider/group/suse.rb40
-rw-r--r--lib/chef/provider/group/usermod.rb34
-rw-r--r--lib/chef/provider/group/windows.rb60
-rw-r--r--lib/chef/provider/http_request.rb108
-rw-r--r--lib/chef/provider/ifconfig.rb257
-rw-r--r--lib/chef/provider/ifconfig/aix.rb58
-rw-r--r--lib/chef/provider/ifconfig/debian.rb91
-rw-r--r--lib/chef/provider/ifconfig/redhat.rb72
-rw-r--r--lib/chef/provider/launchd.rb156
-rw-r--r--lib/chef/provider/link.rb139
-rw-r--r--lib/chef/provider/log.rb57
-rw-r--r--lib/chef/provider/lwrp_base.rb24
-rw-r--r--lib/chef/provider/mdadm.rb93
-rw-r--r--lib/chef/provider/mount.rb76
-rw-r--r--lib/chef/provider/mount/aix.rb113
-rw-r--r--lib/chef/provider/mount/linux.rb67
-rw-r--r--lib/chef/provider/mount/mount.rb170
-rw-r--r--lib/chef/provider/mount/solaris.rb81
-rw-r--r--lib/chef/provider/mount/windows.rb38
-rw-r--r--lib/chef/provider/noop.rb6
-rw-r--r--lib/chef/provider/ohai.rb49
-rw-r--r--lib/chef/provider/osx_profile.rb255
-rw-r--r--lib/chef/provider/package.rb403
-rw-r--r--lib/chef/provider/package/aix.rb140
-rw-r--r--lib/chef/provider/package/apt.rb116
-rw-r--r--lib/chef/provider/package/bff.rb143
-rw-r--r--lib/chef/provider/package/cab.rb188
-rw-r--r--lib/chef/provider/package/chocolatey.rb139
-rw-r--r--lib/chef/provider/package/deb.rb131
-rw-r--r--lib/chef/provider/package/dnf.rb279
-rw-r--r--lib/chef/provider/package/dnf/dnf_helper.py186
-rw-r--r--lib/chef/provider/package/dnf/python_helper.rb220
-rw-r--r--lib/chef/provider/package/dnf/version.rb60
-rw-r--r--lib/chef/provider/package/dpkg.rb92
-rw-r--r--lib/chef/provider/package/easy_install.rb135
-rw-r--r--lib/chef/provider/package/freebsd/base.rb33
-rw-r--r--lib/chef/provider/package/freebsd/pkg.rb114
-rw-r--r--lib/chef/provider/package/freebsd/pkgng.rb36
-rw-r--r--lib/chef/provider/package/freebsd/port.rb16
-rw-r--r--lib/chef/provider/package/homebrew.rb156
-rw-r--r--lib/chef/provider/package/ips.rb50
-rw-r--r--lib/chef/provider/package/macports.rb51
-rw-r--r--lib/chef/provider/package/msu.rb166
-rw-r--r--lib/chef/provider/package/openbsd.rb65
-rw-r--r--lib/chef/provider/package/pacman.rb74
-rw-r--r--lib/chef/provider/package/paludis.rb62
-rw-r--r--lib/chef/provider/package/portage.rb98
-rw-r--r--lib/chef/provider/package/powershell.rb137
-rw-r--r--lib/chef/provider/package/rpm.rb70
-rw-r--r--lib/chef/provider/package/rubygems.rb303
-rw-r--r--lib/chef/provider/package/smartos.rb43
-rw-r--r--lib/chef/provider/package/snap.rb427
-rw-r--r--lib/chef/provider/package/solaris.rb103
-rw-r--r--lib/chef/provider/package/windows.rb139
-rw-r--r--lib/chef/provider/package/windows/exe.rb27
-rw-r--r--lib/chef/provider/package/windows/msi.rb44
-rw-r--r--lib/chef/provider/package/windows/registry_uninstall_entry.rb49
-rw-r--r--lib/chef/provider/package/yum.rb560
-rw-r--r--lib/chef/provider/package/yum/python_helper.rb228
-rw-r--r--lib/chef/provider/package/yum/rpm_utils.rb109
-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.rb60
-rw-r--r--lib/chef/provider/package/yum/yum-dump.py307
-rw-r--r--lib/chef/provider/package/yum/yum_cache.rb353
-rw-r--r--lib/chef/provider/package/yum/yum_helper.py216
-rw-r--r--lib/chef/provider/package/zypper.rb192
-rw-r--r--lib/chef/provider/powershell_script.rb255
-rw-r--r--lib/chef/provider/reboot.rb70
-rw-r--r--lib/chef/provider/registry_key.rb174
-rw-r--r--lib/chef/provider/remote_directory.rb58
-rw-r--r--lib/chef/provider/remote_file.rb36
-rw-r--r--lib/chef/provider/remote_file/cache_control_data.rb18
-rw-r--r--lib/chef/provider/remote_file/content.rb23
-rw-r--r--lib/chef/provider/remote_file/fetcher.rb8
-rw-r--r--lib/chef/provider/remote_file/ftp.rb17
-rw-r--r--lib/chef/provider/remote_file/http.rb31
-rw-r--r--lib/chef/provider/remote_file/local_file.rb15
-rw-r--r--lib/chef/provider/remote_file/network_file.rb29
-rw-r--r--lib/chef/provider/remote_file/sftp.rb19
-rw-r--r--lib/chef/provider/resource_update.rb2
-rw-r--r--lib/chef/provider/route.rb399
-rw-r--r--lib/chef/provider/ruby_block.rb16
-rw-r--r--lib/chef/provider/script.rb53
-rw-r--r--lib/chef/provider/service.rb148
-rw-r--r--lib/chef/provider/service/aix.rb14
-rw-r--r--lib/chef/provider/service/aixinit.rb12
-rw-r--r--lib/chef/provider/service/arch.rb17
-rw-r--r--lib/chef/provider/service/debian.rb155
-rw-r--r--lib/chef/provider/service/freebsd.rb27
-rw-r--r--lib/chef/provider/service/gentoo.rb19
-rw-r--r--lib/chef/provider/service/init.rb17
-rw-r--r--lib/chef/provider/service/insserv.rb20
-rw-r--r--lib/chef/provider/service/invokercd.rb10
-rw-r--r--lib/chef/provider/service/macosx.rb78
-rw-r--r--lib/chef/provider/service/openbsd.rb36
-rw-r--r--lib/chef/provider/service/redhat.rb32
-rw-r--r--lib/chef/provider/service/simple.rb41
-rw-r--r--lib/chef/provider/service/solaris.rb25
-rw-r--r--lib/chef/provider/service/systemd.rb104
-rw-r--r--lib/chef/provider/service/upstart.rb102
-rw-r--r--lib/chef/provider/service/windows.rb417
-rw-r--r--lib/chef/provider/subversion.rb135
-rw-r--r--lib/chef/provider/support/yum_repo.erb19
-rw-r--r--lib/chef/provider/support/zypper_repo.erb17
-rw-r--r--lib/chef/provider/systemd_unit.rb198
-rw-r--r--lib/chef/provider/template.rb19
-rw-r--r--lib/chef/provider/template/content.rb34
-rw-r--r--lib/chef/provider/template_finder.rb14
-rw-r--r--lib/chef/provider/user.rb166
-rw-r--r--lib/chef/provider/user/aix.rb98
-rw-r--r--lib/chef/provider/user/dscl.rb328
-rw-r--r--lib/chef/provider/user/linux.rb70
-rw-r--r--lib/chef/provider/user/mac.rb680
-rw-r--r--lib/chef/provider/user/pw.rb73
-rw-r--r--lib/chef/provider/user/solaris.rb110
-rw-r--r--lib/chef/provider/user/useradd.rb164
-rw-r--r--lib/chef/provider/user/windows.rb72
-rw-r--r--lib/chef/provider/whyrun_safe_ruby_block.rb10
-rw-r--r--lib/chef/provider/windows_script.rb120
-rw-r--r--lib/chef/provider/yum_repository.rb66
-rw-r--r--lib/chef/provider/zypper_repository.rb188
-rw-r--r--lib/chef/provider_resolver.rb58
-rw-r--r--lib/chef/providers.rb234
-rw-r--r--lib/chef/pwsh.rb71
-rw-r--r--lib/chef/recipe.rb76
-rw-r--r--lib/chef/request_id.rb6
-rw-r--r--lib/chef/resource.rb718
-rw-r--r--lib/chef/resource/action_class.rb78
-rw-r--r--lib/chef/resource/alternatives.rb210
-rw-r--r--lib/chef/resource/apt_package.rb59
-rw-r--r--lib/chef/resource/apt_preference.rb148
-rw-r--r--lib/chef/resource/apt_repository.rb479
-rw-r--r--lib/chef/resource/apt_update.rb85
-rw-r--r--lib/chef/resource/archive_file.rb205
-rw-r--r--lib/chef/resource/bash.rb128
-rw-r--r--lib/chef/resource/batch.rb15
-rw-r--r--lib/chef/resource/bff_package.rb40
-rw-r--r--lib/chef/resource/breakpoint.rb78
-rw-r--r--lib/chef/resource/build_essential.rb187
-rw-r--r--lib/chef/resource/cab_package.rb81
-rw-r--r--lib/chef/resource/chef_client_config.rb313
-rw-r--r--lib/chef/resource/chef_client_cron.rb235
-rw-r--r--lib/chef/resource/chef_client_launchd.rb194
-rw-r--r--lib/chef/resource/chef_client_scheduled_task.rb216
-rw-r--r--lib/chef/resource/chef_client_systemd_timer.rb187
-rw-r--r--lib/chef/resource/chef_client_trusted_certificate.rb101
-rw-r--r--lib/chef/resource/chef_gem.rb92
-rw-r--r--lib/chef/resource/chef_handler.rb283
-rw-r--r--lib/chef/resource/chef_sleep.rb72
-rw-r--r--lib/chef/resource/chef_vault_secret.rb135
-rw-r--r--lib/chef/resource/chocolatey_config.rb102
-rw-r--r--lib/chef/resource/chocolatey_feature.rb97
-rw-r--r--lib/chef/resource/chocolatey_package.rb64
-rw-r--r--lib/chef/resource/chocolatey_source.rb148
-rw-r--r--lib/chef/resource/conditional.rb6
-rw-r--r--lib/chef/resource/cookbook_file.rb33
-rw-r--r--lib/chef/resource/cron.rb216
-rw-r--r--lib/chef/resource/cron/_cron_shared.rb99
-rw-r--r--lib/chef/resource/cron/cron.rb46
-rw-r--r--lib/chef/resource/cron/cron_d.rb188
-rw-r--r--lib/chef/resource/cron_access.rb102
-rw-r--r--lib/chef/resource/csh.rb15
-rw-r--r--lib/chef/resource/deploy.rb443
-rw-r--r--lib/chef/resource/deploy_revision.rb31
-rw-r--r--lib/chef/resource/directory.rb43
-rw-r--r--lib/chef/resource/dmg_package.rb202
-rw-r--r--lib/chef/resource/dnf_package.rb79
-rw-r--r--lib/chef/resource/dpkg_package.rb22
-rw-r--r--lib/chef/resource/dsc_resource.rb43
-rw-r--r--lib/chef/resource/dsc_script.rb84
-rw-r--r--lib/chef/resource/easy_install_package.rb32
-rw-r--r--lib/chef/resource/env.rb65
-rw-r--r--lib/chef/resource/erl_call.rb85
-rw-r--r--lib/chef/resource/execute.rb690
-rw-r--r--lib/chef/resource/file.rb58
-rw-r--r--lib/chef/resource/file/verification.rb44
-rw-r--r--lib/chef/resource/file/verification/systemd_unit.rb68
-rw-r--r--lib/chef/resource/freebsd_package.rb37
-rw-r--r--lib/chef/resource/gem_package.rb87
-rw-r--r--lib/chef/resource/git.rb44
-rw-r--r--lib/chef/resource/group.rb90
-rw-r--r--lib/chef/resource/helpers/cron_validations.rb101
-rw-r--r--lib/chef/resource/homebrew_cask.rb104
-rw-r--r--lib/chef/resource/homebrew_package.rb47
-rw-r--r--lib/chef/resource/homebrew_tap.rb91
-rw-r--r--lib/chef/resource/homebrew_update.rb110
-rw-r--r--lib/chef/resource/hostname.rb260
-rw-r--r--lib/chef/resource/http_request.rb38
-rw-r--r--lib/chef/resource/ifconfig.rb211
-rw-r--r--lib/chef/resource/ips_package.rb24
-rw-r--r--lib/chef/resource/kernel_module.rb227
-rw-r--r--lib/chef/resource/ksh.rb15
-rw-r--r--lib/chef/resource/launchd.rb301
-rw-r--r--lib/chef/resource/link.rb97
-rw-r--r--lib/chef/resource/locale.rb184
-rw-r--r--lib/chef/resource/log.rb76
-rw-r--r--lib/chef/resource/lwrp_base.rb41
-rw-r--r--lib/chef/resource/macos_userdefaults.rb256
-rw-r--r--lib/chef/resource/macosx_service.rb34
-rw-r--r--lib/chef/resource/macports_package.rb16
-rw-r--r--lib/chef/resource/mdadm.rb149
-rw-r--r--lib/chef/resource/mount.rb192
-rw-r--r--lib/chef/resource/msu_package.rb66
-rw-r--r--lib/chef/resource/notify_group.rb74
-rw-r--r--lib/chef/resource/ohai.rb84
-rw-r--r--lib/chef/resource/ohai_hint.rb123
-rw-r--r--lib/chef/resource/openbsd_package.rb22
-rw-r--r--lib/chef/resource/openssl_dhparam.rb110
-rw-r--r--lib/chef/resource/openssl_ec_private_key.rb119
-rw-r--r--lib/chef/resource/openssl_ec_public_key.rb96
-rw-r--r--lib/chef/resource/openssl_rsa_private_key.rb115
-rw-r--r--lib/chef/resource/openssl_rsa_public_key.rb97
-rw-r--r--lib/chef/resource/openssl_x509_certificate.rb267
-rw-r--r--lib/chef/resource/openssl_x509_crl.rb152
-rw-r--r--lib/chef/resource/openssl_x509_request.rb187
-rw-r--r--lib/chef/resource/osx_profile.rb333
-rw-r--r--lib/chef/resource/package.rb41
-rw-r--r--lib/chef/resource/pacman_package.rb9
-rw-r--r--lib/chef/resource/paludis_package.rb26
-rw-r--r--lib/chef/resource/perl.rb15
-rw-r--r--lib/chef/resource/plist.rb222
-rw-r--r--lib/chef/resource/portage_package.rb25
-rw-r--r--lib/chef/resource/powershell_package.rb50
-rw-r--r--lib/chef/resource/powershell_package_source.rb171
-rw-r--r--lib/chef/resource/powershell_script.rb62
-rw-r--r--lib/chef/resource/python.rb14
-rw-r--r--lib/chef/resource/reboot.rb76
-rw-r--r--lib/chef/resource/registry_key.rb158
-rw-r--r--lib/chef/resource/remote_directory.rb113
-rw-r--r--lib/chef/resource/remote_file.rb154
-rw-r--r--lib/chef/resource/resource_notification.rb82
-rw-r--r--lib/chef/resource/rhsm_errata.rb50
-rw-r--r--lib/chef/resource/rhsm_errata_level.rb56
-rw-r--r--lib/chef/resource/rhsm_register.rb195
-rw-r--r--lib/chef/resource/rhsm_repo.rb66
-rw-r--r--lib/chef/resource/rhsm_subscription.rb99
-rw-r--r--lib/chef/resource/route.rb121
-rw-r--r--lib/chef/resource/rpm_package.rb21
-rw-r--r--lib/chef/resource/ruby.rb11
-rw-r--r--lib/chef/resource/ruby_block.rb28
-rw-r--r--lib/chef/resource/scm.rb185
-rw-r--r--lib/chef/resource/scm/_scm.rb50
-rw-r--r--lib/chef/resource/scm/git.rb144
-rw-r--r--lib/chef/resource/scm/subversion.rb73
-rw-r--r--lib/chef/resource/script.rb57
-rw-r--r--lib/chef/resource/service.rb226
-rw-r--r--lib/chef/resource/smartos_package.rb20
-rw-r--r--lib/chef/resource/snap_package.rb38
-rw-r--r--lib/chef/resource/solaris_package.rb20
-rw-r--r--lib/chef/resource/ssh_known_hosts_entry.rb164
-rw-r--r--lib/chef/resource/subversion.rb44
-rw-r--r--lib/chef/resource/sudo.rb267
-rw-r--r--lib/chef/resource/support/client.erb64
-rw-r--r--lib/chef/resource/support/cron.d.erb28
-rw-r--r--lib/chef/resource/support/cron_access.erb4
-rw-r--r--lib/chef/resource/support/ssh_known_hosts.erb3
-rw-r--r--lib/chef/resource/support/sudoer.erb17
-rw-r--r--lib/chef/resource/support/ulimit.erb41
-rw-r--r--lib/chef/resource/swap_file.rb228
-rw-r--r--lib/chef/resource/sysctl.rb233
-rw-r--r--lib/chef/resource/systemd_unit.rb104
-rw-r--r--lib/chef/resource/template.rb64
-rw-r--r--lib/chef/resource/timestamped_deploy.rb26
-rw-r--r--lib/chef/resource/timezone.rb178
-rw-r--r--lib/chef/resource/user.rb190
-rw-r--r--lib/chef/resource/user/aix_user.rb6
-rw-r--r--lib/chef/resource/user/dscl_user.rb12
-rw-r--r--lib/chef/resource/user/linux_user.rb15
-rw-r--r--lib/chef/resource/user/mac_user.rb122
-rw-r--r--lib/chef/resource/user/pw_user.rb6
-rw-r--r--lib/chef/resource/user/solaris_user.rb6
-rw-r--r--lib/chef/resource/user/windows_user.rb15
-rw-r--r--lib/chef/resource/user_ulimit.rb116
-rw-r--r--lib/chef/resource/whyrun_safe_ruby_block.rb2
-rw-r--r--lib/chef/resource/windows_ad_join.rb242
-rw-r--r--lib/chef/resource/windows_audit_policy.rb232
-rw-r--r--lib/chef/resource/windows_auto_run.rb99
-rw-r--r--lib/chef/resource/windows_certificate.rb356
-rw-r--r--lib/chef/resource/windows_dfs_folder.rb77
-rw-r--r--lib/chef/resource/windows_dfs_namespace.rb115
-rw-r--r--lib/chef/resource/windows_dfs_server.rb80
-rw-r--r--lib/chef/resource/windows_dns_record.rb95
-rw-r--r--lib/chef/resource/windows_dns_zone.rb84
-rw-r--r--lib/chef/resource/windows_env.rb230
-rw-r--r--lib/chef/resource/windows_feature.rb149
-rw-r--r--lib/chef/resource/windows_feature_dism.rb233
-rw-r--r--lib/chef/resource/windows_feature_powershell.rb242
-rw-r--r--lib/chef/resource/windows_firewall_profile.rb196
-rw-r--r--lib/chef/resource/windows_firewall_rule.rb326
-rw-r--r--lib/chef/resource/windows_font.rb135
-rw-r--r--lib/chef/resource/windows_package.rb152
-rw-r--r--lib/chef/resource/windows_pagefile.rb238
-rw-r--r--lib/chef/resource/windows_path.rb92
-rw-r--r--lib/chef/resource/windows_printer.rb166
-rw-r--r--lib/chef/resource/windows_printer_port.rb166
-rw-r--r--lib/chef/resource/windows_script.rb33
-rw-r--r--lib/chef/resource/windows_security_policy.rb165
-rw-r--r--lib/chef/resource/windows_service.rb230
-rw-r--r--lib/chef/resource/windows_share.rb345
-rw-r--r--lib/chef/resource/windows_shortcut.rb90
-rw-r--r--lib/chef/resource/windows_task.rb1070
-rw-r--r--lib/chef/resource/windows_uac.rb114
-rw-r--r--lib/chef/resource/windows_user_privilege.rb223
-rw-r--r--lib/chef/resource/windows_workgroup.rb130
-rw-r--r--lib/chef/resource/yum_package.rb161
-rw-r--r--lib/chef/resource/yum_repository.rb229
-rw-r--r--lib/chef/resource/zypper_package.rb49
-rw-r--r--lib/chef/resource/zypper_repository.rb116
-rw-r--r--lib/chef/resource_builder.rb81
-rw-r--r--lib/chef/resource_collection.rb41
-rw-r--r--lib/chef/resource_collection/resource_collection_serialization.rb26
-rw-r--r--lib/chef/resource_collection/resource_list.rb22
-rw-r--r--lib/chef/resource_collection/resource_set.rb89
-rw-r--r--lib/chef/resource_collection/stepable_iterator.rb4
-rw-r--r--lib/chef/resource_definition.rb16
-rw-r--r--lib/chef/resource_definition_list.rb8
-rw-r--r--lib/chef/resource_inspector.rb118
-rw-r--r--lib/chef/resource_reporter.rb189
-rw-r--r--lib/chef/resource_resolver.rb62
-rw-r--r--lib/chef/resources.rb236
-rw-r--r--lib/chef/rest.rb209
-rw-r--r--lib/chef/role.rb73
-rw-r--r--lib/chef/run_context.rb262
-rw-r--r--lib/chef/run_context/cookbook_compiler.rb164
-rw-r--r--lib/chef/run_list.rb19
-rw-r--r--lib/chef/run_list/run_list_expansion.rb37
-rw-r--r--lib/chef/run_list/run_list_item.rb16
-rw-r--r--lib/chef/run_list/versioned_recipe_list.rb36
-rw-r--r--lib/chef/run_lock.rb25
-rw-r--r--lib/chef/run_status.rb37
-rw-r--r--lib/chef/runner.rb77
-rw-r--r--lib/chef/scan_access_control.rb12
-rw-r--r--lib/chef/search/query.rb85
-rw-r--r--lib/chef/server_api.rb31
-rw-r--r--lib/chef/server_api_versions.rb63
-rw-r--r--lib/chef/shell.rb239
-rw-r--r--lib/chef/shell/ext.rb353
-rw-r--r--lib/chef/shell/model_wrapper.rb9
-rw-r--r--lib/chef/shell/shell_session.rb62
-rw-r--r--lib/chef/shell_out.rb13
-rw-r--r--lib/chef/tasks/chef_repo.rake200
-rw-r--r--lib/chef/train_transport.rb29
-rw-r--r--lib/chef/user.rb73
-rw-r--r--lib/chef/user_v1.rb113
-rw-r--r--lib/chef/util/backup.rb12
-rw-r--r--lib/chef/util/diff.rb54
-rw-r--r--lib/chef/util/dsc/configuration_generator.rb98
-rw-r--r--lib/chef/util/dsc/lcm_output_parser.rb82
-rw-r--r--lib/chef/util/dsc/local_configuration_manager.rb89
-rw-r--r--lib/chef/util/dsc/resource_store.rb22
-rw-r--r--lib/chef/util/editor.rb2
-rw-r--r--lib/chef/util/file_edit.rb35
-rw-r--r--lib/chef/util/path_helper.rb2
-rw-r--r--lib/chef/util/powershell/cmdlet.rb173
-rw-r--r--lib/chef/util/powershell/cmdlet_result.rb61
-rw-r--r--lib/chef/util/powershell/ps_credential.rb36
-rw-r--r--lib/chef/util/selinux.rb23
-rw-r--r--lib/chef/util/threaded_job_queue.rb6
-rw-r--r--lib/chef/util/windows/logon_session.rb129
-rw-r--r--lib/chef/util/windows/net_group.rb60
-rw-r--r--lib/chef/util/windows/net_use.rb32
-rw-r--r--lib/chef/util/windows/net_user.rb69
-rw-r--r--lib/chef/util/windows/volume.rb36
-rw-r--r--lib/chef/version.rb12
-rw-r--r--lib/chef/version/platform.rb20
-rw-r--r--lib/chef/version_class.rb6
-rw-r--r--lib/chef/version_constraint.rb14
-rw-r--r--lib/chef/version_constraint/platform.rb6
-rw-r--r--lib/chef/version_string.rb20
-rw-r--r--lib/chef/whitelist.rb86
-rw-r--r--lib/chef/win32/api.rb34
-rw-r--r--lib/chef/win32/api/command_line_helper.rb89
-rw-r--r--lib/chef/win32/api/crypto.rb26
-rw-r--r--lib/chef/win32/api/error.rb24
-rw-r--r--lib/chef/win32/api/file.rb181
-rw-r--r--lib/chef/win32/api/installer.rb20
-rw-r--r--lib/chef/win32/api/memory.rb12
-rw-r--r--lib/chef/win32/api/net.rb221
-rw-r--r--lib/chef/win32/api/process.rb8
-rw-r--r--lib/chef/win32/api/psapi.rb6
-rw-r--r--lib/chef/win32/api/registry.rb10
-rw-r--r--lib/chef/win32/api/security.rb220
-rw-r--r--lib/chef/win32/api/synchronization.rb14
-rw-r--r--lib/chef/win32/api/system.rb18
-rw-r--r--lib/chef/win32/api/unicode.rb10
-rw-r--r--lib/chef/win32/crypto.rb12
-rw-r--r--lib/chef/win32/error.rb12
-rw-r--r--lib/chef/win32/eventlog.rb10
-rw-r--r--lib/chef/win32/file.rb53
-rw-r--r--lib/chef/win32/file/info.rb5
-rw-r--r--lib/chef/win32/file/version_info.rb43
-rw-r--r--lib/chef/win32/handle.rb10
-rw-r--r--lib/chef/win32/memory.rb10
-rw-r--r--lib/chef/win32/mutex.rb12
-rw-r--r--lib/chef/win32/net.rb36
-rw-r--r--lib/chef/win32/process.rb16
-rw-r--r--lib/chef/win32/registry.rb95
-rw-r--r--lib/chef/win32/security.rb186
-rw-r--r--lib/chef/win32/security/ace.rb10
-rw-r--r--lib/chef/win32/security/acl.rb13
-rw-r--r--lib/chef/win32/security/securable_object.rb28
-rw-r--r--lib/chef/win32/security/security_descriptor.rb14
-rw-r--r--lib/chef/win32/security/sid.rb72
-rw-r--r--lib/chef/win32/security/token.rb14
-rw-r--r--[-rwxr-xr-x]lib/chef/win32/system.rb8
-rw-r--r--lib/chef/win32/unicode.rb16
-rw-r--r--lib/chef/win32/version.rb73
-rw-r--r--lib/chef/win32_service_constants.rb143
-rw-r--r--lib/chef/workstation_config_loader.rb2
-rw-r--r--omnibus/.gitignore5
-rw-r--r--omnibus/.kitchen.vmware.yml6
-rw-r--r--omnibus/.kitchen.yml137
-rw-r--r--omnibus/Berksfile8
-rw-r--r--omnibus/CHEF-EULA.md48
-rw-r--r--omnibus/Gemfile24
-rw-r--r--omnibus/Gemfile.lock552
-rw-r--r--omnibus/README.md100
-rw-r--r--omnibus/config/projects/angrychef.rb6
-rw-r--r--omnibus/config/projects/chef.rb59
-rw-r--r--omnibus/config/software/chef-appbundle.rb18
-rw-r--r--omnibus/config/software/chef-complete.rb20
-rw-r--r--omnibus/config/software/chef-gem-binding_of_caller.rb10
-rw-r--r--omnibus/config/software/chef-gem-byebug.rb10
-rw-r--r--omnibus/config/software/chef-gem-debug_inspector.rb10
-rw-r--r--omnibus/config/software/chef-gem-ffi-yajl.rb12
-rw-r--r--omnibus/config/software/chef-gem-ffi.rb12
-rw-r--r--omnibus/config/software/chef-gem-json.rb11
-rw-r--r--omnibus/config/software/chef-gem-libyajl2.rb10
-rw-r--r--omnibus/config/software/chef-gem-mini_portile2.rb10
-rw-r--r--omnibus/config/software/chef-gem-nokogiri.rb13
-rw-r--r--omnibus/config/software/chef-gem-pkg-config.rb10
-rw-r--r--omnibus/config/software/chef-gem-ruby-prof.rb10
-rw-r--r--omnibus/config/software/chef-gem-ruby-shadow.rb11
-rw-r--r--omnibus/config/software/chef-remove-docs.rb34
-rw-r--r--omnibus/config/software/chef.rb83
-rw-r--r--omnibus/config/software/more-ruby-cleanup.rb143
-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/openssl-customization/windows/ssl_env_hack.rb8
-rw-r--r--omnibus/kitchen.yml118
-rw-r--r--omnibus/omnibus-test.ps1126
-rw-r--r--omnibus/omnibus-test.sh154
-rw-r--r--omnibus/omnibus.rb9
-rwxr-xr-xomnibus/package-scripts/angrychef/postinst13
-rwxr-xr-xomnibus/package-scripts/angrychef/postrm22
-rwxr-xr-xomnibus/package-scripts/angrychef/preinst23
-rwxr-xr-xomnibus/package-scripts/chef-fips/postinst111
-rwxr-xr-xomnibus/package-scripts/chef-fips/postrm42
-rwxr-xr-xomnibus/package-scripts/chef/postinst13
-rwxr-xr-xomnibus/package-scripts/chef/postrm22
-rwxr-xr-xomnibus/package-scripts/chef/preinst23
-rw-r--r--omnibus/resources/chef/dmg/background.pngbin44066 -> 41912 bytes
-rw-r--r--omnibus/resources/chef/ips/chef-symlinks.erb6
-rw-r--r--omnibus/resources/chef/msi/assets/LICENSE.rtf133
-rw-r--r--omnibus/resources/chef/msi/localization-en-us.wxl.erb12
-rw-r--r--omnibus/resources/chef/msi/source.wxs.erb158
-rw-r--r--omnibus/resources/chef/pkg/entitlements.plist8
-rw-r--r--omnibus/resources/chef/pkg/license.html.erb443
-rw-r--r--omnibus_overrides.rb36
-rw-r--r--spec/data/client.d_00/02-strings.rb2
-rw-r--r--spec/data/cookbooks/apache2/metadata.json33
-rw-r--r--spec/data/cookbooks/irssi/files/default/irssi.response2
-rw-r--r--spec/data/cookbooks/java/metadata.json33
-rw-r--r--spec/data/cookbooks/openldap/libraries/openldap.rb2
-rw-r--r--spec/data/cookbooks/openldap/metadata.rb4
-rw-r--r--spec/data/cookbooks/openldap/templates/default/openldap_nested_variable_stuff.erb1
-rw-r--r--spec/data/cookbooks/starter/chefignore8
-rw-r--r--spec/data/cookbooks/starter/files/sample.txt1
-rw-r--r--spec/data/cookbooks/starter/metadata.rb2
-rw-r--r--spec/data/cookbooks/starter/recipes/default.rb4
-rw-r--r--spec/data/cookbooks/wget/files/default/wget.response2
-rw-r--r--spec/data/lwrp/providers/buck_passer.rb4
-rw-r--r--spec/data/lwrp/providers/buck_passer_2.rb4
-rw-r--r--spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb4
-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/mac_users/10.7-8.plist.xml559
-rw-r--r--spec/data/mac_users/10.7-8.shadow.xml11
-rw-r--r--spec/data/mac_users/10.7.plist.xml559
-rw-r--r--spec/data/mac_users/10.7.shadow.xml11
-rw-r--r--spec/data/mac_users/10.8.plist.xml559
-rw-r--r--spec/data/mac_users/10.8.shadow.xml21
-rw-r--r--spec/data/metadata/quick_start/metadata.rb9
-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/rubygems.org/latest_specs.4.8.gzbin0 -> 86 bytes
-rw-r--r--spec/data/rubygems.org/nonexistent_gembin0 -> 4 bytes
-rw-r--r--spec/data/rubygems.org/sexp_processorbin0 -> 2737 bytes
-rw-r--r--spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rzbin0 -> 519 bytes
-rw-r--r--spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file1
-rw-r--r--spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file1
-rw-r--r--spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file1
-rw-r--r--spec/data/run_context/cookbooks/dependency1/providers/unparsed_file1
-rw-r--r--spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file1
-rw-r--r--spec/data/run_context/cookbooks/dependency1/resources/unparsed_file1
-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/snap_package/async_result_success.json6
-rw-r--r--spec/data/snap_package/change_id_result.json175
-rw-r--r--spec/data/snap_package/find_result_failure.json10
-rw-r--r--spec/data/snap_package/find_result_success.json70
-rw-r--r--spec/data/snap_package/get_by_name_result_failure.json10
-rw-r--r--spec/data/snap_package/get_by_name_result_success.json38
-rw-r--r--spec/data/snap_package/get_conf_success.json10
-rw-r--r--spec/data/snap_package/result_failure.json9
-rw-r--r--spec/data/ssl/binary/chef-rspec-der.certbin0 -> 1174 bytes
-rw-r--r--spec/data/ssl/binary/chef-rspec-der.keybin0 -> 1191 bytes
-rw-r--r--spec/data/ssl/chef-rspec.cert30
-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/templates/failed.erb5
-rw-r--r--spec/data/trusted_certs/example_no_cn.crt36
-rw-r--r--spec/data/windows_certificates/base64_test.cer20
-rw-r--r--spec/data/windows_certificates/othertest.cer20
-rw-r--r--spec/data/windows_certificates/test.cer20
-rw-r--r--spec/data/windows_certificates/test.p7bbin0 -> 2619 bytes
-rw-r--r--spec/data/windows_certificates/test.pem20
-rw-r--r--spec/data/windows_certificates/test.pfxbin0 -> 2637 bytes
-rw-r--r--spec/functional/application_spec.rb2
-rwxr-xr-xspec/functional/assets/chefinittest10
-rw-r--r--spec/functional/assets/inittest37
-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/assets/zypprepo/chef_rpm-1.10-1.aarch64.rpmbin0 -> 2096 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.i686.rpmbin0 -> 2148 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64.rpmbin0 -> 2118 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64le.rpmbin0 -> 2098 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.s390x.rpmbin0 -> 2102 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.src.rpmbin0 -> 2132 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.10-1.x86_64.rpmbin0 -> 2110 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.aarch64.rpmbin0 -> 2096 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.i686.rpmbin0 -> 2149 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64.rpmbin0 -> 2119 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64le.rpmbin0 -> 2098 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.s390x.rpmbin0 -> 2102 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.src.rpmbin0 -> 2133 bytes
-rw-r--r--spec/functional/assets/zypprepo/chef_rpm-1.2-1.x86_64.rpmbin0 -> 2113 bytes
-rw-r--r--spec/functional/audit/rspec_formatter_spec.rb54
-rw-r--r--spec/functional/audit/runner_spec.rb121
-rw-r--r--spec/functional/dsl/reboot_pending_spec.rb38
-rw-r--r--spec/functional/dsl/registry_helper_spec.rb10
-rw-r--r--spec/functional/event_loggers/windows_eventlog_spec.rb25
-rw-r--r--spec/functional/file_content_management/deploy_strategies_spec.rb50
-rw-r--r--spec/functional/http/simple_spec.rb44
-rw-r--r--spec/functional/knife/configure_spec.rb4
-rw-r--r--spec/functional/knife/cookbook_delete_spec.rb109
-rw-r--r--spec/functional/knife/exec_spec.rb6
-rw-r--r--spec/functional/knife/rehash_spec.rb2
-rw-r--r--spec/functional/knife/smoke_test.rb2
-rw-r--r--spec/functional/knife/ssh_spec.rb146
-rw-r--r--spec/functional/mixin/from_file_spec.rb93
-rw-r--r--spec/functional/mixin/powershell_out_spec.rb16
-rw-r--r--spec/functional/mixin/shell_out_spec.rb12
-rw-r--r--spec/functional/mixin/user_context_spec.rb119
-rw-r--r--spec/functional/notifications_spec.rb2
-rwxr-xr-xspec/functional/provider/remote_file/cache_control_data_spec.rb2
-rw-r--r--spec/functional/provider/whyrun_safe_ruby_block_spec.rb2
-rw-r--r--spec/functional/rebooter_spec.rb40
-rwxr-xr-xspec/functional/resource/aix_service_spec.rb19
-rwxr-xr-xspec/functional/resource/aixinit_service_spec.rb25
-rw-r--r--spec/functional/resource/apt_package_spec.rb383
-rw-r--r--spec/functional/resource/base.rb28
-rw-r--r--spec/functional/resource/bash_spec.rb63
-rw-r--r--spec/functional/resource/batch_spec.rb8
-rw-r--r--spec/functional/resource/bff_spec.rb14
-rw-r--r--spec/functional/resource/chocolatey_package_spec.rb70
-rw-r--r--spec/functional/resource/cookbook_file_spec.rb10
-rw-r--r--spec/functional/resource/cron_spec.rb72
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb881
-rw-r--r--spec/functional/resource/directory_spec.rb2
-rw-r--r--spec/functional/resource/dnf_package_spec.rb1277
-rw-r--r--spec/functional/resource/dpkg_package_spec.rb10
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb13
-rw-r--r--spec/functional/resource/dsc_script_spec.rb247
-rwxr-xr-xspec/functional/resource/env_spec.rb192
-rw-r--r--spec/functional/resource/execute_spec.rb33
-rw-r--r--spec/functional/resource/file_spec.rb4
-rw-r--r--spec/functional/resource/git_spec.rb344
-rw-r--r--spec/functional/resource/group_spec.rb151
-rw-r--r--spec/functional/resource/ifconfig_spec.rb33
-rw-r--r--spec/functional/resource/insserv_spec.rb206
-rw-r--r--spec/functional/resource/launchd_spec.rb232
-rw-r--r--spec/functional/resource/link_spec.rb132
-rw-r--r--spec/functional/resource/locale_spec.rb108
-rw-r--r--spec/functional/resource/mount_spec.rb44
-rw-r--r--spec/functional/resource/msu_package_spec.rb107
-rw-r--r--spec/functional/resource/ohai_spec.rb2
-rw-r--r--spec/functional/resource/package_spec.rb386
-rw-r--r--spec/functional/resource/powershell_package_source_spec.rb107
-rw-r--r--spec/functional/resource/powershell_script_spec.rb207
-rw-r--r--spec/functional/resource/reboot_spec.rb8
-rw-r--r--spec/functional/resource/registry_spec.rb276
-rw-r--r--spec/functional/resource/remote_directory_spec.rb2
-rw-r--r--spec/functional/resource/remote_file_spec.rb215
-rw-r--r--spec/functional/resource/rpm_spec.rb27
-rw-r--r--spec/functional/resource/template_spec.rb43
-rw-r--r--spec/functional/resource/timezone_spec.rb41
-rw-r--r--spec/functional/resource/user/dscl_spec.rb24
-rw-r--r--spec/functional/resource/user/mac_user_spec.rb207
-rw-r--r--spec/functional/resource/user/useradd_spec.rb698
-rw-r--r--spec/functional/resource/user/windows_spec.rb150
-rw-r--r--spec/functional/resource/windows_certificate_spec.rb316
-rw-r--r--spec/functional/resource/windows_env_spec.rb285
-rw-r--r--spec/functional/resource/windows_firewall_rule_spec.rb93
-rw-r--r--spec/functional/resource/windows_font_spec.rb49
-rw-r--r--spec/functional/resource/windows_package_spec.rb49
-rw-r--r--spec/functional/resource/windows_path_spec.rb68
-rw-r--r--spec/functional/resource/windows_security_policy_spec.rb86
-rw-r--r--spec/functional/resource/windows_service_spec.rb15
-rw-r--r--spec/functional/resource/windows_share_spec.rb103
-rw-r--r--spec/functional/resource/windows_task_spec.rb1969
-rw-r--r--spec/functional/resource/windows_user_privilege_spec.rb192
-rw-r--r--spec/functional/resource/yum_package_spec.rb981
-rw-r--r--spec/functional/resource/zypper_package_spec.rb247
-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.rb72
-rw-r--r--spec/functional/shell_spec.rb86
-rw-r--r--spec/functional/tiny_server_spec.rb2
-rw-r--r--spec/functional/util/path_helper_spec.rb2
-rw-r--r--spec/functional/util/powershell/cmdlet_spec.rb111
-rw-r--r--spec/functional/version_spec.rb13
-rw-r--r--spec/functional/win32/crypto_spec.rb26
-rw-r--r--spec/functional/win32/registry_spec.rb158
-rw-r--r--spec/functional/win32/security_spec.rb161
-rw-r--r--spec/functional/win32/service_manager_spec.rb10
-rw-r--r--spec/functional/win32/sid_spec.rb4
-rw-r--r--spec/functional/win32/version_info_spec.rb12
-rw-r--r--spec/functional/win32/versions_spec.rb22
-rw-r--r--spec/integration/client/client_spec.rb860
-rw-r--r--spec/integration/client/exit_code_spec.rb165
-rw-r--r--spec/integration/client/ipv6_spec.rb85
-rw-r--r--spec/integration/compliance/compliance_spec.rb81
-rw-r--r--spec/integration/knife/chef_fs_data_store_spec.rb331
-rw-r--r--spec/integration/knife/chef_repo_path_spec.rb925
-rw-r--r--spec/integration/knife/chef_repository_file_system_spec.rb188
-rw-r--r--spec/integration/knife/chefignore_spec.rb205
-rw-r--r--spec/integration/knife/client_bulk_delete_spec.rb129
-rw-r--r--spec/integration/knife/client_create_spec.rb17
-rw-r--r--spec/integration/knife/client_delete_spec.rb39
-rw-r--r--spec/integration/knife/client_key_create_spec.rb7
-rw-r--r--spec/integration/knife/client_key_delete_spec.rb9
-rw-r--r--spec/integration/knife/client_key_list_spec.rb13
-rw-r--r--spec/integration/knife/client_key_show_spec.rb3
-rw-r--r--spec/integration/knife/client_list_spec.rb21
-rw-r--r--spec/integration/knife/client_show_spec.rb3
-rw-r--r--spec/integration/knife/common_options_spec.rb125
-rw-r--r--spec/integration/knife/config_list_spec.rb220
-rw-r--r--spec/integration/knife/config_show_spec.rb192
-rw-r--r--spec/integration/knife/config_use_spec.rb198
-rw-r--r--spec/integration/knife/cookbook_api_ipv6_spec.rb85
-rw-r--r--spec/integration/knife/cookbook_bulk_delete_spec.rb41
-rw-r--r--spec/integration/knife/cookbook_download_spec.rb61
-rw-r--r--spec/integration/knife/cookbook_list_spec.rb23
-rw-r--r--spec/integration/knife/cookbook_show_spec.rb188
-rw-r--r--spec/integration/knife/cookbook_upload_spec.rb102
-rw-r--r--spec/integration/knife/data_bag_create_spec.rb101
-rw-r--r--spec/integration/knife/data_bag_delete_spec.rb27
-rw-r--r--spec/integration/knife/data_bag_edit_spec.rb105
-rw-r--r--spec/integration/knife/data_bag_from_file_spec.rb113
-rw-r--r--spec/integration/knife/data_bag_list_spec.rb13
-rw-r--r--spec/integration/knife/data_bag_show_spec.rb74
-rw-r--r--spec/integration/knife/delete_spec.rb1247
-rw-r--r--spec/integration/knife/deps_spec.rb435
-rw-r--r--spec/integration/knife/diff_spec.rb285
-rw-r--r--spec/integration/knife/download_spec.rb1183
-rw-r--r--spec/integration/knife/environment_compare_spec.rb43
-rw-r--r--spec/integration/knife/environment_create_spec.rb5
-rw-r--r--spec/integration/knife/environment_delete_spec.rb3
-rw-r--r--spec/integration/knife/environment_from_file_spec.rb139
-rw-r--r--spec/integration/knife/environment_list_spec.rb13
-rw-r--r--spec/integration/knife/environment_show_spec.rb59
-rw-r--r--spec/integration/knife/list_spec.rb1515
-rw-r--r--spec/integration/knife/node_bulk_delete_spec.rb23
-rw-r--r--spec/integration/knife/node_create_spec.rb7
-rw-r--r--spec/integration/knife/node_delete_spec.rb19
-rw-r--r--spec/integration/knife/node_environment_set_spec.rb10
-rw-r--r--spec/integration/knife/node_from_file_spec.rb41
-rw-r--r--spec/integration/knife/node_list_spec.rb15
-rw-r--r--spec/integration/knife/node_run_list_add_spec.rb11
-rw-r--r--spec/integration/knife/node_run_list_remove_spec.rb5
-rw-r--r--spec/integration/knife/node_run_list_set_spec.rb5
-rw-r--r--spec/integration/knife/node_show_spec.rb5
-rw-r--r--spec/integration/knife/raw_spec.rb349
-rw-r--r--spec/integration/knife/redirection_spec.rb34
-rw-r--r--spec/integration/knife/role_bulk_delete_spec.rb23
-rw-r--r--spec/integration/knife/role_create_spec.rb5
-rw-r--r--spec/integration/knife/role_delete_spec.rb19
-rw-r--r--spec/integration/knife/role_from_file_spec.rb99
-rw-r--r--spec/integration/knife/role_list_spec.rb15
-rw-r--r--spec/integration/knife/role_show_spec.rb27
-rw-r--r--spec/integration/knife/search_node_spec.rb40
-rw-r--r--spec/integration/knife/serve_spec.rb85
-rw-r--r--spec/integration/knife/show_spec.rb166
-rw-r--r--spec/integration/knife/upload_spec.rb1071
-rw-r--r--spec/integration/ohai/ohai_spec.rb61
-rw-r--r--spec/integration/recipes/accumulator_spec.rb233
-rw-r--r--spec/integration/recipes/lwrp_inline_resources_spec.rb57
-rw-r--r--spec/integration/recipes/lwrp_spec.rb37
-rw-r--r--spec/integration/recipes/noop_resource_spec.rb4
-rw-r--r--spec/integration/recipes/notifies_spec.rb486
-rw-r--r--spec/integration/recipes/notifying_block_spec.rb28
-rw-r--r--spec/integration/recipes/provider_choice.rb9
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb617
-rw-r--r--spec/integration/recipes/remote_directory.rb8
-rw-r--r--spec/integration/recipes/resource_action_spec.rb225
-rw-r--r--spec/integration/recipes/resource_converge_if_changed_spec.rb290
-rw-r--r--spec/integration/recipes/resource_load_spec.rb193
-rw-r--r--spec/integration/recipes/unified_mode_spec.rb877
-rw-r--r--spec/integration/recipes/use_partial_spec.rb112
-rw-r--r--spec/integration/solo/solo_spec.rb154
-rw-r--r--spec/scripts/ssl-serve.rb47
-rw-r--r--spec/spec_helper.rb258
-rw-r--r--spec/stress/win32/file_spec.rb8
-rw-r--r--spec/stress/win32/memory_spec.rb2
-rw-r--r--spec/stress/win32/security_spec.rb8
-rw-r--r--spec/support/chef_helpers.rb38
-rw-r--r--spec/support/key_helpers.rb4
-rw-r--r--spec/support/lib/chef/provider/easy.rb2
-rw-r--r--spec/support/lib/chef/provider/openldap_includer.rb2
-rw-r--r--spec/support/lib/chef/provider/snakeoil.rb3
-rw-r--r--spec/support/lib/chef/resource/cat.rb5
-rw-r--r--spec/support/lib/chef/resource/one_two_three_four.rb5
-rw-r--r--spec/support/lib/chef/resource/openldap_includer.rb4
-rw-r--r--spec/support/lib/chef/resource/with_state.rb4
-rw-r--r--spec/support/lib/chef/resource/zen_follower.rb6
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb7
-rw-r--r--spec/support/matchers/leak.rb8
-rw-r--r--spec/support/mock/constant.rb52
-rw-r--r--spec/support/mock/platform.rb38
-rw-r--r--spec/support/platform_helpers.rb150
-rw-r--r--spec/support/platforms/prof/gc.rb14
-rw-r--r--spec/support/platforms/prof/win32.rb2
-rw-r--r--spec/support/platforms/win32/spec_service.rb56
-rw-r--r--spec/support/recipe_dsl_helper.rb83
-rw-r--r--spec/support/shared/context/client.rb286
-rw-r--r--spec/support/shared/context/config.rb3
-rw-r--r--spec/support/shared/context/win32.rb2
-rw-r--r--spec/support/shared/examples/client.rb53
-rw-r--r--spec/support/shared/functional/directory_resource.rb26
-rw-r--r--spec/support/shared/functional/execute_resource.rb150
-rw-r--r--spec/support/shared/functional/file_resource.rb63
-rw-r--r--spec/support/shared/functional/http.rb70
-rw-r--r--spec/support/shared/functional/knife.rb2
-rw-r--r--spec/support/shared/functional/securable_resource.rb349
-rw-r--r--spec/support/shared/functional/securable_resource_with_reporting.rb13
-rw-r--r--spec/support/shared/functional/win32_service.rb14
-rw-r--r--spec/support/shared/functional/windows_script.rb108
-rw-r--r--spec/support/shared/integration/app_server_support.rb40
-rw-r--r--spec/support/shared/integration/integration_helper.rb77
-rw-r--r--spec/support/shared/integration/knife_support.rb56
-rw-r--r--spec/support/shared/unit/api_error_inspector.rb24
-rw-r--r--spec/support/shared/unit/api_versioning.rb10
-rw-r--r--spec/support/shared/unit/application_dot_d.rb19
-rw-r--r--spec/support/shared/unit/execute_resource.rb61
-rw-r--r--spec/support/shared/unit/file_system_support.rb7
-rw-r--r--spec/support/shared/unit/knife_shared.rb4
-rw-r--r--spec/support/shared/unit/mock_shellout.rb2
-rw-r--r--spec/support/shared/unit/platform_introspector.rb26
-rw-r--r--spec/support/shared/unit/provider/file.rb78
-rw-r--r--spec/support/shared/unit/provider/package/package_shared.rb95
-rw-r--r--spec/support/shared/unit/provider/useradd_based_user_provider.rb108
-rw-r--r--spec/support/shared/unit/resource/static_provider_resolution.rb2
-rw-r--r--spec/support/shared/unit/script_resource.rb52
-rw-r--r--spec/support/shared/unit/user_and_client_shared.rb10
-rw-r--r--spec/support/shared/unit/windows_script_resource.rb49
-rw-r--r--spec/tiny_server.rb20
-rw-r--r--spec/unit/action_collection_spec.rb19
-rw-r--r--spec/unit/api_client/registration_spec.rb66
-rw-r--r--spec/unit/api_client_spec.rb28
-rw-r--r--spec/unit/api_client_v1_spec.rb32
-rw-r--r--spec/unit/application/apply_spec.rb11
-rw-r--r--spec/unit/application/client_spec.rb274
-rw-r--r--spec/unit/application/exit_code_spec.rb132
-rw-r--r--spec/unit/application/knife_spec.rb32
-rw-r--r--spec/unit/application/solo_spec.rb40
-rw-r--r--spec/unit/application_spec.rb270
-rw-r--r--spec/unit/audit/audit_event_proxy_spec.rb318
-rw-r--r--spec/unit/audit/audit_reporter_spec.rb439
-rw-r--r--spec/unit/audit/control_group_data_spec.rb482
-rw-r--r--spec/unit/audit/logger_spec.rb42
-rw-r--r--spec/unit/audit/rspec_formatter_spec.rb29
-rw-r--r--spec/unit/audit/runner_spec.rb144
-rw-r--r--spec/unit/chef_class_spec.rb132
-rw-r--r--spec/unit/chef_fs/config_spec.rb14
-rw-r--r--spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb82
-rw-r--r--spec/unit/chef_fs/data_handler/data_handler_base_spec.rb65
-rw-r--r--spec/unit/chef_fs/data_handler/group_handler_spec.rb2
-rw-r--r--spec/unit/chef_fs/diff_spec.rb108
-rw-r--r--spec/unit/chef_fs/file_pattern_spec.rb6
-rw-r--r--spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb2
-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.rb2
-rw-r--r--spec/unit/chef_fs/file_system/repository/directory_spec.rb10
-rw-r--r--spec/unit/chef_fs/file_system_spec.rb20
-rw-r--r--spec/unit/chef_fs/parallelizer.rb477
-rw-r--r--spec/unit/chef_fs/parallelizer_spec.rb479
-rw-r--r--spec/unit/chef_fs/path_util_spec.rb8
-rw-r--r--spec/unit/chef_spec.rb2
-rw-r--r--spec/unit/client_spec.rb501
-rw-r--r--spec/unit/compliance/fetcher/automate_spec.rb134
-rw-r--r--spec/unit/compliance/fetcher/chef_server_spec.rb93
-rw-r--r--spec/unit/compliance/reporter/automate_spec.rb427
-rw-r--r--spec/unit/compliance/reporter/chef_server_automate_spec.rb177
-rw-r--r--spec/unit/compliance/reporter/compliance_enforcer_spec.rb48
-rw-r--r--spec/unit/compliance/runner_spec.rb168
-rw-r--r--spec/unit/config_fetcher_spec.rb48
-rw-r--r--spec/unit/config_spec.rb4
-rw-r--r--spec/unit/cookbook/chefignore_spec.rb44
-rw-r--r--spec/unit/cookbook/cookbook_version_loader_spec.rb67
-rw-r--r--spec/unit/cookbook/file_vendor_spec.rb42
-rw-r--r--spec/unit/cookbook/gem_installer_spec.rb114
-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.rb542
-rw-r--r--spec/unit/cookbook/synchronizer_spec.rb355
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb15
-rw-r--r--spec/unit/cookbook_loader_spec.rb126
-rw-r--r--spec/unit/cookbook_manifest_spec.rb97
-rw-r--r--spec/unit/cookbook_site_streaming_uploader_spec.rb19
-rw-r--r--spec/unit/cookbook_spec.rb28
-rw-r--r--spec/unit/cookbook_uploader_spec.rb32
-rw-r--r--spec/unit/cookbook_version_file_specificity_spec.rb285
-rw-r--r--spec/unit/cookbook_version_spec.rb153
-rw-r--r--spec/unit/daemon_spec.rb41
-rw-r--r--spec/unit/data_bag_item_spec.rb24
-rw-r--r--spec/unit/data_bag_spec.rb19
-rw-r--r--spec/unit/data_collector/config_validation_spec.rb208
-rw-r--r--spec/unit/data_collector/messages/helpers_spec.rb186
-rw-r--r--spec/unit/data_collector/messages_spec.rb190
-rw-r--r--spec/unit/data_collector_spec.rb1163
-rw-r--r--spec/unit/decorator/lazy_array_spec.rb2
-rw-r--r--spec/unit/decorator/lazy_spec.rb4
-rw-r--r--spec/unit/decorator_spec.rb48
-rw-r--r--spec/unit/deprecated_spec.rb65
-rw-r--r--spec/unit/deprecation_spec.rb25
-rw-r--r--spec/unit/digester_spec.rb2
-rw-r--r--spec/unit/dsl/audit_spec.rb43
-rw-r--r--spec/unit/dsl/data_query_spec.rb7
-rw-r--r--spec/unit/dsl/declare_resource_spec.rb13
-rw-r--r--spec/unit/dsl/platform_introspection_spec.rb67
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb17
-rw-r--r--spec/unit/dsl/recipe_spec.rb19
-rw-r--r--spec/unit/dsl/registry_helper_spec.rb4
-rw-r--r--spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb7
-rw-r--r--spec/unit/encrypted_data_bag_item_spec.rb21
-rw-r--r--spec/unit/environment_spec.rb88
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb58
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb2
-rw-r--r--spec/unit/exceptions_spec.rb6
-rw-r--r--spec/unit/file_access_control_spec.rb22
-rw-r--r--spec/unit/file_cache_spec.rb2
-rw-r--r--spec/unit/file_content_management/deploy/cp_spec.rb2
-rw-r--r--spec/unit/file_content_management/deploy/mv_unix_spec.rb22
-rw-r--r--spec/unit/file_content_management/deploy/mv_windows_spec.rb58
-rw-r--r--spec/unit/file_content_management/tempfile_spec.rb4
-rw-r--r--spec/unit/formatters/base_spec.rb43
-rw-r--r--spec/unit/formatters/doc_spec.rb20
-rw-r--r--spec/unit/formatters/error_description_spec.rb89
-rw-r--r--spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb2
-rw-r--r--spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb34
-rw-r--r--spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb14
-rw-r--r--spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb4
-rw-r--r--spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb4
-rw-r--r--spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb4
-rw-r--r--spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb10
-rw-r--r--spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb16
-rw-r--r--spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb28
-rw-r--r--spec/unit/guard_interpreter_spec.rb4
-rw-r--r--spec/unit/handler/json_file_spec.rb4
-rw-r--r--spec/unit/handler_spec.rb6
-rw-r--r--spec/unit/http/api_versions_spec.rb100
-rw-r--r--spec/unit/http/authenticator_spec.rb78
-rw-r--r--spec/unit/http/http_request_spec.rb8
-rw-r--r--spec/unit/http/json_input_spec.rb4
-rw-r--r--spec/unit/http/simple_spec.rb2
-rw-r--r--spec/unit/http/socketless_chef_zero_client_spec.rb35
-rw-r--r--spec/unit/http/ssl_policies_spec.rb193
-rw-r--r--spec/unit/http/validate_content_length_spec.rb16
-rw-r--r--spec/unit/http_spec.rb15
-rw-r--r--spec/unit/json_compat_spec.rb36
-rw-r--r--spec/unit/key_spec.rb44
-rw-r--r--spec/unit/knife/bootstrap/chef_vault_handler_spec.rb32
-rw-r--r--spec/unit/knife/bootstrap/client_builder_spec.rb26
-rw-r--r--spec/unit/knife/bootstrap/train_connector_spec.rb244
-rw-r--r--spec/unit/knife/bootstrap_spec.rb1823
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb12
-rw-r--r--spec/unit/knife/client_create_spec.rb25
-rw-r--r--spec/unit/knife/client_delete_spec.rb18
-rw-r--r--spec/unit/knife/client_reregister_spec.rb2
-rw-r--r--spec/unit/knife/configure_client_spec.rb22
-rw-r--r--spec/unit/knife/configure_spec.rb77
-rw-r--r--spec/unit/knife/cookbook_bulk_delete_spec.rb6
-rw-r--r--spec/unit/knife/cookbook_create_spec.rb261
-rw-r--r--spec/unit/knife/cookbook_delete_spec.rb20
-rw-r--r--spec/unit/knife/cookbook_download_spec.rb124
-rw-r--r--spec/unit/knife/cookbook_list_spec.rb18
-rw-r--r--spec/unit/knife/cookbook_metadata_from_file_spec.rb13
-rw-r--r--spec/unit/knife/cookbook_metadata_spec.rb229
-rw-r--r--spec/unit/knife/cookbook_show_spec.rb106
-rw-r--r--spec/unit/knife/cookbook_site_download_spec.rb150
-rw-r--r--spec/unit/knife/cookbook_site_install_spec.rb200
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb209
-rw-r--r--spec/unit/knife/cookbook_site_unshare_spec.rb77
-rw-r--r--spec/unit/knife/cookbook_test_spec.rb84
-rw-r--r--spec/unit/knife/cookbook_upload_spec.rb157
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb214
-rw-r--r--spec/unit/knife/core/cookbook_scm_repo_spec.rb48
-rw-r--r--spec/unit/knife/core/custom_manifest_loader_spec.rb41
-rw-r--r--spec/unit/knife/core/gem_glob_loader_spec.rb20
-rw-r--r--spec/unit/knife/core/hashed_command_loader_spec.rb18
-rw-r--r--spec/unit/knife/core/node_editor_spec.rb38
-rw-r--r--spec/unit/knife/core/object_loader_spec.rb4
-rw-r--r--spec/unit/knife/core/status_presenter_spec.rb54
-rw-r--r--spec/unit/knife/core/subcommand_loader_spec.rb10
-rw-r--r--spec/unit/knife/core/ui_spec.rb214
-rw-r--r--spec/unit/knife/core/windows_bootstrap_context_spec.rb238
-rw-r--r--spec/unit/knife/data_bag_create_spec.rb150
-rw-r--r--spec/unit/knife/data_bag_edit_spec.rb9
-rw-r--r--spec/unit/knife/data_bag_from_file_spec.rb7
-rw-r--r--spec/unit/knife/data_bag_secret_options_spec.rb38
-rw-r--r--spec/unit/knife/data_bag_show_spec.rb23
-rw-r--r--spec/unit/knife/environment_compare_spec.rb12
-rw-r--r--spec/unit/knife/environment_create_spec.rb2
-rw-r--r--spec/unit/knife/environment_delete_spec.rb2
-rw-r--r--spec/unit/knife/environment_edit_spec.rb4
-rw-r--r--spec/unit/knife/environment_from_file_spec.rb6
-rw-r--r--spec/unit/knife/environment_list_spec.rb2
-rw-r--r--spec/unit/knife/environment_show_spec.rb2
-rw-r--r--spec/unit/knife/index_rebuild_spec.rb125
-rw-r--r--spec/unit/knife/key_create_spec.rb12
-rw-r--r--spec/unit/knife/key_delete_spec.rb14
-rw-r--r--spec/unit/knife/key_edit_spec.rb16
-rw-r--r--spec/unit/knife/key_helper.rb2
-rw-r--r--spec/unit/knife/key_list_spec.rb10
-rw-r--r--spec/unit/knife/key_show_spec.rb14
-rw-r--r--spec/unit/knife/knife_help.rb92
-rw-r--r--spec/unit/knife/node_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/node_delete_spec.rb33
-rw-r--r--spec/unit/knife/node_edit_spec.rb22
-rw-r--r--spec/unit/knife/node_environment_set_spec.rb7
-rw-r--r--spec/unit/knife/node_from_file_spec.rb6
-rw-r--r--spec/unit/knife/node_list_spec.rb2
-rw-r--r--spec/unit/knife/node_policy_set_spec.rb122
-rw-r--r--spec/unit/knife/node_run_list_add_spec.rb6
-rw-r--r--spec/unit/knife/node_run_list_remove_spec.rb4
-rw-r--r--spec/unit/knife/node_run_list_set_spec.rb2
-rw-r--r--spec/unit/knife/node_show_spec.rb4
-rw-r--r--spec/unit/knife/osc_user_create_spec.rb93
-rw-r--r--spec/unit/knife/osc_user_delete_spec.rb44
-rw-r--r--spec/unit/knife/osc_user_edit_spec.rb52
-rw-r--r--spec/unit/knife/osc_user_list_spec.rb37
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb58
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb46
-rw-r--r--spec/unit/knife/raw_spec.rb6
-rw-r--r--spec/unit/knife/role_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/role_create_spec.rb6
-rw-r--r--spec/unit/knife/role_delete_spec.rb6
-rw-r--r--spec/unit/knife/role_edit_spec.rb4
-rw-r--r--spec/unit/knife/role_env_run_list_add_spec.rb24
-rw-r--r--spec/unit/knife/role_env_run_list_clear_spec.rb14
-rw-r--r--spec/unit/knife/role_env_run_list_remove_spec.rb14
-rw-r--r--spec/unit/knife/role_env_run_list_replace_spec.rb14
-rw-r--r--spec/unit/knife/role_env_run_list_set_spec.rb14
-rw-r--r--spec/unit/knife/role_from_file_spec.rb6
-rw-r--r--spec/unit/knife/role_list_spec.rb2
-rw-r--r--spec/unit/knife/role_run_list_add_spec.rb24
-rw-r--r--spec/unit/knife/role_run_list_clear_spec.rb14
-rw-r--r--spec/unit/knife/role_run_list_remove_spec.rb14
-rw-r--r--spec/unit/knife/role_run_list_replace_spec.rb14
-rw-r--r--spec/unit/knife/role_run_list_set_spec.rb14
-rw-r--r--spec/unit/knife/ssh_spec.rb397
-rw-r--r--spec/unit/knife/ssl_check_spec.rb56
-rw-r--r--spec/unit/knife/ssl_fetch_spec.rb74
-rw-r--r--spec/unit/knife/status_spec.rb11
-rw-r--r--spec/unit/knife/supermarket_download_spec.rb152
-rw-r--r--spec/unit/knife/supermarket_install_spec.rb202
-rw-r--r--spec/unit/knife/supermarket_list_spec.rb70
-rw-r--r--spec/unit/knife/supermarket_search_spec.rb85
-rw-r--r--spec/unit/knife/supermarket_share_spec.rb209
-rw-r--r--spec/unit/knife/supermarket_unshare_spec.rb78
-rw-r--r--spec/unit/knife/tag_create_spec.rb2
-rw-r--r--spec/unit/knife/tag_delete_spec.rb2
-rw-r--r--spec/unit/knife/user_create_spec.rb34
-rw-r--r--spec/unit/knife/user_delete_spec.rb23
-rw-r--r--spec/unit/knife/user_edit_spec.rb20
-rw-r--r--spec/unit/knife/user_list_spec.rb2
-rw-r--r--spec/unit/knife/user_reregister_spec.rb22
-rw-r--r--spec/unit/knife/user_show_spec.rb21
-rw-r--r--spec/unit/knife_spec.rb266
-rw-r--r--spec/unit/lib_backcompat_spec.rb34
-rw-r--r--spec/unit/log/syslog_spec.rb26
-rw-r--r--spec/unit/log/winevt_spec.rb37
-rw-r--r--spec/unit/log_spec.rb24
-rw-r--r--spec/unit/lwrp_spec.rb249
-rw-r--r--spec/unit/mash_spec.rb51
-rw-r--r--spec/unit/mixin/api_version_request_handling_spec.rb38
-rw-r--r--spec/unit/mixin/checksum_spec.rb18
-rw-r--r--spec/unit/mixin/command_spec.rb107
-rw-r--r--spec/unit/mixin/deep_merge_spec.rb15
-rw-r--r--spec/unit/mixin/default_paths_spec.rb92
-rw-r--r--spec/unit/mixin/deprecation_spec.rb2
-rw-r--r--spec/unit/mixin/enforce_ownership_and_permissions_spec.rb18
-rw-r--r--spec/unit/mixin/homebrew_user_spec.rb10
-rw-r--r--spec/unit/mixin/lazy_module_include.rb2
-rw-r--r--spec/unit/mixin/openssl_helper_spec.rb892
-rw-r--r--spec/unit/mixin/params_validate_spec.rb201
-rw-r--r--spec/unit/mixin/path_sanity_spec.rb92
-rw-r--r--spec/unit/mixin/powershell_exec_spec.rb90
-rw-r--r--spec/unit/mixin/powershell_out_spec.rb48
-rw-r--r--spec/unit/mixin/powershell_type_coercions_spec.rb17
-rw-r--r--spec/unit/mixin/properties_spec.rb18
-rw-r--r--spec/unit/mixin/proxified_socket_spec.rb2
-rw-r--r--spec/unit/mixin/securable_spec.rb61
-rw-r--r--spec/unit/mixin/shell_out_spec.rb419
-rw-r--r--spec/unit/mixin/subclass_directive_spec.rb6
-rw-r--r--spec/unit/mixin/template_spec.rb115
-rw-r--r--spec/unit/mixin/unformatter_spec.rb9
-rw-r--r--spec/unit/mixin/uris_spec.rb4
-rw-r--r--spec/unit/mixin/user_context_spec.rb99
-rw-r--r--spec/unit/mixin/versioned_api_spec.rb128
-rw-r--r--spec/unit/mixin/which.rb170
-rw-r--r--spec/unit/mixin/windows_architecture_helper_spec.rb16
-rw-r--r--spec/unit/mixin/xml_escape_spec.rb4
-rw-r--r--spec/unit/monkey_patches/uri_spec.rb34
-rw-r--r--spec/unit/monologger_spec.rb2
-rw-r--r--spec/unit/node/attribute_spec.rb407
-rw-r--r--spec/unit/node/immutable_collections_spec.rb249
-rw-r--r--spec/unit/node/vivid_mash_spec.rb255
-rw-r--r--spec/unit/node_map_spec.rb162
-rw-r--r--spec/unit/node_spec.rb610
-rw-r--r--spec/unit/org_spec.rb12
-rw-r--r--spec/unit/platform/query_helpers_spec.rb183
-rw-r--r--spec/unit/platform_spec.rb241
-rw-r--r--spec/unit/policy_builder/dynamic_spec.rb14
-rw-r--r--spec/unit/policy_builder/expand_node_object_spec.rb41
-rw-r--r--spec/unit/policy_builder/policyfile_spec.rb199
-rw-r--r--spec/unit/policy_builder_spec.rb2
-rw-r--r--spec/unit/property/state_spec.rb70
-rw-r--r--spec/unit/property/validation_spec.rb231
-rw-r--r--spec/unit/property_spec.rb383
-rw-r--r--spec/unit/provider/apt_preference_spec.rb91
-rw-r--r--spec/unit/provider/apt_repository_spec.rb222
-rw-r--r--spec/unit/provider/apt_update_spec.rb37
-rw-r--r--spec/unit/provider/batch_spec.rb130
-rw-r--r--spec/unit/provider/breakpoint_spec.rb53
-rw-r--r--spec/unit/provider/cookbook_file/content_spec.rb4
-rw-r--r--spec/unit/provider/cookbook_file_spec.rb9
-rw-r--r--spec/unit/provider/cron/unix_spec.rb37
-rw-r--r--spec/unit/provider/cron_spec.rb977
-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.rb31
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb219
-rw-r--r--spec/unit/provider/dsc_script_spec.rb26
-rw-r--r--spec/unit/provider/env/windows_spec.rb103
-rw-r--r--spec/unit/provider/env_spec.rb310
-rw-r--r--spec/unit/provider/erl_call_spec.rb85
-rw-r--r--spec/unit/provider/execute_spec.rb59
-rw-r--r--spec/unit/provider/file/content_spec.rb8
-rw-r--r--spec/unit/provider/file_spec.rb10
-rw-r--r--spec/unit/provider/git_spec.rb373
-rw-r--r--spec/unit/provider/group/dscl_spec.rb124
-rw-r--r--spec/unit/provider/group/gpasswd_spec.rb33
-rw-r--r--spec/unit/provider/group/groupadd_spec.rb236
-rw-r--r--spec/unit/provider/group/groupmod_spec.rb45
-rw-r--r--spec/unit/provider/group/pw_spec.rb37
-rw-r--r--spec/unit/provider/group/solaris_spec.rb106
-rw-r--r--spec/unit/provider/group/suse_spec.rb90
-rw-r--r--spec/unit/provider/group/usermod_spec.rb43
-rw-r--r--spec/unit/provider/group/windows_spec.rb27
-rw-r--r--spec/unit/provider/group_spec.rb37
-rw-r--r--spec/unit/provider/http_request_spec.rb4
-rw-r--r--spec/unit/provider/ifconfig/aix_spec.rb60
-rw-r--r--spec/unit/provider/ifconfig/debian_spec.rb68
-rw-r--r--spec/unit/provider/ifconfig/redhat_spec.rb18
-rw-r--r--spec/unit/provider/ifconfig_spec.rb105
-rw-r--r--spec/unit/provider/launchd_spec.rb178
-rw-r--r--spec/unit/provider/link_spec.rb68
-rw-r--r--spec/unit/provider/log_spec.rb39
-rw-r--r--spec/unit/provider/mdadm_spec.rb16
-rw-r--r--spec/unit/provider/mount/aix_spec.rb146
-rw-r--r--spec/unit/provider/mount/linux_spec.rb107
-rw-r--r--spec/unit/provider/mount/mount_spec.rb220
-rw-r--r--spec/unit/provider/mount/solaris_spec.rb200
-rw-r--r--spec/unit/provider/mount/windows_spec.rb13
-rw-r--r--spec/unit/provider/mount_spec.rb40
-rw-r--r--spec/unit/provider/ohai_spec.rb84
-rw-r--r--spec/unit/provider/osx_profile_spec.rb255
-rw-r--r--spec/unit/provider/package/aix_spec.rb185
-rw-r--r--spec/unit/provider/package/apt_spec.rb979
-rw-r--r--spec/unit/provider/package/bff_spec.rb187
-rw-r--r--spec/unit/provider/package/cab_spec.rb272
-rw-r--r--spec/unit/provider/package/chocolatey_spec.rb160
-rw-r--r--spec/unit/provider/package/deb_spec.rb135
-rw-r--r--spec/unit/provider/package/dnf/python_helper_spec.rb29
-rw-r--r--spec/unit/provider/package/dpkg_spec.rb111
-rw-r--r--spec/unit/provider/package/easy_install_spec.rb114
-rw-r--r--spec/unit/provider/package/freebsd/pkg_spec.rb274
-rw-r--r--spec/unit/provider/package/freebsd/pkgng_spec.rb45
-rw-r--r--spec/unit/provider/package/freebsd/port_spec.rb50
-rw-r--r--spec/unit/provider/package/homebrew_spec.rb467
-rw-r--r--spec/unit/provider/package/ips_spec.rb220
-rw-r--r--spec/unit/provider/package/macports_spec.rb58
-rw-r--r--spec/unit/provider/package/msu_spec.rb283
-rw-r--r--spec/unit/provider/package/openbsd_spec.rb54
-rw-r--r--spec/unit/provider/package/pacman_spec.rb211
-rw-r--r--spec/unit/provider/package/paludis_spec.rb76
-rw-r--r--spec/unit/provider/package/portage_spec.rb201
-rw-r--r--spec/unit/provider/package/powershell_spec.rb503
-rw-r--r--spec/unit/provider/package/rpm_spec.rb91
-rw-r--r--spec/unit/provider/package/rubygems_spec.rb664
-rw-r--r--spec/unit/provider/package/smartos_spec.rb50
-rw-r--r--spec/unit/provider/package/snap_spec.rb208
-rw-r--r--spec/unit/provider/package/solaris_spec.rb92
-rw-r--r--spec/unit/provider/package/windows/exe_spec.rb24
-rw-r--r--spec/unit/provider/package/windows/msi_spec.rb25
-rw-r--r--spec/unit/provider/package/windows/registry_uninstall_entry_spec.rb78
-rw-r--r--spec/unit/provider/package/windows_spec.rb84
-rw-r--r--spec/unit/provider/package/yum/python_helper_spec.rb29
-rw-r--r--spec/unit/provider/package/yum/yum_cache_spec.rb90
-rw-r--r--spec/unit/provider/package/yum_spec.rb2268
-rw-r--r--spec/unit/provider/package/zypper_spec.rb320
-rw-r--r--spec/unit/provider/package_spec.rb326
-rw-r--r--spec/unit/provider/powershell_script_spec.rb95
-rw-r--r--spec/unit/provider/registry_key_spec.rb196
-rw-r--r--spec/unit/provider/remote_directory_spec.rb32
-rw-r--r--spec/unit/provider/remote_file/cache_control_data_spec.rb16
-rw-r--r--spec/unit/provider/remote_file/content_spec.rb17
-rw-r--r--spec/unit/provider/remote_file/fetcher_spec.rb13
-rw-r--r--spec/unit/provider/remote_file/ftp_spec.rb2
-rw-r--r--spec/unit/provider/remote_file/http_spec.rb52
-rw-r--r--spec/unit/provider/remote_file/local_file_spec.rb6
-rw-r--r--spec/unit/provider/remote_file/network_file_spec.rb15
-rw-r--r--spec/unit/provider/remote_file/sftp_spec.rb10
-rw-r--r--spec/unit/provider/remote_file_spec.rb8
-rw-r--r--spec/unit/provider/route_spec.rb104
-rw-r--r--spec/unit/provider/script_spec.rb87
-rw-r--r--spec/unit/provider/service/aix_service_spec.rb18
-rw-r--r--spec/unit/provider/service/aixinit_service_spec.rb10
-rw-r--r--spec/unit/provider/service/arch_service_spec.rb103
-rw-r--r--spec/unit/provider/service/debian_service_spec.rb260
-rw-r--r--spec/unit/provider/service/freebsd_service_spec.rb126
-rw-r--r--spec/unit/provider/service/gentoo_service_spec.rb24
-rw-r--r--spec/unit/provider/service/init_service_spec.rb82
-rw-r--r--spec/unit/provider/service/insserv_service_spec.rb10
-rw-r--r--spec/unit/provider/service/invokercd_service_spec.rb82
-rw-r--r--spec/unit/provider/service/macosx_spec.rb441
-rw-r--r--spec/unit/provider/service/openbsd_service_spec.rb50
-rw-r--r--spec/unit/provider/service/redhat_spec.rb60
-rw-r--r--spec/unit/provider/service/simple_service_spec.rb54
-rw-r--r--spec/unit/provider/service/solaris_smf_service_spec.rb73
-rw-r--r--spec/unit/provider/service/systemd_service_spec.rb215
-rw-r--r--spec/unit/provider/service/upstart_service_spec.rb132
-rw-r--r--spec/unit/provider/service/windows_spec.rb755
-rw-r--r--spec/unit/provider/service_spec.rb10
-rw-r--r--spec/unit/provider/subversion_spec.rb66
-rw-r--r--spec/unit/provider/systemd_unit_spec.rb645
-rw-r--r--spec/unit/provider/template/content_spec.rb92
-rw-r--r--spec/unit/provider/template_spec.rb13
-rw-r--r--spec/unit/provider/user/aix_spec.rb96
-rw-r--r--spec/unit/provider/user/dscl_spec.rb564
-rw-r--r--spec/unit/provider/user/linux_spec.rb43
-rw-r--r--spec/unit/provider/user/mac_spec.rb38
-rw-r--r--spec/unit/provider/user/pw_spec.rb90
-rw-r--r--spec/unit/provider/user/solaris_spec.rb138
-rw-r--r--spec/unit/provider/user/windows_spec.rb42
-rw-r--r--spec/unit/provider/user_spec.rb182
-rw-r--r--spec/unit/provider/windows_env_spec.rb385
-rw-r--r--spec/unit/provider/windows_path_spec.rb60
-rw-r--r--spec/unit/provider/windows_task_spec.rb436
-rw-r--r--spec/unit/provider/yum_repository_spec.rb2
-rw-r--r--spec/unit/provider/zypper_repository_spec.rb176
-rw-r--r--spec/unit/provider_resolver_spec.rb935
-rw-r--r--spec/unit/provider_spec.rb24
-rw-r--r--spec/unit/recipe_spec.rb282
-rw-r--r--spec/unit/resource/alternatives_spec.rb120
-rw-r--r--spec/unit/resource/apt_package_spec.rb32
-rw-r--r--spec/unit/resource/apt_preference_spec.rb39
-rw-r--r--spec/unit/resource/apt_repository_spec.rb60
-rw-r--r--spec/unit/resource/apt_update_spec.rb26
-rw-r--r--spec/unit/resource/archive_file_spec.rb56
-rw-r--r--spec/unit/resource/bash_spec.rb25
-rw-r--r--spec/unit/resource/batch_spec.rb19
-rw-r--r--spec/unit/resource/bff_package_spec.rb51
-rw-r--r--spec/unit/resource/breakpoint_spec.rb45
-rw-r--r--spec/unit/resource/build_essential_spec.rb77
-rw-r--r--spec/unit/resource/cab_package_spec.rb64
-rw-r--r--spec/unit/resource/chef_client_config_spec.rb137
-rw-r--r--spec/unit/resource/chef_client_cron_spec.rb156
-rw-r--r--spec/unit/resource/chef_client_launchd_spec.rb127
-rw-r--r--spec/unit/resource/chef_client_scheduled_task_spec.rb112
-rw-r--r--spec/unit/resource/chef_client_systemd_timer_spec.rb108
-rw-r--r--spec/unit/resource/chef_client_trusted_certificate_spec.rb54
-rw-r--r--spec/unit/resource/chef_gem_spec.rb101
-rw-r--r--spec/unit/resource/chef_handler_spec.rb40
-rw-r--r--spec/unit/resource/chef_sleep_spec.rb30
-rw-r--r--spec/unit/resource/chef_vault_secret_spec.rb40
-rw-r--r--spec/unit/resource/chocolatey_config_spec.rb93
-rw-r--r--spec/unit/resource/chocolatey_feature_spec.rb89
-rw-r--r--spec/unit/resource/chocolatey_package_spec.rb63
-rw-r--r--spec/unit/resource/chocolatey_source_spec.rb151
-rw-r--r--spec/unit/resource/conditional_spec.rb10
-rw-r--r--spec/unit/resource/cookbook_file_spec.rb73
-rw-r--r--spec/unit/resource/cron_access_spec.rb36
-rw-r--r--spec/unit/resource/cron_d_spec.rb48
-rw-r--r--spec/unit/resource/cron_spec.rb163
-rw-r--r--spec/unit/resource/csh_spec.rb25
-rw-r--r--spec/unit/resource/deploy_revision_spec.rb42
-rw-r--r--spec/unit/resource/deploy_spec.rb283
-rw-r--r--spec/unit/resource/directory_spec.rb59
-rw-r--r--spec/unit/resource/dmg_package_spec.rb39
-rw-r--r--spec/unit/resource/dnf_package_spec.rb108
-rw-r--r--spec/unit/resource/dpkg_package_spec.rb30
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb22
-rw-r--r--spec/unit/resource/dsc_script_spec.rb100
-rw-r--r--spec/unit/resource/easy_install_package_spec.rb39
-rw-r--r--spec/unit/resource/env_spec.rb85
-rw-r--r--spec/unit/resource/erl_call_spec.rb81
-rw-r--r--spec/unit/resource/execute_spec.rb257
-rw-r--r--spec/unit/resource/file/verification/systemd_unit_spec.rb103
-rw-r--r--spec/unit/resource/file/verification_spec.rb54
-rw-r--r--spec/unit/resource/file_spec.rb119
-rw-r--r--spec/unit/resource/freebsd_package_spec.rb78
-rw-r--r--spec/unit/resource/gem_package_spec.rb37
-rw-r--r--spec/unit/resource/git_spec.rb50
-rw-r--r--spec/unit/resource/group_spec.rb155
-rw-r--r--spec/unit/resource/helpers/cron_validations_spec.rb81
-rw-r--r--spec/unit/resource/homebrew_cask_spec.rb40
-rw-r--r--spec/unit/resource/homebrew_package_spec.rb20
-rw-r--r--spec/unit/resource/homebrew_tap_spec.rb44
-rw-r--r--spec/unit/resource/homebrew_update_spec.rb30
-rw-r--r--spec/unit/resource/hostname_spec.rb47
-rw-r--r--spec/unit/resource/http_request_spec.rb42
-rw-r--r--spec/unit/resource/ifconfig_spec.rb20
-rw-r--r--spec/unit/resource/ips_package_spec.rb30
-rw-r--r--spec/unit/resource/kernel_module_spec.rb44
-rw-r--r--spec/unit/resource/ksh_spec.rb25
-rw-r--r--spec/unit/resource/launchd_spec.rb48
-rw-r--r--spec/unit/resource/link_spec.rb118
-rw-r--r--spec/unit/resource/locale_spec.rb189
-rw-r--r--spec/unit/resource/log_spec.rb55
-rw-r--r--spec/unit/resource/macos_user_defaults_spec.rb136
-rw-r--r--spec/unit/resource/macosx_service.rb37
-rw-r--r--spec/unit/resource/macports_package_spec.rb21
-rw-r--r--spec/unit/resource/mdadm_spec.rb85
-rw-r--r--spec/unit/resource/mount_spec.rb229
-rw-r--r--spec/unit/resource/msu_package_spec.rb67
-rw-r--r--spec/unit/resource/notify_group_spec.rb34
-rw-r--r--spec/unit/resource/ohai_hint_spec.rb44
-rw-r--r--spec/unit/resource/ohai_spec.rb87
-rw-r--r--spec/unit/resource/openbsd_package_spec.rb40
-rw-r--r--spec/unit/resource/openssl_dhparam_spec.rb61
-rw-r--r--spec/unit/resource/openssl_ec_private_key_spec.rb64
-rw-r--r--spec/unit/resource/openssl_ec_public_key_spec.rb43
-rw-r--r--spec/unit/resource/openssl_rsa_private_key_spec.rb64
-rw-r--r--spec/unit/resource/openssl_rsa_public_key_spec.rb43
-rw-r--r--spec/unit/resource/openssl_x509_certificate_spec.rb72
-rw-r--r--spec/unit/resource/openssl_x509_crl_spec.rb61
-rw-r--r--spec/unit/resource/openssl_x509_request.rb68
-rw-r--r--spec/unit/resource/osx_profile_spec.rb322
-rw-r--r--spec/unit/resource/package_spec.rb79
-rw-r--r--spec/unit/resource/pacman_package_spec.rb20
-rw-r--r--spec/unit/resource/paludis_package_spec.rb36
-rw-r--r--spec/unit/resource/perl_spec.rb24
-rw-r--r--spec/unit/resource/plist_spec.rb130
-rw-r--r--spec/unit/resource/portage_package_spec.rb28
-rw-r--r--spec/unit/resource/powershell_package_source_spec.rb219
-rw-r--r--spec/unit/resource/powershell_package_spec.rb98
-rw-r--r--spec/unit/resource/powershell_script_spec.rb103
-rw-r--r--spec/unit/resource/python_spec.rb22
-rw-r--r--spec/unit/resource/reboot_spec.rb47
-rw-r--r--spec/unit/resource/registry_key_spec.rb202
-rw-r--r--spec/unit/resource/remote_directory_spec.rb107
-rw-r--r--spec/unit/resource/remote_file_spec.rb185
-rw-r--r--spec/unit/resource/resource_notification_spec.rb18
-rw-r--r--spec/unit/resource/rhsm_errata_level_spec.rb50
-rw-r--r--spec/unit/resource/rhsm_errata_spec.rb39
-rw-r--r--spec/unit/resource/rhsm_register_spec.rb266
-rw-r--r--spec/unit/resource/rhsm_repo_spec.rb70
-rw-r--r--spec/unit/resource/rhsm_subscription_spec.rb98
-rw-r--r--spec/unit/resource/route_spec.rb85
-rw-r--r--spec/unit/resource/rpm_package_spec.rb28
-rw-r--r--spec/unit/resource/ruby_block_spec.rb35
-rw-r--r--spec/unit/resource/ruby_spec.rb21
-rw-r--r--spec/unit/resource/scm/git_spec.rb110
-rw-r--r--spec/unit/resource/scm/scm.rb122
-rw-r--r--spec/unit/resource/scm/subversion_spec.rb90
-rw-r--r--spec/unit/resource/scm_spec.rb193
-rw-r--r--spec/unit/resource/script_spec.rb19
-rw-r--r--spec/unit/resource/service_spec.rb202
-rw-r--r--spec/unit/resource/smartos_package_spec.rb23
-rw-r--r--spec/unit/resource/snap_package_spec.rb60
-rw-r--r--spec/unit/resource/solaris_package_spec.rb38
-rw-r--r--spec/unit/resource/ssh_known_hosts_entry_spec.rb50
-rw-r--r--spec/unit/resource/subversion_spec.rb71
-rw-r--r--spec/unit/resource/sudo_spec.rb99
-rw-r--r--spec/unit/resource/swap_file_spec.rb39
-rw-r--r--spec/unit/resource/sysctl_spec.rb76
-rw-r--r--spec/unit/resource/systemd_unit_spec.rb119
-rw-r--r--spec/unit/resource/template_spec.rb136
-rw-r--r--spec/unit/resource/timestamped_deploy_spec.rb32
-rw-r--r--spec/unit/resource/timezone_spec.rb102
-rw-r--r--spec/unit/resource/user/windows_user_spec.rb36
-rw-r--r--spec/unit/resource/user_spec.rb110
-rw-r--r--spec/unit/resource/user_ulimit_spec.rb53
-rw-r--r--spec/unit/resource/windows_ad_join_spec.rb55
-rw-r--r--spec/unit/resource/windows_audit_policy_spec.rb64
-rw-r--r--spec/unit/resource/windows_auto_run_spec.rb50
-rw-r--r--spec/unit/resource/windows_certificate_spec.rb95
-rw-r--r--spec/unit/resource/windows_dfs_folder_spec.rb39
-rw-r--r--spec/unit/resource/windows_dfs_namespace_spec.rb39
-rw-r--r--spec/unit/resource/windows_dfs_server_spec.rb34
-rw-r--r--spec/unit/resource/windows_dns_record_spec.rb55
-rw-r--r--spec/unit/resource/windows_dns_zone_spec.rb51
-rw-r--r--spec/unit/resource/windows_env_spec.rb75
-rw-r--r--spec/unit/resource/windows_feature_dism_spec.rb57
-rw-r--r--spec/unit/resource/windows_feature_powershell_spec.rb83
-rw-r--r--spec/unit/resource/windows_feature_spec.rb64
-rw-r--r--spec/unit/resource/windows_firewall_profile_spec.rb77
-rw-r--r--spec/unit/resource/windows_firewall_rule_spec.rb505
-rw-r--r--spec/unit/resource/windows_font_spec.rb43
-rw-r--r--spec/unit/resource/windows_package_spec.rb54
-rw-r--r--spec/unit/resource/windows_pagefile_spec.rb49
-rw-r--r--spec/unit/resource/windows_path_spec.rb40
-rw-r--r--spec/unit/resource/windows_printer_port_spec.rb62
-rw-r--r--spec/unit/resource/windows_printer_spec.rb52
-rw-r--r--spec/unit/resource/windows_service_spec.rb105
-rw-r--r--spec/unit/resource/windows_share_spec.rb48
-rw-r--r--spec/unit/resource/windows_shortcut_spec.rb38
-rw-r--r--spec/unit/resource/windows_task_spec.rb403
-rw-r--r--spec/unit/resource/windows_uac_spec.rb50
-rw-r--r--spec/unit/resource/windows_user_privilege_spec.rb55
-rw-r--r--spec/unit/resource/windows_workgroup_spec.rb74
-rw-r--r--spec/unit/resource/yum_package_spec.rb73
-rw-r--r--spec/unit/resource/yum_repository_spec.rb123
-rw-r--r--spec/unit/resource/zypper_package_spec.rb51
-rw-r--r--spec/unit/resource/zypper_repository_spec.rb115
-rw-r--r--spec/unit/resource_collection/resource_list_spec.rb4
-rw-r--r--spec/unit/resource_collection/resource_set_spec.rb73
-rw-r--r--spec/unit/resource_collection/stepable_iterator_spec.rb4
-rw-r--r--spec/unit/resource_collection_spec.rb33
-rw-r--r--spec/unit/resource_definition_spec.rb10
-rw-r--r--spec/unit/resource_inspector_spec.rb60
-rw-r--r--spec/unit/resource_reporter_spec.rb480
-rw-r--r--spec/unit/resource_spec.rb413
-rw-r--r--spec/unit/rest/auth_credentials_spec.rb292
-rw-r--r--spec/unit/rest_spec.rb753
-rw-r--r--spec/unit/role_spec.rb111
-rw-r--r--spec/unit/run_context/child_run_context_spec.rb13
-rw-r--r--spec/unit/run_context/cookbook_compiler_spec.rb64
-rw-r--r--spec/unit/run_context_spec.rb6
-rw-r--r--spec/unit/run_list/run_list_expansion_spec.rb17
-rw-r--r--spec/unit/run_list/run_list_item_spec.rb8
-rw-r--r--spec/unit/run_list/versioned_recipe_list_spec.rb37
-rw-r--r--spec/unit/run_list_spec.rb16
-rw-r--r--spec/unit/run_lock_spec.rb10
-rw-r--r--spec/unit/run_status_spec.rb4
-rw-r--r--spec/unit/runner_spec.rb54
-rw-r--r--spec/unit/scan_access_control_spec.rb10
-rw-r--r--spec/unit/search/query_spec.rb91
-rw-r--r--spec/unit/server_api_spec.rb151
-rw-r--r--spec/unit/server_api_versions_spec.rb66
-rw-r--r--spec/unit/shell/model_wrapper_spec.rb12
-rw-r--r--spec/unit/shell/shell_ext_spec.rb59
-rw-r--r--spec/unit/shell/shell_session_spec.rb219
-rw-r--r--spec/unit/shell_out_spec.rb18
-rw-r--r--spec/unit/shell_spec.rb40
-rw-r--r--spec/unit/train_transport_spec.rb85
-rw-r--r--spec/unit/user_spec.rb18
-rw-r--r--spec/unit/user_v1_spec.rb84
-rw-r--r--spec/unit/util/backup_spec.rb6
-rw-r--r--spec/unit/util/diff_spec.rb20
-rw-r--r--spec/unit/util/dsc/configuration_generator_spec.rb91
-rw-r--r--spec/unit/util/dsc/lcm_output_parser_spec.rb282
-rw-r--r--spec/unit/util/dsc/local_configuration_manager_spec.rb147
-rw-r--r--spec/unit/util/dsc/resource_store.rb4
-rw-r--r--spec/unit/util/editor_spec.rb2
-rw-r--r--spec/unit/util/file_edit_spec.rb80
-rw-r--r--spec/unit/util/powershell/cmdlet_spec.rb106
-rw-r--r--spec/unit/util/powershell/ps_credential_spec.rb11
-rw-r--r--spec/unit/util/selinux_spec.rb47
-rw-r--r--spec/unit/util/threaded_job_queue_spec.rb15
-rw-r--r--spec/unit/util/windows/logon_session_spec.rb285
-rw-r--r--spec/unit/version_class_spec.rb10
-rw-r--r--spec/unit/version_constraint_spec.rb2
-rw-r--r--spec/unit/version_string_spec.rb79
-rw-r--r--spec/unit/win32/error_spec.rb77
-rw-r--r--spec/unit/win32/link_spec.rb73
-rw-r--r--spec/unit/win32/registry_spec.rb20
-rw-r--r--spec/unit/win32/security_spec.rb136
-rw-r--r--spec/unit/windows_service_spec.rb14
-rwxr-xr-xtasks/bin/bundle-platform20
-rw-r--r--tasks/bin/bundle-platform.bat2
-rw-r--r--tasks/bin/bundler_patch.rb27
-rwxr-xr-xtasks/bin/create-override-gemfile110
-rwxr-xr-xtasks/bin/gem-version-diff37
-rwxr-xr-xtasks/bin/run_external_test86
-rw-r--r--tasks/bundle.rb97
-rw-r--r--tasks/bundle_util.rb110
-rw-r--r--tasks/cbgb.rb84
-rw-r--r--tasks/changelog.rb13
-rw-r--r--tasks/dependencies.rb119
-rwxr-xr-xtasks/docs.rb303
-rw-r--r--tasks/gemfile_util.rb390
-rw-r--r--tasks/maintainers.rb211
-rw-r--r--tasks/rspec.rb41
-rw-r--r--tasks/spellcheck.rb58
m---------vendor/bundle/bundler/gems/bundler-audit-4e32fca89d750
m---------vendor/bundle/bundler/gems/chefstyle-52a0d55a9e8f0
-rw-r--r--version_policy.rb115
3197 files changed, 146402 insertions, 106403 deletions
diff --git a/.bldr.toml b/.bldr.toml
new file mode 100644
index 0000000000..438c546484
--- /dev/null
+++ b/.bldr.toml
@@ -0,0 +1,6 @@
+[chef-infra-client]
+build_targets = [
+ "x86_64-linux",
+ "x86_64-linux-kernel2",
+ "x86_64-windows"
+]
diff --git a/.buildkite/hooks/pre-command b/.buildkite/hooks/pre-command
new file mode 100644
index 0000000000..6f9dcecdf6
--- /dev/null
+++ b/.buildkite/hooks/pre-command
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -eu
+
+# Only execute in the verify pipeline
+[[ "$BUILDKITE_PIPELINE_NAME" =~ verify$ ]] || exit 0
+
+docker ps || true
+free -m || true
+
+# We've now seen cases where origin/master on the build hosts can get
+# out of date. This causes us to build components unnecessarily.
+# Fetching it here hopefully will prevent this situation.
+echo "Fetching origin/master"
+git fetch origin master
+
+# DEBUGGING FOR RELENG
+# Fetch the git tags to see if that addresses the weird smart build behavior for Habitat
+git fetch --tags --force
+
+# Rebase onto current master to ensure this PR is closer to what happens when it's merged.
+# Only do this if it's actually a branch (i.e. a PR or a manually created build), not a
+# post-merge CI run of master.
+if [[ "$BUILDKITE_BRANCH" != "master" ]]; then
+ git config user.email "you@example.com" # these are needed for the rebase attempt
+ git config user.name "Your Name"
+ master=$(git show-ref -s --abbrev origin/master)
+ pr_head=$(git show-ref -s --abbrev HEAD)
+ github="https://github.com/chef/chef/commit/"
+ if git rebase origin/master >/dev/null; then
+ buildkite-agent annotate --style success --context "rebase-pr-branch-${master}" \
+ "Rebased onto master ([${master}](${github}${master}))."
+ else
+ git rebase --abort
+ buildkite-agent annotate --style warning --context "rebase-pr-branch-${master}" \
+ "Couldn't rebase onto master ([${master}](${github}${master})), building PR HEAD ([${pr_head}](${github}${pr_head}))."
+ fi
+fi
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/build.docker.yml b/.expeditor/build.docker.yml
new file mode 100644
index 0000000000..fed7a9cfaa
--- /dev/null
+++ b/.expeditor/build.docker.yml
@@ -0,0 +1 @@
+image_registry: chef \ No newline at end of file
diff --git a/.expeditor/build.habitat.yml b/.expeditor/build.habitat.yml
new file mode 100644
index 0000000000..4e13b540ec
--- /dev/null
+++ b/.expeditor/build.habitat.yml
@@ -0,0 +1,2 @@
+---
+origin: chef
diff --git a/.expeditor/config.yml b/.expeditor/config.yml
new file mode 100644
index 0000000000..7d6a1815b5
--- /dev/null
+++ b/.expeditor/config.yml
@@ -0,0 +1,210 @@
+# Documentation available at https://expeditor.chef.io/docs/getting-started/
+---
+
+# The name of the product keys for this product (from mixlib-install)
+product_key:
+ - chef
+
+# Slack channel in Chef Software slack to send notifications about build failures, etc
+slack:
+ notify_channel: chef-infra-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
+ - chef-bin
+ - chef-utils
+
+pipelines:
+ - verify:
+ public: true
+ - docker/build
+ - habitat/build
+ - habitat/test:
+ definition: .expeditor/habitat-test.pipeline.yml
+ trigger: default
+ - omnibus/release
+ - omnibus/adhoc:
+ definition: .expeditor/release.omnibus.yml
+ env:
+ - ADHOC: true
+
+github:
+ # 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}}"
+ # allow bumping the minor release via label
+ minor_bump_labels:
+ - "Expeditor: Bump Version Minor"
+ # allow bumping the major release via label
+ major_bump_labels:
+ - "Expeditor: Bump Version Major"
+ # 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: 17*
+ - chef-16:
+ version_constraint: 16*
+ - chef-15:
+ version_constraint: 15*
+
+changelog:
+ rollup_header: Changes not yet released to stable
+
+# These actions are taken, in order they are specified, anytime a Pull Request is merged.
+merge_actions:
+ - built_in:bump_version:
+ ignore_labels:
+ - "Expeditor: Skip Version Bump"
+ - "Expeditor: Skip All"
+ - bash:.expeditor/update_version.sh:
+ only_if: built_in:bump_version
+ - built_in:update_changelog:
+ ignore_labels:
+ - "Expeditor: Skip Changelog"
+ - "Expeditor: Skip All"
+ - trigger_pipeline:habitat/build:
+ ignore_labels:
+ - "Expeditor: Skip Habitat"
+ - "Expeditor: Skip All"
+ only_if: built_in:bump_version
+ - trigger_pipeline:omnibus/release:
+ ignore_labels:
+ - "Expeditor: Skip Omnibus"
+ - "Expeditor: Skip All"
+ only_if: built_in:bump_version
+
+subscriptions:
+ # the omnibus/docker/gem chain
+ - workload: artifact_published:unstable:chef:{{version_constraint}}
+ actions:
+ - trigger_pipeline:docker/build
+ - workload: artifact_published:current:chef:{{version_constraint}}
+ actions:
+ - built_in:promote_docker_images
+ - workload: artifact_published:stable:chef:{{version_constraint}}
+ actions:
+ - built_in:rollover_changelog
+ - bash:.expeditor/update_dockerfile.sh
+ - built_in:promote_docker_images
+ - built_in:publish_rubygems
+ - built_in:promote_habitat_packages
+ - built_in:notify_chefio_slack_channels
+
+ # the habitat chain
+ - workload: buildkite_hab_build_group_published:{{agent_id}}:*
+ actions:
+ # when all of the hab package publish to the unstable channel, test and promote them
+ - trigger_pipeline:habitat/test
+
+ # subscriptions to Ruby gem dependencies' releases, open PR for updates
+ - workload: ruby_gem_published:mixlib-archive-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:mixlib-authentication-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:mixlib-cli-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:mixlib-log-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:mixlib-shellout-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:chef-vault-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:chef-zero-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:ohai-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:inspec-core-bin-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:train-core-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:win32-process-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:win32-service-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:win32-taskscheduler-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:ffi-yajl-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:libyajl2-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:cheffish-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:license-acceptance-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:ffi-libarchive-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:plist-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:ffi-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:net-ssh-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:tty-prompt-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:tty-screen-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:tty-table-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:pastel-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:erubis-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:bcrypt_pbkdf-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:ed25519-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:addressable-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:proxifier-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:syslog-logger-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:uuidtools-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:iniparse-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:net-sftp-*
+ actions:
+ - bash:.expeditor/update_dep.sh
+ - workload: ruby_gem_published:fauxhai-ng-*
+ actions:
+ - bash:.expeditor/update_dep.sh \ No newline at end of file
diff --git a/.expeditor/habitat-test.pipeline.yml b/.expeditor/habitat-test.pipeline.yml
new file mode 100644
index 0000000000..859d4eb1f1
--- /dev/null
+++ b/.expeditor/habitat-test.pipeline.yml
@@ -0,0 +1,62 @@
+---
+expeditor:
+ defaults:
+ buildkite:
+ timeout_in_minutes: 60
+ retry:
+ automatic:
+ limit: 1
+
+steps:
+
+- label: ":linux: Validate Linux"
+ commands:
+ - sudo ./.expeditor/scripts/install-hab.sh x86_64-linux
+ - 'echo "--- :hammer_and_wrench: Installing $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUX"'
+ - sudo hab pkg install $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUX
+ - sudo ./habitat/tests/test.sh $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUX
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: ":linux: Validate Linux (kernel2)"
+ commands:
+ - sudo ./.expeditor/scripts/install-hab.sh x86_64-linux-kernel2
+ - 'echo "--- :hammer_and_wrench: Installing $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUXKERNEL2"'
+ - sudo hab pkg install $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUXKERNEL2
+ - sudo ./habitat/tests/test.sh $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUXKERNEL2
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: ":windows: Validate Habitat Builds of Chef Infra"
+ commands:
+ - powershell -File ./.expeditor/scripts/ensure-minimum-viable-hab.ps1
+ - 'Write-Host "--- :hammer_and_wrench: Installing $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64WINDOWS"'
+ - hab pkg install $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64WINDOWS
+ - powershell -File ./habitat/tests/test.ps1 -PackageIdentifier $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64WINDOWS
+ expeditor:
+ executor:
+ windows:
+ privileged: true
+ single-use: true
+
+# Wait for the package testing to succeed before promoting whatever was tested.
+- wait
+
+- label: ":habicat: Promoting packages to the current channel."
+ commands:
+ - hab pkg promote $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUX current x86_64-linux
+ - hab pkg promote $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64LINUXKERNEL2 current x86_64-linux-kernel2
+ - hab pkg promote $EXPEDITOR_PKG_IDENTS_CHEFINFRACLIENTX86_64WINDOWS current x86_64-windows
+ expeditor:
+ executor:
+ docker:
+ secrets:
+ HAB_AUTH_TOKEN:
+ path: account/static/habitat/chef-ci
+ field: auth_token
diff --git a/.expeditor/release.omnibus.yml b/.expeditor/release.omnibus.yml
new file mode 100644
index 0000000000..0736e929a5
--- /dev/null
+++ b/.expeditor/release.omnibus.yml
@@ -0,0 +1,69 @@
+---
+project-name: chef
+config: omnibus/omnibus.rb
+test-path: omnibus/omnibus-test.sh
+test-path-windows: omnibus/omnibus-test.ps1
+fips-platforms:
+ - el-*-x86_64
+ - windows-*
+builder-to-testers-map:
+ aix-7.1-powerpc:
+ - aix-7.1-powerpc
+ - aix-7.2-powerpc
+ debian-9-x86_64:
+ - debian-9-x86_64
+ - debian-10-x86_64
+ debian-10-aarch64:
+ - debian-10-aarch64
+ el-6-x86_64:
+ - el-6-x86_64
+ el-7-aarch64:
+ - el-7-aarch64
+ - el-8-aarch64
+ - amazon-2-aarch64
+ el-7-ppc64:
+ - el-7-ppc64
+ el-7-ppc64le:
+ - el-7-ppc64le
+ el-7-s390x:
+ - el-7-s390x
+ - el-8-s390x
+ el-7-x86_64:
+ - el-7-x86_64
+ - el-8-x86_64
+ - amazon-2-x86_64
+ freebsd-11-amd64:
+ - freebsd-11-amd64
+ - freebsd-12-amd64
+ mac_os_x-10.13-x86_64:
+ - mac_os_x-10.13-x86_64
+ - mac_os_x-10.14-x86_64
+ - mac_os_x-10.15-x86_64
+ - mac_os_x-11.0-x86_64
+ sles-12-s390x:
+ - sles-12-s390x
+ - sles-15-s390x
+ sles-12-x86_64:
+ - sles-12-x86_64
+ - sles-15-x86_64
+ sles-15-aarch64:
+ - sles-15-aarch64
+ solaris2-5.11-i386:
+ - solaris2-5.11-i386
+ solaris2-5.11-sparc:
+ - solaris2-5.11-sparc
+ ubuntu-18.04-aarch64:
+ - ubuntu-18.04-aarch64
+ - ubuntu-20.04-aarch64
+ ubuntu-18.04-x86_64:
+ - ubuntu-18.04-x86_64
+ - ubuntu-20.04-x86_64
+ windows-2012r2-i386:
+ - windows-2012r2-i386
+ windows-2012r2-x86_64:
+ - windows-2012-x86_64
+ - windows-2012r2-x86_64
+ - windows-2016-x86_64
+ - windows-2019-x86_64
+ - windows-8-x86_64
+ - windows-10-x86_64
diff --git a/.expeditor/scripts/bk_container_prep.sh b/.expeditor/scripts/bk_container_prep.sh
new file mode 100755
index 0000000000..e065f20579
--- /dev/null
+++ b/.expeditor/scripts/bk_container_prep.sh
@@ -0,0 +1,27 @@
+# This script gets a container ready to run our various tests in BuildKite
+
+echo "--- Container Config..."
+
+source /etc/os-release
+echo $PRETTY_NAME
+
+echo "ruby version:"
+ruby -v
+echo "bundler version:"
+bundle -v
+
+echo "--- Preparing Container..."
+
+export FORCE_FFI_YAJL="ext"
+export CHEF_LICENSE="accept-no-persist"
+export BUNDLE_GEMFILE="/workdir/Gemfile"
+
+# make sure we have the network tools in place for various network specs
+if [ -f /etc/debian_version ]; then
+ touch /etc/network/interfaces
+fi
+
+# remove default bundler config if there is one
+rm -f .bundle/config
+
+echo "+++ Run tests"
diff --git a/.expeditor/scripts/bk_linux_exec.sh b/.expeditor/scripts/bk_linux_exec.sh
new file mode 100755
index 0000000000..f1202bbcbb
--- /dev/null
+++ b/.expeditor/scripts/bk_linux_exec.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Enable IPv6 in docker
+echo "--- Enabling ipv6 on docker"
+sudo systemctl stop docker
+dockerd_config="/etc/docker/daemon.json"
+sudo echo "$(jq '. + {"ipv6": true, "fixed-cidr-v6": "2001:2019:6002::/80", "ip-forward": false}' $dockerd_config)" > $dockerd_config
+sudo systemctl start docker
+
+# Install C and C++
+echo "--- Installing package deps"
+sudo yum install -y gcc gcc-c++ openssl-devel readline-devel zlib-devel
+
+# Install omnibus-toolchain for git bundler and gem
+echo "--- Installing omnibus toolchain"
+curl -fsSL https://chef.io/chef/install.sh | sudo bash -s -- -P omnibus-toolchain
+
+# Set Environment Variables
+export BUNDLE_GEMFILE=$PWD/kitchen-tests/Gemfile
+export FORCE_FFI_YAJL=ext
+export CHEF_LICENSE="accept-silent"
+export PATH=$PATH:/opt/omnibus-toolchain/embedded/bin
+
+# Update Gems
+echo "--- Installing Gems"
+echo 'gem: --no-document' >> ~/.gemrc
+sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
+/opt/omnibus-toolchain/bin/bundle install --jobs=3 --retry=3 --path=../vendor/bundle
+
+echo "--- Config information"
+
+echo "!!!! RUBY VERSION !!!!"
+ruby --version
+echo "!!!! BUNDLER LOCATION !!!!"
+which bundle
+echo "!!!! BUNDLER VERSION !!!!"
+bundle -v
+echo "!!!! DOCKER VERSION !!!!"
+docker version
+echo "!!!! DOCKER STATUS !!!!"
+sudo service docker status
+
+echo "+++ Running tests" \ No newline at end of file
diff --git a/.expeditor/scripts/bk_run_choco.ps1 b/.expeditor/scripts/bk_run_choco.ps1
new file mode 100644
index 0000000000..49f9186701
--- /dev/null
+++ b/.expeditor/scripts/bk_run_choco.ps1
@@ -0,0 +1,9 @@
+$CurrentDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
+$PrepScript = Join-Path $CurrentDirectory "bk_win_prep.ps1"
+Invoke-Expression $PrepScript
+
+choco --version
+
+echo "+++ bundle exec rspec chocolatey_package_spec"
+bundle exec rspec spec/functional/resource/chocolatey_package_spec.rb
+if (-not $?) { throw "Chef chocolatey functional tests failing." }
diff --git a/.expeditor/scripts/bk_win_functional.ps1 b/.expeditor/scripts/bk_win_functional.ps1
new file mode 100644
index 0000000000..05f8e57248
--- /dev/null
+++ b/.expeditor/scripts/bk_win_functional.ps1
@@ -0,0 +1,33 @@
+Write-Output "--- system details"
+$Properties = 'Caption', 'CSName', 'Version', 'BuildType', 'OSArchitecture'
+Get-CimInstance Win32_OperatingSystem | Select-Object $Properties | Format-Table -AutoSize
+
+# chocolatey functional tests fail so delete the chocolatey binary to avoid triggering them
+Remove-Item -Path C:\ProgramData\chocolatey\bin\choco.exe -ErrorAction SilentlyContinue
+
+$ErrorActionPreference = 'Stop'
+
+Write-Output "--- Enable Ruby 2.7"
+
+Write-Output "Add Uru to Environment PATH"
+$env:PATH = "C:\Program Files (x86)\Uru;" + $env:PATH
+[Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine)
+
+Write-Output "Register Installed Ruby Version 2.7 With Uru"
+Start-Process "C:\Program Files (x86)\Uru\uru_rt.exe" -ArgumentList 'admin add C:\ruby27\bin' -Wait
+uru 271
+if (-not $?) { throw "Can't Activate Ruby. Did Uru Registration Succeed?" }
+ruby -v
+if (-not $?) { throw "Can't run Ruby. Is it installed?" }
+
+Write-Output "--- configure winrm"
+winrm quickconfig -q
+
+Write-Output "--- bundle install"
+bundle config set --local without 'omnibus_package'
+bundle install --jobs=3 --retry=3
+if (-not $?) { throw "Unable to install gem dependencies" }
+
+Write-Output "+++ bundle exec rake spec:functional"
+bundle exec rake spec:functional
+if (-not $?) { throw "Chef functional specs failing." }
diff --git a/.expeditor/scripts/bk_win_integration.ps1 b/.expeditor/scripts/bk_win_integration.ps1
new file mode 100644
index 0000000000..6b0debc790
--- /dev/null
+++ b/.expeditor/scripts/bk_win_integration.ps1
@@ -0,0 +1,12 @@
+$CurrentDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
+$PrepScript = Join-Path $CurrentDirectory "bk_win_prep.ps1"
+Invoke-Expression $PrepScript
+
+# Set-Item -Path Env:Path -Value ($Env:Path + ";C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin")
+$Env:Path="C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin;C:\ruby27\bin;C:\ci-studio-common\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\ProgramData\chocolatey\bin;C:\Program Files\Git\cmd;C:\Users\ContainerAdministrator\AppData\Local\Microsoft\WindowsApps;C:\Go\bin;C:\Users\ContainerAdministrator\go\bin"
+
+winrm quickconfig -q
+
+echo "+++ bundle exec rake spec:integration"
+bundle exec rake spec:integration
+if (-not $?) { throw "Chef integration specs failing." }
diff --git a/.expeditor/scripts/bk_win_prep.ps1 b/.expeditor/scripts/bk_win_prep.ps1
new file mode 100644
index 0000000000..37796da468
--- /dev/null
+++ b/.expeditor/scripts/bk_win_prep.ps1
@@ -0,0 +1,15 @@
+echo "--- system details"
+$Properties = 'Caption', 'CSName', 'Version', 'BuildType', 'OSArchitecture'
+Get-CimInstance Win32_OperatingSystem | Select-Object $Properties | Format-Table -AutoSize
+
+echo "ruby version:"
+ruby -v
+if (-not $?) { throw "Can't run Ruby. Is it installed?" }
+
+echo "bundler version: "
+bundle --version
+if (-not $?) { throw "Can't run Bundler. Is it installed?" }
+
+echo "--- bundle install"
+bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+if (-not $?) { throw "Unable to install gem dependencies" } \ No newline at end of file
diff --git a/.expeditor/scripts/bk_win_unit.ps1 b/.expeditor/scripts/bk_win_unit.ps1
new file mode 100644
index 0000000000..f1f28ade05
--- /dev/null
+++ b/.expeditor/scripts/bk_win_unit.ps1
@@ -0,0 +1,9 @@
+$CurrentDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
+$PrepScript = Join-Path $CurrentDirectory "bk_win_prep.ps1"
+Invoke-Expression $PrepScript
+
+echo "+++ bundle exec rake"
+bundle exec rake spec:unit
+if (-not $?) { throw "Chef unit tests failing." }
+bundle exec rake component_specs
+if (-not $?) { throw "Chef component specs failing." }
diff --git a/.expeditor/scripts/ensure-minimum-viable-hab.ps1 b/.expeditor/scripts/ensure-minimum-viable-hab.ps1
new file mode 100644
index 0000000000..10bfeb0fa8
--- /dev/null
+++ b/.expeditor/scripts/ensure-minimum-viable-hab.ps1
@@ -0,0 +1,8 @@
+[Version]$hab_version = (hab --version).split(" ")[1].split("/")[0]
+if ($hab_version -lt [Version]"0.85.0" ) {
+ Write-Host "--- :habicat: Installing the version of Habitat required"
+ install-habitat --version 0.85.0.20190916
+ if (-not $?) { throw "Hab version is older than 0.85 and could not update it." }
+} else {
+ Write-Host "--- :habicat: :thumbsup: Minimum required version of Habitat already installed"
+}
diff --git a/.expeditor/scripts/install-hab.sh b/.expeditor/scripts/install-hab.sh
new file mode 100755
index 0000000000..75e910bfab
--- /dev/null
+++ b/.expeditor/scripts/install-hab.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+export HAB_LICENSE="accept"
+export HAB_NONINTERACTIVE="true"
+
+hab_target="$1"
+
+# print error message followed by usage and exit
+error () {
+ local message="$1"
+
+ echo -e "\nERROR: ${message}\n" >&2
+
+ exit 1
+}
+
+[[ -n "$hab_target" ]] || error 'no hab target provided'
+
+echo "--- :habicat: Installing latest version of Habitat"
+rm -rf /hab
+curl https://raw.githubusercontent.com/habitat-sh/habitat/master/components/hab/install.sh | bash -s -- -t "$hab_target"
+hab license accept
diff --git a/.expeditor/scripts/verify-plan.ps1 b/.expeditor/scripts/verify-plan.ps1
new file mode 100644
index 0000000000..614d472964
--- /dev/null
+++ b/.expeditor/scripts/verify-plan.ps1
@@ -0,0 +1,37 @@
+#!/usr/bin/env powershell
+
+#Requires -Version 5
+
+param(
+ # The name of the plan that is to be built.
+ [string]$Plan
+)
+
+$env:HAB_ORIGIN = 'ci'
+$Plan = 'chef-infra-client'
+
+Write-Host "--- :8ball: :windows: Verifying $Plan"
+
+powershell -File "./.expeditor/scripts/ensure-minimum-viable-hab.ps1"
+if (-not $?) { throw "Could not ensure the minimum hab version required is installed." }
+
+Write-Host "--- :key: Generating fake origin key"
+hab origin key generate $env:HAB_ORIGIN
+
+$project_root = "$(git rev-parse --show-toplevel)"
+Set-Location $project_root
+
+Write-Host "--- :construction: Building $Plan"
+$env:DO_CHECK=$true; hab pkg build .
+if (-not $?) { throw "unable to build"}
+
+. results/last_build.ps1
+if (-not $?) { throw "unable to determine details about this build"}
+
+Write-Host "--- :hammer_and_wrench: Installing $pkg_ident"
+hab pkg install results/$pkg_artifact
+if (-not $?) { throw "unable to install this build"}
+
+Write-Host "--- :mag_right: Testing $Plan"
+powershell -File "./habitat/tests/test.ps1" -PackageIdentifier $pkg_ident
+if (-not $?) { throw "package didn't pass the test suite" }
diff --git a/.expeditor/scripts/verify-plan.sh b/.expeditor/scripts/verify-plan.sh
new file mode 100755
index 0000000000..b207334267
--- /dev/null
+++ b/.expeditor/scripts/verify-plan.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+export HAB_ORIGIN='ci'
+export PLAN='chef-infra-client'
+export CHEF_LICENSE="accept-no-persist"
+export HAB_LICENSE="accept-no-persist"
+export HAB_NONINTERACTIVE="true"
+
+# print error message followed by usage and exit
+error () {
+ local message="$1"
+
+ echo -e "\nERROR: ${message}\n" >&2
+
+ exit 1
+}
+
+echo "--- :8ball: :linux: Verifying $PLAN"
+project_root="$(git rev-parse --show-toplevel)"
+
+echo "--- :key: Generating fake origin key"
+hab origin key generate "$HAB_ORIGIN"
+
+echo "--- :construction: Building $PLAN (solely for verification testing)"
+(
+ cd "$project_root" || error 'cannot change directory to project root'
+ DO_CHECK=true hab pkg build . || error 'unable to build'
+)
+
+source "${project_root}/results/last_build.env" || error 'unable to determine details about this build'
+
+echo "--- :hammer_and_wrench: Installing $pkg_ident"
+hab pkg install "${project_root}/results/$pkg_artifact" || error 'unable to install this build'
+
+echo "--- :mag_right: Testing $PLAN"
+${project_root}/habitat/tests/test.sh "$pkg_ident" || error 'failures during test of executables'
diff --git a/.expeditor/update_dep.sh b/.expeditor/update_dep.sh
new file mode 100644
index 0000000000..baf950c408
--- /dev/null
+++ b/.expeditor/update_dep.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+############################################################################
+# What is this script?
+#
+# Chef uses a workflow tool called Expeditor to manage version bumps, changelogs
+# and releases. When a dependency of chef is released, expeditor is triggered
+# against this repository to run this script. It bumps our gem lock files and opens
+# a PR. That way humans can do hard work and bots can open gem bump PRs.
+############################################################################
+
+set -evx
+
+function new_gem_included() {
+ git diff | grep -E '^\+' | grep "${EXPEDITOR_GEM_NAME} (${EXPEDITOR_VERSION})"
+}
+
+branch="expeditor/${EXPEDITOR_GEM_NAME}_${EXPEDITOR_VERSION}"
+git checkout -b "$branch"
+
+tries=12
+for (( i=1; i<=$tries; i+=1 )); do
+ bundle lock --update
+ new_gem_included && break || sleep 20
+ if [ $i -eq $tries ]; then
+ echo "Searching for '${EXPEDITOR_GEM_NAME} (${EXPEDITOR_VERSION})' ${i} times and did not find it"
+ exit 1
+ else
+ echo "Searched ${i} times for '${EXPEDITOR_GEM_NAME} (${EXPEDITOR_VERSION})'"
+ fi
+done
+
+git add .
+
+# give a friendly message for the commit and make sure it's noted for any future audit of our codebase that no
+# DCO sign-off is needed for this sort of PR since it contains no intellectual property
+git commit --message "Bump $EXPEDITOR_GEM_NAME to $EXPEDITOR_VERSION" --message "This pull request was triggered automatically via Expeditor when $EXPEDITOR_GEM_NAME $EXPEDITOR_VERSION was promoted to Rubygems." --message "This change falls under the obvious fix policy so no Developer Certificate of Origin (DCO) sign-off is required."
+
+open_pull_request "$EXPEDITOR_BRANCH"
+
+# Get back to master and cleanup the leftovers - any changed files left over at the end of this script will get committed to master.
+git checkout -
+git branch -D "$branch"
diff --git a/.expeditor/update_dockerfile.sh b/.expeditor/update_dockerfile.sh
new file mode 100755
index 0000000000..a9d266ff74
--- /dev/null
+++ b/.expeditor/update_dockerfile.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+############################################################################
+# What is this script?
+#
+# Chef uses a workflow tool called Expeditor to manage version bumps, changelogs
+# and releases. When the current release of Chef is promoted to stable this script
+# is run by Expeditor to update the version in the Dockerfile to match the stable
+# release.
+############################################################################
+
+set -evx
+
+sed -i -r "s/^ARG VERSION=.+/ARG VERSION=${EXPEDITOR_VERSION}/" Dockerfile
diff --git a/.expeditor/update_version.sh b/.expeditor/update_version.sh
new file mode 100755
index 0000000000..2a75c4b2f9
--- /dev/null
+++ b/.expeditor/update_version.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+############################################################################
+# What is this script?
+#
+# Chef uses a workflow tool called Expeditor to manage version bumps, changelogs
+# and releases. After a PR is merged in Chef Expeditor calls this script to update
+# the PATCH version in the VERSION file as well as the version.rb file in both chef
+# and chef-config. When that's done it bundle updates to pull in that new chef-config.
+############################################################################
+
+set -evx
+
+VERSION=$(cat VERSION)
+
+sed -i -r "s/^(\s*)VERSION = \".+\"/\1VERSION = \"${VERSION}\"/" chef-config/lib/chef-config/version.rb
+sed -i -r "s/^(\s*)VERSION = \".+\"/\1VERSION = \"${VERSION}\"/" chef-bin/lib/chef-bin/version.rb
+sed -i -r "s/^(\s*)VERSION = \".+\"/\1VERSION = \"${VERSION}\"/" chef-utils/lib/chef-utils/version.rb
+sed -i -r "s/VersionString\.new\(\".+\"\)/VersionString.new(\"${VERSION}\")/" lib/chef/version.rb
+
+# Update the version inside Gemfile.lock
+bundle update chef chef-config chef-utils --jobs=7 --conservative
+
+# Once Expeditor finishes 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/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml
new file mode 100644
index 0000000000..242e87e891
--- /dev/null
+++ b/.expeditor/verify.pipeline.yml
@@ -0,0 +1,617 @@
+---
+expeditor:
+ cached_folders:
+ - vendor
+ defaults:
+ buildkite:
+ retry:
+ automatic:
+ limit: 1
+ timeout_in_minutes: 45
+
+steps:
+
+#########################################################################
+ # Tests Ruby 2.7
+#########################################################################
+
+- label: "Integration Ubuntu 18.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+ privileged: true
+
+- label: "Functional Ubuntu 18.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - apt-get update -y
+ - apt-get install -y cron locales # needed for functional tests to pass
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+ privileged: true
+
+- label: "Unit Ubuntu 18.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+- label: "Integration Ubuntu 20.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-20.04:2.7
+ privileged: true
+
+- label: "Functional Ubuntu 20.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - apt-get update -y
+ - apt-get install -y cron locales # needed for functional tests to pass
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-20.04:2.7
+ privileged: true
+
+- label: "Unit Ubuntu 20.04 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-20.04:2.7
+
+- label: "Integration CentOS 7 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/centos-7:2.7
+ privileged: true
+
+- label: "Functional CentOS 7 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - yum install -y crontabs e2fsprogs
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/centos-7:2.7
+ privileged: true
+
+- label: "Unit CentOS 7 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/centos-7:2.7
+
+- label: "Integration openSUSE 15 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - zypper install -y cron insserv-compat
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/opensuse-15:2.7
+ privileged: true
+
+- label: "Functional openSUSE 15 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - zypper install -y cronie insserv-compat
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/opensuse-15:2.7
+ privileged: true
+
+- label: "Unit openSUSE 15 :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - zypper install -y cron insserv-compat
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/opensuse-15:2.7
+
+- label: "Integration Fedora :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/fedora-latest:2.7
+ privileged: true
+
+- label: "Functional Fedora :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - dnf install -y crontabs e2fsprogs
+ - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/fedora-latest:2.7
+ privileged: true
+ environment:
+ - FORCE_FFI_YAJL=ext
+ - CHEF_LICENSE=accept-no-persist
+
+- label: "Unit Fedora :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/fedora-latest:2.7
+
+- label: "Functional Windows :ruby: 2.7"
+ commands:
+ - .expeditor/scripts/bk_win_functional.ps1
+ expeditor:
+ executor:
+ windows:
+ privileged: true
+ single-use: true
+ shell: ["powershell", "-Command"]
+
+- label: "Integration Windows :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_win_integration.ps1
+ expeditor:
+ executor:
+ docker:
+ host_os: windows
+ image: rubydistros/windows-2019:2.7
+ environment:
+ - FORCE_FFI_YAJL=ext
+ - CHEF_LICENSE=accept-no-persist
+ shell: ["powershell", "-Command"]
+
+- label: "Chocolatey Windows :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_run_choco.ps1
+ expeditor:
+ executor:
+ docker:
+ host_os: windows
+ image: rubydistros/windows-2019:2.7
+ shell: ["powershell", "-Command"]
+
+- label: "Unit Windows :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_win_unit.ps1
+ expeditor:
+ executor:
+ docker:
+ host_os: windows
+ image: rubydistros/windows-2019:2.7
+ environment:
+ - FORCE_FFI_YAJL=ext
+ - CHEF_LICENSE=accept-no-persist
+ shell: ["powershell", "-Command"]
+
+#########################################################################
+# Tests Ruby 2.6
+#########################################################################
+
+- label: "Chefstyle :ruby: 2.6"
+ commands:
+ - gem install bundler -v 2.1.4 # match Ruby 2.7 bundler
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package ruby_prof
+ - bundle exec rake style
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.6
+
+- label: "Integration :ruby: 2.6"
+ commands:
+ - gem install bundler -v 2.1.4 # match Ruby 2.7 bundler
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:integration
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.6
+ privileged: true
+
+- label: "Functional :ruby: 2.6"
+ commands:
+ - gem install bundler -v 2.1.4 # match Ruby 2.7 bundler
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - apt-get update -y
+ - apt-get install -y cron locales net-tools # needed for functional tests to pass
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:functional
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.6
+ privileged: true
+
+- label: "Unit :ruby: 2.6"
+ commands:
+ - gem install bundler -v 2.1.4 # match Ruby 2.7 bundler
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec rake spec:unit
+ - bundle exec rake component_specs
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.6
+
+#########################################################################
+ # EXTERNAL GEM TESTING
+#########################################################################
+
+- label: "chef-sugar gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test chef/chef-sugar master rake
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+- label: "chef-zero gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test chef/chef-zero master rake pedant
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+ environment:
+ - PEDANT_OPTS=--skip-oc_id
+ - CHEF_FS=true
+
+- label: "cheffish gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test chef/cheffish master rake spec
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+- label: "chefspec gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test chefspec/chefspec master rake
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+- label: "knife-windows gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test chef/knife-windows master rake spec
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+- label: "berkshelf gem :ruby: 2.7"
+ commands:
+ - /workdir/.expeditor/scripts/bk_container_prep.sh
+ - apt-get update -y
+ - apt-get install -y graphviz
+ - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package
+ - bundle exec tasks/bin/run_external_test berkshelf/berkshelf master rake
+ expeditor:
+ executor:
+ docker:
+ image: rubydistros/ubuntu-18.04:2.7
+
+#########################################################################
+ # START TEST KITCHEN ONLY
+#########################################################################
+
+- label: "Kitchen: Amazon Linux 2"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-amazonlinux-2
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Ubuntu 18.04"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-ubuntu-1804
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Ubuntu 20.04"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-ubuntu-2004
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Ubuntu 21.04"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-ubuntu-2104
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Debian 9"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-debian-9
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Debian 10"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-debian-10
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Debian 11"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-debian-11
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: CentOS 6"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-centos-6
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: CentOS 7"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-centos-7
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: CentOS 8"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-centos-8
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Oracle Linux 7"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-oraclelinux-7
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Oracle Linux 8"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-oraclelinux-8
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: Fedora latest"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-fedora-latest
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Kitchen: openSUSE Leap: 15"
+ commands:
+ - .expeditor/scripts/bk_linux_exec.sh
+ - cd kitchen-tests
+ - /opt/omnibus-toolchain/bin/bundle exec kitchen test end-to-end-opensuse-leap-15
+ artifact_paths:
+ - $PWD/.kitchen/logs/kitchen.log
+ env:
+ KITCHEN_YAML: kitchen.yml
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: "Spellcheck"
+ commands:
+ - npm install -g cspell
+ - rake -f tasks/spellcheck.rb spellcheck
+ soft_fail: true
+ expeditor:
+ executor:
+ docker:
+
+- label: ":habicat: Linux plan"
+ commands:
+ - sudo ./.expeditor/scripts/install-hab.sh 'x86_64-linux'
+ - sudo ./.expeditor/scripts/verify-plan.sh
+ timeout_in_minutes: 60
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: ":habicat: Linux plan (kernel2)"
+ commands:
+ - sudo ./.expeditor/scripts/install-hab.sh 'x86_64-linux-kernel2'
+ - sudo ./.expeditor/scripts/verify-plan.sh
+ timeout_in_minutes: 60
+ expeditor:
+ executor:
+ linux:
+ privileged: true
+ single-use: true
+
+- label: ":habicat: Windows plan"
+ commands:
+ - ./.expeditor/scripts/verify-plan.ps1
+ timeout_in_minutes: 60
+ expeditor:
+ executor:
+ windows:
+ privileged: true
+ single-use: true
+ shell: ["powershell", "-Command"]
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..ab9841707a
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,5 @@
+# Order is important. The last matching pattern has the most precedence.
+
+* @chef/chef-infra-reviewers @chef/chef-infra-approvers @chef/chef-infra-owners
+.expeditor/ @chef/jex-team
+*.md @chef/docs-team
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 562e43e797..0000000000
--- a/.github/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/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md
new file mode 100644
index 0000000000..9b62551d8b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md
@@ -0,0 +1,43 @@
+---
+name: 🐛 Bug Report
+about: If something isn't working as expected 🤔.
+labels: "Status: Untriaged"
+---
+
+<!---
+!!!!!! 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/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md
new file mode 100644
index 0000000000..9f4a958fea
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md
@@ -0,0 +1,40 @@
+---
+name: Design Proposal
+about: I have a significant change I would like to propose and discuss before starting
+labels: "Status: Untriaged"
+---
+
+### When a Change Needs a Design Proposal
+
+A design proposal should be opened any time a change meets one of the following qualifications:
+
+- Significantly changes the user experience of a project in a way that impacts users.
+- Significantly changes the underlying architecture of the project in a way that impacts other developers.
+- Changes the development or testing process of the project such as a change of CI systems or test frameworks.
+
+### Why We Use This Process
+
+- Allows all interested parties (including any community member) to discuss large impact changes to a project.
+- Serves as a durable paper trail for discussions regarding project architecture.
+- Forces design discussions to occur before PRs are created.
+- Reduces PR refactoring and rejected PRs.
+
+---
+
+<!--- Proposal description and rationale. -->
+
+## Motivation
+
+<!---
+ As a <<user_profile>>,
+ I want to <<functionality>>,
+ so that <<benefit>>.
+ -->
+
+## Specification
+
+<!--- A detailed description of the planned implementation. -->
+
+## Downstream Impact
+
+<!--- Which other tools will be impacted by this work? -->
diff --git a/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..65bf5a06b1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md
@@ -0,0 +1,17 @@
+---
+name: 🚀 Enhancement Request
+about: I have a suggestion (and may want to implement it 🙂)!
+labels: "Status: Untriaged"
+---
+
+### Describe the Enhancement:
+<!--- What you are trying to achieve that you can't? -->
+
+### Describe the Need:
+<!--- What kind of user do you believe would utilize this enhancement, and how many users might want this functionality -->
+
+### Current Alternative
+<!--- Is there a current alternative that you can utilize to workaround the lack of this enhancement -->
+
+### Can We Help You Implement This?:
+<!--- The best way to ensure your enhancement is built is to help implement the enhancement yourself. If you're interested in helping out we'd love to give you a hand to make this possible. Let us know if there's something you need. -->
diff --git a/.github/ISSUE_TEMPLATE/RESOURCE_REQUEST.md b/.github/ISSUE_TEMPLATE/RESOURCE_REQUEST.md
new file mode 100644
index 0000000000..50a8e2af0d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/RESOURCE_REQUEST.md
@@ -0,0 +1,26 @@
+---
+name: 💪 Resource Request
+about: I have a suggestion for a new resource in Chef (and may want to implement it 🙌)!
+labels: "Status: Untriaged"
+---
+
+### Core Chef Resource Checklist
+
+Before suggesting a resource for inclusion please make sure your suggestion meets these criteria for resources built into Chef:
+ - [ ] Automates an operating system component that ships by default on systems such as authentication, raid, disk partitions, firewalls, containers, or virtualization systems.
+ - [ ] Does not attempt automate 3rd party applications such as database, web, or application servers, which are best suited for cookbooks due to their fast moving nature.
+
+### Describe the resource:
+<!--- Tell us about the resource -->
+
+### Why should this be included out of the box?:
+<!--- Why do you believe this is best suited to be included in the chef-client vs. a cookbook? -->
+
+### What operating systems would it run on?
+<!--- Is this a general purpose resource that would run on every operating systems or is it specific to an OS such as Linux, macOS, or Windows? -->
+
+### Current cookbook implementation:
+<!--- Is there currently a cookbook that ships with this resource? If so please let us know. We'll need full permission from the authors and a compatible license in order to move a resource from a cookbook. -->
+
+### Can We Help You Implement This?:
+<!--- The best way to move a resource into Chef is to help move it yourself. If you're interested in helping out we'd love to give you a hand to make this possible. Let us know if there's something you need. -->
diff --git a/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
new file mode 100644
index 0000000000..68c5664792
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
@@ -0,0 +1,12 @@
+---
+name: 🤗 Support Question
+about: If you have a question 💬, please check out our Slack!
+---
+
+We use GitHub issues to track bugs and feature requests. If you need help please post to our Mailing List or join the Chef Community Slack.
+
+ * Chef Community Slack at http://community-slack.chef.io/.
+ * Chef Mailing List https://discourse.chef.io/
+
+
+ Support issues opened here will be closed and redirected to Slack or Discourse.
diff --git a/.github/lock.yml b/.github/lock.yml
new file mode 100644
index 0000000000..5edc4037bd
--- /dev/null
+++ b/.github/lock.yml
@@ -0,0 +1 @@
+daysUntilLock: 14
diff --git a/.gitignore b/.gitignore
index 1e60843467..fdf31a58e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,11 @@ tags
*/tags
*~
.chef
+results
+*.swp
+
+# Docker
+.dobi
# You should check in your Gemfile.lock in applications, and not in gems
external_tests/*.lock
@@ -15,7 +20,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 +29,7 @@ binstubs/
# IDE files
.project
+.idea
# Documentation
_site/*
@@ -37,9 +43,53 @@ 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
+spec/data/chef_guid
/config/
+
+vendor/
+kitchen-tests/vendor
+
+# Visual Studio Code files
+.vscode
+
+# ignore nodes generated during local testing
+nodes/
+
+# chef-config
+chef-config/.bundle
+chef-config/Gemfile.lock
+chef-config/pkg
+
+# chef-bin
+chef-bin/.bundle
+chef-bin/Gemfile.lock
+chef-bin/pkg
+
+# chef-utils
+chef-utils/.bundle
+chef-utils/Gemfile.lock
+chef-utils/pkg
+
+# auto generated docs from the docs.rb rake task
+docs_site
+
+# Rendered Templates
+ext/win32-eventlog/chef-log.man
+distro/powershell/chef/chef.psm1
+
+# tool logs
+mkmf.log
+
+# ignore byebug command history file.
+.byebug_history
+
+# our custom dictionary pulled from https://github.com/chef/chef_dictionary/
+chef_dictionary.txt
diff --git a/.mailmap b/.mailmap
index 614100091a..142d587a4e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -168,3 +168,7 @@ 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/.rspec b/.rspec
deleted file mode 100644
index eb3ef03653..0000000000
--- a/.rspec
+++ /dev/null
@@ -1,2 +0,0 @@
---color
--fd
diff --git a/.rubocop.yml b/.rubocop.yml
index 3c2a48e548..eb41ed78fa 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,6 +1,32 @@
AllCops:
+ TargetRubyVersion: 2.6
Exclude:
- "spec/data/**/*"
- "vendor/**/*"
- "pkg/**/*"
- "chef-config/pkg/**/*"
+ - "habitat/**/*"
+Security/Eval:
+ Enabled: false
+Lint/UselessAssignment:
+ Enabled: false
+Lint/DeprecatedClassMethods:
+ Enabled: false
+Lint/AmbiguousRegexpLiteral:
+ Enabled: false
+Lint/AssignmentInCondition:
+ Enabled: false
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+Lint/ShadowingOuterLocalVariable:
+ Enabled: false
+Lint/IneffectiveAccessModifier:
+ Enabled: false
+
+# set additional paths
+Chef/Ruby/UnlessDefinedRequire:
+ Include:
+ - 'lib/**/*'
+ - 'chef-bin/**/*'
+ - 'chef-config/lib/**/*'
+ - 'chef-utils/lib/**/*'
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 95b69ed215..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,288 +0,0 @@
-language: ruby
-sudo: false
-cache: bundler
-
-# 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)
- - 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 || true
-
-# do not run expensive spec tests on PRs, only on branches
-branches:
- only:
- - master
- - 10-stable
- - 11-stable
-
-env:
- global:
- - FORCE_FFI_YAJL=ext
- - BUNDLE_ENABLE_TRAMPOLINE=1
-
-matrix:
- include:
- - rvm: 2.2.5
- sudo: true
- script: sudo -E $(which bundle) exec rake spec;
- # also remove integration / external tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - rvm: 2.3.1
- sudo: true
- script: sudo -E $(which bundle) exec rake spec;
- # also remove integration / external tests
- 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: sudo -E $(which bundle) exec rake spec;
- # also remove integration / external tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - env:
- CHEFSTYLE: 1
- rvm: 2.3.1
- script: bundle exec rake style
- # also remove integration / external tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- - env:
- AUDIT_CHECK: 1
- rvm: 2.3.1
- script: bundle exec bundle-audit check --update
- # also remove integration / external tests
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- #
- # External tests
- #
- - env:
- TEST_GEM: chef-provisioning
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.3.1
- - env:
- TEST_GEM: chef-provisioning-aws
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.3.1
- - env:
- TEST_GEM: chef-sugar
- script: tasks/bin/run_external_test $TEST_GEM rake
- rvm: 2.3.1
- - env:
- - TEST_GEM: chef-zero
- script: tasks/bin/run_external_test $TEST_GEM rake spec cheffs
- rvm: 2.3.1
- - env:
- TEST_GEM: cheffish
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.3.1
- - 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.3.1
- - env:
- TEST_GEM: foodcritic
- script: tasks/bin/run_external_test $TEST_GEM rake test
- rvm: 2.3.1
- - env:
- TEST_GEM: halite
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.3.1
- - env:
- TEST_GEM: knife-windows
- script: tasks/bin/run_external_test $TEST_GEM rake unit_spec
- rvm: 2.3.1
- - env:
- TEST_GEM: poise
- script: tasks/bin/run_external_test $TEST_GEM rake spec
- rvm: 2.3.1
- ### START TEST KITCHEN ONLY ###
- #
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test ubuntu-1204
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - UBUNTU=12.04
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test ubuntu-1404
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - UBUNTU=14.04
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test ubuntu-1604
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - UBUNTU=16.04
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test debian-7
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - DEBIAN=7
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test debian-8
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - DEBIAN=8
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test centos-6
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - CENTOS=6
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test centos-7
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - CENTOS=7
- - KITCHEN_YAML=.kitchen.travis.yml
- - rvm: 2.3.1
- 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)
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- before_script:
- - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
- - cd kitchen-tests
- script:
- - bundle exec kitchen test fedora-23
- after_failure:
- - cat .kitchen/logs/kitchen.log
- env:
- - FEDORA=23
- - KITCHEN_YAML=.kitchen.travis.yml
- ### END TEST KITCHEN ONLY ###
- - rvm: 2.3.1
- sudo: required
- dist: trusty
- 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)
- - sudo apt-get update
- - sudo apt-get -y install squid3 git curl
- bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
- env:
- - PROXY_TESTS_DIR=proxy_tests/files/default/scripts
- - PROXY_TESTS_REPO=$PROXY_TESTS_DIR/repo
- script:
- - bundle exec chef-client --version
- - git clone https://github.com/chef/proxy_tests.git
- - rvmsudo -E bundle exec bash $PROXY_TESTS_DIR/run_tests.sh chef_client \* \* /tmp/out.txt
- after_script:
- - cat /tmp/out.txt
- - sudo cat /var/log/squid3/cache.log
- - sudo cat /var/log/squid3/access.log
-
- allow_failures:
- - rvm: rbx
-
-notifications:
- on_change: true
- on_failure: true
- on_success: change
- on_pull_requests: false
- irc:
- channels:
- - chat.freenode.net#chef-hacking
diff --git a/CBGB.md b/CBGB.md
deleted file mode 100644
index 2ce26fc3ab..0000000000
--- a/CBGB.md
+++ /dev/null
@@ -1,40 +0,0 @@
-<!-- This is a generated file. Please do not edit directly -->
-<!-- Modify CBGB.toml file and run `rake cbgb:generate` to regenerate -->
-
-# Chef Board of Governance (CBGB)
-
- Chef was designed from the outset to have a very open structure, including open design, open contribution, and consistent use of tools across the project. Given the large numbers of contributors, users, and companies with a stake in the future of the project, Chef leadership has established an advisory board, as part of its long term commitment to open governance.
-
- The Chef Board of Governance (CBGB) shall advise the Leadership on matters related to supporting the long-term governance, structure, and roadmap of the Project.
-
-More information can be found in the [Chef Board of Governance RFC](Chef Board of Governance).
-
-# Board of Governors
-
-## Project Lead
-
-* [Adam Jacob](https://github.com/adamhjk)
-
-### Users/Contributors (4)
-
-* [Ranjib Dey](https://github.com/ranjib)
-* [Doug Ireton](https://github.com/dougireton)
-* [Noah Kantrowitz](https://github.com/coderanger)
-* [Charity Majors](https://github.com/charity)
-
-
-### Corporate Contributors (4)
-
-* Etsy - Katherine Daniels
-* Facebook - Phil Dibowitz
-* Nordstrom - Mark Ayers
-* PagerDuty - Evan Gilman
-
-
-### Lieutenants (3)
-
-* [Jon Cowie](https://github.com/jonlives)
-* [Joshua Timberman](https://github.com/jtimberman)
-* [Seth Vargo](https://github.com/sethvargo)
-
-
diff --git a/CBGB.toml b/CBGB.toml
deleted file mode 100644
index 07e9c9a25a..0000000000
--- a/CBGB.toml
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-# This file is structured to be consumed by both humans and computers.
-# It is a TOML document containing Markdown
-#
-[Preamble]
- title = "Chef Board of Governance (CBGB)"
- text = """
- Chef was designed from the outset to have a very open structure, including open design, open contribution, and consistent use of tools across the project. Given the large numbers of contributors, users, and companies with a stake in the future of the project, Chef leadership has established an advisory board, as part of its long term commitment to open governance.
-
- The Chef Board of Governance (CBGB) shall advise the Leadership on matters related to supporting the long-term governance, structure, and roadmap of the Project.
-
-More information can be found in the [Chef Board of Governance RFC](Chef Board of Governance).
-"""
-
-[Org]
- [Org.Lead]
- title = "Project Lead"
- person = "adamhjk"
-
- [Org.Contributors]
- title = "Users/Contributors (4)"
- governers = [
- "ranjibdey",
- "dougireton",
- "coderanger",
- "charitymajors"
- ]
-
- [Org.Corporate-Contributors]
- title = "Corporate Contributors (4)"
- governers = [
- "etsy",
- "facebook",
- "nordstrom",
- "pagerduty"
- ]
-
- [Org.Lieutenants]
- title = "Lieutenants (3)"
- governers = [
- "jonlives",
- "jtimberman",
- "sethvargo"
- ]
-
-[people]
- [people.adamhjk]
- Name = "Adam Jacob"
- GitHub = "adamhjk"
- IRC = "holoway"
-
- [people.jonlives]
- Name = "Jon Cowie"
- GitHub = "jonlives"
- IRC = "jonlives"
-
- [people.coderanger]
- Name = "Noah Kantrowitz"
- GitHub = "coderanger"
-
- [people.jtimberman]
- Name = "Joshua Timberman"
- GitHub = "jtimberman"
-
- [people.ranjibdey]
- Name = "Ranjib Dey"
- GitHub = "ranjib"
-
- [people.sethvargo]
- Name = "Seth Vargo"
- GitHub = "sethvargo"
-
- [people.dougireton]
- Name = "Doug Ireton"
- GitHub = "dougireton"
-
- [people.charitymajors]
- Name = "Charity Majors"
- GitHub = "charity"
-
-[corporations]
- [corporations.etsy]
- Name = "Etsy"
- Person = "Katherine Daniels"
-
- [corporations.facebook]
- Name = "Facebook"
- Person = "Phil Dibowitz"
-
- [corporations.nordstrom]
- Name = "Nordstrom"
- Person = "Mark Ayers"
-
- [corporations.pagerduty]
- Name = "PagerDuty"
- Person = "Evan Gilman"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f76184539f..78babc6b97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,2779 @@
-# Change Log
+<!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
+This changelog lists individual merged pull requests to Chef Infra Client and geared towards developers. For a list of significant changes per release see the [Chef Infra Client Release Notes](https://docs.chef.io/release_notes_client/).
+
+<!-- latest_release 17.0.32 -->
+## [v17.0.32](https://github.com/chef/chef/tree/v17.0.32) (2021-01-06)
+
+#### Merged Pull Requests
+- Add Chef Infra Client 15.15 release notes [#10847](https://github.com/chef/chef/pull/10847) ([tas50](https://github.com/tas50))
+<!-- latest_release -->
+
+<!-- release_rollup since=16.8.14 -->
+### Changes not yet released to stable
+
+#### Merged Pull Requests
+- Add Chef Infra Client 15.15 release notes [#10847](https://github.com/chef/chef/pull/10847) ([tas50](https://github.com/tas50)) <!-- 17.0.32 -->
+- load_current_resource for systemd service more efficiently [#10776](https://github.com/chef/chef/pull/10776) ([joshuamiller01](https://github.com/joshuamiller01)) <!-- 17.0.31 -->
+- Add 16.9 release notes [#10835](https://github.com/chef/chef/pull/10835) ([tas50](https://github.com/tas50)) <!-- 17.0.30 -->
+- Simplify the code in the hostname resource [#10832](https://github.com/chef/chef/pull/10832) ([tas50](https://github.com/tas50)) <!-- 17.0.29 -->
+- Add a new `reposdir` property in the `yum_repository` resource [#10830](https://github.com/chef/chef/pull/10830) ([tas50](https://github.com/tas50)) <!-- 17.0.28 -->
+- Update Nokogiri to 1.11.0 [#10829](https://github.com/chef/chef/pull/10829) ([tas50](https://github.com/tas50)) <!-- 17.0.27 -->
+- Fix homebrew_cask for the new syntax [#10822](https://github.com/chef/chef/pull/10822) ([tas50](https://github.com/tas50)) <!-- 17.0.26 -->
+- Pin ffi for now to prevent i386 windows failures [#10810](https://github.com/chef/chef/pull/10810) ([tas50](https://github.com/tas50)) <!-- 17.0.25 -->
+- Add gemspec metadata [#10809](https://github.com/chef/chef/pull/10809) ([tas50](https://github.com/tas50)) <!-- 17.0.24 -->
+- Bump omnibus from `d13ae16` to `44f1303` in /omnibus [#10806](https://github.com/chef/chef/pull/10806) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) <!-- 17.0.23 -->
+- Bump omnibus-software from `457df26` to `869ef4e` in /omnibus [#10808](https://github.com/chef/chef/pull/10808) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) <!-- 17.0.22 -->
+- Bump test-kitchen from 2.8.0 to 2.9.0 in /omnibus [#10807](https://github.com/chef/chef/pull/10807) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) <!-- 17.0.21 -->
+- Remove the evals in the omnibus gemfile for Dependabot [#10805](https://github.com/chef/chef/pull/10805) ([tas50](https://github.com/tas50)) <!-- 17.0.20 -->
+- Update bcrypt_pbkdf to support Ruby 3 [#10804](https://github.com/chef/chef/pull/10804) ([tas50](https://github.com/tas50)) <!-- 17.0.19 -->
+- Remove the runtime dep on bundler [#10801](https://github.com/chef/chef/pull/10801) ([tas50](https://github.com/tas50)) <!-- 17.0.18 -->
+- Coerce uid to integer in Windows user resource. [#10803](https://github.com/chef/chef/pull/10803) ([phiggins](https://github.com/phiggins)) <!-- 17.0.17 -->
+- Consolidate Windows cert tests to improve CI runtime [#10794](https://github.com/chef/chef/pull/10794) ([phiggins](https://github.com/phiggins)) <!-- 17.0.16 -->
+- Fix escaping in doc string. [#10793](https://github.com/chef/chef/pull/10793) ([phiggins](https://github.com/phiggins)) <!-- 17.0.15 -->
+- Add Test Kitchen testing for Debian 11 [#10791](https://github.com/chef/chef/pull/10791) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Replace Ubuntu 20.10 testing with 21.04 [#10790](https://github.com/chef/chef/pull/10790) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Remove Test Kitchen tests for Amazon Linux 201X [#10789](https://github.com/chef/chef/pull/10789) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Ensure we can still install RHEL 7 GCC on RHEL 6 in testing [#10723](https://github.com/chef/chef/pull/10723) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Don&#39;t install util-linux into the containers in CI [#10787](https://github.com/chef/chef/pull/10787) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Removed unused rubygems from the omnibus overrides file [#10788](https://github.com/chef/chef/pull/10788) ([tas50](https://github.com/tas50)) <!-- 17.0.14 -->
+- Stop updating bundler in CI [#10786](https://github.com/chef/chef/pull/10786) ([tas50](https://github.com/tas50)) <!-- 17.0.13 -->
+- Fix dnf_package version and arch property support and idempotency [#9847](https://github.com/chef/chef/pull/9847) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 17.0.12 -->
+- Stub http requests in rubygems tests. [#10761](https://github.com/chef/chef/pull/10761) ([phiggins](https://github.com/phiggins)) <!-- 17.0.11 -->
+- Cleanup knife status [#10782](https://github.com/chef/chef/pull/10782) ([phiggins](https://github.com/phiggins)) <!-- 17.0.11 -->
+- Fix knife status json output for EC2 instance with no public IP. [#10781](https://github.com/chef/chef/pull/10781) ([phiggins](https://github.com/phiggins)) <!-- 17.0.10 -->
+- Refactor the code for windows_security_policy resource [#10699](https://github.com/chef/chef/pull/10699) ([chef-davin](https://github.com/chef-davin)) <!-- 17.0.9 -->
+- Cleanup some more disabled style cops [#10780](https://github.com/chef/chef/pull/10780) ([tas50](https://github.com/tas50)) <!-- 17.0.8 -->
+- Resolve Lint/ParenthesesAsGroupedExpression warnings [#10779](https://github.com/chef/chef/pull/10779) ([tas50](https://github.com/tas50)) <!-- 17.0.7 -->
+- Misc minor perf bumps [#10773](https://github.com/chef/chef/pull/10773) ([tas50](https://github.com/tas50)) <!-- 17.0.6 -->
+- Remove support for Ubuntu 16.04 [#10765](https://github.com/chef/chef/pull/10765) ([tas50](https://github.com/tas50)) <!-- 17.0.5 -->
+- Pull in the new FFI we need for M1 Macs [#10772](https://github.com/chef/chef/pull/10772) ([tas50](https://github.com/tas50)) <!-- 17.0.4 -->
+- Fixed cron_d resource ignoring sensitive property in Chef 17 [#10767](https://github.com/chef/chef/pull/10767) ([axl89](https://github.com/axl89)) <!-- 17.0.3 -->
+- Remove EOL RHEL 6 32bit builds [#10721](https://github.com/chef/chef/pull/10721) ([tas50](https://github.com/tas50)) <!-- 17.0.2 -->
+- Chef 17: Assume Rubygems 1.8 in the rubygems provider / specs [#10379](https://github.com/chef/chef/pull/10379) ([tas50](https://github.com/tas50)) <!-- 17.0.1 -->
+- Bump Chef Infra to 17 [#10760](https://github.com/chef/chef/pull/10760) ([tas50](https://github.com/tas50)) <!-- 17.0.0 -->
+- Update links to Compliance Phase documentation in log messages. [#10755](https://github.com/chef/chef/pull/10755) ([phiggins](https://github.com/phiggins)) <!-- 16.9.2 -->
+- Fix failures in ssl handler [#10751](https://github.com/chef/chef/pull/10751) ([phiggins](https://github.com/phiggins)) <!-- 16.9.1 -->
+- Cleanup bootstrap&#39;s trusted_certs_dir tests. [#10754](https://github.com/chef/chef/pull/10754) ([phiggins](https://github.com/phiggins)) <!-- 16.9.0 -->
+- Remove old test script. [#10746](https://github.com/chef/chef/pull/10746) ([phiggins](https://github.com/phiggins)) <!-- 16.8.19 -->
+- Update train to 3.4.4 [#10745](https://github.com/chef/chef/pull/10745) ([tas50](https://github.com/tas50)) <!-- 16.8.19 -->
+- locale: Update the locale-gen timeout to 1800s [#10743](https://github.com/chef/chef/pull/10743) ([tas50](https://github.com/tas50)) <!-- 16.8.18 -->
+- Add audit cookbook&#39;s chef_node_attribute_enabled to Compliance Phase. [#10735](https://github.com/chef/chef/pull/10735) ([phiggins](https://github.com/phiggins)) <!-- 16.8.17 -->
+- Improve our automated resource documentation generation [#10739](https://github.com/chef/chef/pull/10739) ([tas50](https://github.com/tas50)) <!-- 16.8.16 -->
+- knife bootstrap: Windows Trusted cert path slashes fix [#10740](https://github.com/chef/chef/pull/10740) ([axelrtgs](https://github.com/axelrtgs)) <!-- 16.8.15 -->
+<!-- release_rollup -->
+
+<!-- latest_stable_release -->
+## [v16.8.14](https://github.com/chef/chef/tree/v16.8.14) (2020-12-12)
+
+#### Merged Pull Requests
+- Bump libarchive to 3.5.0 [#10730](https://github.com/chef/chef/pull/10730) ([tas50](https://github.com/tas50))
+- Update openSSL to 1.0.2x [#10732](https://github.com/chef/chef/pull/10732) ([tas50](https://github.com/tas50))
+- Update libiconv to 1.16 [#10731](https://github.com/chef/chef/pull/10731) ([tas50](https://github.com/tas50))
+- Raise error and retry with PTY on sudo password prompt [#10728](https://github.com/chef/chef/pull/10728) ([rveznaver](https://github.com/rveznaver))
+- Fix broken code in compliance runner&#39;s send_report. [#10733](https://github.com/chef/chef/pull/10733) ([phiggins](https://github.com/phiggins))
+<!-- latest_stable_release -->
+
+## [v16.8.9](https://github.com/chef/chef/tree/v16.8.9) (2020-12-11)
+
+#### Merged Pull Requests
+- Resolve new spacing offenses in RuboCop 1.4 [#10691](https://github.com/chef/chef/pull/10691) ([tas50](https://github.com/tas50))
+- Use URI::DEFAULT_PARSER.make_regexp instead of URI.regexp [#10674](https://github.com/chef/chef/pull/10674) ([tas50](https://github.com/tas50))
+- Update the Docker file to use the RHEL 7 package [#10700](https://github.com/chef/chef/pull/10700) ([tas50](https://github.com/tas50))
+- replace usages of Cmdlet class with powershell_exec [#10683](https://github.com/chef/chef/pull/10683) ([mwrock](https://github.com/mwrock))
+- Enable git cache to speed up builds [#10689](https://github.com/chef/chef/pull/10689) ([tas50](https://github.com/tas50))
+- Add back macOS builds to the omnibus pipeline [#10701](https://github.com/chef/chef/pull/10701) ([tas50](https://github.com/tas50))
+- Update changelog with missing updates from previous prs [#10703](https://github.com/chef/chef/pull/10703) ([nkierpiec](https://github.com/nkierpiec))
+- Pin our InSpec version and use Chefstyle from rubygems for now [#10702](https://github.com/chef/chef/pull/10702) ([tas50](https://github.com/tas50))
+- Allow remote_file consider certificates stored under /etc/chef/trusted_certs [#10704](https://github.com/chef/chef/pull/10704) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Add new Compliance Phase replicating the functionality previously in the audit cookbook [#10547](https://github.com/chef/chef/pull/10547) ([phiggins](https://github.com/phiggins))
+- only test dsc_script on 64 bit and document that it will fail on 32 bit clients [#10708](https://github.com/chef/chef/pull/10708) ([mwrock](https://github.com/mwrock))
+- Switch back to chefstyle from github [#10709](https://github.com/chef/chef/pull/10709) ([tas50](https://github.com/tas50))
+- only run dsc_script functional tests on 64 bit ruby [#10710](https://github.com/chef/chef/pull/10710) ([mwrock](https://github.com/mwrock))
+- Update license-acceptance gem to 2.1.13 [#10714](https://github.com/chef/chef/pull/10714) ([tas50](https://github.com/tas50))
+- Update Train to 3.4.1 [#10716](https://github.com/chef/chef/pull/10716) ([tas50](https://github.com/tas50))
+- only run systemd unit tests on linux [#10718](https://github.com/chef/chef/pull/10718) ([mwrock](https://github.com/mwrock))
+- Fix for deprecation warning in knife ssh [#10717](https://github.com/chef/chef/pull/10717) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- windows_certificate: Add exportable option to pfx certificate [#10711](https://github.com/chef/chef/pull/10711) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update all deps to the latest [#10720](https://github.com/chef/chef/pull/10720) ([tas50](https://github.com/tas50))
+- Fix failing tests on Solaris / Enable Solaris builds again [#10719](https://github.com/chef/chef/pull/10719) ([mwrock](https://github.com/mwrock))
+- hostname: Avoid erroring out when hostname is not set on mac [#10724](https://github.com/chef/chef/pull/10724) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update to InSpec 4.24 [#10726](https://github.com/chef/chef/pull/10726) ([tas50](https://github.com/tas50))
+
+## [v16.7.61](https://github.com/chef/chef/tree/v16.7.61) (2020-11-26)
+
+#### Merged Pull Requests
+- Minor updates for documentation generation [#10505](https://github.com/chef/chef/pull/10505) ([tas50](https://github.com/tas50))
+- Update powershell_script description to match docs site. [#10508](https://github.com/chef/chef/pull/10508) ([phiggins](https://github.com/phiggins))
+- More resource documentation improvement [#10509](https://github.com/chef/chef/pull/10509) ([tas50](https://github.com/tas50))
+- Resource documentation updates from review [#10510](https://github.com/chef/chef/pull/10510) ([tas50](https://github.com/tas50))
+- Avoid declaring arrays in loops [#10513](https://github.com/chef/chef/pull/10513) ([tas50](https://github.com/tas50))
+- Update docs generation task to handle Chef 16 required format [#10518](https://github.com/chef/chef/pull/10518) ([tas50](https://github.com/tas50))
+- Avoid a slow hash merge [#10517](https://github.com/chef/chef/pull/10517) ([tas50](https://github.com/tas50))
+- Avoid using complex regexes when we can use include? [#10516](https://github.com/chef/chef/pull/10516) ([tas50](https://github.com/tas50))
+- Fix bad formatting in a deprecation message [#10521](https://github.com/chef/chef/pull/10521) ([tas50](https://github.com/tas50))
+- Remove constantize method from Chef::Mixin::ConvertToClassName [#10522](https://github.com/chef/chef/pull/10522) ([tas50](https://github.com/tas50))
+- Add required_ruby_version to chef-utils and chef-config [#10525](https://github.com/chef/chef/pull/10525) ([tas50](https://github.com/tas50))
+- Added functional test for windows_package with remote_file_attributes. [#10526](https://github.com/chef/chef/pull/10526) ([antima-gupta](https://github.com/antima-gupta))
+- Simplify the ifconfig provides statement on Ubuntu/Debian [#10528](https://github.com/chef/chef/pull/10528) ([tas50](https://github.com/tas50))
+- Refactor ResourceGuardInterpreter [#10494](https://github.com/chef/chef/pull/10494) ([phiggins](https://github.com/phiggins))
+- Move the alias for attribute to property right into the property mixin [#10520](https://github.com/chef/chef/pull/10520) ([tas50](https://github.com/tas50))
+- Add bridge property to ifconfig for RHEL based systems [#10529](https://github.com/chef/chef/pull/10529) ([tas50](https://github.com/tas50))
+- Test ifconfig in Test Kitchen and add examples to the resource [#10530](https://github.com/chef/chef/pull/10530) ([tas50](https://github.com/tas50))
+- Use a native resource in the ifconfig debian provider [#10533](https://github.com/chef/chef/pull/10533) ([tas50](https://github.com/tas50))
+- Update train-core &amp; pull in the faster MSI installs [#10534](https://github.com/chef/chef/pull/10534) ([tas50](https://github.com/tas50))
+- Fix LWRP build cache [#10536](https://github.com/chef/chef/pull/10536) ([tecracer-theinen](https://github.com/tecracer-theinen))
+- Minor gem cleanup for chef-bin/chef-utils/chef-config [#10539](https://github.com/chef/chef/pull/10539) ([tas50](https://github.com/tas50))
+- Remove the announcement rake task + minor task updates [#10540](https://github.com/chef/chef/pull/10540) ([tas50](https://github.com/tas50))
+- Remove the yard doc generation task / group [#10541](https://github.com/chef/chef/pull/10541) ([tas50](https://github.com/tas50))
+- Bump Ohai to 16.7 and cacerts to the latest [#10542](https://github.com/chef/chef/pull/10542) ([tas50](https://github.com/tas50))
+- ensure powershell_package commands are run with tls 1.2 [#10543](https://github.com/chef/chef/pull/10543) ([mwrock](https://github.com/mwrock))
+- Remove coderay and ffi-yajl-bench binstubs [#10544](https://github.com/chef/chef/pull/10544) ([tas50](https://github.com/tas50))
+- Remove unused monkeypatch on net/http. [#10548](https://github.com/chef/chef/pull/10548) ([phiggins](https://github.com/phiggins))
+- Remove an empty before block in a spec [#10550](https://github.com/chef/chef/pull/10550) ([tas50](https://github.com/tas50))
+- Remove references to monkeypatch method. [#10551](https://github.com/chef/chef/pull/10551) ([phiggins](https://github.com/phiggins))
+- Add Test Kitchen testing on Ubuntu 20.10 [#10553](https://github.com/chef/chef/pull/10553) ([tas50](https://github.com/tas50))
+- ifconfig is not compatible with Fedora 33 or later [#10555](https://github.com/chef/chef/pull/10555) ([tas50](https://github.com/tas50))
+- Add back Oracle 8 Test Kitchen testing [#10554](https://github.com/chef/chef/pull/10554) ([tas50](https://github.com/tas50))
+- Final batch of unified_mode providers [#10557](https://github.com/chef/chef/pull/10557) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec to 4.23.15 [#10559](https://github.com/chef/chef/pull/10559) ([tas50](https://github.com/tas50))
+- Merge repetitive conditionals [#10558](https://github.com/chef/chef/pull/10558) ([tas50](https://github.com/tas50))
+- Mount resources not idempotent with label fixes [#10566](https://github.com/chef/chef/pull/10566) ([antima-gupta](https://github.com/antima-gupta))
+- Simplify a weird conditional in chef-config [#10560](https://github.com/chef/chef/pull/10560) ([tas50](https://github.com/tas50))
+- Improve the package docs generation + resolve rubocop warnings [#10567](https://github.com/chef/chef/pull/10567) ([tas50](https://github.com/tas50))
+- Correctly generate docs yaml files to include package warnings [#10569](https://github.com/chef/chef/pull/10569) ([tas50](https://github.com/tas50))
+- Improve resource documentation [#10570](https://github.com/chef/chef/pull/10570) ([tas50](https://github.com/tas50))
+- Fix some spelling / cookstyle errors in the git examples [#10575](https://github.com/chef/chef/pull/10575) ([tas50](https://github.com/tas50))
+- Remove support for nexentacore and opensolaris which are both a decade EOL [#10573](https://github.com/chef/chef/pull/10573) ([tas50](https://github.com/tas50))
+- Remove rspec_junit_formatter and rspec version pins [#10579](https://github.com/chef/chef/pull/10579) ([phiggins](https://github.com/phiggins))
+- Don&#39;t run rspec with documentation formatter. [#10578](https://github.com/chef/chef/pull/10578) ([phiggins](https://github.com/phiggins))
+- Update Ohai to 16.7.4 and win32-process to 0.9.0 [#10580](https://github.com/chef/chef/pull/10580) ([tas50](https://github.com/tas50))
+- Fix secret options in windows bootstrap [#10577](https://github.com/chef/chef/pull/10577) ([mwrock](https://github.com/mwrock))
+- Remove the provider_resolver specs that are not helpful [#10576](https://github.com/chef/chef/pull/10576) ([tas50](https://github.com/tas50))
+- Remove a few more files from our install artifact [#10581](https://github.com/chef/chef/pull/10581) ([tas50](https://github.com/tas50))
+- Remove the provides :package for solaris_package [#10572](https://github.com/chef/chef/pull/10572) ([tas50](https://github.com/tas50))
+- Fix download errors during knife bootstrap on windows due to lack of TLS 1.2 support [#10574](https://github.com/chef/chef/pull/10574) ([TimothyTitan](https://github.com/TimothyTitan))
+- Avoid a splat operator where we don&#39;t need one [#10583](https://github.com/chef/chef/pull/10583) ([tas50](https://github.com/tas50))
+- Simplify regexes by removing extra character classes [#10584](https://github.com/chef/chef/pull/10584) ([tas50](https://github.com/tas50))
+- Improve Windows resource performance by converting powershell_out usage to powershell_exec [#10545](https://github.com/chef/chef/pull/10545) ([mwrock](https://github.com/mwrock))
+- Update ohai to 16.7.9 and rspec to 3.10 [#10587](https://github.com/chef/chef/pull/10587) ([tas50](https://github.com/tas50))
+- Fix homebrew_update [#10586](https://github.com/chef/chef/pull/10586) ([phiggins](https://github.com/phiggins))
+- Freeze strings in chef-utils [#10590](https://github.com/chef/chef/pull/10590) ([tas50](https://github.com/tas50))
+- Namespace ResourceInspector to avoid conflicts with Inspec&#39;s [#10595](https://github.com/chef/chef/pull/10595) ([phiggins](https://github.com/phiggins))
+- Improve auto generated resource docs [#10596](https://github.com/chef/chef/pull/10596) ([tas50](https://github.com/tas50))
+- Use tr where we don&#39;t need gsub and a regex [#10597](https://github.com/chef/chef/pull/10597) ([tas50](https://github.com/tas50))
+- Remove duplicate Gemfile gems + update ohai to 16.7.13 [#10602](https://github.com/chef/chef/pull/10602) ([tas50](https://github.com/tas50))
+- Use .compact instead of .select/.reject to remove nils [#10601](https://github.com/chef/chef/pull/10601) ([tas50](https://github.com/tas50))
+- Update to the new chefstyle [#10603](https://github.com/chef/chef/pull/10603) ([tas50](https://github.com/tas50))
+- Collapse several duplicate branches down [#10604](https://github.com/chef/chef/pull/10604) ([tas50](https://github.com/tas50))
+- Collapse more duplicate branches [#10605](https://github.com/chef/chef/pull/10605) ([tas50](https://github.com/tas50))
+- Use ||= where we can [#10609](https://github.com/chef/chef/pull/10609) ([tas50](https://github.com/tas50))
+- Don&#39;t uses regexes in splits when we don&#39;t need to [#10610](https://github.com/chef/chef/pull/10610) ([tas50](https://github.com/tas50))
+- Added deprecation warning for enforce_path_sanity [#10613](https://github.com/chef/chef/pull/10613) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Cleanup Chef::JSONCompat [#10612](https://github.com/chef/chef/pull/10612) ([phiggins](https://github.com/phiggins))
+- chef_client_config: Resolve invalid configuration in client.rb [#10608](https://github.com/chef/chef/pull/10608) ([srb3](https://github.com/srb3))
+- Update Ohai to 16.7.18 and Fauxhai to 8.4 [#10619](https://github.com/chef/chef/pull/10619) ([tas50](https://github.com/tas50))
+- Update the yaml we generate for resource documentation [#10622](https://github.com/chef/chef/pull/10622) ([tas50](https://github.com/tas50))
+- mount: Fixes for findmount output causing idempotency issues [#10614](https://github.com/chef/chef/pull/10614) ([antima-gupta](https://github.com/antima-gupta))
+- Add additional property coerce specs to mount [#10625](https://github.com/chef/chef/pull/10625) ([tas50](https://github.com/tas50))
+- provide a registry_key example that creates a multibyte binary value [#10630](https://github.com/chef/chef/pull/10630) ([mwrock](https://github.com/mwrock))
+- Fix ps specs [#10633](https://github.com/chef/chef/pull/10633) ([mwrock](https://github.com/mwrock))
+- Prevent failures generating docs [#10634](https://github.com/chef/chef/pull/10634) ([tas50](https://github.com/tas50))
+- Change how zypper_package calculates the candidate_version [#10631](https://github.com/chef/chef/pull/10631) ([lamont-granquist](https://github.com/lamont-granquist))
+- knife bootstrap deps require net/ssh [#10638](https://github.com/chef/chef/pull/10638) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update omnibus to remove the chef-sugar dep [#10629](https://github.com/chef/chef/pull/10629) ([tas50](https://github.com/tas50))
+- mount: changes to fix solaris test failure [#10643](https://github.com/chef/chef/pull/10643) ([antima-gupta](https://github.com/antima-gupta))
+- Fix group output and windows support [#10642](https://github.com/chef/chef/pull/10642) ([jaymzh](https://github.com/jaymzh))
+- pull in v0.2.1 of powershell shim that fixes .net resolver [#10644](https://github.com/chef/chef/pull/10644) ([mwrock](https://github.com/mwrock))
+- Fix zypper_package CI failures [#10648](https://github.com/chef/chef/pull/10648) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix idempotency issues in build_essential on the mac [#10647](https://github.com/chef/chef/pull/10647) ([tas50](https://github.com/tas50))
+- mount: Changes to fix creating multiple entries in fstab [#10472](https://github.com/chef/chef/pull/10472) ([antima-gupta](https://github.com/antima-gupta))
+- Remove pry-remote from the package [#10651](https://github.com/chef/chef/pull/10651) ([tas50](https://github.com/tas50))
+- Update fauxhai, chef-vault, and chefstyle to the latest [#10653](https://github.com/chef/chef/pull/10653) ([tas50](https://github.com/tas50))
+- Update mixlib-shellout to 3.2.2 [#10654](https://github.com/chef/chef/pull/10654) ([tas50](https://github.com/tas50))
+- update pwsh in powershell_exec to 7.1.0 and add comments explaining how to pull in updates [#10652](https://github.com/chef/chef/pull/10652) ([mwrock](https://github.com/mwrock))
+- user: Log what changed when updating a user [#10656](https://github.com/chef/chef/pull/10656) ([jaymzh](https://github.com/jaymzh))
+- Update the docs generation for the new format [#10659](https://github.com/chef/chef/pull/10659) ([tas50](https://github.com/tas50))
+- include password in guard inherited attributes [#10672](https://github.com/chef/chef/pull/10672) ([mwrock](https://github.com/mwrock))
+- Update ohai and win32-service to the latest [#10673](https://github.com/chef/chef/pull/10673) ([tas50](https://github.com/tas50))
+- Avoid ambiguous regexes [#10675](https://github.com/chef/chef/pull/10675) ([tas50](https://github.com/tas50))
+- Mount: Fixes for Mount resource changes broke specs on AIX [#10671](https://github.com/chef/chef/pull/10671) ([antima-gupta](https://github.com/antima-gupta))
+- bump ohai, win32-service, and omnibus deps [#10685](https://github.com/chef/chef/pull/10685) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.7.37 [#10686](https://github.com/chef/chef/pull/10686) ([tas50](https://github.com/tas50))
+- Skip appx packaging on Windows [#10650](https://github.com/chef/chef/pull/10650) ([tas50](https://github.com/tas50))
+- Bump omnibus / omnibus-software to the latest [#10690](https://github.com/chef/chef/pull/10690) ([tas50](https://github.com/tas50))
+- Resolve NameError running mac_user resource [#10692](https://github.com/chef/chef/pull/10692) ([tas50](https://github.com/tas50))
+
+## [v16.6.14](https://github.com/chef/chef/tree/v16.6.14) (2020-10-14)
+
+#### Merged Pull Requests
+- Pull in Ohai 16.6 and train-core/train-winrm updates [#10474](https://github.com/chef/chef/pull/10474) ([tas50](https://github.com/tas50))
+- Update to the windows_audit_policy resource to fix a bug on failure-only auditing [#10473](https://github.com/chef/chef/pull/10473) ([chef-davin](https://github.com/chef-davin))
+- add ruby-3.0 hash methods to immutabilize_hash [#10475](https://github.com/chef/chef/pull/10475) ([lamont-granquist](https://github.com/lamont-granquist))
+- add interpreter arg to powershell_out allowing it to call pwsh.exe [#10478](https://github.com/chef/chef/pull/10478) ([mwrock](https://github.com/mwrock))
+- Update to Ruby 2.7.2 / Rubygems 3.1.4 [#10480](https://github.com/chef/chef/pull/10480) ([tas50](https://github.com/tas50))
+- Remove extra safe navigation [#10482](https://github.com/chef/chef/pull/10482) ([tas50](https://github.com/tas50))
+- add interpreter arg to powershell_exec allowing it to run powershell core [#10476](https://github.com/chef/chef/pull/10476) ([mwrock](https://github.com/mwrock))
+- fix specs on powershell v4 and below [#10484](https://github.com/chef/chef/pull/10484) ([mwrock](https://github.com/mwrock))
+- Update Ohai to 16.6.1 [#10486](https://github.com/chef/chef/pull/10486) ([tas50](https://github.com/tas50))
+- add interpreter to handle pwsh and powershell to powershell_script [#10488](https://github.com/chef/chef/pull/10488) ([mwrock](https://github.com/mwrock))
+- Changing ifconfig provider to reduce blank lines in redhat and debian ifconfigs [#10489](https://github.com/chef/chef/pull/10489) ([jmherbst](https://github.com/jmherbst))
+- Bump Chefstyle &amp; other deps [#10498](https://github.com/chef/chef/pull/10498) ([tas50](https://github.com/tas50))
+- provide powershell_exec functionality on x86 [#10495](https://github.com/chef/chef/pull/10495) ([mwrock](https://github.com/mwrock))
+- chef_client_config resource [#10365](https://github.com/chef/chef/pull/10365) ([tas50](https://github.com/tas50))
+- Update examples and descriptions for better automated documentation [#10500](https://github.com/chef/chef/pull/10500) ([tas50](https://github.com/tas50))
+- Update cacerts, ohai, and winrm to the latest [#10502](https://github.com/chef/chef/pull/10502) ([tas50](https://github.com/tas50))
+- Support for ohai target mode [#10418](https://github.com/chef/chef/pull/10418) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v16.5.77](https://github.com/chef/chef/tree/v16.5.77) (2020-09-29)
+
+#### Merged Pull Requests
+- Add missing requires to chef/policy_builder/dynamic [#10446](https://github.com/chef/chef/pull/10446) ([tas50](https://github.com/tas50))
+- Pull in the new tty-table to unlock new license-acceptance [#10450](https://github.com/chef/chef/pull/10450) ([tas50](https://github.com/tas50))
+- Check for full names in Homebrew package info [#10360](https://github.com/chef/chef/pull/10360) ([ed-brex](https://github.com/ed-brex))
+- Remove unused method [#10449](https://github.com/chef/chef/pull/10449) ([007lva](https://github.com/007lva))
+- Fix examples markdown in chef_handler resource. [#10459](https://github.com/chef/chef/pull/10459) ([phiggins](https://github.com/phiggins))
+- Simplify Hash transforms &amp; minor code refactoring [#10447](https://github.com/chef/chef/pull/10447) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update require gating to include chef-utils/chef-config &amp; gate more [#10451](https://github.com/chef/chef/pull/10451) ([tas50](https://github.com/tas50))
+- Use ChefUtils::Dist::Infra::PRODUCT for locale warning instead of &quot;Chef&quot; [#10461](https://github.com/chef/chef/pull/10461) ([ramereth](https://github.com/ramereth))
+- Preparing 16.5 hotfix patch to fix Workstation build issue [#10462](https://github.com/chef/chef/pull/10462) ([tyler-ball](https://github.com/tyler-ball))
+- autoload addressable/uri on :URI inside addressable module [#10464](https://github.com/chef/chef/pull/10464) ([mwrock](https://github.com/mwrock))
+- Remove unnecessary require. [#10465](https://github.com/chef/chef/pull/10465) ([phiggins](https://github.com/phiggins))
+- Use Ruby 2.6 endless Range syntax [#10463](https://github.com/chef/chef/pull/10463) ([007lva](https://github.com/007lva))
+- Bump dependencies to latest + resolve Chefstyle warning [#10468](https://github.com/chef/chef/pull/10468) ([tas50](https://github.com/tas50))
+
+## [v16.5.64](https://github.com/chef/chef/tree/v16.5.64) (2020-09-17)
+
+#### Merged Pull Requests
+- Add new chef_client_trusted_certificate resource [#10331](https://github.com/chef/chef/pull/10331) ([tas50](https://github.com/tas50))
+- Simplify macos detection in specs to include big sur [#10335](https://github.com/chef/chef/pull/10335) ([tas50](https://github.com/tas50))
+- New exit code to signal chef-client exits due to configuration errors [#10302](https://github.com/chef/chef/pull/10302) ([NaomiReeves](https://github.com/NaomiReeves))
+- Bump all deps to the latest for the require optimizations [#10337](https://github.com/chef/chef/pull/10337) ([tas50](https://github.com/tas50))
+- fix chocolatey and x86 windows omnibus builds [#10339](https://github.com/chef/chef/pull/10339) ([mwrock](https://github.com/mwrock))
+- Avoid knife ssh freeze on windows [#9482](https://github.com/chef/chef/pull/9482) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Start building S390x packages again [#10338](https://github.com/chef/chef/pull/10338) ([btm](https://github.com/btm))
+- separate omnibus rspec path from options [#10343](https://github.com/chef/chef/pull/10343) ([mwrock](https://github.com/mwrock))
+- Add macOS 11.0 (Big Sur) packages [#10332](https://github.com/chef/chef/pull/10332) ([tas50](https://github.com/tas50))
+- make &#39;knife config&#39; options shorter/easier [#10346](https://github.com/chef/chef/pull/10346) ([vsingh-msys](https://github.com/vsingh-msys))
+- knife config list-profiles UI with tty-table [#10341](https://github.com/chef/chef/pull/10341) ([vsingh-msys](https://github.com/vsingh-msys))
+- Fix dll copying in Gemfile to remove Dir.pwd [#10325](https://github.com/chef/chef/pull/10325) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix some CI failures [#10347](https://github.com/chef/chef/pull/10347) ([phiggins](https://github.com/phiggins))
+- Bump deps and resolve new Chefstyle warnings [#10349](https://github.com/chef/chef/pull/10349) ([tas50](https://github.com/tas50))
+- Add initial take at chef_client_launchd [#10348](https://github.com/chef/chef/pull/10348) ([tas50](https://github.com/tas50))
+- Fix habitat test script [#10350](https://github.com/chef/chef/pull/10350) ([phiggins](https://github.com/phiggins))
+- Update Ohai to 16.4.13 [#10353](https://github.com/chef/chef/pull/10353) ([tas50](https://github.com/tas50))
+- more config specs cleanup &amp; remove deprecated from knife config list [#10351](https://github.com/chef/chef/pull/10351) ([vsingh-msys](https://github.com/vsingh-msys))
+- Validate nice values in the launchd resource [#10359](https://github.com/chef/chef/pull/10359) ([tas50](https://github.com/tas50))
+- Add back nice functionality to chef_client_cron [#10358](https://github.com/chef/chef/pull/10358) ([tas50](https://github.com/tas50))
+- Improve input handling and validation in chef_client_launchd [#10357](https://github.com/chef/chef/pull/10357) ([tas50](https://github.com/tas50))
+- chef_client_launchd: reorder properties and fix log permissions [#10361](https://github.com/chef/chef/pull/10361) ([tas50](https://github.com/tas50))
+- Update InSpec to 4.22.22 [#10363](https://github.com/chef/chef/pull/10363) ([tas50](https://github.com/tas50))
+- Fixed mount Resource for bind mounts is not idempotent. [#10171](https://github.com/chef/chef/pull/10171) ([antima-gupta](https://github.com/antima-gupta))
+- More updates to the chef_client_* resources [#10362](https://github.com/chef/chef/pull/10362) ([tas50](https://github.com/tas50))
+- Remove duplicate requires in the Provider class [#10369](https://github.com/chef/chef/pull/10369) ([tas50](https://github.com/tas50))
+- chef_client_systemd_timer: Add the ability to set CPUQuota on the chef-client unit [#10381](https://github.com/chef/chef/pull/10381) ([tas50](https://github.com/tas50))
+- Update all deps to current [#10385](https://github.com/chef/chef/pull/10385) ([tas50](https://github.com/tas50))
+- Allow removing profiles in osx_profile on Big Sur [#10386](https://github.com/chef/chef/pull/10386) ([tas50](https://github.com/tas50))
+- Fix nil deep_merging [#10382](https://github.com/chef/chef/pull/10382) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add a :reboot_delay property to the windows_ad_join resource [#10388](https://github.com/chef/chef/pull/10388) ([chef-davin](https://github.com/chef-davin))
+- Add --logfile to chef-apply command [#10389](https://github.com/chef/chef/pull/10389) ([tas50](https://github.com/tas50))
+- chef_client_launchd: create a launchd daemon to handle the client restart [#10390](https://github.com/chef/chef/pull/10390) ([tas50](https://github.com/tas50))
+- Resolve RuboCop Style/RedundantInterpolation warnings [#10394](https://github.com/chef/chef/pull/10394) ([tas50](https://github.com/tas50))
+- [data-collector] improved output_locations validation &amp; bug fixes [#10393](https://github.com/chef/chef/pull/10393) ([vsingh-msys](https://github.com/vsingh-msys))
+- Improve cli boot performance by prefering autoload over requires [#10383](https://github.com/chef/chef/pull/10383) ([mwrock](https://github.com/mwrock))
+- rhsm_register: Avoid potentially checking if we need to register twice [#10395](https://github.com/chef/chef/pull/10395) ([tas50](https://github.com/tas50))
+- Use include? to example strings when we don&#39;t need a regex [#10396](https://github.com/chef/chef/pull/10396) ([tas50](https://github.com/tas50))
+- Mock File.expand_path to fix window C:/ dir appended in absolute path [#10398](https://github.com/chef/chef/pull/10398) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update Ohai to 16.5 [#10399](https://github.com/chef/chef/pull/10399) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2w [#10402](https://github.com/chef/chef/pull/10402) ([tas50](https://github.com/tas50))
+- Remove a redundant spec loop [#10370](https://github.com/chef/chef/pull/10370) ([tas50](https://github.com/tas50))
+- Add Patents link to chef infra &amp; solo client [#10400](https://github.com/chef/chef/pull/10400) ([vsingh-msys](https://github.com/vsingh-msys))
+- autoload license_acceptance/acceptor in knife loading [#10405](https://github.com/chef/chef/pull/10405) ([mwrock](https://github.com/mwrock))
+- Enable s390x RHEL8 and SLES15 platforms [#10376](https://github.com/chef/chef/pull/10376) ([jaymalasinha](https://github.com/jaymalasinha))
+- Allow for license-acceptance 2.0 gem [#10406](https://github.com/chef/chef/pull/10406) ([tas50](https://github.com/tas50))
+- Allow cpu_quota values &gt; 100 [#10408](https://github.com/chef/chef/pull/10408) ([tas50](https://github.com/tas50))
+- Use __dir__ instead of getting the dir of __FILE__ [#10401](https://github.com/chef/chef/pull/10401) ([tas50](https://github.com/tas50))
+- Add an ohai timing test to find busted DNS on CI testers [#10371](https://github.com/chef/chef/pull/10371) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update the windows_firewall_profile resource to fix NoMethodError [#10412](https://github.com/chef/chef/pull/10412) ([chef-davin](https://github.com/chef-davin))
+- Remove debug puts from snap_package [#10409](https://github.com/chef/chef/pull/10409) ([tas50](https://github.com/tas50))
+- Add system_name property to rhsm_register resource [#10413](https://github.com/chef/chef/pull/10413) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Update sysctl resource description to match reality [#10416](https://github.com/chef/chef/pull/10416) ([tas50](https://github.com/tas50))
+- allow the use of SIDs in windows securable resources [#10423](https://github.com/chef/chef/pull/10423) ([mwrock](https://github.com/mwrock))
+- Update the validation of the privilege property on the windows_user_privilege resource [#10422](https://github.com/chef/chef/pull/10422) ([tas50](https://github.com/tas50))
+- Remove the Ubuntu azure pipeline test [#10434](https://github.com/chef/chef/pull/10434) ([tas50](https://github.com/tas50))
+- Move dist implementation into chef-utils [#9834](https://github.com/chef/chef/pull/9834) ([bobchaos](https://github.com/bobchaos))
+- Add examples to the ohai resource [#10432](https://github.com/chef/chef/pull/10432) ([tas50](https://github.com/tas50))
+- Move TrainTransport to ChefConfig [#10436](https://github.com/chef/chef/pull/10436) ([lamont-granquist](https://github.com/lamont-granquist))
+- More resource documentation improvements [#10435](https://github.com/chef/chef/pull/10435) ([tas50](https://github.com/tas50))
+- Resolve Lint/RedundantRequireStatement &amp; Style/RedundantCondition warnings [#10437](https://github.com/chef/chef/pull/10437) ([tas50](https://github.com/tas50))
+- Speed up a openssl helper specs [#10438](https://github.com/chef/chef/pull/10438) ([tas50](https://github.com/tas50))
+- Resolve Style/RedundantSort warnings [#10439](https://github.com/chef/chef/pull/10439) ([tas50](https://github.com/tas50))
+- Docs fixes from review [#10440](https://github.com/chef/chef/pull/10440) ([tas50](https://github.com/tas50))
+- Update InSpec to the latest [#10443](https://github.com/chef/chef/pull/10443) ([tas50](https://github.com/tas50))
+- Fix idempotency in the osx_profile resource and avoid writing data to disk [#10444](https://github.com/chef/chef/pull/10444) ([tas50](https://github.com/tas50))
+- Update to the latest license_scout gem [#10445](https://github.com/chef/chef/pull/10445) ([tas50](https://github.com/tas50))
+
+## [v16.4.41](https://github.com/chef/chef/tree/v16.4.41) (2020-08-19)
+
+#### Merged Pull Requests
+- Refactor the timezone resource to properly load the current timezone [#10323](https://github.com/chef/chef/pull/10323) ([tas50](https://github.com/tas50))
+- Add missing requires for knife configure command [#10329](https://github.com/chef/chef/pull/10329) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.4.11 to resolve Windows IP detection [#10327](https://github.com/chef/chef/pull/10327) ([tas50](https://github.com/tas50))
+
+## [v16.4.38](https://github.com/chef/chef/tree/v16.4.38) (2020-08-18)
+
+#### Merged Pull Requests
+- Fix typo and macOS version evaluation logic in `osx_profile` resource [#10319](https://github.com/chef/chef/pull/10319) ([ChefAustin](https://github.com/ChefAustin))
+- Further improve osx_profile error message and add a test [#10320](https://github.com/chef/chef/pull/10320) ([tas50](https://github.com/tas50))
+- Fix removing non-existent profile with osx_profile [#10324](https://github.com/chef/chef/pull/10324) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v16.4.35](https://github.com/chef/chef/tree/v16.4.35) (2020-08-18)
+
+#### Merged Pull Requests
+- Add dobi-powered Docker build pipelines [#10180](https://github.com/chef/chef/pull/10180) ([nkierpiec](https://github.com/nkierpiec))
+- Introduce EXPEDITOR_VERSION for docker image tag [#10231](https://github.com/chef/chef/pull/10231) ([nkierpiec](https://github.com/nkierpiec))
+- Fix warning in test for redefined shared example group [#10232](https://github.com/chef/chef/pull/10232) ([phiggins](https://github.com/phiggins))
+- Update omnibus-software to slim our builds [#10234](https://github.com/chef/chef/pull/10234) ([tas50](https://github.com/tas50))
+- Make sure darwin is always detected [#10235](https://github.com/chef/chef/pull/10235) ([tas50](https://github.com/tas50))
+- Compensate for libyaml changes in yaml parsing test. [#10220](https://github.com/chef/chef/pull/10220) ([phiggins](https://github.com/phiggins))
+- Bump omnibus-software to the latest [#10236](https://github.com/chef/chef/pull/10236) ([tas50](https://github.com/tas50))
+- Fix install windows features when install state is removed [#9820](https://github.com/chef/chef/pull/9820) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update libffi to 3.3 [#10238](https://github.com/chef/chef/pull/10238) ([tas50](https://github.com/tas50))
+- Remove debug code from libxml2 [#10241](https://github.com/chef/chef/pull/10241) ([tas50](https://github.com/tas50))
+- Cleanup more files from our package&#39;s gem installs [#10242](https://github.com/chef/chef/pull/10242) ([tas50](https://github.com/tas50))
+- Further slim down libxml2 [#10244](https://github.com/chef/chef/pull/10244) ([tas50](https://github.com/tas50))
+- Use .match? when we don&#39;t need data from a regex match [#10248](https://github.com/chef/chef/pull/10248) ([tas50](https://github.com/tas50))
+- Convert osx_profile to custom resource [#10239](https://github.com/chef/chef/pull/10239) ([lamont-granquist](https://github.com/lamont-granquist))
+- Resolve Performance/RangeInclude warnings [#10245](https://github.com/chef/chef/pull/10245) ([tas50](https://github.com/tas50))
+- Use sort.reverse instead of sort { |a, b| b &lt;=&gt; a } [#10247](https://github.com/chef/chef/pull/10247) ([tas50](https://github.com/tas50))
+- Use .key? instead of keys.include [#10246](https://github.com/chef/chef/pull/10246) ([tas50](https://github.com/tas50))
+- Use tr not gsub for string replacement [#10251](https://github.com/chef/chef/pull/10251) ([tas50](https://github.com/tas50))
+- apt_repository: Small code refactor in key_is_valid? method [#10253](https://github.com/chef/chef/pull/10253) ([phiggins](https://github.com/phiggins))
+- Use .count instead of .select{}.count [#10252](https://github.com/chef/chef/pull/10252) ([tas50](https://github.com/tas50))
+- Update RuboCop config to Ruby 2.6 [#10254](https://github.com/chef/chef/pull/10254) ([tas50](https://github.com/tas50))
+- Convert openssl resources to unified_mode [#10249](https://github.com/chef/chef/pull/10249) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove unnecessary Mixin::ShellOut in providers [#10256](https://github.com/chef/chef/pull/10256) ([tas50](https://github.com/tas50))
+- Fix second chef run hang for windows_font resource [#10257](https://github.com/chef/chef/pull/10257) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Use find instead of select.first [#10255](https://github.com/chef/chef/pull/10255) ([tas50](https://github.com/tas50))
+- Pull Ohai 16.4.2 into Chef Infra Client [#10259](https://github.com/chef/chef/pull/10259) ([tas50](https://github.com/tas50))
+- added configuration options for chef-server [#10213](https://github.com/chef/chef/pull/10213) ([tehlers320](https://github.com/tehlers320))
+- Remove more requires that come for free [#10258](https://github.com/chef/chef/pull/10258) ([tas50](https://github.com/tas50))
+- Convert windows custom resources to unified_mode [#10260](https://github.com/chef/chef/pull/10260) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove lzma and bzip binaries from our builds [#10263](https://github.com/chef/chef/pull/10263) ([tas50](https://github.com/tas50))
+- Speed up our bundle installs by always running 3 jobs [#10264](https://github.com/chef/chef/pull/10264) ([tas50](https://github.com/tas50))
+- Bump InSpec/Train/omnibus-software [#10267](https://github.com/chef/chef/pull/10267) ([tas50](https://github.com/tas50))
+- Cleanup extra binaries from libxml2 and libxslt [#10265](https://github.com/chef/chef/pull/10265) ([tas50](https://github.com/tas50))
+- Combine attr_reader / attr_writer into attr_accessor [#10268](https://github.com/chef/chef/pull/10268) ([tas50](https://github.com/tas50))
+- Update spellcheck config with nice stuff from other repos. [#10261](https://github.com/chef/chef/pull/10261) ([phiggins](https://github.com/phiggins))
+- Resolve Style/RedundantAssignment warnings [#10269](https://github.com/chef/chef/pull/10269) ([tas50](https://github.com/tas50))
+- chef_client_systemd_timer: Fix failures in the :remove action [#10273](https://github.com/chef/chef/pull/10273) ([tas50](https://github.com/tas50))
+- powershell_script: Use native properties and don&#39;t coerce user provided flags with powershell defaults [#10274](https://github.com/chef/chef/pull/10274) ([phiggins](https://github.com/phiggins))
+- Mark deep_merge as private [#10277](https://github.com/chef/chef/pull/10277) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove an unnecessary shared_context from execute resource tests [#10278](https://github.com/chef/chef/pull/10278) ([phiggins](https://github.com/phiggins))
+- Clean up some interdependencies in script resource tests. [#10279](https://github.com/chef/chef/pull/10279) ([phiggins](https://github.com/phiggins))
+- Fix broken powershell_script test. [#10280](https://github.com/chef/chef/pull/10280) ([phiggins](https://github.com/phiggins))
+- client-run per resource error detail [#10237](https://github.com/chef/chef/pull/10237) ([vsingh-msys](https://github.com/vsingh-msys))
+- Bump Ohai / Cheffish to the latest [#10281](https://github.com/chef/chef/pull/10281) ([tas50](https://github.com/tas50))
+- double quotes in example for string interpolation [#10288](https://github.com/chef/chef/pull/10288) ([karlamrhein](https://github.com/karlamrhein))
+- Renew an expired SSL Certificate [#10286](https://github.com/chef/chef/pull/10286) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Bump Ohai to 16.4.9 [#10291](https://github.com/chef/chef/pull/10291) ([tas50](https://github.com/tas50))
+- Remove a duplicate require [#10293](https://github.com/chef/chef/pull/10293) ([tas50](https://github.com/tas50))
+- Use macos? helper in homebrew_update [#10290](https://github.com/chef/chef/pull/10290) ([tas50](https://github.com/tas50))
+- Update chef-telemetry and ohai to the latest [#10295](https://github.com/chef/chef/pull/10295) ([tas50](https://github.com/tas50))
+- Bump deps for require perf optimizations [#10296](https://github.com/chef/chef/pull/10296) ([tas50](https://github.com/tas50))
+- Optimize requires for non-omnibus installs [#10294](https://github.com/chef/chef/pull/10294) ([tas50](https://github.com/tas50))
+- Update Fauxhai to 8.3 [#10297](https://github.com/chef/chef/pull/10297) ([tas50](https://github.com/tas50))
+- Update Omnibus kitchen config with public boxes [#10292](https://github.com/chef/chef/pull/10292) ([tas50](https://github.com/tas50))
+- Minor tweaks to the platform checks in the specs [#10289](https://github.com/chef/chef/pull/10289) ([tas50](https://github.com/tas50))
+- Fix File.exist throughout the codebase [#10284](https://github.com/chef/chef/pull/10284) ([tas50](https://github.com/tas50))
+- Convert the functional tests over to macos? as well [#10303](https://github.com/chef/chef/pull/10303) ([tas50](https://github.com/tas50))
+- do not require source when removing powershell package source [#10301](https://github.com/chef/chef/pull/10301) ([kimbernator](https://github.com/kimbernator))
+- Change output to clearly show checksums are displayed incompletely [#10300](https://github.com/chef/chef/pull/10300) ([tecracer-theinen](https://github.com/tecracer-theinen))
+- Delete deprecated Fauxhai definitions [#10305](https://github.com/chef/chef/pull/10305) ([tas50](https://github.com/tas50))
+- Use net/http and openssl instead of net/https [#10283](https://github.com/chef/chef/pull/10283) ([tas50](https://github.com/tas50))
+- Remove the unused win32-dir dependency [#10308](https://github.com/chef/chef/pull/10308) ([tas50](https://github.com/tas50))
+- Fix bootstrapping windows-from-linux [#10298](https://github.com/chef/chef/pull/10298) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add 16.4 release notes [#10304](https://github.com/chef/chef/pull/10304) ([tas50](https://github.com/tas50))
+- Update train to 3.3.16 [#10309](https://github.com/chef/chef/pull/10309) ([tas50](https://github.com/tas50))
+
+## [v16.3.45](https://github.com/chef/chef/tree/v16.3.45) (2020-07-29)
+
+#### Merged Pull Requests
+- Bump deps to current [#10210](https://github.com/chef/chef/pull/10210) ([tas50](https://github.com/tas50))
+- Mark a private method as private with yard [#10212](https://github.com/chef/chef/pull/10212) ([tas50](https://github.com/tas50))
+- Use powershell_out vs. powershell_script in hostname [#10147](https://github.com/chef/chef/pull/10147) ([tas50](https://github.com/tas50))
+- Define local test variables to avoid test load order bug. [#10214](https://github.com/chef/chef/pull/10214) ([phiggins](https://github.com/phiggins))
+- Fix NoMethodError when server returns a 406. [#10216](https://github.com/chef/chef/pull/10216) ([phiggins](https://github.com/phiggins))
+- Fix protocol negotiation for unversioned objects [#10218](https://github.com/chef/chef/pull/10218) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update libarchive, liblzma, and nokogiri to the latest [#10221](https://github.com/chef/chef/pull/10221) ([tas50](https://github.com/tas50))
+
+## [v16.3.38](https://github.com/chef/chef/tree/v16.3.38) (2020-07-27)
+
+#### Merged Pull Requests
+- Reserve deprecation ID that was used in Chef-15 [#10091](https://github.com/chef/chef/pull/10091) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix syslog logging on Chef-16 [#10097](https://github.com/chef/chef/pull/10097) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump inspec-core-bin to 4.21.3 [#10101](https://github.com/chef/chef/pull/10101) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Use bash not execute in build_essential for better output [#10096](https://github.com/chef/chef/pull/10096) ([tas50](https://github.com/tas50))
+- Bump train-core to 3.3.6 [#10104](https://github.com/chef/chef/pull/10104) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Update windows_security_policy for better idempotency [#10064](https://github.com/chef/chef/pull/10064) ([chef-davin](https://github.com/chef-davin))
+- Update windows_security_policy to use powershell_out to be compatible with 32 bit windows [#10107](https://github.com/chef/chef/pull/10107) ([chef-davin](https://github.com/chef-davin))
+- Implement ENFORCE_LICENSE dist constant for knife bootstrap [#9992](https://github.com/chef/chef/pull/9992) ([ramereth](https://github.com/ramereth))
+- Switch back to a supported nokogiri release [#10114](https://github.com/chef/chef/pull/10114) ([tas50](https://github.com/tas50))
+- Fix warning message for cb / core resource conflict [#10117](https://github.com/chef/chef/pull/10117) ([tas50](https://github.com/tas50))
+- Add spaces after attrs [#10124](https://github.com/chef/chef/pull/10124) ([tas50](https://github.com/tas50))
+- Avoid assigning variables before returning if we don&#39;t have to [#10123](https://github.com/chef/chef/pull/10123) ([tas50](https://github.com/tas50))
+- Fix `powershell_exec!` test. [#10126](https://github.com/chef/chef/pull/10126) ([phiggins](https://github.com/phiggins))
+- expand_path with __dir__ instead of __FILE__ [#10125](https://github.com/chef/chef/pull/10125) ([tas50](https://github.com/tas50))
+- Allow iso8601 gem version 0.13 [#10128](https://github.com/chef/chef/pull/10128) ([tas50](https://github.com/tas50))
+- Fix some windows package tests [#10129](https://github.com/chef/chef/pull/10129) ([phiggins](https://github.com/phiggins))
+- windows_dns_record: add dns_server property with default of localhost [#10120](https://github.com/chef/chef/pull/10120) ([jeremyciak](https://github.com/jeremyciak))
+- Fix systemd unit test on Windows. [#10127](https://github.com/chef/chef/pull/10127) ([phiggins](https://github.com/phiggins))
+- Add introduced field to new propertyin windows_dns_record [#10132](https://github.com/chef/chef/pull/10132) ([tas50](https://github.com/tas50))
+- Remove reference to deprecated option in test helper. [#10136](https://github.com/chef/chef/pull/10136) ([phiggins](https://github.com/phiggins))
+- Support legacy DSS host keys with knife-ssh [#10133](https://github.com/chef/chef/pull/10133) ([tas50](https://github.com/tas50))
+- Disable Naming/AsciiIdentifiers in our specs [#10139](https://github.com/chef/chef/pull/10139) ([tas50](https://github.com/tas50))
+- Fix openssl config tests on Windows [#10138](https://github.com/chef/chef/pull/10138) ([phiggins](https://github.com/phiggins))
+- Fix git sync if the local branch already exist [#9998](https://github.com/chef/chef/pull/9998) ([lotooo](https://github.com/lotooo))
+- Fix spellcheck CI task [#10142](https://github.com/chef/chef/pull/10142) ([phiggins](https://github.com/phiggins))
+- Minor chefstyle fixes in the spellcheck task [#10145](https://github.com/chef/chef/pull/10145) ([tas50](https://github.com/tas50))
+- Fix some windows unit tests [#10144](https://github.com/chef/chef/pull/10144) ([phiggins](https://github.com/phiggins))
+- Don&#39;t run the dnf test that Windows doesn&#39;t like on Windows. [#10149](https://github.com/chef/chef/pull/10149) ([phiggins](https://github.com/phiggins))
+- Fix two warnings in tests. [#10150](https://github.com/chef/chef/pull/10150) ([phiggins](https://github.com/phiggins))
+- Test and Promote Habitat builds on Linux [#10102](https://github.com/chef/chef/pull/10102) ([christopher-snapp](https://github.com/christopher-snapp))
+- Fix extra quote in habitat test pipeline config [#10156](https://github.com/chef/chef/pull/10156) ([christopher-snapp](https://github.com/christopher-snapp))
+- Fix execute resource with integer user parameter. [#10157](https://github.com/chef/chef/pull/10157) ([phiggins](https://github.com/phiggins))
+- Fixed `knife cookbook upload -o` windows path issue [#10146](https://github.com/chef/chef/pull/10146) ([antima-gupta](https://github.com/antima-gupta))
+- Bump the server api version to 2 [#10140](https://github.com/chef/chef/pull/10140) ([lamont-granquist](https://github.com/lamont-granquist))
+- Use rspec constant stubbing. [#10155](https://github.com/chef/chef/pull/10155) ([phiggins](https://github.com/phiggins))
+- Don&#39;t allow setting expectations on nil. [#10154](https://github.com/chef/chef/pull/10154) ([phiggins](https://github.com/phiggins))
+- Update to Chefstyle 1.2 + some fixes [#10158](https://github.com/chef/chef/pull/10158) ([tas50](https://github.com/tas50))
+- Remove smartos detection / support in our package scripts [#10161](https://github.com/chef/chef/pull/10161) ([tas50](https://github.com/tas50))
+- Workaround rubygems ssl test failure [#10160](https://github.com/chef/chef/pull/10160) ([phiggins](https://github.com/phiggins))
+- Move the rehash methods into the subcommand loader [#10159](https://github.com/chef/chef/pull/10159) ([tas50](https://github.com/tas50))
+- Bump omnibus-software to pull in the new cacerts [#10163](https://github.com/chef/chef/pull/10163) ([tas50](https://github.com/tas50))
+- Avoid requiring spec_helper more than once [#10148](https://github.com/chef/chef/pull/10148) ([phiggins](https://github.com/phiggins))
+- Create the windows_firewall_profile resource for use with enabling/disabling and configuring the Windows firewall Domain, Private, and Public profiles [#9994](https://github.com/chef/chef/pull/9994) ([chef-davin](https://github.com/chef-davin))
+- Don&#39;t allow tests to set an expectation without specific error type [#10153](https://github.com/chef/chef/pull/10153) ([phiggins](https://github.com/phiggins))
+- update changelog reference to pull request for windows_security_policy 32bit windows compatibility PR [#10169](https://github.com/chef/chef/pull/10169) ([chef-davin](https://github.com/chef-davin))
+- Replace single quotes with double [#10176](https://github.com/chef/chef/pull/10176) ([mattray](https://github.com/mattray))
+- Reset logger in test to avoid global state persisting. [#10170](https://github.com/chef/chef/pull/10170) ([phiggins](https://github.com/phiggins))
+- don&#39;t check list-profiles for already selected bad profile [#10162](https://github.com/chef/chef/pull/10162) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- fix windows package for Chef-16 regression [#10177](https://github.com/chef/chef/pull/10177) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec to 4.22 and mixlib-shellout to 3.1.1 [#10178](https://github.com/chef/chef/pull/10178) ([tas50](https://github.com/tas50))
+- Replace highline ui.ask password prompt with tty-prompt gem [#10131](https://github.com/chef/chef/pull/10131) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update InSpec to 4.22.1 [#10183](https://github.com/chef/chef/pull/10183) ([tas50](https://github.com/tas50))
+- Remove cookbook uploader global state [#10185](https://github.com/chef/chef/pull/10185) ([phiggins](https://github.com/phiggins))
+- Extract shell_out helper method to chef-utils for reuse in ohai, etc [#10137](https://github.com/chef/chef/pull/10137) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rework macos_userdefaults resource [#10118](https://github.com/chef/chef/pull/10118) ([tas50](https://github.com/tas50))
+- Move some Ruby omnibus cleanup to omnibus-software [#10189](https://github.com/chef/chef/pull/10189) ([tas50](https://github.com/tas50))
+- require mixlib-shellout 3.1.1+ and pull in new ohai [#10191](https://github.com/chef/chef/pull/10191) ([tas50](https://github.com/tas50))
+- Set log level to :fatal in test to prevent excess logging. [#10188](https://github.com/chef/chef/pull/10188) ([phiggins](https://github.com/phiggins))
+- Update the ruby-cleanup omnibus-software configs [#10195](https://github.com/chef/chef/pull/10195) ([tas50](https://github.com/tas50))
+- Update ruby cleanup omnibus software again [#10196](https://github.com/chef/chef/pull/10196) ([tas50](https://github.com/tas50))
+- Fix chef-resource-inspector to handle classes in equal_to [#10197](https://github.com/chef/chef/pull/10197) ([tas50](https://github.com/tas50))
+- Update the examples to match the actual usage of the windows_firewall_profile resource. [#10198](https://github.com/chef/chef/pull/10198) ([chef-davin](https://github.com/chef-davin))
+- Implement default_paths API [#10193](https://github.com/chef/chef/pull/10193) ([lamont-granquist](https://github.com/lamont-granquist))
+- Reduce path_helper allocations [#10200](https://github.com/chef/chef/pull/10200) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rename Attribute Whitelist/Blacklist to Allowed/Blocked [#10199](https://github.com/chef/chef/pull/10199) ([tas50](https://github.com/tas50))
+- Fix default_paths patch [#10202](https://github.com/chef/chef/pull/10202) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump Ohai to 16.3.0 [#10203](https://github.com/chef/chef/pull/10203) ([tas50](https://github.com/tas50))
+- Remove bad filter from git spec tests [#10204](https://github.com/chef/chef/pull/10204) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix el6 selinux [#10206](https://github.com/chef/chef/pull/10206) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove some test cruft around diffing [#10205](https://github.com/chef/chef/pull/10205) ([phiggins](https://github.com/phiggins))
+
+## [v16.2.73](https://github.com/chef/chef/tree/v16.2.73) (2020-07-01)
+
+#### Merged Pull Requests
+- More aggressively deprecate config_value [#10025](https://github.com/chef/chef/pull/10025) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix broken tests after updating diff-lcs dep [#10052](https://github.com/chef/chef/pull/10052) ([phiggins](https://github.com/phiggins))
+- Setup cspell to pull from our common dictionary [#10021](https://github.com/chef/chef/pull/10021) ([tas50](https://github.com/tas50))
+- Bump diff-lcs to get bugfix. [#10057](https://github.com/chef/chef/pull/10057) ([phiggins](https://github.com/phiggins))
+- Inline some constants to prevent redefenition warnings in tests. [#10047](https://github.com/chef/chef/pull/10047) ([phiggins](https://github.com/phiggins))
+- consume powershell shim DLLs from hab package [#10022](https://github.com/chef/chef/pull/10022) ([mwrock](https://github.com/mwrock))
+- Fixed broken chef-apply with -j [#10066](https://github.com/chef/chef/pull/10066) ([komazarari](https://github.com/komazarari))
+- Bump train-core to 3.3.4 [#10067](https://github.com/chef/chef/pull/10067) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Bump the external tests to 2.7 [#10062](https://github.com/chef/chef/pull/10062) ([lamont-granquist](https://github.com/lamont-granquist))
+- specs: Remove more methods defined on the top-level scope [#10060](https://github.com/chef/chef/pull/10060) ([phiggins](https://github.com/phiggins))
+- Update the windows_user_privilege resource to have a `:clear` action [#10063](https://github.com/chef/chef/pull/10063) ([chef-davin](https://github.com/chef-davin))
+- fix habitat based windows service tests [#10070](https://github.com/chef/chef/pull/10070) ([mwrock](https://github.com/mwrock))
+- Update ffi-libarchive for windows fixes / pin the dep [#10072](https://github.com/chef/chef/pull/10072) ([tas50](https://github.com/tas50))
+- Remove Azure private verify pipeline [#10019](https://github.com/chef/chef/pull/10019) ([christopher-snapp](https://github.com/christopher-snapp))
+- Pick some of the unit test fixes from #10068 [#10074](https://github.com/chef/chef/pull/10074) ([lamont-granquist](https://github.com/lamont-granquist))
+- Revert openssl digest update that broke FIPS [#10058](https://github.com/chef/chef/pull/10058) ([tas50](https://github.com/tas50))
+- Update tests and docs for umask on Windows. [#10077](https://github.com/chef/chef/pull/10077) ([phiggins](https://github.com/phiggins))
+- Revert &quot;Merge pull request #10052 from chef/fix-diff-tests&quot; [#10078](https://github.com/chef/chef/pull/10078) ([phiggins](https://github.com/phiggins))
+- specs: Remove test file that added a method to top-level scope. [#10027](https://github.com/chef/chef/pull/10027) ([phiggins](https://github.com/phiggins))
+- Bump inspec-core-bin to 4.21.1 [#10081](https://github.com/chef/chef/pull/10081) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Pin dff-lcs, bump ohai, &amp; misc omnibus updates [#10085](https://github.com/chef/chef/pull/10085) ([tas50](https://github.com/tas50))
+- Fix release build tests [#10088](https://github.com/chef/chef/pull/10088) ([phiggins](https://github.com/phiggins))
+- Fix release build tests, part 2 [#10089](https://github.com/chef/chef/pull/10089) ([phiggins](https://github.com/phiggins))
+- Fix release build tests, part 3 [#10093](https://github.com/chef/chef/pull/10093) ([phiggins](https://github.com/phiggins))
+
+## [v16.2.50](https://github.com/chef/chef/tree/v16.2.50) (2020-06-23)
+
+#### Merged Pull Requests
+- Bump inspec-core-bin to 4.20.10 [#10017](https://github.com/chef/chef/pull/10017) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- windows_security_policy was using resource_name instead of provides [#10018](https://github.com/chef/chef/pull/10018) ([chef-davin](https://github.com/chef-davin))
+- Fix for knife config use-profile doesn&#39;t validate that the profile exist [#10011](https://github.com/chef/chef/pull/10011) ([Vasu1105](https://github.com/Vasu1105))
+- Add more examples to the resource code [#10020](https://github.com/chef/chef/pull/10020) ([tas50](https://github.com/tas50))
+- Resource doc updates [#10024](https://github.com/chef/chef/pull/10024) ([phiggins](https://github.com/phiggins))
+- Bump ohai to 16.2.1 [#10035](https://github.com/chef/chef/pull/10035) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+
+## [v16.2.44](https://github.com/chef/chef/tree/v16.2.44) (2020-06-17)
+
+#### Merged Pull Requests
+- Chef-16.1 breaking change [#9890](https://github.com/chef/chef/pull/9890) ([lamont-granquist](https://github.com/lamont-granquist))
+- Adds the homebrew_update resource [#9896](https://github.com/chef/chef/pull/9896) ([damacus](https://github.com/damacus))
+- Add an input property to the execute resource for passing input on STDIN [#9910](https://github.com/chef/chef/pull/9910) ([phiggins](https://github.com/phiggins))
+- Add ssl_verify option for remote_file [#9833](https://github.com/chef/chef/pull/9833) ([jaymzh](https://github.com/jaymzh))
+- Update &amp; add resource descriptions for documentation generation [#9923](https://github.com/chef/chef/pull/9923) ([tas50](https://github.com/tas50))
+- Update to ssl_verify_mode on remote_file [#9925](https://github.com/chef/chef/pull/9925) ([jaymzh](https://github.com/jaymzh))
+- Improve auto-generated docs [#9926](https://github.com/chef/chef/pull/9926) ([tas50](https://github.com/tas50))
+- Fix chefstyle violations. [#9929](https://github.com/chef/chef/pull/9929) ([phiggins](https://github.com/phiggins))
+- Make sure file is properly scoped in cron_access [#9931](https://github.com/chef/chef/pull/9931) ([tas50](https://github.com/tas50))
+- hostname: Remove support for Solaris 5.10 [#9928](https://github.com/chef/chef/pull/9928) ([tas50](https://github.com/tas50))
+- hostname: Improve the windows reboot message [#9927](https://github.com/chef/chef/pull/9927) ([tas50](https://github.com/tas50))
+- Update chef-telemetry to 1.0.8 and InSpec to 4.19 [#9934](https://github.com/chef/chef/pull/9934) ([tas50](https://github.com/tas50))
+- Set up CI with Azure Pipelines [#9894](https://github.com/chef/chef/pull/9894) ([btm](https://github.com/btm))
+- Add additional testing for macOS hosts [#9939](https://github.com/chef/chef/pull/9939) ([tas50](https://github.com/tas50))
+- archive_file: better handle mode property and deprecate Integer values [#9950](https://github.com/chef/chef/pull/9950) ([tas50](https://github.com/tas50))
+- archive_file: move ffi-libarchive into a simple helper method [#9951](https://github.com/chef/chef/pull/9951) ([tas50](https://github.com/tas50))
+- Add nightly cleanup of orphaned test resources [#9943](https://github.com/chef/chef/pull/9943) ([christopher-snapp](https://github.com/christopher-snapp))
+- Pin FFI &lt; 1.13 and bump inspec-core-bin to 4.19.2 [#9954](https://github.com/chef/chef/pull/9954) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fixed Powershell_Package does not throw error when it cannot connect … [#9946](https://github.com/chef/chef/pull/9946) ([sanga1794](https://github.com/sanga1794))
+- Add spellcheck to CI [#9957](https://github.com/chef/chef/pull/9957) ([phiggins](https://github.com/phiggins))
+- Fix zypper_repository key handling on SLES 15+ [#9956](https://github.com/chef/chef/pull/9956) ([tas50](https://github.com/tas50))
+- update learn chef name, discourse name, and copyright year [#9958](https://github.com/chef/chef/pull/9958) ([bennyvasquez](https://github.com/bennyvasquez))
+- Fix rspec warning about `not_to raise_error` with a specific exception. [#9937](https://github.com/chef/chef/pull/9937) ([phiggins](https://github.com/phiggins))
+- Change script resources to use pipes rather than writing to temp files [#9932](https://github.com/chef/chef/pull/9932) ([phiggins](https://github.com/phiggins))
+- Update to the chef_client_scheduled_task resource frequency_modify default functionality [#9920](https://github.com/chef/chef/pull/9920) ([chef-davin](https://github.com/chef-davin))
+- Update train-core to the latest [#9959](https://github.com/chef/chef/pull/9959) ([tas50](https://github.com/tas50))
+- Fix wrong unit test exposed by cleaning up rspec deprecations. [#9961](https://github.com/chef/chef/pull/9961) ([phiggins](https://github.com/phiggins))
+- Add more resource docs + improve yaml generation [#9960](https://github.com/chef/chef/pull/9960) ([tas50](https://github.com/tas50))
+- knife vault on windows 10 fails due to ERROR: Chef::Exceptions::InvalidDataBagPath [#9952](https://github.com/chef/chef/pull/9952) ([snehaldwivedi](https://github.com/snehaldwivedi))
+- Add Windows 8 Tester [#9971](https://github.com/chef/chef/pull/9971) ([christopher-snapp](https://github.com/christopher-snapp))
+- Let the user know what protocol we&#39;re using in knife bootstrap [#9973](https://github.com/chef/chef/pull/9973) ([tas50](https://github.com/tas50))
+- Warn during bootstrapping when using validation keys [#9974](https://github.com/chef/chef/pull/9974) ([tas50](https://github.com/tas50))
+- Allow for the latest net-ssh and ffi 1.13.1 [#9978](https://github.com/chef/chef/pull/9978) ([tas50](https://github.com/tas50))
+- Stop producing packages for EOL Debian 8 [#9981](https://github.com/chef/chef/pull/9981) ([tas50](https://github.com/tas50))
+- Use /etc/chef for bootstrapping instead of ChefConfig [#9984](https://github.com/chef/chef/pull/9984) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update with 2020 MVPs [#9985](https://github.com/chef/chef/pull/9985) ([Xorima](https://github.com/Xorima))
+- Small code cleanups in script/windows_script [#9979](https://github.com/chef/chef/pull/9979) ([phiggins](https://github.com/phiggins))
+- Fix snap_package bugs [#9944](https://github.com/chef/chef/pull/9944) ([jaymzh](https://github.com/jaymzh))
+- Use .match? not =~ when match values aren&#39;t necessary [#9989](https://github.com/chef/chef/pull/9989) ([tas50](https://github.com/tas50))
+- Disable snap dokken tests for now [#9993](https://github.com/chef/chef/pull/9993) ([tas50](https://github.com/tas50))
+- Fix how enforce_license is set in run method for chef-apply [#9963](https://github.com/chef/chef/pull/9963) ([ramereth](https://github.com/ramereth))
+- Create windows_audit_policy resource [#9980](https://github.com/chef/chef/pull/9980) ([chef-davin](https://github.com/chef-davin))
+- Add &quot;most recent call first&quot; to traceback message [#9967](https://github.com/chef/chef/pull/9967) ([zfjagann](https://github.com/zfjagann))
+- Improve resource documentation [#9995](https://github.com/chef/chef/pull/9995) ([tas50](https://github.com/tas50))
+- Cron and Cron_d resource weekday property fixes [#10001](https://github.com/chef/chef/pull/10001) ([tas50](https://github.com/tas50))
+- Cleanup more resource examples [#10002](https://github.com/chef/chef/pull/10002) ([tas50](https://github.com/tas50))
+- Silence exception output in threaded test. [#10005](https://github.com/chef/chef/pull/10005) ([phiggins](https://github.com/phiggins))
+- Soft fail spellchecker in ci [#10003](https://github.com/chef/chef/pull/10003) ([phiggins](https://github.com/phiggins))
+- Fix for windows_audit_policy bug for when a value for the subcategory property isn&#39;t entered [#10007](https://github.com/chef/chef/pull/10007) ([chef-davin](https://github.com/chef-davin))
+- Update InSpec to 4.20.6 [#10008](https://github.com/chef/chef/pull/10008) ([tas50](https://github.com/tas50))
+- Bump ohai to 16.2.0 [#10009](https://github.com/chef/chef/pull/10009) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add a umask property for resources. [#10000](https://github.com/chef/chef/pull/10000) ([phiggins](https://github.com/phiggins))
+- Minor docs updates and MacOS -&gt; macOS [#10010](https://github.com/chef/chef/pull/10010) ([tas50](https://github.com/tas50))
+- Update the list of allowed policies for the windows_security_policy resource [#10012](https://github.com/chef/chef/pull/10012) ([chef-davin](https://github.com/chef-davin))
+
+## [v16.1.16](https://github.com/chef/chef/tree/v16.1.16) (2020-05-27)
+
+#### Merged Pull Requests
+- fix windows bootstrap; replicate chef/chef/pull/9839 by Chad Jessup [#9878](https://github.com/chef/chef/pull/9878) ([bobchaos](https://github.com/bobchaos))
+- Fix failures inspecting a single cookbook [#9882](https://github.com/chef/chef/pull/9882) ([tas50](https://github.com/tas50))
+- Fix ruby 2.7 keyword argument warning. [#9883](https://github.com/chef/chef/pull/9883) ([phiggins](https://github.com/phiggins))
+- Add spellcheck config and fix a bunch of violations [#9826](https://github.com/chef/chef/pull/9826) ([phiggins](https://github.com/phiggins))
+- Added armv6l and armv7l to arm? and armhf? helpers [#9887](https://github.com/chef/chef/pull/9887) ([mattray](https://github.com/mattray))
+- Small cleanups to checksum logic in file/windows_package/chef::mixin::checksum [#9886](https://github.com/chef/chef/pull/9886) ([phiggins](https://github.com/phiggins))
+- Fix bootstrapping windows from linux [#9889](https://github.com/chef/chef/pull/9889) ([btm](https://github.com/btm))
+- Fixed armv6l and armv7l tests. [#9892](https://github.com/chef/chef/pull/9892) ([mattray](https://github.com/mattray))
+- [chef-utils] fix typos [#9895](https://github.com/chef/chef/pull/9895) ([michel-slm](https://github.com/michel-slm))
+- Update openssl to 1.0.2v [#9903](https://github.com/chef/chef/pull/9903) ([tas50](https://github.com/tas50))
+- macos_userdefaults: Fix ruby 2.7 keyword args warning and refactor a bit [#9897](https://github.com/chef/chef/pull/9897) ([phiggins](https://github.com/phiggins))
+- Refactor verify pipeline to use Ruby 2.7 on Windows docker image [#9877](https://github.com/chef/chef/pull/9877) ([christopher-snapp](https://github.com/christopher-snapp))
+- Windows functional test should be single-use [#9908](https://github.com/chef/chef/pull/9908) ([christopher-snapp](https://github.com/christopher-snapp))
+- Update our usage of OpenSSL::Digest to avoid Ruby 3 breaking change [#9905](https://github.com/chef/chef/pull/9905) ([tas50](https://github.com/tas50))
+- Pull in updated omnibus-software for rubygems perf patch [#9916](https://github.com/chef/chef/pull/9916) ([tas50](https://github.com/tas50))
+
+## [v16.1.0](https://github.com/chef/chef/tree/v16.1.0) (2020-05-15)
+
+#### Merged Pull Requests
+- Remove the config hash defaults [#9827](https://github.com/chef/chef/pull/9827) ([lamont-granquist](https://github.com/lamont-granquist))
+- Try removing asdf entirely and just use omnibus-toolchain [#9828](https://github.com/chef/chef/pull/9828) ([tas50](https://github.com/tas50))
+- Use the dist values in server error messages [#9830](https://github.com/chef/chef/pull/9830) ([tas50](https://github.com/tas50))
+- Stop upgrading rubygems in tests [#9835](https://github.com/chef/chef/pull/9835) ([tas50](https://github.com/tas50))
+- Update rubyinstaller-devkit-2.6.6-1-x64.exe SHA [#9845](https://github.com/chef/chef/pull/9845) ([christopher-snapp](https://github.com/christopher-snapp))
+- Add examples to windows_security_policy [#9842](https://github.com/chef/chef/pull/9842) ([IanMadd](https://github.com/IanMadd))
+- Avoid ruby deprecation warnings in cab_package and powershell_script [#9850](https://github.com/chef/chef/pull/9850) ([tas50](https://github.com/tas50))
+- Add Debian 10 aarch64 support [#9837](https://github.com/chef/chef/pull/9837) ([christopher-snapp](https://github.com/christopher-snapp))
+- Test PRs against Ruby 2.7 [#9856](https://github.com/chef/chef/pull/9856) ([tas50](https://github.com/tas50))
+- Add Ubuntu 20.04 testing to our pipeline [#9859](https://github.com/chef/chef/pull/9859) ([tas50](https://github.com/tas50))
+- private end to end windows 10 pipeline [#9857](https://github.com/chef/chef/pull/9857) ([btm](https://github.com/btm))
+- Switch end-to-end-windows-10 back to use the Linux scripts [#9862](https://github.com/chef/chef/pull/9862) ([btm](https://github.com/btm))
+- Remove most of the emojis from the buildkite tests [#9863](https://github.com/chef/chef/pull/9863) ([btm](https://github.com/btm))
+- Switch Ruby functional test on Windows to Ruby 2.7 [#9864](https://github.com/chef/chef/pull/9864) ([tas50](https://github.com/tas50))
+- Update Chefstyle to 1.0.5, Ohai to 16.1.1, and cheffish to 15.0.3 [#9869](https://github.com/chef/chef/pull/9869) ([tas50](https://github.com/tas50))
+- Fixes and tests for Chef-16 regression in launchd [#9868](https://github.com/chef/chef/pull/9868) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump inspec-core to 4.18.114 [#9870](https://github.com/chef/chef/pull/9870) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add 16.1 release notes [#9873](https://github.com/chef/chef/pull/9873) ([tas50](https://github.com/tas50))
+
+## [v16.0.287](https://github.com/chef/chef/tree/v16.0.287) (2020-05-07)
+
+#### Merged Pull Requests
+- More cleanup of our installed gems [#9802](https://github.com/chef/chef/pull/9802) ([tas50](https://github.com/tas50))
+- Update omnibus-software for new msys2 path [#9801](https://github.com/chef/chef/pull/9801) ([jaymalasinha](https://github.com/jaymalasinha))
+- Pin chef-telementry to 1.0.3 to avoid ffi build breakage [#9808](https://github.com/chef/chef/pull/9808) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 4.18.111 [#9803](https://github.com/chef/chef/pull/9803) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fix windows_package not allowing version to be an array [#9810](https://github.com/chef/chef/pull/9810) ([tas50](https://github.com/tas50))
+- Align all our comments with the code [#9811](https://github.com/chef/chef/pull/9811) ([tas50](https://github.com/tas50))
+- fix launchd provider for chef-16 [#9812](https://github.com/chef/chef/pull/9812) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add ohai_hint and openssl_* testing in Test Kitchen [#9813](https://github.com/chef/chef/pull/9813) ([tas50](https://github.com/tas50))
+- Update omnibus-software for Windows lalr1.java removal [#9815](https://github.com/chef/chef/pull/9815) ([btm](https://github.com/btm))
+- Expand our sudo, sysctl, execute, apt_update and apt_preference testing [#9816](https://github.com/chef/chef/pull/9816) ([tas50](https://github.com/tas50))
+- Cache the gems during the Test Kitchen tests [#9818](https://github.com/chef/chef/pull/9818) ([tas50](https://github.com/tas50))
+- Don&#39;t update rubygems / bundler in the kitchen tests [#9819](https://github.com/chef/chef/pull/9819) ([tas50](https://github.com/tas50))
+- Only require the driver_name property when creating printer ports [#9824](https://github.com/chef/chef/pull/9824) ([tas50](https://github.com/tas50))
+- Fix a misspelling in knife subcommand. [#9825](https://github.com/chef/chef/pull/9825) ([phiggins](https://github.com/phiggins))
+
+## [v16.0.275](https://github.com/chef/chef/tree/v16.0.275) (2020-05-05)
+
+#### Merged Pull Requests
+- Add Chef Infra Client 16 release notes [#9614](https://github.com/chef/chef/pull/9614) ([tas50](https://github.com/tas50))
+- Fixes to support unbundled rake use [#9759](https://github.com/chef/chef/pull/9759) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix more ruby 2.7 warning logspam [#9762](https://github.com/chef/chef/pull/9762) ([lamont-granquist](https://github.com/lamont-granquist))
+- Improve resource docs [#9764](https://github.com/chef/chef/pull/9764) ([tas50](https://github.com/tas50))
+- More resource documentation improvements [#9765](https://github.com/chef/chef/pull/9765) ([tas50](https://github.com/tas50))
+- More improvements to the docs generation [#9767](https://github.com/chef/chef/pull/9767) ([tas50](https://github.com/tas50))
+- Improve resource self documentation [#9773](https://github.com/chef/chef/pull/9773) ([tas50](https://github.com/tas50))
+- Remove an unused spec helper [#9774](https://github.com/chef/chef/pull/9774) ([tas50](https://github.com/tas50))
+- Add more examples to resources [#9775](https://github.com/chef/chef/pull/9775) ([tas50](https://github.com/tas50))
+- Fix symbols and arrays in yaml conversion tool [#9769](https://github.com/chef/chef/pull/9769) ([phiggins](https://github.com/phiggins))
+- Modified client cron directory permission [#9782](https://github.com/chef/chef/pull/9782) ([DhaneshRaghavan](https://github.com/DhaneshRaghavan))
+- chef_client_cron: cleanup the appropriate cron job [#9784](https://github.com/chef/chef/pull/9784) ([tas50](https://github.com/tas50))
+- Avoid setting package to macports and then overriding to homebrew [#9783](https://github.com/chef/chef/pull/9783) ([tas50](https://github.com/tas50))
+- Another round of description updates &amp;&amp; Docs generator tweaks [#9778](https://github.com/chef/chef/pull/9778) ([tas50](https://github.com/tas50))
+- Fix failing RHEL 6 kitchen tests [#9740](https://github.com/chef/chef/pull/9740) ([tas50](https://github.com/tas50))
+- Fix windows package super bug and cleanup [#9790](https://github.com/chef/chef/pull/9790) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix required errors in apt_preference and sysctl resources [#9791](https://github.com/chef/chef/pull/9791) ([tas50](https://github.com/tas50))
+- fix Habitat Windows package build [#9739](https://github.com/chef/chef/pull/9739) ([robbkidd](https://github.com/robbkidd))
+- Fix failures in the cron_d resource :remove action [#9794](https://github.com/chef/chef/pull/9794) ([tas50](https://github.com/tas50))
+
+## [v16.0.257](https://github.com/chef/chef/tree/v16.0.257) (2020-04-28)
+
+#### Merged Pull Requests
+- Fix mac_user breakage and logging [#9158](https://github.com/chef/chef/pull/9158) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump train-core to 3.2.5 [#9159](https://github.com/chef/chef/pull/9159) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- try unit + functional tests [#9163](https://github.com/chef/chef/pull/9163) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump inspec-core to 4.18.51 [#9168](https://github.com/chef/chef/pull/9168) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- add new option bootstrap_product for install cinc via bootstrap [#9112](https://github.com/chef/chef/pull/9112) ([sawanoboly](https://github.com/sawanoboly))
+- resource archive_file: apply ownership to extracted files only [#9173](https://github.com/chef/chef/pull/9173) ([bobchaos](https://github.com/bobchaos))
+- Fix event log message description format [#9190](https://github.com/chef/chef/pull/9190) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Bump mixlib-cli to 2.1.5 as well as Ohai, cheffish, and telemetry [#9191](https://github.com/chef/chef/pull/9191) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Update all omnibus deps to the latest [#9192](https://github.com/chef/chef/pull/9192) ([tas50](https://github.com/tas50))
+- Update libarchive to 1.0 [#9194](https://github.com/chef/chef/pull/9194) ([tas50](https://github.com/tas50))
+- Bump ffi-yajl to 2.3.3 [#9195](https://github.com/chef/chef/pull/9195) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Bump mixlib-archive to 1.0.5 [#9196](https://github.com/chef/chef/pull/9196) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Bump chef-vault to 4.0.1, chef-zero to 14.0.17, mixlib-shellout to 3.0.9, and mixlib-authentication to 3.0.6 [#9198](https://github.com/chef/chef/pull/9198) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Last batch of wordmarks removal for chef-config [#9176](https://github.com/chef/chef/pull/9176) ([bobchaos](https://github.com/bobchaos))
+- Remove legacy Net::HTTP not needed in Ruby 2.2+ [#9200](https://github.com/chef/chef/pull/9200) ([tas50](https://github.com/tas50))
+- Expand chef-utils yard comments and make consistent [#9188](https://github.com/chef/chef/pull/9188) ([tas50](https://github.com/tas50))
+- Update all deps to current [#9210](https://github.com/chef/chef/pull/9210) ([tas50](https://github.com/tas50))
+- Fixes for sudo resource fails on 2nd converge when Cmnd_Alias is used [#9186](https://github.com/chef/chef/pull/9186) ([samshinde](https://github.com/samshinde))
+- Clear password flags when setting the password on aix [#9209](https://github.com/chef/chef/pull/9209) ([Triodes](https://github.com/Triodes))
+- Do not build Chef Infra Client on Windows 2008 R2 [#9203](https://github.com/chef/chef/pull/9203) ([tas50](https://github.com/tas50))
+- Update to license_scout 1.1.2 [#9212](https://github.com/chef/chef/pull/9212) ([tas50](https://github.com/tas50))
+- x509_certificate : Add the capability to automatically renew a certificate [#9187](https://github.com/chef/chef/pull/9187) ([julienhuon](https://github.com/julienhuon))
+- Fix for windows task not idempotent on the windows19 and window… [#9223](https://github.com/chef/chef/pull/9223) ([Vasu1105](https://github.com/Vasu1105))
+- Use the right class in knife supermarket install [#9217](https://github.com/chef/chef/pull/9217) ([tas50](https://github.com/tas50))
+- Use /etc/chef for bootstrapping instead of CONF_DIR [#9226](https://github.com/chef/chef/pull/9226) ([marcparadise](https://github.com/marcparadise))
+- Windows Path on Bootstrap [#8669](https://github.com/chef/chef/pull/8669) ([Xorima](https://github.com/Xorima))
+- Update openssl to 1.0.2u [#9229](https://github.com/chef/chef/pull/9229) ([tas50](https://github.com/tas50))
+- Generate metadata.json from metadata.rb if not exist before knife cookbook upload or knife upload or berkshelf upload [#9073](https://github.com/chef/chef/pull/9073) ([Vasu1105](https://github.com/Vasu1105))
+- Add time_out property in cron resource [#9153](https://github.com/chef/chef/pull/9153) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Remove RHEL 6 s390x (zLinux) support [#9233](https://github.com/chef/chef/pull/9233) ([jaymalasinha](https://github.com/jaymalasinha))
+- Update to Ohai 16.0.2 [#9246](https://github.com/chef/chef/pull/9246) ([tas50](https://github.com/tas50))
+- Require Ruby 2.6+ [#9247](https://github.com/chef/chef/pull/9247) ([tas50](https://github.com/tas50))
+- WIP: Chef-16 resource cleanup + unified_mode [#9174](https://github.com/chef/chef/pull/9174) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add windows helpers for install type [#9235](https://github.com/chef/chef/pull/9235) ([tas50](https://github.com/tas50))
+- move Chef::VersionString to Chef::Utils [#9234](https://github.com/chef/chef/pull/9234) ([lamont-granquist](https://github.com/lamont-granquist))
+- Raises error if there is empty cookbook directory at the time o… [#9011](https://github.com/chef/chef/pull/9011) ([Vasu1105](https://github.com/Vasu1105))
+- Fix most Ruby 2.7 test failures / systemd service provider splat args conversion [#9251](https://github.com/chef/chef/pull/9251) ([lamont-granquist](https://github.com/lamont-granquist))
+- launchd: Fix capitalization of HardResourceLimits [#9239](https://github.com/chef/chef/pull/9239) ([rb2k](https://github.com/rb2k))
+- fix a few more ruby 2.7 specs [#9253](https://github.com/chef/chef/pull/9253) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump train-core to 3.2.14 [#9258](https://github.com/chef/chef/pull/9258) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fixes all notarization issues [#9219](https://github.com/chef/chef/pull/9219) ([jonsmorrow](https://github.com/jonsmorrow))
+- Provider a better error message in Chef::Cookbook::CookbookVersionLoader [#9264](https://github.com/chef/chef/pull/9264) ([tas50](https://github.com/tas50))
+- Use .load! in the Cookbook loader not .load_cookbooks [#9266](https://github.com/chef/chef/pull/9266) ([tas50](https://github.com/tas50))
+- Ruby 2.7 IRB and remaining fixes [#9267](https://github.com/chef/chef/pull/9267) ([lamont-granquist](https://github.com/lamont-granquist))
+- Cleanup the specs for the load_cookbooks warnings [#9269](https://github.com/chef/chef/pull/9269) ([tas50](https://github.com/tas50))
+- switch the load method back to not raising + deprecation [#9274](https://github.com/chef/chef/pull/9274) ([lamont-granquist](https://github.com/lamont-granquist))
+- berks upload skip syntax check fixes [#9281](https://github.com/chef/chef/pull/9281) ([vsingh-msys](https://github.com/vsingh-msys))
+- add berkshelf as an external test [#9284](https://github.com/chef/chef/pull/9284) ([lamont-granquist](https://github.com/lamont-granquist))
+- [chef-16] Remove the data bag secret short option [#9263](https://github.com/chef/chef/pull/9263) ([vsingh-msys](https://github.com/vsingh-msys))
+- Remove more support for Windows 2008 R2 / RHEL 5 [#9261](https://github.com/chef/chef/pull/9261) ([tas50](https://github.com/tas50))
+- Remove the deprecated knife cookbook site commands [#9288](https://github.com/chef/chef/pull/9288) ([tas50](https://github.com/tas50))
+- Remove the sites-cookbooks dir from the cookbook_path default config [#9290](https://github.com/chef/chef/pull/9290) ([tas50](https://github.com/tas50))
+- Merge pull request #9291 from chef/lcg/chef-utils-doc-touchup [#9291](https://github.com/chef/chef/pull/9291) ([lamont-granquist](https://github.com/lamont-granquist))
+- apt_repository: add a description for components when using a PPA [#9289](https://github.com/chef/chef/pull/9289) ([tas50](https://github.com/tas50))
+- Update knife status --long to use cloud attributes not ec2 specific attributes [#7882](https://github.com/chef/chef/pull/7882) ([tas50](https://github.com/tas50))
+- Remove DK wording from knife warning [#9293](https://github.com/chef/chef/pull/9293) ([tas50](https://github.com/tas50))
+- debian 10 ifconfig fix [#9294](https://github.com/chef/chef/pull/9294) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add ruby 2.7 expeditor testing [#9271](https://github.com/chef/chef/pull/9271) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix fuzzy node search to work when the search type is a string rather than a symbol [#9287](https://github.com/chef/chef/pull/9287) ([mimato](https://github.com/mimato))
+- add bk testing against merge commit [#9296](https://github.com/chef/chef/pull/9296) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add windows_nt_version and powershell_version helpers to chef-utils [#9297](https://github.com/chef/chef/pull/9297) ([tas50](https://github.com/tas50))
+- declare_resource.rb: consistently use `/x/y.txt` [#9273](https://github.com/chef/chef/pull/9273) ([michel-slm](https://github.com/michel-slm))
+- revert to &quot;chef&quot; [#9300](https://github.com/chef/chef/pull/9300) ([bobchaos](https://github.com/bobchaos))
+- Move knife-acl gem commands into chef in their own namespaces [#9292](https://github.com/chef/chef/pull/9292) ([tas50](https://github.com/tas50))
+- Add cloud helpers from chef-sugar [#9302](https://github.com/chef/chef/pull/9302) ([lamont-granquist](https://github.com/lamont-granquist))
+- chef-utils: add which/train_helpers/path_sanity to the DSL [#9303](https://github.com/chef/chef/pull/9303) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add chef-sugar include_recipe? helper [#9304](https://github.com/chef/chef/pull/9304) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump win32-service to 2.1.5 [#9301](https://github.com/chef/chef/pull/9301) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fix wrong windows_firewall_rule specs which is passing :enable action which does not existurce does not have enable action so passing… [#9298](https://github.com/chef/chef/pull/9298) ([Vasu1105](https://github.com/Vasu1105))
+- Adding entitlement for unsigned memory execution [#9317](https://github.com/chef/chef/pull/9317) ([jonsmorrow](https://github.com/jonsmorrow))
+- mac_user: fixing gid and system properties, and adding hidden property [#9275](https://github.com/chef/chef/pull/9275) ([chilcote](https://github.com/chilcote))
+- Add comment block to sysctl resource [#8409](https://github.com/chef/chef/pull/8409) ([lanky](https://github.com/lanky))
+- Add an introduced field to sysctl and expand testing [#9321](https://github.com/chef/chef/pull/9321) ([tas50](https://github.com/tas50))
+- Document the new hidden field in mac_user ships in 15.8 [#9322](https://github.com/chef/chef/pull/9322) ([tas50](https://github.com/tas50))
+- Expand the chef-utils yard for better vscode plugin generation [#9326](https://github.com/chef/chef/pull/9326) ([tas50](https://github.com/tas50))
+- Add chef-sugar virtualization helpers [#9315](https://github.com/chef/chef/pull/9315) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add some version string backcompat APIs [#9330](https://github.com/chef/chef/pull/9330) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add a default_description in windows_task [#9329](https://github.com/chef/chef/pull/9329) ([tas50](https://github.com/tas50))
+- Swap the methods and the aliases in the chef-utils platforms [#9327](https://github.com/chef/chef/pull/9327) ([tas50](https://github.com/tas50))
+- Pull the windows Ruby installer from S3 for tests [#9332](https://github.com/chef/chef/pull/9332) ([tas50](https://github.com/tas50))
+- Update FFI and pin win32-service to 2.1.5+ [#9337](https://github.com/chef/chef/pull/9337) ([tas50](https://github.com/tas50))
+- Add Debian 10 (Buster) Tester [#9341](https://github.com/chef/chef/pull/9341) ([christopher-snapp](https://github.com/christopher-snapp))
+- Improve welcome text in chef-shell [#9342](https://github.com/chef/chef/pull/9342) ([tas50](https://github.com/tas50))
+- Lazy load as many knife deps as possible [#9343](https://github.com/chef/chef/pull/9343) ([tas50](https://github.com/tas50))
+- Expand the path in knife cookbook upload errors [#9344](https://github.com/chef/chef/pull/9344) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 4.18.85 [#9346](https://github.com/chef/chef/pull/9346) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Update docker? help comment to show it&#39;s since 12.11 [#9348](https://github.com/chef/chef/pull/9348) ([tas50](https://github.com/tas50))
+- Add notify_group resource [#9349](https://github.com/chef/chef/pull/9349) ([lamont-granquist](https://github.com/lamont-granquist))
+- update syntax of `update-rc.d` commands in enable &amp; disable actions [#8884](https://github.com/chef/chef/pull/8884) ([robuye](https://github.com/robuye))
+- Add compile_time property to all resources [#9360](https://github.com/chef/chef/pull/9360) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow setting file_cache_path and file_backup_path value in client.rb during bootstrap [#9361](https://github.com/chef/chef/pull/9361) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Add missing require_relative in chef-utils [#9363](https://github.com/chef/chef/pull/9363) ([tas50](https://github.com/tas50))
+- Bump all deps to the latest [#9365](https://github.com/chef/chef/pull/9365) ([tas50](https://github.com/tas50))
+- Add chef_vault_secret resource from chef-vault cookbook [#9364](https://github.com/chef/chef/pull/9364) ([tas50](https://github.com/tas50))
+- Add examples to various resources [#9366](https://github.com/chef/chef/pull/9366) ([tas50](https://github.com/tas50))
+- windows_ad_join: Fix joining specific domains when domain trusts are involved [#9370](https://github.com/chef/chef/pull/9370) ([srb3](https://github.com/srb3))
+- Use require_relative within the specs [#9375](https://github.com/chef/chef/pull/9375) ([tas50](https://github.com/tas50))
+- Update libarchive to 3.4.2 and nokogiri to 1.10.8 [#9377](https://github.com/chef/chef/pull/9377) ([tas50](https://github.com/tas50))
+- Relax the cheffish constraint [#9379](https://github.com/chef/chef/pull/9379) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix our verbosity help [#9385](https://github.com/chef/chef/pull/9385) ([tas50](https://github.com/tas50))
+- Chefstyle fixes identified with Rubocop 0.80 [#9374](https://github.com/chef/chef/pull/9374) ([tas50](https://github.com/tas50))
+- fix ruby 2.7 URI.unescape deprecation [#9387](https://github.com/chef/chef/pull/9387) ([lamont-granquist](https://github.com/lamont-granquist))
+- Convert more resources to unified_mode [#9386](https://github.com/chef/chef/pull/9386) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update HTTPServerException to be HTTPClientException [#9381](https://github.com/chef/chef/pull/9381) ([tas50](https://github.com/tas50))
+- Parse YAML recipes [#8653](https://github.com/chef/chef/pull/8653) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add distro constants to the bootstrap templates to support non-Chef distros [#9371](https://github.com/chef/chef/pull/9371) ([bobchaos](https://github.com/bobchaos))
+- When bootstrapping don&#39;t send an empty run_list if we are using policyfiles instead [#9156](https://github.com/chef/chef/pull/9156) ([NAshwini](https://github.com/NAshwini))
+- ChefFS &amp; knife environment matching the output [#8986](https://github.com/chef/chef/pull/8986) ([vsingh-msys](https://github.com/vsingh-msys))
+- Bump inspec-core to 4.18.97 [#9388](https://github.com/chef/chef/pull/9388) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Dist windows powershell wrapper [#9065](https://github.com/chef/chef/pull/9065) ([bobchaos](https://github.com/bobchaos))
+- More unified mode conversion and resource cleanup [#9393](https://github.com/chef/chef/pull/9393) ([lamont-granquist](https://github.com/lamont-granquist))
+- Use the chef-utils helpers in our resources [#9397](https://github.com/chef/chef/pull/9397) ([tas50](https://github.com/tas50))
+- Convert the node[:platform_version] to a Chef::VersionString [#9400](https://github.com/chef/chef/pull/9400) ([lamont-granquist](https://github.com/lamont-granquist))
+- Migrating windows_user_privilege resource from windows cookbook [#9279](https://github.com/chef/chef/pull/9279) ([Vasu1105](https://github.com/Vasu1105))
+- Bump all deps to current [#9401](https://github.com/chef/chef/pull/9401) ([tas50](https://github.com/tas50))
+- Update license_scout to 1.1.7 to resolve build failures [#9402](https://github.com/chef/chef/pull/9402) ([tas50](https://github.com/tas50))
+- Cookstyle fixes for our resources [#9395](https://github.com/chef/chef/pull/9395) ([tas50](https://github.com/tas50))
+- Accept exit code 3010 as valid in windows_package [#9396](https://github.com/chef/chef/pull/9396) ([tas50](https://github.com/tas50))
+- Remove the &quot;Core&quot; DSL for Chef-16 [#9411](https://github.com/chef/chef/pull/9411) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update the rhsm_erata* and rhsm_register resources for RHEL 8 [#9409](https://github.com/chef/chef/pull/9409) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.0.7 [#9415](https://github.com/chef/chef/pull/9415) ([tas50](https://github.com/tas50))
+- Remove all the code that checks for Windows Nano [#9417](https://github.com/chef/chef/pull/9417) ([tas50](https://github.com/tas50))
+- Remove support for macOS &lt; 10.12 in the service resource [#9420](https://github.com/chef/chef/pull/9420) ([tas50](https://github.com/tas50))
+- Deprecate supports_powershell_execution_bypass? check [#9418](https://github.com/chef/chef/pull/9418) ([tas50](https://github.com/tas50))
+- directory resource: Remove support for macOS &lt; 10.11 [#9421](https://github.com/chef/chef/pull/9421) ([tas50](https://github.com/tas50))
+- Don&#39;t require/include Mixin Shellout in freebsd_package and openbsd_package [#9423](https://github.com/chef/chef/pull/9423) ([tas50](https://github.com/tas50))
+- Deprecate the older_than_win_2012_or_8? helper [#9424](https://github.com/chef/chef/pull/9424) ([tas50](https://github.com/tas50))
+- Update gcc pinning for solaris to 5.4.0 [#9430](https://github.com/chef/chef/pull/9430) ([jaymalasinha](https://github.com/jaymalasinha))
+- Fix gsub so only file endings of .rb and .json are removed. [#9429](https://github.com/chef/chef/pull/9429) ([joerg](https://github.com/joerg))
+- Remove constraints on specs [#9428](https://github.com/chef/chef/pull/9428) ([tas50](https://github.com/tas50))
+- Remove the last bits of Appveyor from the specs [#9427](https://github.com/chef/chef/pull/9427) ([tas50](https://github.com/tas50))
+- More removal of Windows 2008 R2 support from windows_feature_powershell [#9426](https://github.com/chef/chef/pull/9426) ([tas50](https://github.com/tas50))
+- Remove the mixin powershell includes from resources [#9425](https://github.com/chef/chef/pull/9425) ([tas50](https://github.com/tas50))
+- Deprecate Chef::Platform.supports_msi? [#9422](https://github.com/chef/chef/pull/9422) ([tas50](https://github.com/tas50))
+- Bump train-core to 3.2.23 [#9432](https://github.com/chef/chef/pull/9432) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Replace highline.color with pastel.decorate [#9434](https://github.com/chef/chef/pull/9434) ([btm](https://github.com/btm))
+- Use the action DSL consistently [#9433](https://github.com/chef/chef/pull/9433) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix file descriptor leak in our tests [#9449](https://github.com/chef/chef/pull/9449) ([lamont-granquist](https://github.com/lamont-granquist))
+- Ruby 2.7 omnibus builds [#9454](https://github.com/chef/chef/pull/9454) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add windows_security_policy resource [#9280](https://github.com/chef/chef/pull/9280) ([NAshwini](https://github.com/NAshwini))
+- Update all our links to use the new docs site format / cleanup descriptions [#9445](https://github.com/chef/chef/pull/9445) ([tas50](https://github.com/tas50))
+- timezone: Remove support for ubuntu &lt; 16.04 / Debian &lt; 8 [#9452](https://github.com/chef/chef/pull/9452) ([tas50](https://github.com/tas50))
+- Remove the canonical DSL [#9441](https://github.com/chef/chef/pull/9441) ([lamont-granquist](https://github.com/lamont-granquist))
+- link resource: Remove checks for symoblic link support on Windows [#9455](https://github.com/chef/chef/pull/9455) ([tas50](https://github.com/tas50))
+- Test on Ubuntu 20.04 in Buildkite [#9458](https://github.com/chef/chef/pull/9458) ([tas50](https://github.com/tas50))
+- Disable failing windows tests while we troubleshoot [#9459](https://github.com/chef/chef/pull/9459) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.0.9 [#9461](https://github.com/chef/chef/pull/9461) ([tas50](https://github.com/tas50))
+- Use Ohai&#39;s cloud attributes in knife node / status presenters [#9460](https://github.com/chef/chef/pull/9460) ([tas50](https://github.com/tas50))
+- Stop installing packages on our constainers that are already there [#9465](https://github.com/chef/chef/pull/9465) ([tas50](https://github.com/tas50))
+- Fix windows_certificate functional tests under buildkite [#9467](https://github.com/chef/chef/pull/9467) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Pin to Rake 13.0.1 to prevent double rake [#9464](https://github.com/chef/chef/pull/9464) ([tas50](https://github.com/tas50))
+- Use the aws cli to download ruby in the windows tests [#9468](https://github.com/chef/chef/pull/9468) ([tas50](https://github.com/tas50))
+- Remove support in debian service init for old update-rc [#9453](https://github.com/chef/chef/pull/9453) ([tas50](https://github.com/tas50))
+- Update Fauxhai to 8.0 [#9471](https://github.com/chef/chef/pull/9471) ([tas50](https://github.com/tas50))
+- Fix require breaking windows functional tests in BK [#9472](https://github.com/chef/chef/pull/9472) ([phiggins](https://github.com/phiggins))
+- Expose equal_to values in property / chef-resource-inspector [#9444](https://github.com/chef/chef/pull/9444) ([tas50](https://github.com/tas50))
+- Add the chef-vault helpers from the chef-vault cookbook [#9477](https://github.com/chef/chef/pull/9477) ([tas50](https://github.com/tas50))
+- Bring in the extended Ruby cleanup used in chef-workstation [#9478](https://github.com/chef/chef/pull/9478) ([tas50](https://github.com/tas50))
+- Fix load path in test runs. [#9479](https://github.com/chef/chef/pull/9479) ([phiggins](https://github.com/phiggins))
+- Add always_dump_stacktrace config option &amp; workaround to fix the encoding bugs [#9474](https://github.com/chef/chef/pull/9474) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update all deps to current [#9483](https://github.com/chef/chef/pull/9483) ([tas50](https://github.com/tas50))
+- Add alternatives resource for Linux [#9481](https://github.com/chef/chef/pull/9481) ([tas50](https://github.com/tas50))
+- Remove some of the ruby cleanup to fix builds [#9486](https://github.com/chef/chef/pull/9486) ([tas50](https://github.com/tas50))
+- Fix typo in the cron_access docs [#9485](https://github.com/chef/chef/pull/9485) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.0.12 [#9488](https://github.com/chef/chef/pull/9488) ([tas50](https://github.com/tas50))
+- Remove additional files from the gems in our builds [#9489](https://github.com/chef/chef/pull/9489) ([tas50](https://github.com/tas50))
+- Use Dist constants in resource erb templates [#9491](https://github.com/chef/chef/pull/9491) ([bobchaos](https://github.com/bobchaos))
+- Cleanup a bunch more files and pull in the smaller license-acceptance [#9490](https://github.com/chef/chef/pull/9490) ([tas50](https://github.com/tas50))
+- Add user_ulimit resource from the ulimit cookbook [#9487](https://github.com/chef/chef/pull/9487) ([tas50](https://github.com/tas50))
+- Turn off notifications from log resource by default [#9484](https://github.com/chef/chef/pull/9484) ([lamont-granquist](https://github.com/lamont-granquist))
+- Pull in many of Zenspider&#39;s rspec improvements [#9493](https://github.com/chef/chef/pull/9493) ([tas50](https://github.com/tas50))
+- Use the correct constant in the resource support files [#9506](https://github.com/chef/chef/pull/9506) ([tas50](https://github.com/tas50))
+- add group to windows_firewall_rule resource [#8729](https://github.com/chef/chef/pull/8729) ([pschaumburg](https://github.com/pschaumburg))
+- powershell_package: Use ohai data to get the powershell release instead of shelling out [#9492](https://github.com/chef/chef/pull/9492) ([sanga1794](https://github.com/sanga1794))
+- Refresh DNF package provider and add aarch el8 testing support [#9500](https://github.com/chef/chef/pull/9500) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix windows_firewall_rule resource for rules with multiple profiles [#9511](https://github.com/chef/chef/pull/9511) ([tecracer-theinen](https://github.com/tecracer-theinen))
+- extend windows_firewall_rule abilities with displayname and description [#9514](https://github.com/chef/chef/pull/9514) ([pschaumburg](https://github.com/pschaumburg))
+- Bump train-core to 3.2.26 [#9515](https://github.com/chef/chef/pull/9515) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fix loading the template file in the user_ulimit resource [#9516](https://github.com/chef/chef/pull/9516) ([tas50](https://github.com/tas50))
+- Add support for parsing YAML recipes with chef-apply [#9507](https://github.com/chef/chef/pull/9507) ([btm](https://github.com/btm))
+- Move cron_d validation methods to a helper and use that in the cron resource [#9522](https://github.com/chef/chef/pull/9522) ([tas50](https://github.com/tas50))
+- Add chef_client_cron resource from chef-client cookbook [#9518](https://github.com/chef/chef/pull/9518) ([tas50](https://github.com/tas50))
+- clean up chef_client_cron [#9527](https://github.com/chef/chef/pull/9527) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add : https_for_ca_consumer property to rhsm_register resource [#9525](https://github.com/chef/chef/pull/9525) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Add chef_client_windows_task resource from chef-client cookbook [#9528](https://github.com/chef/chef/pull/9528) ([tas50](https://github.com/tas50))
+- Switch to RUBY_PLATFORM for the windows check [#9531](https://github.com/chef/chef/pull/9531) ([tas50](https://github.com/tas50))
+- Add missing introduced value to the new property in rhsm_register [#9534](https://github.com/chef/chef/pull/9534) ([tas50](https://github.com/tas50))
+- Add a `knife yaml convert` tool [#9508](https://github.com/chef/chef/pull/9508) ([btm](https://github.com/btm))
+- Improve chef_client_* resources [#9540](https://github.com/chef/chef/pull/9540) ([tas50](https://github.com/tas50))
+- Fixed windows share cannot modify frozen string bug [#9524](https://github.com/chef/chef/pull/9524) ([sanga1794](https://github.com/sanga1794))
+- Fix unintuitive behavior of sources on gem resources [#9480](https://github.com/chef/chef/pull/9480) ([phiggins](https://github.com/phiggins))
+- Skip the ifconfig functional tests if we don&#39;t have ifconfig [#9549](https://github.com/chef/chef/pull/9549) ([tas50](https://github.com/tas50))
+- Add Ubuntu 20.04 (x86_64) Tester [#9523](https://github.com/chef/chef/pull/9523) ([christopher-snapp](https://github.com/christopher-snapp))
+- Remove SLES 11 support from build_essential [#9551](https://github.com/chef/chef/pull/9551) ([tas50](https://github.com/tas50))
+- build_essential resource: fix macOS 10.15 and improve installation support with a new :upgrade action for macOS [#9550](https://github.com/chef/chef/pull/9550) ([tas50](https://github.com/tas50))
+- Simplify the matching code per code review [#9552](https://github.com/chef/chef/pull/9552) ([tas50](https://github.com/tas50))
+- Fix cloud? helper to only report true on cloud instances [#9553](https://github.com/chef/chef/pull/9553) ([tas50](https://github.com/tas50))
+- Fix functional tests on Windows 10 by matching less [#9563](https://github.com/chef/chef/pull/9563) ([btm](https://github.com/btm))
+- Add Windows 10 Tester [#9564](https://github.com/chef/chef/pull/9564) ([tas50](https://github.com/tas50))
+- Make the hab version check a full version check not just minor release [#9565](https://github.com/chef/chef/pull/9565) ([TheLunaticScripter](https://github.com/TheLunaticScripter))
+- Stop building S390x packages [#9568](https://github.com/chef/chef/pull/9568) ([tas50](https://github.com/tas50))
+- Update Ruby to 2.7.1 / bundler to 2.1.4 [#9569](https://github.com/chef/chef/pull/9569) ([tas50](https://github.com/tas50))
+- Update Nokogiri to 1.11.0.rc2 [#9572](https://github.com/chef/chef/pull/9572) ([tas50](https://github.com/tas50))
+- Convert more resources to unified_mode [#9570](https://github.com/chef/chef/pull/9570) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add after_resource to pair with current_resource [#9562](https://github.com/chef/chef/pull/9562) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix chef_vault_secret after_resource breakage [#9574](https://github.com/chef/chef/pull/9574) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add Ubuntu 20.04 x86_64 and Ubuntu 18.04 aarch64 testers [#9584](https://github.com/chef/chef/pull/9584) ([tas50](https://github.com/tas50))
+- Change the name_property to be the identity property by default, and to have desired_state: false by default [#9581](https://github.com/chef/chef/pull/9581) ([lamont-granquist](https://github.com/lamont-granquist))
+- Dist windows bootstrap template - fix msi_url [#9596](https://github.com/chef/chef/pull/9596) ([bobchaos](https://github.com/bobchaos))
+- Implement eager_load_libraries metadata option [#9593](https://github.com/chef/chef/pull/9593) ([lamont-granquist](https://github.com/lamont-granquist))
+- Use $env:temp instead of c:/ for functional tests [#9591](https://github.com/chef/chef/pull/9591) ([btm](https://github.com/btm))
+- windows_firewall_rule: Fix idempotency and add icmp_type property [#9544](https://github.com/chef/chef/pull/9544) ([pschaumburg](https://github.com/pschaumburg))
+- Updated introduced version for start_when_available option [#9602](https://github.com/chef/chef/pull/9602) ([Vasu1105](https://github.com/Vasu1105))
+- Improved Ruby download/install for functional tests [#9603](https://github.com/chef/chef/pull/9603) ([btm](https://github.com/btm))
+- Really skip the reboot pending func test if a reboot is pending [#9611](https://github.com/chef/chef/pull/9611) ([btm](https://github.com/btm))
+- Bump mixlib-cli to 2.1.6 [#9615](https://github.com/chef/chef/pull/9615) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Spell checking in comments and log messages [#9609](https://github.com/chef/chef/pull/9609) ([vsingh-msys](https://github.com/vsingh-msys))
+- Cache gems in the verify pipeline to speed up tests [#9607](https://github.com/chef/chef/pull/9607) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 4.18.104 [#9625](https://github.com/chef/chef/pull/9625) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Fixed systemd_unit not respecting sensitive property [#9623](https://github.com/chef/chef/pull/9623) ([sanga1794](https://github.com/sanga1794))
+- Bump train-core to 3.2.27 [#9628](https://github.com/chef/chef/pull/9628) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add arm? helper to chef-utils [#9622](https://github.com/chef/chef/pull/9622) ([tas50](https://github.com/tas50))
+- Bump highline to 2.x [#9633](https://github.com/chef/chef/pull/9633) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix knife bootstrap_version CLI option overriding config option [#9634](https://github.com/chef/chef/pull/9634) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add chef_client_systemd_timer resource [#9624](https://github.com/chef/chef/pull/9624) ([tas50](https://github.com/tas50))
+- Add Amazon Linux 2 Testers [#9641](https://github.com/chef/chef/pull/9641) ([christopher-snapp](https://github.com/christopher-snapp))
+- Add resource partials [#9632](https://github.com/chef/chef/pull/9632) ([lamont-granquist](https://github.com/lamont-granquist))
+- Support non-Linux hosts in chef_client_cron [#9647](https://github.com/chef/chef/pull/9647) ([tas50](https://github.com/tas50))
+- Add the plist resource from the macos cookbook [#9642](https://github.com/chef/chef/pull/9642) ([tas50](https://github.com/tas50))
+- Remove support for SLES11 and update tests to just on openSUSE [#9299](https://github.com/chef/chef/pull/9299) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Fixes for sudo password prompt [#9635](https://github.com/chef/chef/pull/9635) ([vsingh-msys](https://github.com/vsingh-msys))
+- Require at least trainc-core 3.2.28 to resolve our sudo bootstrap issues [#9654](https://github.com/chef/chef/pull/9654) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Use the correct plist class in the mac user provider [#9655](https://github.com/chef/chef/pull/9655) ([tas50](https://github.com/tas50))
+- Remove copyright dates [#9656](https://github.com/chef/chef/pull/9656) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update license scout to fix broken windows builds [#9657](https://github.com/chef/chef/pull/9657) ([tas50](https://github.com/tas50))
+- Updates to timeout properties in various resources [#9659](https://github.com/chef/chef/pull/9659) ([tas50](https://github.com/tas50))
+- Replace a few uses of attributes / parameters in messaging with properties [#9664](https://github.com/chef/chef/pull/9664) ([tas50](https://github.com/tas50))
+- Fix to use correct Amazon Linux 2 queues [#9662](https://github.com/chef/chef/pull/9662) ([christopher-snapp](https://github.com/christopher-snapp))
+- msu_package: Fix cumulative updates installation and provide a 3600s default timeout [#9649](https://github.com/chef/chef/pull/9649) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Add a warning to the end of the chef run for EOL releses [#9666](https://github.com/chef/chef/pull/9666) ([tas50](https://github.com/tas50))
+- msu_package: Removal also requires passing timeout property [#9676](https://github.com/chef/chef/pull/9676) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Ruby 2.7 fix for powershell_out [#9679](https://github.com/chef/chef/pull/9679) ([lamont-granquist](https://github.com/lamont-granquist))
+- Merge ohai resource / provider into a single resource [#9681](https://github.com/chef/chef/pull/9681) ([tas50](https://github.com/tas50))
+- Fix platform_version Chef::VersionString API [#9682](https://github.com/chef/chef/pull/9682) ([lamont-granquist](https://github.com/lamont-granquist))
+- Knife bootstrap options cleanup [#9646](https://github.com/chef/chef/pull/9646) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update Ohai to 16.0.18 for Windows DMI plugin [#9687](https://github.com/chef/chef/pull/9687) ([tas50](https://github.com/tas50))
+- Force requiring properties [#9688](https://github.com/chef/chef/pull/9688) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add multipackage support to homebrew [#7982](https://github.com/chef/chef/pull/7982) ([tas50](https://github.com/tas50))
+- Remove &quot;Code Can&quot; from dmg background [#9690](https://github.com/chef/chef/pull/9690) ([tas50](https://github.com/tas50))
+- [CHORE] with_clean_env is deprecated [#9692](https://github.com/chef/chef/pull/9692) ([damacus](https://github.com/damacus))
+- Bump ruby for windows to 2.7 [#9693](https://github.com/chef/chef/pull/9693) ([TheLunaticScripter](https://github.com/TheLunaticScripter))
+- Remove a few additional files from our builds [#9689](https://github.com/chef/chef/pull/9689) ([tas50](https://github.com/tas50))
+- git resource: Fix for exceptions raised in why-run mode [#9627](https://github.com/chef/chef/pull/9627) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Add multipackage support to pacman package resource [#9694](https://github.com/chef/chef/pull/9694) ([tas50](https://github.com/tas50))
+- Remove deprecated Chef-10 constraint handling [#9663](https://github.com/chef/chef/pull/9663) ([damacus](https://github.com/damacus))
+- Move some of the ruby-cleanup logic into omnibus-software [#9696](https://github.com/chef/chef/pull/9696) ([tas50](https://github.com/tas50))
+- Bump Chefstyle to 1.0.3 and Ohai to 16.0.19 [#9697](https://github.com/chef/chef/pull/9697) ([tas50](https://github.com/tas50))
+- Bump omnibus-software to avoid double pry [#9700](https://github.com/chef/chef/pull/9700) ([tas50](https://github.com/tas50))
+- Remove the unused simplecov depedency [#9699](https://github.com/chef/chef/pull/9699) ([tas50](https://github.com/tas50))
+- Update omnibus-software again to bring in double pry fixes [#9704](https://github.com/chef/chef/pull/9704) ([tas50](https://github.com/tas50))
+- Add Ubuntu 20.04 + SLES 15 aarch64 Testers [#9712](https://github.com/chef/chef/pull/9712) ([christopher-snapp](https://github.com/christopher-snapp))
+- Use the new Chef definition w/o appbundler [#9710](https://github.com/chef/chef/pull/9710) ([tas50](https://github.com/tas50))
+- Fix for Chocolate_resource options causing extra quotes [#9509](https://github.com/chef/chef/pull/9509) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Make sure all the non-multipackage packge resources can&#39;t take arrays [#9721](https://github.com/chef/chef/pull/9721) ([tas50](https://github.com/tas50))
+- Enable caching of all our buildkite jobs again [#9672](https://github.com/chef/chef/pull/9672) ([tas50](https://github.com/tas50))
+- Minor improvements to the resource descriptions [#9725](https://github.com/chef/chef/pull/9725) ([tas50](https://github.com/tas50))
+- Refactor scm, git and subversion resources &amp; fix longstanding git issues [#9726](https://github.com/chef/chef/pull/9726) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix linux plan to use ruby 2.7 [#9728](https://github.com/chef/chef/pull/9728) ([TheLunaticScripter](https://github.com/TheLunaticScripter))
+- Set timeouts in service and windows_service correctly [#9729](https://github.com/chef/chef/pull/9729) ([tas50](https://github.com/tas50))
+- Allow Arrays in msu_package version again [#9730](https://github.com/chef/chef/pull/9730) ([tas50](https://github.com/tas50))
+- Resolve deprecations in chef-utils specs [#9731](https://github.com/chef/chef/pull/9731) ([tas50](https://github.com/tas50))
+- locale: Support setting locale on Windows [#9648](https://github.com/chef/chef/pull/9648) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Use our fork of docker-api in the kitchen tests [#9733](https://github.com/chef/chef/pull/9733) ([tas50](https://github.com/tas50))
+- Update Ohai to 16.0.20 [#9734](https://github.com/chef/chef/pull/9734) ([tas50](https://github.com/tas50))
+- Run Test Kitchen tests on Ruby 2.7 [#9737](https://github.com/chef/chef/pull/9737) ([tas50](https://github.com/tas50))
+- Test and better document the locale resource [#9736](https://github.com/chef/chef/pull/9736) ([tas50](https://github.com/tas50))
+- Remove the Windows version check in windows_share [#9741](https://github.com/chef/chef/pull/9741) ([tas50](https://github.com/tas50))
+- Bump inspec-core-bin to 4.18.108 [#9743](https://github.com/chef/chef/pull/9743) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add powershell_exec! helper to make conversion from powershell_out! easier [#9742](https://github.com/chef/chef/pull/9742) ([tas50](https://github.com/tas50))
+- Update bcrypt_pbkdf to 1.1.0.rc1 [#9749](https://github.com/chef/chef/pull/9749) ([marcparadise](https://github.com/marcparadise))
+- Disable the windows_exec test for now [#9753](https://github.com/chef/chef/pull/9753) ([tas50](https://github.com/tas50))
+- Updates for auto generated docs [#9750](https://github.com/chef/chef/pull/9750) ([phiggins](https://github.com/phiggins))
+- Remove git func test for upstream [#9757](https://github.com/chef/chef/pull/9757) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix the powershell spec to use the right method [#9756](https://github.com/chef/chef/pull/9756) ([tas50](https://github.com/tas50))
+- Rework logging to resolve STDOUT / log_location issues [#9751](https://github.com/chef/chef/pull/9751) ([lamont-granquist](https://github.com/lamont-granquist))
+- Stop pinning rake [#9744](https://github.com/chef/chef/pull/9744) ([tas50](https://github.com/tas50))
+
+## [v15.6.10](https://github.com/chef/chef/tree/v15.6.10) (2019-12-12)
+
+#### Merged Pull Requests
+- Spelling, punctuation, grammar [#9122](https://github.com/chef/chef/pull/9122) ([ehershey](https://github.com/ehershey))
+- restoring correct value to windows event source [#9117](https://github.com/chef/chef/pull/9117) ([bobchaos](https://github.com/bobchaos))
+- [yum_repository] Add indentation for multiple baseurls [#9134](https://github.com/chef/chef/pull/9134) ([bugok](https://github.com/bugok))
+- Tiny spelling typo and grammar [#9135](https://github.com/chef/chef/pull/9135) ([ehershey](https://github.com/ehershey))
+- Resolve non-zero &quot;success&quot; error code issues with linux_user re… [#9105](https://github.com/chef/chef/pull/9105) ([skippyj](https://github.com/skippyj))
+- Bump omnibus-software to remove libtool+pkg-config [#9136](https://github.com/chef/chef/pull/9136) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update Ohai and pull in Ruby perf improvements [#9137](https://github.com/chef/chef/pull/9137) ([tas50](https://github.com/tas50))
+- Bump Omnibus to the latest [#9138](https://github.com/chef/chef/pull/9138) ([tas50](https://github.com/tas50))
+- Update omnnibus-software to add further ruby cleanup [#9141](https://github.com/chef/chef/pull/9141) ([tas50](https://github.com/tas50))
+- Update ruby_prof to 1.0 [#9130](https://github.com/chef/chef/pull/9130) ([tas50](https://github.com/tas50))
+- bump omnibus-software + rhel6 fix [#9145](https://github.com/chef/chef/pull/9145) ([lamont-granquist](https://github.com/lamont-granquist))
+- Specify item path in Node.read! error message [#9147](https://github.com/chef/chef/pull/9147) ([zeusttu](https://github.com/zeusttu))
+- Symbolize config for ssl_verify_mode in credentials [#9064](https://github.com/chef/chef/pull/9064) ([teknofire](https://github.com/teknofire))
+- Replace hardcoded /etc/chef with Chef::Dist::CONF_DIR [#9060](https://github.com/chef/chef/pull/9060) ([bobchaos](https://github.com/bobchaos))
+- Fix for ChefConfig should expand relative paths [#9042](https://github.com/chef/chef/pull/9042) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Fix apt_repository uri single/double quotes and spaces [#9036](https://github.com/chef/chef/pull/9036) ([vsingh-msys](https://github.com/vsingh-msys))
+- Add output for the file provider verification [#9039](https://github.com/chef/chef/pull/9039) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add a new distro constant for chef-apply [#9149](https://github.com/chef/chef/pull/9149) ([bobchaos](https://github.com/bobchaos))
+- Remove duplicate constant for Chef::Dist::SHORT [#9150](https://github.com/chef/chef/pull/9150) ([bobchaos](https://github.com/bobchaos))
+- Bump ohai to 15.6.3 [#9152](https://github.com/chef/chef/pull/9152) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+
+## [v15.5.17](https://github.com/chef/chef/tree/v15.5.17) (2019-11-21)
+
+## [v15.5.16](https://github.com/chef/chef/tree/v15.5.16) (2019-11-21)
+
+#### Merged Pull Requests
+- Require relative in the win32-eventlog rakefile [#9116](https://github.com/chef/chef/pull/9116) ([tas50](https://github.com/tas50))
+
+## [v15.5.15](https://github.com/chef/chef/tree/v15.5.15) (2019-11-19)
+
+#### Merged Pull Requests
+- Improve input validation on windows_package [#9102](https://github.com/chef/chef/pull/9102) ([tas50](https://github.com/tas50))
+- Don&#39;t ship the extra rake tasks in the gem [#9104](https://github.com/chef/chef/pull/9104) ([tas50](https://github.com/tas50))
+- Convert reboot resource to a custom resource with descriptions [#7239](https://github.com/chef/chef/pull/7239) ([tas50](https://github.com/tas50))
+- Remove bonus `.md` to fix link [#9110](https://github.com/chef/chef/pull/9110) ([btm](https://github.com/btm))
+- Fix failures in build_essential on rhel platforms [#9111](https://github.com/chef/chef/pull/9111) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix enforce_path_sanity being set to true [#9114](https://github.com/chef/chef/pull/9114) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v15.5.9](https://github.com/chef/chef/tree/v15.5.9) (2019-11-15)
+
+#### Merged Pull Requests
+- Sync over resource documentation from the docs site [#8999](https://github.com/chef/chef/pull/8999) ([tas50](https://github.com/tas50))
+- Update maintainers link in CONTRIBUTING.md [#9012](https://github.com/chef/chef/pull/9012) ([vsingh-msys](https://github.com/vsingh-msys))
+- correct typos in license headers [#9016](https://github.com/chef/chef/pull/9016) ([pombredanne](https://github.com/pombredanne))
+- Fix knife node show non-existent attributes [#9025](https://github.com/chef/chef/pull/9025) ([vsingh-msys](https://github.com/vsingh-msys))
+- Validate format option values using `in` attribute [#9026](https://github.com/chef/chef/pull/9026) ([vsingh-msys](https://github.com/vsingh-msys))
+- knife ssh: Fix Exception: NoMethodError: undefined method join for nil:NilClass [#9028](https://github.com/chef/chef/pull/9028) ([vsingh-msys](https://github.com/vsingh-msys))
+- [Data-collector] Add cookbooks attribute to run_end_message [#8893](https://github.com/chef/chef/pull/8893) ([vsingh-msys](https://github.com/vsingh-msys))
+- Fix license acceptance in `omnibus/kitchen.yml` [#9030](https://github.com/chef/chef/pull/9030) ([christopher-snapp](https://github.com/christopher-snapp))
+- Fix knife cookbook metadata from file name args [#9032](https://github.com/chef/chef/pull/9032) ([vsingh-msys](https://github.com/vsingh-msys))
+- Turn on unified_mode for 35 core resources [#9034](https://github.com/chef/chef/pull/9034) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump all deps to the latest versions [#9037](https://github.com/chef/chef/pull/9037) ([tas50](https://github.com/tas50))
+- Allow for the mixlib-authentication 3.x gem [#9040](https://github.com/chef/chef/pull/9040) ([tas50](https://github.com/tas50))
+- Fix multiple chefignore file issues [#8925](https://github.com/chef/chef/pull/8925) ([vsingh-msys](https://github.com/vsingh-msys))
+- a Windows Habitat plan [#8900](https://github.com/chef/chef/pull/8900) ([robbkidd](https://github.com/robbkidd))
+- Bump omnibus to 6.1.10 [#9048](https://github.com/chef/chef/pull/9048) ([btm](https://github.com/btm))
+- [knife config get] Fix TypeError: no implicit conversion of nil into String [#9049](https://github.com/chef/chef/pull/9049) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update omnibus [#9050](https://github.com/chef/chef/pull/9050) ([jaymalasinha](https://github.com/jaymalasinha))
+- systemd_unit needs to log [#9046](https://github.com/chef/chef/pull/9046) ([jaymzh](https://github.com/jaymzh))
+- Update lixml2, libxslt, and nokogiri to the latest [#9054](https://github.com/chef/chef/pull/9054) ([tas50](https://github.com/tas50))
+- Updates for Chefstyle with Rubocop 0.75.1 [#9055](https://github.com/chef/chef/pull/9055) ([tas50](https://github.com/tas50))
+- service/systemd_unit: Don&#39;t try to reenable services in an indirect status [#9047](https://github.com/chef/chef/pull/9047) ([jaymzh](https://github.com/jaymzh))
+- dist constants in win logs [#9058](https://github.com/chef/chef/pull/9058) ([bobchaos](https://github.com/bobchaos))
+- Remove useless search arg mangling [#9070](https://github.com/chef/chef/pull/9070) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix the habitat package promotion tests [#9061](https://github.com/chef/chef/pull/9061) ([robbkidd](https://github.com/robbkidd))
+- Add chef-utils gem with various recipe DSL helpers [#8922](https://github.com/chef/chef/pull/8922) ([lamont-granquist](https://github.com/lamont-granquist))
+- 🚧 WIP: remove Oracle 8 from TK tests run in CI [#9074](https://github.com/chef/chef/pull/9074) ([robbkidd](https://github.com/robbkidd))
+- Add new chef_sleep resource [#9081](https://github.com/chef/chef/pull/9081) ([tas50](https://github.com/tas50))
+- rename windows_ruby_platform? to windows_ruby? [#9085](https://github.com/chef/chef/pull/9085) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix action collection nilclass error early in runs [#9083](https://github.com/chef/chef/pull/9083) ([lamont-granquist](https://github.com/lamont-granquist))
+- reserve a number for chef-sugar deprecation warnings [#9086](https://github.com/chef/chef/pull/9086) ([lamont-granquist](https://github.com/lamont-granquist))
+- Switch our tests back to chef-sugar [#9084](https://github.com/chef/chef/pull/9084) ([tas50](https://github.com/tas50))
+- Bump all deps to current [#9087](https://github.com/chef/chef/pull/9087) ([tas50](https://github.com/tas50))
+- [knife list] Validate name argument &amp; raise error if no args provided [#9059](https://github.com/chef/chef/pull/9059) ([vsingh-msys](https://github.com/vsingh-msys))
+- Remove the use of Chef Sugar from the Kitchen tests [#9088](https://github.com/chef/chef/pull/9088) ([tas50](https://github.com/tas50))
+- Revert &quot;Validate name argument for knife list&quot; [#9090](https://github.com/chef/chef/pull/9090) ([tas50](https://github.com/tas50))
+- Update all deps to the latest including omnibus-software with faster Ruby [#9091](https://github.com/chef/chef/pull/9091) ([tas50](https://github.com/tas50))
+- Support multiple profiles in windows_firewall_rule [#8916](https://github.com/chef/chef/pull/8916) ([Happycoil](https://github.com/Happycoil))
+- Additional chef-utils helpers for rhel6/7/8 [#9095](https://github.com/chef/chef/pull/9095) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec to 4.18 [#9096](https://github.com/chef/chef/pull/9096) ([tas50](https://github.com/tas50))
+- Reorder the helper docs and expand platforms and descriptions [#9092](https://github.com/chef/chef/pull/9092) ([tas50](https://github.com/tas50))
+- Bump inspec-core-bin to 4.18.39 [#9098](https://github.com/chef/chef/pull/9098) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Improve resource descriptions and the rake task for docs generation [#9097](https://github.com/chef/chef/pull/9097) ([tas50](https://github.com/tas50))
+- Add Chef Infra Client 15.5. release notes [#9089](https://github.com/chef/chef/pull/9089) ([tas50](https://github.com/tas50))
+
+## [v15.4.45](https://github.com/chef/chef/tree/v15.4.45) (2019-10-15)
+
+#### Merged Pull Requests
+- Revert &quot;Drop privileges before creating files in solo mode&quot; [#8880](https://github.com/chef/chef/pull/8880) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add more resource examples to the codebase [#8868](https://github.com/chef/chef/pull/8868) ([tas50](https://github.com/tas50))
+- Fix for chocolatey_package fails using extra options [#8765](https://github.com/chef/chef/pull/8765) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- bump gems [#8907](https://github.com/chef/chef/pull/8907) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add empty chefcli config_context to fix commands with chefcli in knife.rb [#8911](https://github.com/chef/chef/pull/8911) ([coding-blip](https://github.com/coding-blip))
+- fix converge_if_changed to compare default values [#8912](https://github.com/chef/chef/pull/8912) ([lamont-granquist](https://github.com/lamont-granquist))
+- Require train-winrm &gt;= 0.2.5 [#8914](https://github.com/chef/chef/pull/8914) ([tas50](https://github.com/tas50))
+- [Resource::remote_file] Fix show_progress in remote_file is cau… [#8904](https://github.com/chef/chef/pull/8904) ([vsingh-msys](https://github.com/vsingh-msys))
+- [knife ssh] Fix interactive mode exit error [#8917](https://github.com/chef/chef/pull/8917) ([vsingh-msys](https://github.com/vsingh-msys))
+- Updated package license for macos and windows. [#8910](https://github.com/chef/chef/pull/8910) ([samshinde](https://github.com/samshinde))
+- Fix some places where constants from dist.rb were not used. [#8921](https://github.com/chef/chef/pull/8921) ([Tensibai](https://github.com/Tensibai))
+- Enable Windows Buildkite verification on default image Windows… [#8882](https://github.com/chef/chef/pull/8882) ([jaymalasinha](https://github.com/jaymalasinha))
+- Bump inspec-core to 4.17.7 [#8923](https://github.com/chef/chef/pull/8923) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add #to_yaml method for ImmutableMash &amp; ImmutableArray [#8927](https://github.com/chef/chef/pull/8927) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update inspec to 4.17.11 and cleanup buildkite testing a bit [#8930](https://github.com/chef/chef/pull/8930) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 4.17.14 [#8932](https://github.com/chef/chef/pull/8932) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Swap test gem install of fpm for chef-ruby-lvm [#8934](https://github.com/chef/chef/pull/8934) ([tas50](https://github.com/tas50))
+- Bump inspec-core-bin to 4.17.15 [#8936](https://github.com/chef/chef/pull/8936) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Bump inspec-core to 4.17.15 [#8935](https://github.com/chef/chef/pull/8935) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Add testing in Buildkite for Oracle Linux 6/7/8 [#8937](https://github.com/chef/chef/pull/8937) ([tas50](https://github.com/tas50))
+- bump omnibus gems [#8938](https://github.com/chef/chef/pull/8938) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update openSUSE testing in Buildkite [#8931](https://github.com/chef/chef/pull/8931) ([tas50](https://github.com/tas50))
+- Removing sh -c wrapper around the bootstrapper command string [#8885](https://github.com/chef/chef/pull/8885) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Bump Ruby to 2.6.5 to address CVEs #8951 [#8952](https://github.com/chef/chef/pull/8952) ([christopher-snapp](https://github.com/christopher-snapp))
+- Bump inspec-core-bin to 4.17.17 [#8953](https://github.com/chef/chef/pull/8953) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot]))
+- Revert Gemfile.lock to Bundler 1.17.3 [#8956](https://github.com/chef/chef/pull/8956) ([tas50](https://github.com/tas50))
+- Remove EOL openSUSE Leap 42 testing [#8955](https://github.com/chef/chef/pull/8955) ([tas50](https://github.com/tas50))
+- Add CentOS 8 kitchen testing to buildkite [#8954](https://github.com/chef/chef/pull/8954) ([tas50](https://github.com/tas50))
+- windows_share: make path idempotent by coercing to backwhacks [#8967](https://github.com/chef/chef/pull/8967) ([Happycoil](https://github.com/Happycoil))
+- sudo: perform config validation on the overall sudoers state [#8928](https://github.com/chef/chef/pull/8928) ([samshinde](https://github.com/samshinde))
+- Fix for knife subcommand --help don&#39;t work as intended. [#8915](https://github.com/chef/chef/pull/8915) ([Vasu1105](https://github.com/Vasu1105))
+- Fix Bootstrap password prompt [#8856](https://github.com/chef/chef/pull/8856) ([samshinde](https://github.com/samshinde))
+- Require train ~3.1 for bootstrapping and openssl 1.0.2t [#8968](https://github.com/chef/chef/pull/8968) ([tas50](https://github.com/tas50))
+- windows_ad_join: Add :leave action to for leaving an AD domain [#8379](https://github.com/chef/chef/pull/8379) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- windows_service: don&#39;t update the service if the run_as_user ca… [#8981](https://github.com/chef/chef/pull/8981) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Fix yum &amp; dnf shellout if exit with nonzero status [#8972](https://github.com/chef/chef/pull/8972) ([vsingh-msys](https://github.com/vsingh-msys))
+- Event dispatcher thread local storage [#8950](https://github.com/chef/chef/pull/8950) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix undefined method `each&#39; for String [#8987](https://github.com/chef/chef/pull/8987) ([vsingh-msys](https://github.com/vsingh-msys))
+- Avoid a PATH environment variable update before a windows package install [#8961](https://github.com/chef/chef/pull/8961) ([jeremyhage](https://github.com/jeremyhage))
+- Using umask to avoid race conditions in bootstrap [#8895](https://github.com/chef/chef/pull/8895) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Enhance knife supermarket list &amp; search [#8971](https://github.com/chef/chef/pull/8971) ([vsingh-msys](https://github.com/vsingh-msys))
+- Fix typo for knife download --[no]diff and --[-no]force options. [#8995](https://github.com/chef/chef/pull/8995) ([Vasu1105](https://github.com/Vasu1105))
+- [knife] Deprecate data bag secret (-s) short option due to conflict with --server-url option [#8909](https://github.com/chef/chef/pull/8909) ([vsingh-msys](https://github.com/vsingh-msys))
+- knife: Improve messaging for installing official plugins into DK / Workstationn [#8899](https://github.com/chef/chef/pull/8899) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update train to 3.1.4 and update omnibus-software to fix AIX ruby [#8997](https://github.com/chef/chef/pull/8997) ([tas50](https://github.com/tas50))
+
+## [v15.3.14](https://github.com/chef/chef/tree/v15.3.14) (2019-09-12)
+
+#### Merged Pull Requests
+- Improve the auto-generated docs [#8806](https://github.com/chef/chef/pull/8806) ([tas50](https://github.com/tas50))
+- Update the link to our release cadence information [#8807](https://github.com/chef/chef/pull/8807) ([tas50](https://github.com/tas50))
+- Updated knife cookbook metadata from file command banner [#8812](https://github.com/chef/chef/pull/8812) ([samshinde](https://github.com/samshinde))
+- Begin signing MSI&#39;s with renewed Windows Signing Cert [#8813](https://github.com/chef/chef/pull/8813) ([schisamo](https://github.com/schisamo))
+- Update omnibus build deps to the latest [#8823](https://github.com/chef/chef/pull/8823) ([tas50](https://github.com/tas50))
+- Add unified_mode for resources [#8668](https://github.com/chef/chef/pull/8668) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix crash when showing error about missing profile [#8828](https://github.com/chef/chef/pull/8828) ([andrewdotn](https://github.com/andrewdotn))
+- Add AIX 7.2 platform [#8832](https://github.com/chef/chef/pull/8832) ([jaymalasinha](https://github.com/jaymalasinha))
+- ifconfig: fix regex matching interface name with hyphen [#8756](https://github.com/chef/chef/pull/8756) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Fail on interval runs on windows as interval runs on Windows don&#39;t entirely work [#6777](https://github.com/chef/chef/pull/6777) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix node[:cookbooks] attribute [#8846](https://github.com/chef/chef/pull/8846) ([lamont-granquist](https://github.com/lamont-granquist))
+- remove app_server_support spec file [#8852](https://github.com/chef/chef/pull/8852) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec to 4.12 and Train to 3.0 [#8854](https://github.com/chef/chef/pull/8854) ([tas50](https://github.com/tas50))
+- Bootstrap: Only use sudo when changing ownership if --sudo is passed [#8815](https://github.com/chef/chef/pull/8815) ([vsingh-msys](https://github.com/vsingh-msys))
+- Deprecate macOS 10.12 and add macOS 10.15 support [#8850](https://github.com/chef/chef/pull/8850) ([jaymalasinha](https://github.com/jaymalasinha))
+- Update InSpec to 4.16 and addressable to 2.7.0 [#8857](https://github.com/chef/chef/pull/8857) ([tas50](https://github.com/tas50))
+- Update libarchive to 3.4.0 and pin in omnibus_overrides.rb [#8862](https://github.com/chef/chef/pull/8862) ([tas50](https://github.com/tas50))
+- Bump ohai to 15.3.1 [#8863](https://github.com/chef/chef/pull/8863) ([chef-ci](https://github.com/chef-ci))
+- Migrate Appveyor windows testing to Buildkite [#8867](https://github.com/chef/chef/pull/8867) ([jaymalasinha](https://github.com/jaymalasinha))
+- convert chocolatey resource to use shell_out splat args [#8861](https://github.com/chef/chef/pull/8861) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove duplicate policy_path config [#8864](https://github.com/chef/chef/pull/8864) ([tas50](https://github.com/tas50))
+- Add Chef 15.3 release notes [#8860](https://github.com/chef/chef/pull/8860) ([tas50](https://github.com/tas50))
+- Bootstrap: Set pty true only if required [#8816](https://github.com/chef/chef/pull/8816) ([vsingh-msys](https://github.com/vsingh-msys))
+- Support to provide --local flag to gem installer. [#8847](https://github.com/chef/chef/pull/8847) ([samshinde](https://github.com/samshinde))
+- Add mac_user resource that is compatible with macOS &gt;= 10.14 [#8775](https://github.com/chef/chef/pull/8775) ([ryancragun](https://github.com/ryancragun))
+- Fix for user resource does not handle a gid specified as a string [#8869](https://github.com/chef/chef/pull/8869) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- [macos] fix mac_user platform constraints [#8874](https://github.com/chef/chef/pull/8874) ([ryancragun](https://github.com/ryancragun))
+- Bootstrap: Fix typo when checking for existing chef-client [#8876](https://github.com/chef/chef/pull/8876) ([teknofire](https://github.com/teknofire))
+- Update openssl to 1.0.2t [#8878](https://github.com/chef/chef/pull/8878) ([tas50](https://github.com/tas50))
+
+## [v15.2.20](https://github.com/chef/chef/tree/v15.2.20) (2019-08-08)
+
+#### Merged Pull Requests
+- Bump inspec-core to 4.6.9 [#8701](https://github.com/chef/chef/pull/8701) ([chef-ci](https://github.com/chef-ci))
+- windows_task: Fix for :day option is not accepting integer value [#8705](https://github.com/chef/chef/pull/8705) ([vsingh-msys](https://github.com/vsingh-msys))
+- update bldr config with new hab package name [#8706](https://github.com/chef/chef/pull/8706) ([robbkidd](https://github.com/robbkidd))
+- fixes for chefstyle bump [#8707](https://github.com/chef/chef/pull/8707) ([lamont-granquist](https://github.com/lamont-granquist))
+- Deprecate Ubuntu-14 [#8712](https://github.com/chef/chef/pull/8712) ([jaymalasinha](https://github.com/jaymalasinha))
+- Improve generation of docs site resource pages [#8718](https://github.com/chef/chef/pull/8718) ([tas50](https://github.com/tas50))
+- fix Layout/AlignArguments [#8708](https://github.com/chef/chef/pull/8708) ([lamont-granquist](https://github.com/lamont-granquist))
+- More Chefstyle updates [#8711](https://github.com/chef/chef/pull/8711) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add examples to the apt_repository resource [#8719](https://github.com/chef/chef/pull/8719) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 4.7.3 [#8726](https://github.com/chef/chef/pull/8726) ([chef-ci](https://github.com/chef-ci))
+- Remove rspec config for Travis [#8728](https://github.com/chef/chef/pull/8728) ([tas50](https://github.com/tas50))
+- Roll back Rubygems to 3.0.3 to prevent double bundler install [#8736](https://github.com/chef/chef/pull/8736) ([tas50](https://github.com/tas50))
+- Bump openSSL to 1.0.2s [#8735](https://github.com/chef/chef/pull/8735) ([tas50](https://github.com/tas50))
+- Pull in latest omnibus-software to fix Windows builds [#8739](https://github.com/chef/chef/pull/8739) ([btm](https://github.com/btm))
+- Fix RDoc copy-pasta in Chef::Mixin::ParamsValidate#validate [#8740](https://github.com/chef/chef/pull/8740) ([RubyTuesdayDONO](https://github.com/RubyTuesdayDONO))
+- Remove the unused rspec kitchen tests [#8741](https://github.com/chef/chef/pull/8741) ([tas50](https://github.com/tas50))
+- test that inspec binstub is in the omnibus artifact [#8750](https://github.com/chef/chef/pull/8750) ([lamont-granquist](https://github.com/lamont-granquist))
+- dnf_package fixes for RHEL8 [#8754](https://github.com/chef/chef/pull/8754) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add rspec testing on Fedora in Buildkite [#8759](https://github.com/chef/chef/pull/8759) ([tas50](https://github.com/tas50))
+- Implement disable for disabling kernel_modules [#8747](https://github.com/chef/chef/pull/8747) ([tomdoherty](https://github.com/tomdoherty))
+- Bump InSpec, Ohai, and appbundler to the latest [#8761](https://github.com/chef/chef/pull/8761) ([tas50](https://github.com/tas50))
+- Move chef-client and chef-solo shared code into a base class and remove duplication and skew [#8744](https://github.com/chef/chef/pull/8744) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump train-core to 2.1.19 [#8766](https://github.com/chef/chef/pull/8766) ([chef-ci](https://github.com/chef-ci))
+- Update bzip2 from 1.0.6 -&gt; 1.0.8 to resolve CVEs [#8768](https://github.com/chef/chef/pull/8768) ([tas50](https://github.com/tas50))
+- Duration field in resource report needs to be String [#8767](https://github.com/chef/chef/pull/8767) ([lamont-granquist](https://github.com/lamont-granquist))
+- Tweak data collector exception handling [#8776](https://github.com/chef/chef/pull/8776) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix zypper test failures [#8774](https://github.com/chef/chef/pull/8774) ([jaymalasinha](https://github.com/jaymalasinha))
+- Jsinha/add rhel8 [#8742](https://github.com/chef/chef/pull/8742) ([jaymalasinha](https://github.com/jaymalasinha))
+- Enable Kitchen dockken tests with BK linux executor [#8783](https://github.com/chef/chef/pull/8783) ([jaymalasinha](https://github.com/jaymalasinha))
+- Bump mixlib-shellout to 3.0.7 [#8784](https://github.com/chef/chef/pull/8784) ([chef-ci](https://github.com/chef-ci))
+- Remove Travis / Jenkins config / docs [#8786](https://github.com/chef/chef/pull/8786) ([tas50](https://github.com/tas50))
+- Bump inspec-core-bin to 4.10.4 and Ohai to 15.2.4 [#8788](https://github.com/chef/chef/pull/8788) ([chef-ci](https://github.com/chef-ci))
+- Cleanup of habitat/plan.sh for chef-infra-client [#8789](https://github.com/chef/chef/pull/8789) ([afiune](https://github.com/afiune))
+- Stop building Chef Infra Client on SLES 11 [#8796](https://github.com/chef/chef/pull/8796) ([tas50](https://github.com/tas50))
+- zypper_package upgrade action not working [#8462](https://github.com/chef/chef/pull/8462) ([foobarbam](https://github.com/foobarbam))
+- Fix for rhsm_repo disable does not support wildcard [#8794](https://github.com/chef/chef/pull/8794) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- fix knife node environment set output [#8772](https://github.com/chef/chef/pull/8772) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Chef-15: Make temp dir using mkdir instead of mktemp [#8795](https://github.com/chef/chef/pull/8795) ([vsingh-msys](https://github.com/vsingh-msys))
+- Property: Reorder comparison with NOT_PASSED. [#8781](https://github.com/chef/chef/pull/8781) ([ab](https://github.com/ab))
+- Consistently use NOT_PASSED over alternatives [#8800](https://github.com/chef/chef/pull/8800) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump ohai to 15.2.5 [#8803](https://github.com/chef/chef/pull/8803) ([chef-ci](https://github.com/chef-ci))
+
+## [v15.1.36](https://github.com/chef/chef/tree/v15.1.36) (2019-07-01)
+
+#### Merged Pull Requests
+- Chef Infra Client 15 Release Notes Additional edits [#8543](https://github.com/chef/chef/pull/8543) ([mjingle](https://github.com/mjingle))
+- Only set client_pem in bootstrap_context when validatorless [#8567](https://github.com/chef/chef/pull/8567) ([btm](https://github.com/btm))
+- Fix chef-config requires lines [#8545](https://github.com/chef/chef/pull/8545) ([lamont-granquist](https://github.com/lamont-granquist))
+- Better target mode no-creds errors [#8571](https://github.com/chef/chef/pull/8571) ([lamont-granquist](https://github.com/lamont-granquist))
+- Gate requires with idempotency check [#8544](https://github.com/chef/chef/pull/8544) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix delete_resource for resources [#8570](https://github.com/chef/chef/pull/8570) ([artem-sidorenko](https://github.com/artem-sidorenko))
+- Fix service enable idempotency in sles11 [#8256](https://github.com/chef/chef/pull/8256) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Better target mode shell_out [#8584](https://github.com/chef/chef/pull/8584) ([lamont-granquist](https://github.com/lamont-granquist))
+- Preserve train connection in target mode to prevent running duplicate OS detection commands [#8590](https://github.com/chef/chef/pull/8590) ([btm](https://github.com/btm))
+- Fix for knife bootstrap inheritance issue with knife plugins [#8585](https://github.com/chef/chef/pull/8585) ([Vasu1105](https://github.com/Vasu1105))
+- make which/where be target-mode aware [#8588](https://github.com/chef/chef/pull/8588) ([lamont-granquist](https://github.com/lamont-granquist))
+- launchd: add launch_events property [#8582](https://github.com/chef/chef/pull/8582) ([chilcote](https://github.com/chilcote))
+- Chef 15: Fix order of connection before registering node [#8574](https://github.com/chef/chef/pull/8574) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Add introduced field to launch_events in launchd [#8592](https://github.com/chef/chef/pull/8592) ([tas50](https://github.com/tas50))
+- Chef-15: Add missing deprecated options [#8573](https://github.com/chef/chef/pull/8573) ([vsingh-msys](https://github.com/vsingh-msys))
+- Use Shellwords.join in target-mode shell_out [#8594](https://github.com/chef/chef/pull/8594) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix shellout require idempotency and bump gems [#8595](https://github.com/chef/chef/pull/8595) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fixed issue for chef-client run was throwing error when provided empty string with it [#8200](https://github.com/chef/chef/pull/8200) ([vinay033](https://github.com/vinay033))
+- Enable target mode on ruby_block, log and breakpoint [#8593](https://github.com/chef/chef/pull/8593) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add distro constants for solo, zero and automate [#8460](https://github.com/chef/chef/pull/8460) ([bobchaos](https://github.com/bobchaos))
+- Chef 15: Fix ssh user set from cli [#8558](https://github.com/chef/chef/pull/8558) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Improving error handling for template render [#8562](https://github.com/chef/chef/pull/8562) ([brodock](https://github.com/brodock))
+- Add new chocolatey_feature resource for managing features in Chocolatey [#8581](https://github.com/chef/chef/pull/8581) ([gep13](https://github.com/gep13))
+- Trace output the actual bootstrap template filename [#8619](https://github.com/chef/chef/pull/8619) ([btm](https://github.com/btm))
+- Raise knife exceptions when verbosity is 3 (-VVV) [#8618](https://github.com/chef/chef/pull/8618) ([btm](https://github.com/btm))
+- Add hooks for plugins in knife bootstrap [#8628](https://github.com/chef/chef/pull/8628) ([btm](https://github.com/btm))
+- Create bootstrap template in binmode to fix line endings [#8631](https://github.com/chef/chef/pull/8631) ([btm](https://github.com/btm))
+- more distro constants [#8630](https://github.com/chef/chef/pull/8630) ([bobchaos](https://github.com/bobchaos))
+- Chef-15: Added deprecation check for short arguments [#8626](https://github.com/chef/chef/pull/8626) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Avoid constant warnings [#8633](https://github.com/chef/chef/pull/8633) ([tas50](https://github.com/tas50))
+- Speed up buildkite tests [#8636](https://github.com/chef/chef/pull/8636) ([tas50](https://github.com/tas50))
+- More speedups to the Buildkite PR verification tests [#8639](https://github.com/chef/chef/pull/8639) ([tas50](https://github.com/tas50))
+- Update Buildkite config with Ubuntu/CentOS/openSUSE containers [#8641](https://github.com/chef/chef/pull/8641) ([tas50](https://github.com/tas50))
+- use mixlib-cli&#39;s deprecation mechanism [#8637](https://github.com/chef/chef/pull/8637) ([marcparadise](https://github.com/marcparadise))
+- Make sure to ship the inspec binary [#8660](https://github.com/chef/chef/pull/8660) ([tas50](https://github.com/tas50))
+- Update Habitat Build [#8598](https://github.com/chef/chef/pull/8598) ([ncerny](https://github.com/ncerny))
+- Update Ohai to 15.1.3 and license-acceptance to 1.0.13 [#8661](https://github.com/chef/chef/pull/8661) ([tas50](https://github.com/tas50))
+- Target mode for systemd service helper [#8614](https://github.com/chef/chef/pull/8614) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update omnibus-software to unbreak chef builds [#8665](https://github.com/chef/chef/pull/8665) ([tas50](https://github.com/tas50))
+- Add Chef 12 updating docs [#8664](https://github.com/chef/chef/pull/8664) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump inspec to 4.6.3 [#8666](https://github.com/chef/chef/pull/8666) ([chef-ci](https://github.com/chef-ci))
+- Move the data collector should_be_enabled? check [#8670](https://github.com/chef/chef/pull/8670) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump inspec-core-bin to 4.6.4 [#8672](https://github.com/chef/chef/pull/8672) ([chef-ci](https://github.com/chef-ci))
+- added scaffolding-chef plan [#8659](https://github.com/chef/chef/pull/8659) ([echohack](https://github.com/echohack))
+- [chef-client] [scaffolding-chef] add new build configuration for scaffolding-chef [#8677](https://github.com/chef/chef/pull/8677) ([echohack](https://github.com/echohack))
+- [chef-client] [scaffolding-chef] add new build configuration for scaffolding-chef [#8678](https://github.com/chef/chef/pull/8678) ([echohack](https://github.com/echohack))
+- Bump ohai to 15.1.5 [#8681](https://github.com/chef/chef/pull/8681) ([chef-ci](https://github.com/chef-ci))
+- Update ffi-libarchive to 0.4.10 [#8688](https://github.com/chef/chef/pull/8688) ([tas50](https://github.com/tas50))
+- [scaffolding-chef] Rolling out the scaffolding-chef package [#8679](https://github.com/chef/chef/pull/8679) ([echohack](https://github.com/echohack))
+- Update Rubygems to 3.0.4 [#8689](https://github.com/chef/chef/pull/8689) ([tas50](https://github.com/tas50))
+- Pin to ruby-prof 0.17 [#8690](https://github.com/chef/chef/pull/8690) ([tas50](https://github.com/tas50))
+- Improve how we bundler install in appveyor [#8663](https://github.com/chef/chef/pull/8663) ([tas50](https://github.com/tas50))
+- Ignore noisy outputs at create temp_dir during bootstrap [#8682](https://github.com/chef/chef/pull/8682) ([sawanoboly](https://github.com/sawanoboly))
+- chocolatey_source: Add additional actions and propereties for configuring sources [#8635](https://github.com/chef/chef/pull/8635) ([gep13](https://github.com/gep13))
+- yum_package: Better handle yum exceptions in our helper so we always close the rpmdb [#8692](https://github.com/chef/chef/pull/8692) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add introduced fields for chocolatey_source [#8691](https://github.com/chef/chef/pull/8691) ([tas50](https://github.com/tas50))
+- Enable Habitat build promotion in Expeditor again [#8693](https://github.com/chef/chef/pull/8693) ([tas50](https://github.com/tas50))
+- Fix a typo in the hostname resource property descriptions [#8695](https://github.com/chef/chef/pull/8695) ([tas50](https://github.com/tas50))
+- Add rake task for generating the docs site content [#8694](https://github.com/chef/chef/pull/8694) ([tas50](https://github.com/tas50))
+- Rename the habitat package to chef-infra-client [#8698](https://github.com/chef/chef/pull/8698) ([tas50](https://github.com/tas50))
+- Bump train-core to 2.1.13 [#8699](https://github.com/chef/chef/pull/8699) ([chef-ci](https://github.com/chef-ci))
+- Pass yum_package options to underlying python helper [#8687](https://github.com/chef/chef/pull/8687) ([Annih](https://github.com/Annih))
+
+## [v15.0.300](https://github.com/chef/chef/tree/v15.0.300) (2019-05-16)
+
+#### Merged Pull Requests
+- Add license CLI options to chef-apply command [#8554](https://github.com/chef/chef/pull/8554) ([tas50](https://github.com/tas50))
+- Enable pty for bootstrap ssh [#8560](https://github.com/chef/chef/pull/8560) ([marcparadise](https://github.com/marcparadise))
+
+## [v15.0.298](https://github.com/chef/chef/tree/v15.0.298) (2019-05-15)
+
+#### Merged Pull Requests
+- Bump license-acceptance to 1.0.8 to resolve failures on Windows 2012R2 [#8538](https://github.com/chef/chef/pull/8538) ([chef-ci](https://github.com/chef-ci))
+- Update habitat/plan.sh to allow building of Chef Infra Client 15 [#8552](https://github.com/chef/chef/pull/8552) ([smacfarlane](https://github.com/smacfarlane))
+- Bump license-acceptance to 1.0.11 to resolve failures on Windows 2016 [#8551](https://github.com/chef/chef/pull/8551) ([aaronwalker](https://github.com/aaronwalker))
+- Multiple Bootstrap bug fixes [#8539](https://github.com/chef/chef/pull/8539) ([marcparadise](https://github.com/marcparadise))
+- Bump train-core to 2.1.2 [#8553](https://github.com/chef/chef/pull/8553) ([chef-ci](https://github.com/chef-ci))
+
+## [v15.0.293](https://github.com/chef/chef/tree/v15.0.293) (2019-05-14)
+
+#### Merged Pull Requests
+- Start Chef 15 development [#7785](https://github.com/chef/chef/pull/7785) ([tas50](https://github.com/tas50))
+- Remove the deprecated knife bootstrap --identity-file flag [#7489](https://github.com/chef/chef/pull/7489) ([tas50](https://github.com/tas50))
+- Make all Chef 14 preview resources into full resources [#7786](https://github.com/chef/chef/pull/7786) ([tas50](https://github.com/tas50))
+- Update description fields from the docs site [#7784](https://github.com/chef/chef/pull/7784) ([tas50](https://github.com/tas50))
+- Do the shell_out deprecations for Chef-15. [#7788](https://github.com/chef/chef/pull/7788) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove legacy require_recipe DSL method [#7790](https://github.com/chef/chef/pull/7790) ([tas50](https://github.com/tas50))
+- Remove cookbook merging/shadowing from the cookbooker loader [#7792](https://github.com/chef/chef/pull/7792) ([lamont-granquist](https://github.com/lamont-granquist))
+- shell_out auto-timeout still needs to be restricted to only providers [#7793](https://github.com/chef/chef/pull/7793) ([lamont-granquist](https://github.com/lamont-granquist))
+- add GEMFILE_MOD to pin ohai to github master [#7796](https://github.com/chef/chef/pull/7796) ([lamont-granquist](https://github.com/lamont-granquist))
+- Require mixin::shellout where we use it [#7798](https://github.com/chef/chef/pull/7798) ([tas50](https://github.com/tas50))
+- Allow passing array to supports in mount again [#7803](https://github.com/chef/chef/pull/7803) ([tas50](https://github.com/tas50))
+- Multiple fixes to dmg_package [#7802](https://github.com/chef/chef/pull/7802) ([tas50](https://github.com/tas50))
+- Remove deprecated support for FreeBSD pkg provider [#7789](https://github.com/chef/chef/pull/7789) ([tas50](https://github.com/tas50))
+- Refactor Cookbook loader logic now that we don&#39;t support merging [#7794](https://github.com/chef/chef/pull/7794) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix inspector to properly handle defaults that are symbols [#7813](https://github.com/chef/chef/pull/7813) ([tas50](https://github.com/tas50))
+- Remove unused route resource properties [#7240](https://github.com/chef/chef/pull/7240) ([tas50](https://github.com/tas50))
+- Add windows_certificate and windows_share resources [#7731](https://github.com/chef/chef/pull/7731) ([tas50](https://github.com/tas50))
+- Update win32-certstore to include a license [#7822](https://github.com/chef/chef/pull/7822) ([tas50](https://github.com/tas50))
+- Fix testing / installing on SLES 15 [#7819](https://github.com/chef/chef/pull/7819) ([tas50](https://github.com/tas50))
+- More cookbook loader cleanup and documentation [#7820](https://github.com/chef/chef/pull/7820) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump win32-certstore to 0.1.11 [#7823](https://github.com/chef/chef/pull/7823) ([tas50](https://github.com/tas50))
+- Remove preview resource from windows_certificate &amp; windows_share [#7818](https://github.com/chef/chef/pull/7818) ([tas50](https://github.com/tas50))
+- Fix chef-apply crash for reboot [#7720](https://github.com/chef/chef/pull/7720) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Handle `interactive_enabled` property in windows_task resource [#7814](https://github.com/chef/chef/pull/7814) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Update win32-taskscheduler to 2.0.1 [#7843](https://github.com/chef/chef/pull/7843) ([tas50](https://github.com/tas50))
+- Remove deprecated knife status --hide-healthy flag [#7791](https://github.com/chef/chef/pull/7791) ([tas50](https://github.com/tas50))
+- Remove the deprecated ohai_name property from the ohai resource [#7787](https://github.com/chef/chef/pull/7787) ([tas50](https://github.com/tas50))
+- Added property `description` on windows_task resource [#7777](https://github.com/chef/chef/pull/7777) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Remove knife cookbook test feature [#7323](https://github.com/chef/chef/pull/7323) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 3.0.25 [#7853](https://github.com/chef/chef/pull/7853) ([chef-ci](https://github.com/chef-ci))
+- Set http_disable_auth_on_redirect to true [#7856](https://github.com/chef/chef/pull/7856) ([tas50](https://github.com/tas50))
+- powershell_package doc update [#7857](https://github.com/chef/chef/pull/7857) ([Happycoil](https://github.com/Happycoil))
+- Remove the check for nil code property in the script provider [#7855](https://github.com/chef/chef/pull/7855) ([tas50](https://github.com/tas50))
+- Chef 15 node attribute array fixes [#7840](https://github.com/chef/chef/pull/7840) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add additional github issue templates [#7859](https://github.com/chef/chef/pull/7859) ([tas50](https://github.com/tas50))
+- Remove knife user support for open source Chef Server &lt; 12 [#7841](https://github.com/chef/chef/pull/7841) ([tas50](https://github.com/tas50))
+- Remove the remaining OSC 11 knife user commands [#7868](https://github.com/chef/chef/pull/7868) ([tas50](https://github.com/tas50))
+- Improve resource descriptions for resource documentation automation [#7808](https://github.com/chef/chef/pull/7808) ([tas50](https://github.com/tas50))
+- Add windows_firewall_rule [#7842](https://github.com/chef/chef/pull/7842) ([Happycoil](https://github.com/Happycoil))
+- Make knife command banners consistent [#7869](https://github.com/chef/chef/pull/7869) ([tas50](https://github.com/tas50))
+- Add more validation_messages to properties [#7867](https://github.com/chef/chef/pull/7867) ([tas50](https://github.com/tas50))
+- Fully remove knife cookbook create command [#7852](https://github.com/chef/chef/pull/7852) ([tas50](https://github.com/tas50))
+- Defer running initramfs_command until end of run [#7871](https://github.com/chef/chef/pull/7871) ([tomdoherty](https://github.com/tomdoherty))
+- resource inspector: don&#39;t convert nil to &quot;nil&quot; in default values [#7880](https://github.com/chef/chef/pull/7880) ([tas50](https://github.com/tas50))
+- Add additional descriptions to resource and update others [#7881](https://github.com/chef/chef/pull/7881) ([tas50](https://github.com/tas50))
+- Allow passing multiple ports in windows_firewall [#7879](https://github.com/chef/chef/pull/7879) ([tas50](https://github.com/tas50))
+- Update more descriptions and tweak default handling in chef-resource-inspector [#7884](https://github.com/chef/chef/pull/7884) ([tas50](https://github.com/tas50))
+- Remove Chef provisioning lazy loading [#7866](https://github.com/chef/chef/pull/7866) ([tas50](https://github.com/tas50))
+- add tests for yum version with package_source bug [#7886](https://github.com/chef/chef/pull/7886) ([lamont-granquist](https://github.com/lamont-granquist))
+- fix whitespace in node attributes [ci skip] [#7890](https://github.com/chef/chef/pull/7890) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix the knife integration spec timeouts [#7896](https://github.com/chef/chef/pull/7896) ([lamont-granquist](https://github.com/lamont-granquist))
+- windows_ad_join: Switch to UPN format usernames for use with AD cmdlets [#7895](https://github.com/chef/chef/pull/7895) ([stuartpreston](https://github.com/stuartpreston))
+- Make sure we define windows_task resource on *nix systems [#7903](https://github.com/chef/chef/pull/7903) ([tas50](https://github.com/tas50))
+- Add nillability to attribute deep merging [#7892](https://github.com/chef/chef/pull/7892) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update deps to bring in the new ca-certs [#7897](https://github.com/chef/chef/pull/7897) ([tas50](https://github.com/tas50))
+- Always run policy_file if a policy_file or policy_group exists [#7910](https://github.com/chef/chef/pull/7910) ([tas50](https://github.com/tas50))
+- windows_feature: Move provider logic into the default of the install_method property [#7912](https://github.com/chef/chef/pull/7912) ([tas50](https://github.com/tas50))
+- Update inspec-core to 3.0.46 [#7924](https://github.com/chef/chef/pull/7924) ([tas50](https://github.com/tas50))
+- Replace usage of win_friendly_path helper in windows_certificate [#7927](https://github.com/chef/chef/pull/7927) ([tas50](https://github.com/tas50))
+- Update Cheffish to 14.0.4 [#7936](https://github.com/chef/chef/pull/7936) ([tas50](https://github.com/tas50))
+- use --no-tty during apt-keys fro gpg - fixes #7913 [#7914](https://github.com/chef/chef/pull/7914) ([EugenMayer](https://github.com/EugenMayer))
+- windows_feature_dism: support installed deleted features [#7905](https://github.com/chef/chef/pull/7905) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Improve resource descriptions for documentation [#7929](https://github.com/chef/chef/pull/7929) ([tas50](https://github.com/tas50))
+- Add additional resource description fields [#7938](https://github.com/chef/chef/pull/7938) ([tas50](https://github.com/tas50))
+- Require chef-zero 14.0.11 or later to resolve Rack gem CVEs [#7940](https://github.com/chef/chef/pull/7940) ([tas50](https://github.com/tas50))
+- windows_certificate: Add testing of the defaults and allowed properties [#7917](https://github.com/chef/chef/pull/7917) ([tas50](https://github.com/tas50))
+- Fully convert remote_directory to use properties [#7947](https://github.com/chef/chef/pull/7947) ([tas50](https://github.com/tas50))
+- Replace several uses of attribute with property in resources [#7943](https://github.com/chef/chef/pull/7943) ([tas50](https://github.com/tas50))
+- Convert service resource to use properties [#7946](https://github.com/chef/chef/pull/7946) ([tas50](https://github.com/tas50))
+- windows_workgroup: Coerce the provided reboot property and add more tests [#7916](https://github.com/chef/chef/pull/7916) ([tas50](https://github.com/tas50))
+- Remove unused yum_timeout and yum_lock_timeout configs [#7909](https://github.com/chef/chef/pull/7909) ([tas50](https://github.com/tas50))
+- Allow Integers for all group / owner properties [#7948](https://github.com/chef/chef/pull/7948) ([tas50](https://github.com/tas50))
+- Chef-15: require instead of load libraries [#7954](https://github.com/chef/chef/pull/7954) ([lamont-granquist](https://github.com/lamont-granquist))
+- Chef-15: switch default of allow_downgrade to true [#7953](https://github.com/chef/chef/pull/7953) ([lamont-granquist](https://github.com/lamont-granquist))
+- windows_share: Fix idempotency by removing the &quot;everyone&quot; access [#7956](https://github.com/chef/chef/pull/7956) ([tas50](https://github.com/tas50))
+- windows_share: Accounts to be revoked should be provided as an individually quoted string array [#7959](https://github.com/chef/chef/pull/7959) ([stuartpreston](https://github.com/stuartpreston))
+- wipe the installer direction before installation [#7964](https://github.com/chef/chef/pull/7964) ([lamont-granquist](https://github.com/lamont-granquist))
+- need -rf to remove dirs [#7966](https://github.com/chef/chef/pull/7966) ([lamont-granquist](https://github.com/lamont-granquist))
+- windows_share: Avoid ConvertTo-Json errors on Windows 2012r2 with powershell 4 [#7961](https://github.com/chef/chef/pull/7961) ([derekgroh](https://github.com/derekgroh))
+- Update inspec to 3.0.52 [#7978](https://github.com/chef/chef/pull/7978) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2q [#7979](https://github.com/chef/chef/pull/7979) ([tas50](https://github.com/tas50))
+- Support apt-get --allow-downgrades [#7963](https://github.com/chef/chef/pull/7963) ([lamont-granquist](https://github.com/lamont-granquist))
+- cab_package: Chef should fail when specified package is not applicable to the image [#7951](https://github.com/chef/chef/pull/7951) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Added windows support for the timezone resource [#7806](https://github.com/chef/chef/pull/7806) ([username-is-already-taken2](https://github.com/username-is-already-taken2))
+- gem_package provider supports --no-document and rubygems 3.x [#7986](https://github.com/chef/chef/pull/7986) ([lamont-granquist](https://github.com/lamont-granquist))
+- pull the ohai version from the bundle not from master [#7987](https://github.com/chef/chef/pull/7987) ([lamont-granquist](https://github.com/lamont-granquist))
+- Make sure which mixin requires chef_class [#7989](https://github.com/chef/chef/pull/7989) ([tas50](https://github.com/tas50))
+- better kithen ohai pinning [#7998](https://github.com/chef/chef/pull/7998) ([lamont-granquist](https://github.com/lamont-granquist))
+- Initial suppport for snap packages [#7999](https://github.com/chef/chef/pull/7999) ([lamont-granquist](https://github.com/lamont-granquist))
+- package resource: Add RHEL 8 support to DNF package installer [#8003](https://github.com/chef/chef/pull/8003) ([pixdrift](https://github.com/pixdrift))
+- RHEL8 yum_package fix. [#8005](https://github.com/chef/chef/pull/8005) ([lamont-granquist](https://github.com/lamont-granquist))
+- Pin the ohai definition to use the ohai version from Gemfile.lock [#8012](https://github.com/chef/chef/pull/8012) ([tas50](https://github.com/tas50))
+- Fix locking ohai to to the value in the Gemfile.lock [#8014](https://github.com/chef/chef/pull/8014) ([tas50](https://github.com/tas50))
+- Update InSpec to 3.0.61 and Ohai to 15.0.20 [#8010](https://github.com/chef/chef/pull/8010) ([tas50](https://github.com/tas50))
+- timezone: updated description to include windows [#8018](https://github.com/chef/chef/pull/8018) ([Stromweld](https://github.com/Stromweld))
+- Require Ruby 2.5 or later [#8023](https://github.com/chef/chef/pull/8023) ([tas50](https://github.com/tas50))
+- Bugfixes to powershell_package_source [#8025](https://github.com/chef/chef/pull/8025) ([Happycoil](https://github.com/Happycoil))
+- Allow the use of tagged?(tags) method in both only_if and not_if blocks [#7977](https://github.com/chef/chef/pull/7977) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Chef 15: Windows Server 2019 version detection [#8031](https://github.com/chef/chef/pull/8031) ([stuartpreston](https://github.com/stuartpreston))
+- Remove travis apt proxy before running functional tests [#8040](https://github.com/chef/chef/pull/8040) ([lamont-granquist](https://github.com/lamont-granquist))
+- minimal_ohai: Add init_package plugin as a required plugin [#7980](https://github.com/chef/chef/pull/7980) ([tas50](https://github.com/tas50))
+- fix EBUSY errors in preinst script [#8046](https://github.com/chef/chef/pull/8046) ([lamont-granquist](https://github.com/lamont-granquist))
+- Added property `comment` on Windows group. [#8038](https://github.com/chef/chef/pull/8038) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- windows_ad_join: suppress sensitive stderr [#8054](https://github.com/chef/chef/pull/8054) ([Happycoil](https://github.com/Happycoil))
+- Adding VC Redistributable files required for powershell_exec on Windows [#8059](https://github.com/chef/chef/pull/8059) ([stuartpreston](https://github.com/stuartpreston))
+- windows_certificate: Fix invalid byte sequence errors with pfx certicates [#8008](https://github.com/chef/chef/pull/8008) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update inspec to 3.1 and bump all the mixlibs [#8062](https://github.com/chef/chef/pull/8062) ([tas50](https://github.com/tas50))
+- Bump license_scout to 1.0.20 for licensing tests [#8065](https://github.com/chef/chef/pull/8065) ([tas50](https://github.com/tas50))
+- windows_certificate: Fix failures in delete action fails if certificate doesn&#39;t exist [#8000](https://github.com/chef/chef/pull/8000) ([Vasu1105](https://github.com/Vasu1105))
+- Disable s3 omnibus cache [#8068](https://github.com/chef/chef/pull/8068) ([tas50](https://github.com/tas50))
+- Update train-core to 1.6.3 for smaller size and new winrm options [#8074](https://github.com/chef/chef/pull/8074) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 3.2.6 [#8076](https://github.com/chef/chef/pull/8076) ([tas50](https://github.com/tas50))
+- Bump multiple deps to the latest [#8085](https://github.com/chef/chef/pull/8085) ([tas50](https://github.com/tas50))
+- Update rubygems to 2.7.7 and bundler to 1.17.3 [#8091](https://github.com/chef/chef/pull/8091) ([tas50](https://github.com/tas50))
+- Support Ruby 2.6 and add Ruby 2.6 testing [#7922](https://github.com/chef/chef/pull/7922) ([tas50](https://github.com/tas50))
+- windows_task resource: Allow non-system users without password [#7918](https://github.com/chef/chef/pull/7918) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Don&#39;t system exit on ohai CriticalPluginFailure [#8098](https://github.com/chef/chef/pull/8098) ([joshuamiller01](https://github.com/joshuamiller01))
+- user resource: Remove support for macOS 10.7 and 10.7 upgraded to 10.8+ [#8110](https://github.com/chef/chef/pull/8110) ([tas50](https://github.com/tas50))
+- Add a bit more yard to chef-config/config [#8119](https://github.com/chef/chef/pull/8119) ([tas50](https://github.com/tas50))
+- Update license scout 1.0.21 [#8130](https://github.com/chef/chef/pull/8130) ([tas50](https://github.com/tas50))
+- Update license_scout to 1.0.22 [#8133](https://github.com/chef/chef/pull/8133) ([tas50](https://github.com/tas50))
+- ssh_known_host_entry: Use the host name_property in debug logging [#8124](https://github.com/chef/chef/pull/8124) ([tas50](https://github.com/tas50))
+- openssl_ec_private_key / openssl_x509_request.rb: properly use the path properties when specified [#8122](https://github.com/chef/chef/pull/8122) ([tas50](https://github.com/tas50))
+- homebrew_cask / homebrew_tap: Properly use the cask_name and tap_name properties [#8123](https://github.com/chef/chef/pull/8123) ([tas50](https://github.com/tas50))
+- windows_printer: prevent failures when deleting printers and using device_id property [#8125](https://github.com/chef/chef/pull/8125) ([tas50](https://github.com/tas50))
+- Updates homebrew_cask tap name [#8139](https://github.com/chef/chef/pull/8139) ([jeroenj](https://github.com/jeroenj))
+- Fix cask resource running each chef-client run [#8140](https://github.com/chef/chef/pull/8140) ([jeroenj](https://github.com/jeroenj))
+- Allow for mixlib-archive 1.x [#8137](https://github.com/chef/chef/pull/8137) ([tas50](https://github.com/tas50))
+- Sysctl: Allow slashes in key or block name [#8136](https://github.com/chef/chef/pull/8136) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Hide knife cookbook site &amp; null by setting them to deprecated category [#8148](https://github.com/chef/chef/pull/8148) ([tas50](https://github.com/tas50))
+- Add a deprecation warning to knife cookbook site [#8149](https://github.com/chef/chef/pull/8149) ([tas50](https://github.com/tas50))
+- Remove &#39;attributes&#39; attribute from cookbook metadata [#8151](https://github.com/chef/chef/pull/8151) ([tas50](https://github.com/tas50))
+- Bump misc deps the latest [#8153](https://github.com/chef/chef/pull/8153) ([tas50](https://github.com/tas50))
+- Remove bogus &quot;solaris&quot; platform from specs [#8159](https://github.com/chef/chef/pull/8159) ([tas50](https://github.com/tas50))
+- Remove hpux support from group&#39;s usermod provider [#8160](https://github.com/chef/chef/pull/8160) ([tas50](https://github.com/tas50))
+- Use the latest omnibus-software and nokogiri [#8162](https://github.com/chef/chef/pull/8162) ([tas50](https://github.com/tas50))
+- windows_certificate: Ensure all actions are fully idempotent [#8118](https://github.com/chef/chef/pull/8118) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- apt_repository: Don&#39;t create gpg temporary files owned by root in the running user&#39;s home directory [#8104](https://github.com/chef/chef/pull/8104) ([vijaymmali1990](https://github.com/vijaymmali1990))
+- Remove support for unsupported opensuse &lt; 42 from group provider [#8158](https://github.com/chef/chef/pull/8158) ([tas50](https://github.com/tas50))
+- Misc YARD updates for knife [#8169](https://github.com/chef/chef/pull/8169) ([tas50](https://github.com/tas50))
+- Cleanup requires / includes in knife supermarket [#8166](https://github.com/chef/chef/pull/8166) ([tas50](https://github.com/tas50))
+- [knife] Remove duplicate code blocks in the knife cookbook upload command [#8135](https://github.com/chef/chef/pull/8135) ([f9n](https://github.com/f9n))
+- Update Rubygems to 3.0.2 [#8174](https://github.com/chef/chef/pull/8174) ([tas50](https://github.com/tas50))
+- git: Don&#39;t display the repo URL when sensitive property is set [#8179](https://github.com/chef/chef/pull/8179) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update knife bootstrap template to use up to date omnitruck URL [#8190](https://github.com/chef/chef/pull/8190) ([mivok](https://github.com/mivok))
+- Convert execute_resource remaining properties to use properties [#8178](https://github.com/chef/chef/pull/8178) ([Vasu1105](https://github.com/Vasu1105))
+- windows_certificate: Import PFX certificates with their private keys [#8193](https://github.com/chef/chef/pull/8193) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- windows_task: Properly set command / argumentts so resource updates behave as expected [#8201](https://github.com/chef/chef/pull/8201) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- chef-solo: Fixes for extra cookbook_path with parent dir that doesn&#39;t exist causes crash [#8202](https://github.com/chef/chef/pull/8202) ([vsingh-msys](https://github.com/vsingh-msys))
+- powershell_script: Prefer user provided flags over the defaults [#8167](https://github.com/chef/chef/pull/8167) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Bump InSpec to 3.5.0 [#8211](https://github.com/chef/chef/pull/8211) ([tas50](https://github.com/tas50))
+- Add windows_dfs and windows_dns resources [#8198](https://github.com/chef/chef/pull/8198) ([tas50](https://github.com/tas50))
+- Add windows_uac resource [#8212](https://github.com/chef/chef/pull/8212) ([tas50](https://github.com/tas50))
+- More consist descriptions for resource name properties [#8216](https://github.com/chef/chef/pull/8216) ([tas50](https://github.com/tas50))
+- add ed25519 gemset and update omnibus-software [#8221](https://github.com/chef/chef/pull/8221) ([lamont-granquist](https://github.com/lamont-granquist))
+- Alter how we set set group members in solaris / unify group testing [#8226](https://github.com/chef/chef/pull/8226) ([tas50](https://github.com/tas50))
+- Chef::Config: Uniform config dir path separator [#8219](https://github.com/chef/chef/pull/8219) ([vsingh-msys](https://github.com/vsingh-msys))
+- windows_certificate: Add support to import Base 64 encoded CER certificates [#8229](https://github.com/chef/chef/pull/8229) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Update scripts to use new EXPEDITOR_ environment variables [#8232](https://github.com/chef/chef/pull/8232) ([tduffield](https://github.com/tduffield))
+- chocolatey_package: use provided options when determing available options to allow using private sources [#8230](https://github.com/chef/chef/pull/8230) ([vsingh-msys](https://github.com/vsingh-msys))
+- Cleanup the user resource and convert it to the resource DSL + delete user_add provider [#8228](https://github.com/chef/chef/pull/8228) ([tas50](https://github.com/tas50))
+- Update InSpec to 3.6.6 [#8235](https://github.com/chef/chef/pull/8235) ([tas50](https://github.com/tas50))
+- add LazyModuleInclude to Universal DSL [#8243](https://github.com/chef/chef/pull/8243) ([lamont-granquist](https://github.com/lamont-granquist))
+- pin rbnacl to 5.x [#8244](https://github.com/chef/chef/pull/8244) ([lamont-granquist](https://github.com/lamont-granquist))
+- allow setting mode for openssl_dhparam after creation [#8245](https://github.com/chef/chef/pull/8245) ([btm](https://github.com/btm))
+- Update libxml2 to 2.9.9 [#8240](https://github.com/chef/chef/pull/8240) ([tas50](https://github.com/tas50))
+- windows_share: Improve path comparison to prevent convering on each run [#8248](https://github.com/chef/chef/pull/8248) ([Xorima](https://github.com/Xorima))
+- rollback rbnacl [#8255](https://github.com/chef/chef/pull/8255) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update omnibus gemfile deps to remove pry [#8257](https://github.com/chef/chef/pull/8257) ([tas50](https://github.com/tas50))
+- Remove checks / patches for old versions of Ruby [#8259](https://github.com/chef/chef/pull/8259) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2r [#8258](https://github.com/chef/chef/pull/8258) ([tas50](https://github.com/tas50))
+- windows_certificate: Import nested certificates while importing P7B certs. [#8242](https://github.com/chef/chef/pull/8242) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- mount: Add proper new lines when on AIX to prevent failures [#8271](https://github.com/chef/chef/pull/8271) ([gsreynolds](https://github.com/gsreynolds))
+- Update rubygems to 3.0.3 [#8276](https://github.com/chef/chef/pull/8276) ([tas50](https://github.com/tas50))
+- Refactor windows_service unit tests [#8279](https://github.com/chef/chef/pull/8279) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Extract ActionCollection out of ResourceReporter, overhaul DataCollector [#8063](https://github.com/chef/chef/pull/8063) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow win32-service 2.x and bump InSpec to 3.7.1 [#8285](https://github.com/chef/chef/pull/8285) ([tas50](https://github.com/tas50))
+- Remove the rake task to generate a pre-announcement [#8288](https://github.com/chef/chef/pull/8288) ([tas50](https://github.com/tas50))
+- Remove audit mode from chef-client [#7728](https://github.com/chef/chef/pull/7728) ([tas50](https://github.com/tas50))
+- Loosen win32-certstore pin and bump to 0.3.0 [#8286](https://github.com/chef/chef/pull/8286) ([tas50](https://github.com/tas50))
+- Add misc YARD comments [#8287](https://github.com/chef/chef/pull/8287) ([tas50](https://github.com/tas50))
+- windows_service: Fix action :start to not resets credentials on service [#8278](https://github.com/chef/chef/pull/8278) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- fix unsolvable Gemfile.lock [#8300](https://github.com/chef/chef/pull/8300) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow the use of `--delete-entire-chef-repo` [#8298](https://github.com/chef/chef/pull/8298) ([ABewsher](https://github.com/ABewsher))
+- Early allocation of the Chef::RunContext [#8301](https://github.com/chef/chef/pull/8301) ([lamont-granquist](https://github.com/lamont-granquist))
+- Loosen mixlib deps to allow for the latest versions [#8304](https://github.com/chef/chef/pull/8304) ([tas50](https://github.com/tas50))
+- Update Ruby to 2.5.5 [#8296](https://github.com/chef/chef/pull/8296) ([tas50](https://github.com/tas50))
+- Pin expeditor to ruby 2.5.3 and bump train to 1.7.6 [#8308](https://github.com/chef/chef/pull/8308) ([tas50](https://github.com/tas50))
+- Remove the travis gem from our gemfile [#8310](https://github.com/chef/chef/pull/8310) ([tas50](https://github.com/tas50))
+- Update chef-zero to 14.0.12 [#8312](https://github.com/chef/chef/pull/8312) ([tas50](https://github.com/tas50))
+- Update win32-service gem and fix #8195 [#8322](https://github.com/chef/chef/pull/8322) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Update InSpec to 3.7.11 and mixlib-cli to 2.0.3 [#8329](https://github.com/chef/chef/pull/8329) ([tas50](https://github.com/tas50))
+- Remove Ubuntu 14.04 testing [#8330](https://github.com/chef/chef/pull/8330) ([tas50](https://github.com/tas50))
+- Update Ruby to 2.6.2 [#8333](https://github.com/chef/chef/pull/8333) ([tas50](https://github.com/tas50))
+- Update inspec to 3.9.0 [#8336](https://github.com/chef/chef/pull/8336) ([tas50](https://github.com/tas50))
+- fix data collector non-utf8 file output [#8337](https://github.com/chef/chef/pull/8337) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove windows-api pin and update Gemfile.lock [#8328](https://github.com/chef/chef/pull/8328) ([jaymalasinha](https://github.com/jaymalasinha))
+- Update nokogiri to 1.10.2 [#8338](https://github.com/chef/chef/pull/8338) ([tas50](https://github.com/tas50))
+- locale: Add support to set all LC ENV variables and deprecate LC_ALL [#8324](https://github.com/chef/chef/pull/8324) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Add PROJECT_NAME to omnibus-test scripts [#8346](https://github.com/chef/chef/pull/8346) ([tas50](https://github.com/tas50))
+- Add Ruby 2.6 testing Appveyor [#8349](https://github.com/chef/chef/pull/8349) ([tas50](https://github.com/tas50))
+- Implement Chef::Resource#copy_properties_from [#8344](https://github.com/chef/chef/pull/8344) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add Debian 10 testing to Travis [#8348](https://github.com/chef/chef/pull/8348) ([tas50](https://github.com/tas50))
+- Avoid occasionally randomly reusing a gid in tests [#8352](https://github.com/chef/chef/pull/8352) ([btm](https://github.com/btm))
+- Don&#39;t force DSC functional tests to PS4 [#8359](https://github.com/chef/chef/pull/8359) ([btm](https://github.com/btm))
+- Add Verification tests in Buildkite [#8357](https://github.com/chef/chef/pull/8357) ([jaymalasinha](https://github.com/jaymalasinha))
+- Drop privileges before creating files in solo mode [#8361](https://github.com/chef/chef/pull/8361) ([btm](https://github.com/btm))
+- Add a new archive_file resource from the libarchive cookbook [#8028](https://github.com/chef/chef/pull/8028) ([tas50](https://github.com/tas50))
+- Allow empty strings in -o to result in empty override run list [#8370](https://github.com/chef/chef/pull/8370) ([lamont-granquist](https://github.com/lamont-granquist))
+- Limit locale resource to Linux [#8375](https://github.com/chef/chef/pull/8375) ([btm](https://github.com/btm))
+- Prevent accidentally configuring windows_service properties [#8351](https://github.com/chef/chef/pull/8351) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Allow encrypting a previously unencrypted data bag [#8077](https://github.com/chef/chef/pull/8077) ([vijaymmali1990](https://github.com/vijaymmali1990))
+- Remove a functional test for the reboot_pending DSL [#8383](https://github.com/chef/chef/pull/8383) ([btm](https://github.com/btm))
+- Improve the error message when no config can be loaded [#8389](https://github.com/chef/chef/pull/8389) ([tas50](https://github.com/tas50))
+- Use color in chef-solo by default on Windows [#8390](https://github.com/chef/chef/pull/8390) ([tas50](https://github.com/tas50))
+- Sync the CLI option descriptions between chef-client and chef-solo [#8391](https://github.com/chef/chef/pull/8391) ([tas50](https://github.com/tas50))
+- Replace highline with tty-screen in knife list [#8381](https://github.com/chef/chef/pull/8381) ([tas50](https://github.com/tas50))
+- fix default/override attribute blacklists and whitelists [#8396](https://github.com/chef/chef/pull/8396) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add ed25519 deps, bump highline and net-ssh pins and pull inspec from git [#8380](https://github.com/chef/chef/pull/8380) ([tas50](https://github.com/tas50))
+- Triggering expeditor version bump [#8405](https://github.com/chef/chef/pull/8405) ([marcparadise](https://github.com/marcparadise))
+- Replace kitchen-appbundle-updater in Travis tests with Test Kitchen lifecycle hooks [#8403](https://github.com/chef/chef/pull/8403) ([lamont-granquist](https://github.com/lamont-granquist))
+- Merge the local and travis kitchen tests into a single config [#8406](https://github.com/chef/chef/pull/8406) ([tas50](https://github.com/tas50))
+- Switch to inspec/train from gems [#8407](https://github.com/chef/chef/pull/8407) ([tas50](https://github.com/tas50))
+- Add Chef::Dist to abstract branding details to a single location [#8368](https://github.com/chef/chef/pull/8368) ([bobchaos](https://github.com/bobchaos))
+- Implement new owner/review structure + expand dev docs [#8350](https://github.com/chef/chef/pull/8350) ([tas50](https://github.com/tas50))
+- Move ed25519 gems into omnibus [#8410](https://github.com/chef/chef/pull/8410) ([tas50](https://github.com/tas50))
+- Refactor bootstrapping to use train as the transport with full Windows bootstrap support [#8253](https://github.com/chef/chef/pull/8253) ([marcparadise](https://github.com/marcparadise))
+- Fix for write permissions were not working properly on windows [#8168](https://github.com/chef/chef/pull/8168) ([vijaymmali1990](https://github.com/vijaymmali1990))
+- windows_task: Add start_when_available support [#8420](https://github.com/chef/chef/pull/8420) ([vsingh-msys](https://github.com/vsingh-msys))
+- Add the introduced field to snap_package [#8412](https://github.com/chef/chef/pull/8412) ([tas50](https://github.com/tas50))
+- &quot;chef-client&quot; =&gt; #{Chef::Dist::CLIENT} [#8418](https://github.com/chef/chef/pull/8418) ([bobchaos](https://github.com/bobchaos))
+- Implement bootstrap directly with train [#8419](https://github.com/chef/chef/pull/8419) ([marcparadise](https://github.com/marcparadise))
+- Enable license acceptance during bootstrap [#8411](https://github.com/chef/chef/pull/8411) ([marcparadise](https://github.com/marcparadise))
+- Remove chef-* bin files from chef gem [#8413](https://github.com/chef/chef/pull/8413) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec preview to 4.2.0 [#8426](https://github.com/chef/chef/pull/8426) ([tas50](https://github.com/tas50))
+- Update property descriptions for new resources [#8424](https://github.com/chef/chef/pull/8424) ([tas50](https://github.com/tas50))
+- remove windows executables from windows gemspec [#8430](https://github.com/chef/chef/pull/8430) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fixed bootstrap error while using --msi-url option with knife bootstrap winrm [#8435](https://github.com/chef/chef/pull/8435) ([Vasu1105](https://github.com/Vasu1105))
+- fix chef-bin bundling in omnibus [#8439](https://github.com/chef/chef/pull/8439) ([lamont-granquist](https://github.com/lamont-granquist))
+- file: Tell people what file a link is pointing at in warning messages [#8417](https://github.com/chef/chef/pull/8417) ([jaymzh](https://github.com/jaymzh))
+- Update InSpec to 4.3.2 [#8444](https://github.com/chef/chef/pull/8444) ([jaymalasinha](https://github.com/jaymalasinha))
+- [CHEF-8422] Fix incorrect deprecation warnings [#8429](https://github.com/chef/chef/pull/8429) ([marcparadise](https://github.com/marcparadise))
+- Remove old maintainer gems from the Gemfile [#8445](https://github.com/chef/chef/pull/8445) ([tas50](https://github.com/tas50))
+- Update to Ruby 2.6.3 [#8446](https://github.com/chef/chef/pull/8446) ([tas50](https://github.com/tas50))
+- Replace Chef Client by its constant in Chef::Dist [#8448](https://github.com/chef/chef/pull/8448) ([Tensibai](https://github.com/Tensibai))
+- [CHEF-8432] Ensure default protocol is used properly. Use correct &#39;require&#39; before accessing Net::SSH constants. [#8440](https://github.com/chef/chef/pull/8440) ([marcparadise](https://github.com/marcparadise))
+- Fixed empty value for knife status long output [#8415](https://github.com/chef/chef/pull/8415) ([vijaymmali1990](https://github.com/vijaymmali1990))
+- Add connstant for Chef Server and improve help messaging [#8452](https://github.com/chef/chef/pull/8452) ([tas50](https://github.com/tas50))
+- Update more brand names to current [#8454](https://github.com/chef/chef/pull/8454) ([tas50](https://github.com/tas50))
+- Chef-15: cookbook compiler should parse only .rb files [#8456](https://github.com/chef/chef/pull/8456) ([lamont-granquist](https://github.com/lamont-granquist))
+- Resolve exceptions when running knife diff [#8459](https://github.com/chef/chef/pull/8459) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix for cron resource get confused by environment/property mismatch [#8455](https://github.com/chef/chef/pull/8455) ([btm](https://github.com/btm))
+- Restore bootstrap pre-release support [#8442](https://github.com/chef/chef/pull/8442) ([marcparadise](https://github.com/marcparadise))
+- Move more DSL helpers into universal so they&#39;re available everywhere [#8457](https://github.com/chef/chef/pull/8457) ([lamont-granquist](https://github.com/lamont-granquist))
+- [CHEF-8423] Upgrade train-core to 2.1.0 for windows detection over ssh [#8465](https://github.com/chef/chef/pull/8465) ([marcparadise](https://github.com/marcparadise))
+- Add logic to require acceptance of the Chef license to run the client [#8354](https://github.com/chef/chef/pull/8354) ([tyler-ball](https://github.com/tyler-ball))
+- Initial target_mode implementation [#7758](https://github.com/chef/chef/pull/7758) ([btm](https://github.com/btm))
+- Chef 15: Unable to create temp dir on windows system [#8476](https://github.com/chef/chef/pull/8476) ([vsingh-msys](https://github.com/vsingh-msys))
+- Remove the Chef 11 admin flag from knife client create [#8473](https://github.com/chef/chef/pull/8473) ([tas50](https://github.com/tas50))
+- package: move response_file and response_file_variables out of base package resource [#8307](https://github.com/chef/chef/pull/8307) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Update a few more knife.rb references to include config.rb [#8474](https://github.com/chef/chef/pull/8474) ([tas50](https://github.com/tas50))
+- Point people to Learn Chef in the post install message [#8483](https://github.com/chef/chef/pull/8483) ([tas50](https://github.com/tas50))
+- Remove resource collision deprecations [#8470](https://github.com/chef/chef/pull/8470) ([lamont-granquist](https://github.com/lamont-granquist))
+- Check for directories on Windows before creating [#8487](https://github.com/chef/chef/pull/8487) ([marcparadise](https://github.com/marcparadise))
+- Target mode code tweaks [#8480](https://github.com/chef/chef/pull/8480) ([lamont-granquist](https://github.com/lamont-granquist))
+- knife bootstrap should only request license when installing Chef 15 [#8471](https://github.com/chef/chef/pull/8471) ([tyler-ball](https://github.com/tyler-ball))
+- Chef 15: FATAL: Configuration error SyntaxError in client.rb during bootstrap [#8496](https://github.com/chef/chef/pull/8496) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update the omnibus build license to the Chef EULA [#8498](https://github.com/chef/chef/pull/8498) ([btm](https://github.com/btm))
+- Clean up omnibus installer error and remove chef-fips [#8499](https://github.com/chef/chef/pull/8499) ([lamont-granquist](https://github.com/lamont-granquist))
+- Convert EULA to a local file [#8505](https://github.com/chef/chef/pull/8505) ([btm](https://github.com/btm))
+- Convert require to require_relative [#8508](https://github.com/chef/chef/pull/8508) ([lamont-granquist](https://github.com/lamont-granquist))
+- windows_feature: Fix failures on windows 2008r2 [#8492](https://github.com/chef/chef/pull/8492) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- CHEF_LICENSE environment variables should be quoted [#8513](https://github.com/chef/chef/pull/8513) ([tyler-ball](https://github.com/tyler-ball))
+- Fix for Chef::Exceptions::Win32APIError: The operation completed successfully. [#8451](https://github.com/chef/chef/pull/8451) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Chef 15: bootstrap options --max-wait raises NoMethodError: undefined method / [#8489](https://github.com/chef/chef/pull/8489) ([vsingh-msys](https://github.com/vsingh-msys))
+- Add comments to the Dockerfile explaining how it all works [#8509](https://github.com/chef/chef/pull/8509) ([tas50](https://github.com/tas50))
+- Use exports compatibile with /bin/sh in the bootstrap script [#8507](https://github.com/chef/chef/pull/8507) ([MarkGibbons](https://github.com/MarkGibbons))
+- Change some more require to require_relative [#8519](https://github.com/chef/chef/pull/8519) ([lamont-granquist](https://github.com/lamont-granquist))
+- [knife-ec2-547] Update config_source to support using knife classes without requiring merge_config [#8506](https://github.com/chef/chef/pull/8506) ([marcparadise](https://github.com/marcparadise))
+- Pin bundler to 1.17.2 which is included in Ruby 2.6 [#8518](https://github.com/chef/chef/pull/8518) ([tas50](https://github.com/tas50))
+- Chef 15: Minor winrm check code refactor [#8522](https://github.com/chef/chef/pull/8522) ([vsingh-msys](https://github.com/vsingh-msys))
+- Update to Chef Infra Client in Add/Remove Programs &amp; Event Log [#8520](https://github.com/chef/chef/pull/8520) ([tas50](https://github.com/tas50))
+- Use new Net:SSH host key verify values [#8524](https://github.com/chef/chef/pull/8524) ([btm](https://github.com/btm))
+- Chef 15: Add --session-timeout bootstrap option for both ssh &amp; winrm [#8521](https://github.com/chef/chef/pull/8521) ([vsingh-msys](https://github.com/vsingh-msys))
+- Rename the windows_dfs :install actions to :create [#8527](https://github.com/chef/chef/pull/8527) ([tas50](https://github.com/tas50))
+
+## [v14.12.9](https://github.com/chef/chef/tree/v14.12.9) (2019-04-20)
+
+#### Merged Pull Requests
+- Backport #8077 to Chef 14 [#8384](https://github.com/chef/chef/pull/8384) ([btm](https://github.com/btm))
+- Update win32-service + bump inspec to 3.9.3 [#8387](https://github.com/chef/chef/pull/8387) ([tas50](https://github.com/tas50))
+- Add placeholder license acceptance flags [#8398](https://github.com/chef/chef/pull/8398) ([tas50](https://github.com/tas50))
+- Fix default/override attribute blacklists and whitelists [#8400](https://github.com/chef/chef/pull/8400) ([tas50](https://github.com/tas50))
+- Improve the error message when no config can be loaded [#8401](https://github.com/chef/chef/pull/8401) ([tas50](https://github.com/tas50))
+- Sync the CLI option descriptions between chef-client and chef-solo [#8402](https://github.com/chef/chef/pull/8402) ([tas50](https://github.com/tas50))
+
+## [v14.12.3](https://github.com/chef/chef/tree/v14.12.3) (2019-04-16)
+
+#### Merged Pull Requests
+- windows_certificate: Import nested certificates while importing P7B certs [#8274](https://github.com/chef/chef/pull/8274) ([tas50](https://github.com/tas50))
+- Backport fix for #8080 [#8303](https://github.com/chef/chef/pull/8303) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Loosen the win32-cerstore and win32-service dependencies [#8295](https://github.com/chef/chef/pull/8295) ([tas50](https://github.com/tas50))
+- Remove travis gem dep and bump versions of multiple components [#8315](https://github.com/chef/chef/pull/8315) ([tas50](https://github.com/tas50))
+- Remove windows-api pin and update InSpec to 3.9.0 [#8327](https://github.com/chef/chef/pull/8327) ([jaymalasinha](https://github.com/jaymalasinha))
+- Update Ruby to 2.5.5 and nokogiri to 1.10.2 [#8339](https://github.com/chef/chef/pull/8339) ([tas50](https://github.com/tas50))
+- Backport two test fixes/changes to chef-14 [#8364](https://github.com/chef/chef/pull/8364) ([btm](https://github.com/btm))
+- Drop privileges before creating files in solo mode [#8372](https://github.com/chef/chef/pull/8372) ([btm](https://github.com/btm))
+
+## [v14.11.21](https://github.com/chef/chef/tree/v14.11.21) (2019-03-07)
+
+#### Merged Pull Requests
+- Update rubygems to 2.7.8 and bundler to 1.17.3 [#8194](https://github.com/chef/chef/pull/8194) ([tas50](https://github.com/tas50))
+- windows_certificate: Import PFX certificates with their private keys [#8206](https://github.com/chef/chef/pull/8206) ([tas50](https://github.com/tas50))
+- windows_task: Properly set command / arguments so resource updates behave as expected [#8205](https://github.com/chef/chef/pull/8205) ([tas50](https://github.com/tas50))
+- Update knife bootstrap template to use up to date omnitruck URL [#8207](https://github.com/chef/chef/pull/8207) ([tas50](https://github.com/tas50))
+- chef-solo: Fixes for extra cookbook_path with parent dir that doesn&#39;t exist causes crash [#8209](https://github.com/chef/chef/pull/8209) ([tas50](https://github.com/tas50))
+- Update InSpec to 3.5.0 [#8214](https://github.com/chef/chef/pull/8214) ([tas50](https://github.com/tas50))
+- Chef-14: add ed25519 gemset and update omnibus-software [#8222](https://github.com/chef/chef/pull/8222) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update InSpec to 3.6.6 [#8237](https://github.com/chef/chef/pull/8237) ([tas50](https://github.com/tas50))
+- Chef-14: add lazy module include to universal DSL [#8246](https://github.com/chef/chef/pull/8246) ([lamont-granquist](https://github.com/lamont-granquist))
+- Chef-14: rollback rbnacl [#8254](https://github.com/chef/chef/pull/8254) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update libxml2 to 2.9.9 [#8238](https://github.com/chef/chef/pull/8238) ([tas50](https://github.com/tas50))
+- Use proper paths on Windows in chef-config [#8261](https://github.com/chef/chef/pull/8261) ([tas50](https://github.com/tas50))
+- windows_share: Improve path comparison to prevent convering on each run [#8262](https://github.com/chef/chef/pull/8262) ([tas50](https://github.com/tas50))
+- chocolatey_package: use provided options when determing available options to allow using private sources [#8263](https://github.com/chef/chef/pull/8263) ([tas50](https://github.com/tas50))
+- openssl_dhparam: allow changing file mode on subsequent runs [#8264](https://github.com/chef/chef/pull/8264) ([tas50](https://github.com/tas50))
+- More consist descriptions for resource name properties [#8265](https://github.com/chef/chef/pull/8265) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2r [#8266](https://github.com/chef/chef/pull/8266) ([tas50](https://github.com/tas50))
+- windows_certificate: Add support to import Base 64 encoded CER certificates [#8267](https://github.com/chef/chef/pull/8267) ([tas50](https://github.com/tas50))
+- Update InSpec to 3.7.1 [#8268](https://github.com/chef/chef/pull/8268) ([tas50](https://github.com/tas50))
+- Update cacerts to 2019-01-22 file [#8270](https://github.com/chef/chef/pull/8270) ([tas50](https://github.com/tas50))
+- mount: Add proper new lines when on AIX to prevent failures [#8273](https://github.com/chef/chef/pull/8273) ([tas50](https://github.com/tas50))
+- Update Rubygems to 2.7.9 + Add release notes for Chef 14.11 [#8272](https://github.com/chef/chef/pull/8272) ([tas50](https://github.com/tas50))
+
+## [v14.10.9](https://github.com/chef/chef/tree/v14.10.9) (2019-01-30)
+
+#### Merged Pull Requests
+- Bump all deps to the latest versions [#8154](https://github.com/chef/chef/pull/8154) ([tas50](https://github.com/tas50))
+- windows_certificate: Ensure all actions are fully idempotent [#8163](https://github.com/chef/chef/pull/8163) ([tas50](https://github.com/tas50))
+- Add a deprecation warning to knife cookbook site [#8164](https://github.com/chef/chef/pull/8164) ([tas50](https://github.com/tas50))
+- Update nokogiri to 1.10.1 [#8172](https://github.com/chef/chef/pull/8172) ([tas50](https://github.com/tas50))
+- Bump all deps to current [#8173](https://github.com/chef/chef/pull/8173) ([tas50](https://github.com/tas50))
+- Cleanup dependencies and comments in knife plugins [#8183](https://github.com/chef/chef/pull/8183) ([tas50](https://github.com/tas50))
+- apt_repository: Don&#39;t create gpg temporary files owned by root in the running user&#39;s home directory [#8184](https://github.com/chef/chef/pull/8184) ([tas50](https://github.com/tas50))
+- Update inspec to 3.3.14 [#8185](https://github.com/chef/chef/pull/8185) ([tas50](https://github.com/tas50))
+- Officially deprecate cookbook shadowing and audit mode [#8187](https://github.com/chef/chef/pull/8187) ([tas50](https://github.com/tas50))
+- Backport git resource fix in #8179 and update win32-certstore [#8189](https://github.com/chef/chef/pull/8189) ([tas50](https://github.com/tas50))
+- Bump InSpec to 3.4.1 [#8192](https://github.com/chef/chef/pull/8192) ([tas50](https://github.com/tas50))
+
+## [v14.9.13](https://github.com/chef/chef/tree/v14.9.13) (2019-01-22)
+
+#### Merged Pull Requests
+- Bump mixlib-archive, mixlib-shellout, semverse, and train-core to the latest [#8041](https://github.com/chef/chef/pull/8041) ([tas50](https://github.com/tas50))
+- Chef 14: Windows Server 2019 version detection [#8033](https://github.com/chef/chef/pull/8033) ([stuartpreston](https://github.com/stuartpreston))
+- Bump mixlib-shellout to 2.4.2 and inspec to 3.0.64 [#8027](https://github.com/chef/chef/pull/8027) ([chef-ci](https://github.com/chef-ci))
+- Bump mixlib-archive to 0.4.19 and mixlib-shellout to 2.4.4 [#8037](https://github.com/chef/chef/pull/8037) ([chef-ci](https://github.com/chef-ci))
+- Backport: Bugfixes to powershell_package_source [#8050](https://github.com/chef/chef/pull/8050) ([stuartpreston](https://github.com/stuartpreston))
+- Backport: Allow setting the comment on a Windows group [#8052](https://github.com/chef/chef/pull/8052) ([stuartpreston](https://github.com/stuartpreston))
+- Backport: windows_ad_join: suppress sensitive stderr [#8061](https://github.com/chef/chef/pull/8061) ([stuartpreston](https://github.com/stuartpreston))
+- Bump inspec-core to 3.1.3 [#8048](https://github.com/chef/chef/pull/8048) ([chef-ci](https://github.com/chef-ci))
+- Backport: Adding VC Redistributable files required for powershell_exec on Windows [#8060](https://github.com/chef/chef/pull/8060) ([stuartpreston](https://github.com/stuartpreston))
+- Bump license_scout to 1.0.20 for licensing tests [#8064](https://github.com/chef/chef/pull/8064) ([tas50](https://github.com/tas50))
+- Bump train-core to 1.6.3 [#8067](https://github.com/chef/chef/pull/8067) ([chef-ci](https://github.com/chef-ci))
+- windows_certificate: Fix failures in delete action fails if certificate doesn&#39;t exist [#8073](https://github.com/chef/chef/pull/8073) ([tas50](https://github.com/tas50))
+- chef 14: windows_certificate: Fix invalid byte sequence errors with pfx certicates [#8071](https://github.com/chef/chef/pull/8071) ([tas50](https://github.com/tas50))
+- chef 14: minimal_ohai: Add init_package plugin as a required plugin [#8072](https://github.com/chef/chef/pull/8072) ([tas50](https://github.com/tas50))
+- Disable s3 omnibus cache [#8069](https://github.com/chef/chef/pull/8069) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 3.2.6 [#8075](https://github.com/chef/chef/pull/8075) ([chef-ci](https://github.com/chef-ci))
+- Bump mixlib-cli to 2.0.0 and win32-certstore to 0.2.1 [#8092](https://github.com/chef/chef/pull/8092) ([chef-ci](https://github.com/chef-ci))
+- Support and test on Ruby 2.6 [#8121](https://github.com/chef/chef/pull/8121) ([tas50](https://github.com/tas50))
+- windows_task resource: allow non-system users without password for interactive tasks [#8120](https://github.com/chef/chef/pull/8120) ([tas50](https://github.com/tas50))
+- Chef 14: A windows support to the timezone resource [#8129](https://github.com/chef/chef/pull/8129) ([tas50](https://github.com/tas50))
+- Update license scout to 1.0.22 [#8131](https://github.com/chef/chef/pull/8131) ([tas50](https://github.com/tas50))
+- Backport various name_property fixes in resources from Chef 15 [#8134](https://github.com/chef/chef/pull/8134) ([tas50](https://github.com/tas50))
+- Allow for mixlib-archive 1.x [#8141](https://github.com/chef/chef/pull/8141) ([tas50](https://github.com/tas50))
+- sysctl: Allow slashes in key or block name [#8142](https://github.com/chef/chef/pull/8142) ([tas50](https://github.com/tas50))
+- homebrew_cask: Ensure the resource is fully idempotent [#8143](https://github.com/chef/chef/pull/8143) ([tas50](https://github.com/tas50))
+
+## [v14.8.12](https://github.com/chef/chef/tree/v14.8.12) (2018-12-13)
+
+#### Merged Pull Requests
+- Chef 14 Backport: fix the knife integration spec timeouts [#7899](https://github.com/chef/chef/pull/7899) ([lamont-granquist](https://github.com/lamont-granquist))
+- Chef 14 Backport: windows_ad_join: Switch to UPN format usernames for use with AD cmdlets [#7906](https://github.com/chef/chef/pull/7906) ([stuartpreston](https://github.com/stuartpreston))
+- Chef 14 Backport: Make sure we define windows_task resource on *nix systems [#7907](https://github.com/chef/chef/pull/7907) ([stuartpreston](https://github.com/stuartpreston))
+- Update inspec and ca-certs to the latest [#7898](https://github.com/chef/chef/pull/7898) ([tas50](https://github.com/tas50))
+- Backport: windows_feature: Move provider logic into the default of the install_method property [#7920](https://github.com/chef/chef/pull/7920) ([tas50](https://github.com/tas50))
+- Bump InSpec to 3.0.46 [#7931](https://github.com/chef/chef/pull/7931) ([tas50](https://github.com/tas50))
+- Replace usage of win_friendly_path helper in windows_certificate [#7932](https://github.com/chef/chef/pull/7932) ([tas50](https://github.com/tas50))
+- Update cheffish to 14.0.4 [#7937](https://github.com/chef/chef/pull/7937) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 3.0.52 [#7945](https://github.com/chef/chef/pull/7945) ([chef-ci](https://github.com/chef-ci))
+- Chef 14: Replace several uses of attribute with property in resources [#7967](https://github.com/chef/chef/pull/7967) ([tas50](https://github.com/tas50))
+- Chef 14: windows_certificate: Add testing of the defaults and allowed properties [#7968](https://github.com/chef/chef/pull/7968) ([tas50](https://github.com/tas50))
+- Chef 14: Improve resource descriptions [#7969](https://github.com/chef/chef/pull/7969) ([tas50](https://github.com/tas50))
+- Chef 14: windows_workgroup: Coerce the provided reboot property and add more t ests [#7972](https://github.com/chef/chef/pull/7972) ([tas50](https://github.com/tas50))
+- Chef 14: windows_share: Properly split the users to be revoked using quotes [#7974](https://github.com/chef/chef/pull/7974) ([tas50](https://github.com/tas50))
+- Chef 14: apt_repository: prevent gpg key import on newer Debian releases [#7971](https://github.com/chef/chef/pull/7971) ([tas50](https://github.com/tas50))
+- Chef 14: windows_share: Fix idempotency by removing the &quot;everyone&quot; access [#7973](https://github.com/chef/chef/pull/7973) ([tas50](https://github.com/tas50))
+- Chef 14: removed features are also available for installation in windows_feature_dism [#7970](https://github.com/chef/chef/pull/7970) ([tas50](https://github.com/tas50))
+- Update to openssl 1.0.2q [#7976](https://github.com/chef/chef/pull/7976) ([tas50](https://github.com/tas50))
+- cab_package: Fail if the cab does not apply to the current windows image [#7992](https://github.com/chef/chef/pull/7992) ([tas50](https://github.com/tas50))
+- gem_package: support the --no-document flag needed for Ruby 2.6 / rubygems 3 [#7994](https://github.com/chef/chef/pull/7994) ([tas50](https://github.com/tas50))
+- windows_share: Avoid ConvertTo-Json errors on Windows 2012r2 with powershell 4 [#7991](https://github.com/chef/chef/pull/7991) ([tas50](https://github.com/tas50))
+- apt_package: Support downgrades for apt packages [#7993](https://github.com/chef/chef/pull/7993) ([tas50](https://github.com/tas50))
+- Make sure which mixin requires chef_class [#7995](https://github.com/chef/chef/pull/7995) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 3.0.61 [#8002](https://github.com/chef/chef/pull/8002) ([chef-ci](https://github.com/chef-ci))
+- dnf_package: Add RHEL 8 support [#8006](https://github.com/chef/chef/pull/8006) ([tas50](https://github.com/tas50))
+- Bump ohai to 14.8.10 for improved virtualization and platform detection [#8019](https://github.com/chef/chef/pull/8019) ([chef-ci](https://github.com/chef-ci))
+- Make sure the ohai CLI uses the same version of ohai as chef-client [#8020](https://github.com/chef/chef/pull/8020) ([tas50](https://github.com/tas50))
+
+## [v14.7.17](https://github.com/chef/chef/tree/v14.7.17) (2018-11-08)
+
+#### Merged Pull Requests
+- Allow passing array to supports in mount resource again [#7809](https://github.com/chef/chef/pull/7809) ([tas50](https://github.com/tas50))
+- Automated resource documentation improvements [#7811](https://github.com/chef/chef/pull/7811) ([tas50](https://github.com/tas50))
+- Backport: Add macOS support to the timezone resource [#7830](https://github.com/chef/chef/pull/7830) ([tas50](https://github.com/tas50))
+- Backport: Fix inspector to properly handle defaults that are symbols [#7826](https://github.com/chef/chef/pull/7826) ([tas50](https://github.com/tas50))
+- Backport: Fix SLES 15 upgrades removing the symlinks [#7827](https://github.com/chef/chef/pull/7827) ([tas50](https://github.com/tas50))
+- Backport: Add windows_share and windows_certificate resources [#7833](https://github.com/chef/chef/pull/7833) ([tas50](https://github.com/tas50))
+- Backport: Handle `interactive_enabled` property in windows_task resource [#7832](https://github.com/chef/chef/pull/7832) ([tas50](https://github.com/tas50))
+- Backport: Multiple fixes to dmg_package including functional EULA acceptance [#7831](https://github.com/chef/chef/pull/7831) ([tas50](https://github.com/tas50))
+- Backport: Fix chef-apply crash for reboot [#7828](https://github.com/chef/chef/pull/7828) ([tas50](https://github.com/tas50))
+- Update win32-taskscheduler to 2.0.1 [#7844](https://github.com/chef/chef/pull/7844) ([tas50](https://github.com/tas50))
+- Backport: Added `description` property on windows_task resource [#7848](https://github.com/chef/chef/pull/7848) ([btm](https://github.com/btm))
+- Backport: Add default_descriptions to properties [#7873](https://github.com/chef/chef/pull/7873) ([tas50](https://github.com/tas50))
+- Backport: Make knife command banners consistent [#7874](https://github.com/chef/chef/pull/7874) ([tas50](https://github.com/tas50))
+- Add more validation_messages to properties [#7875](https://github.com/chef/chef/pull/7875) ([tas50](https://github.com/tas50))
+- Backport: Add windows_firewall_rule resource [#7876](https://github.com/chef/chef/pull/7876) ([tas50](https://github.com/tas50))
+- Backport: Resource property description updates [#7887](https://github.com/chef/chef/pull/7887) ([tas50](https://github.com/tas50))
+- Backport: Allow multiple local and remote ports in the windows_firewall_rule resource [#7888](https://github.com/chef/chef/pull/7888) ([tas50](https://github.com/tas50))
+- Backport: Defer running initramfs_command until end of run [#7889](https://github.com/chef/chef/pull/7889) ([tas50](https://github.com/tas50))
+- fix whitespace in node attributes [ci skip] [#7891](https://github.com/chef/chef/pull/7891) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v14.6.47](https://github.com/chef/chef/tree/v14.6.47) (2018-10-26)
+
+#### Merged Pull Requests
+- zypper_package: Add new global_options property [#7518](https://github.com/chef/chef/pull/7518) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Bump chef-vault to 3.4.2 [#7675](https://github.com/chef/chef/pull/7675) ([chef-ci](https://github.com/chef-ci))
+- Adds full_name property to user resource for Windows. [#7677](https://github.com/chef/chef/pull/7677) ([Vasu1105](https://github.com/Vasu1105))
+- Upgrade to rspec 3.8.x [#7691](https://github.com/chef/chef/pull/7691) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fixed introduced version to 14.6 for newly added properties in zypper_package and windows_user resource as it got released in 14.6. [#7692](https://github.com/chef/chef/pull/7692) ([Vasu1105](https://github.com/Vasu1105))
+- Minor optimization in yum_helper.py to avoid RPM DB corruption under certain scenarios [#7696](https://github.com/chef/chef/pull/7696) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- replace some instances of to_hash with to_h [#7697](https://github.com/chef/chef/pull/7697) ([lamont-granquist](https://github.com/lamont-granquist))
+- Sanitize inputs to Gem::Version in comparison operation of Package provider superclass [#7703](https://github.com/chef/chef/pull/7703) ([lamont-granquist](https://github.com/lamont-granquist))
+- Change the allow_downgrade pseudo-default in the package provider superclass to true [#7701](https://github.com/chef/chef/pull/7701) ([lamont-granquist](https://github.com/lamont-granquist))
+- short circuit before the version_compare call [#7705](https://github.com/chef/chef/pull/7705) ([lamont-granquist](https://github.com/lamont-granquist))
+- add some big FIXMEs [#7706](https://github.com/chef/chef/pull/7706) ([lamont-granquist](https://github.com/lamont-granquist))
+- fixed typo in description property of rhsm_errata_level resource [#7710](https://github.com/chef/chef/pull/7710) ([freakinhippie](https://github.com/freakinhippie))
+- Bump inspec-core to 2.3.5 [#7709](https://github.com/chef/chef/pull/7709) ([chef-ci](https://github.com/chef-ci))
+- Bump inspec-core to 2.3.10 [#7723](https://github.com/chef/chef/pull/7723) ([chef-ci](https://github.com/chef-ci))
+- Add chef-cleanup omnibus-software defn [#7725](https://github.com/chef/chef/pull/7725) ([lamont-granquist](https://github.com/lamont-granquist))
+- better docs for Chef::Knife::Bootstrap#validate_options! [#7719](https://github.com/chef/chef/pull/7719) ([bankair](https://github.com/bankair))
+- Cleanup the Test Kitchen setup in omnibus [#7726](https://github.com/chef/chef/pull/7726) ([tas50](https://github.com/tas50))
+- Only include the Windows distro files on Windows [#7727](https://github.com/chef/chef/pull/7727) ([tas50](https://github.com/tas50))
+- Add the timezone resource from the timezone_lwrp cookbook [#7736](https://github.com/chef/chef/pull/7736) ([tas50](https://github.com/tas50))
+- Enable x86_64-linux-kernel2 habitat builds for chef-client [#7722](https://github.com/chef/chef/pull/7722) ([smacfarlane](https://github.com/smacfarlane))
+- Bump win32-taskscheduler to 1.0.12 [#7740](https://github.com/chef/chef/pull/7740) ([chef-ci](https://github.com/chef-ci))
+- Bump ohai to 14.6.2 [#7742](https://github.com/chef/chef/pull/7742) ([chef-ci](https://github.com/chef-ci))
+- Bump win32-taskscheduler to 2.0 [#7743](https://github.com/chef/chef/pull/7743) ([btm](https://github.com/btm))
+- When a property regex fails don&#39;t call it an option [#7745](https://github.com/chef/chef/pull/7745) ([tas50](https://github.com/tas50))
+- Bump inspec-core to 2.3.23 [#7747](https://github.com/chef/chef/pull/7747) ([chef-ci](https://github.com/chef-ci))
+- Bump inspec-core to 2.3.24 [#7748](https://github.com/chef/chef/pull/7748) ([chef-ci](https://github.com/chef-ci))
+- Update omnibus deps [#7749](https://github.com/chef/chef/pull/7749) ([tas50](https://github.com/tas50))
+- Update Nokogiri to 1.8.5 [#7750](https://github.com/chef/chef/pull/7750) ([tas50](https://github.com/tas50))
+- Node Attributes: Build ImmutableMash properly in deep_merge! [#7752](https://github.com/chef/chef/pull/7752) ([lamont-granquist](https://github.com/lamont-granquist))
+- File provider: fix sticky bits management / preservation [#7753](https://github.com/chef/chef/pull/7753) ([lamont-granquist](https://github.com/lamont-granquist))
+- Run more Travis tests on Ruby 2.5.1 [#7755](https://github.com/chef/chef/pull/7755) ([tas50](https://github.com/tas50))
+- Add support for localized system account to windows_task resource [#7679](https://github.com/chef/chef/pull/7679) ([jugatsu](https://github.com/jugatsu))
+- Update omnibus to use ruby-cleanup definition [#7757](https://github.com/chef/chef/pull/7757) ([tas50](https://github.com/tas50))
+- Bump mixlib-archive to 0.4.18 [#7759](https://github.com/chef/chef/pull/7759) ([chef-ci](https://github.com/chef-ci))
+- Bump train-core to 1.5.4 [#7760](https://github.com/chef/chef/pull/7760) ([chef-ci](https://github.com/chef-ci))
+- Update Ruby to 2.5.3 [#7766](https://github.com/chef/chef/pull/7766) ([tas50](https://github.com/tas50))
+- [chef/chef]Fix duplicate logs [#7698](https://github.com/chef/chef/pull/7698) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Throw better error on invalid resources actions [#7729](https://github.com/chef/chef/pull/7729) ([tas50](https://github.com/tas50))
+- Don&#39;t ship contributing.md and VERSION file in the gem [#7769](https://github.com/chef/chef/pull/7769) ([tas50](https://github.com/tas50))
+- Fix registry key bug when sensitive is true [#7767](https://github.com/chef/chef/pull/7767) ([josh-barker](https://github.com/josh-barker))
+- Use the Chefstyle gem instead of a git checkout [#7770](https://github.com/chef/chef/pull/7770) ([tas50](https://github.com/tas50))
+- Switch back to chefstyle from git and use the updated chef omnibus def [#7772](https://github.com/chef/chef/pull/7772) ([tas50](https://github.com/tas50))
+- Update InSpec to 3.0 [#7773](https://github.com/chef/chef/pull/7773) ([tas50](https://github.com/tas50))
+- Update chef-vault and serverspec to the latest [#7774](https://github.com/chef/chef/pull/7774) ([tas50](https://github.com/tas50))
+- Move iso8601 gem to windows only gemspec [#7778](https://github.com/chef/chef/pull/7778) ([tas50](https://github.com/tas50))
+- Add some retry/delay in HTTP functional tests [#7780](https://github.com/chef/chef/pull/7780) ([schisamo](https://github.com/schisamo))
+- Pin rake to 12.3.0 to prevent installing 2 copies in our install [#7779](https://github.com/chef/chef/pull/7779) ([tas50](https://github.com/tas50))
+- Fix locale on RHEL 6 / Amazon Linux [#7782](https://github.com/chef/chef/pull/7782) ([tas50](https://github.com/tas50))
+
+## [v14.5.33](https://github.com/chef/chef/tree/v14.5.33) (2018-09-25)
+
+#### Merged Pull Requests
+- windows_service: Remove potentially sensitive info from the log [#7659](https://github.com/chef/chef/pull/7659) ([stuartpreston](https://github.com/stuartpreston))
+- windows_feature: Fix exception message grammar [#7669](https://github.com/chef/chef/pull/7669) ([dgreeninger](https://github.com/dgreeninger))
+- Add @jjlimepoint as a maintainer for chef-provisioning [#7649](https://github.com/chef/chef/pull/7649) ([jjlimepoint](https://github.com/jjlimepoint))
+- Deprecate ohai resource&#39;s ohai_name property [#7667](https://github.com/chef/chef/pull/7667) ([tas50](https://github.com/tas50))
+- Fix failures in windows_ad_join in 14.5.27 [#7673](https://github.com/chef/chef/pull/7673) ([tas50](https://github.com/tas50))
+- Update to the latest omnibus-software for builds [#7676](https://github.com/chef/chef/pull/7676) ([tas50](https://github.com/tas50))
+
+## [v14.5.27](https://github.com/chef/chef/tree/v14.5.27) (2018-09-20)
+
+#### Merged Pull Requests
+- Bump mixlib-archive to 0.4.16 [#7595](https://github.com/chef/chef/pull/7595) ([chef-ci](https://github.com/chef-ci))
+- Add additional property docs + update existing docs [#7600](https://github.com/chef/chef/pull/7600) ([tas50](https://github.com/tas50))
+- Simplify the rake task to updating gem dependencies [#7602](https://github.com/chef/chef/pull/7602) ([lamont-granquist](https://github.com/lamont-granquist))
+- Build chef omnibus package with Chef 14 / Berkshelf 7 [#7603](https://github.com/chef/chef/pull/7603) ([tas50](https://github.com/tas50))
+- windows_auto_run: Avoid declare_resource where it&#39;s not needed [#7608](https://github.com/chef/chef/pull/7608) ([tas50](https://github.com/tas50))
+- Allow resource_inspector be used outside the binary [#7609](https://github.com/chef/chef/pull/7609) ([tas50](https://github.com/tas50))
+- Update property descriptions and remove extra nil types [#7604](https://github.com/chef/chef/pull/7604) ([tas50](https://github.com/tas50))
+- Update inspec-core to 2.2.78 [#7606](https://github.com/chef/chef/pull/7606) ([tas50](https://github.com/tas50))
+- Shorten the resource collision deprecation message [#7601](https://github.com/chef/chef/pull/7601) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update rubyzip to 1.2.2 [#7618](https://github.com/chef/chef/pull/7618) ([tas50](https://github.com/tas50))
+- Remove the CBGB [#7619](https://github.com/chef/chef/pull/7619) ([nathenharvey](https://github.com/nathenharvey))
+- Add additional resource descriptions [#7623](https://github.com/chef/chef/pull/7623) ([tas50](https://github.com/tas50))
+- Remove unnecessary declare_resource usage in build_essential [#7624](https://github.com/chef/chef/pull/7624) ([tas50](https://github.com/tas50))
+- Add introduced versions for properties and more descriptions [#7627](https://github.com/chef/chef/pull/7627) ([tas50](https://github.com/tas50))
+- Properly capitalize PowerShell in descriptions and errors [#7630](https://github.com/chef/chef/pull/7630) ([tas50](https://github.com/tas50))
+- Add required properties to the resource inspector output [#7631](https://github.com/chef/chef/pull/7631) ([tas50](https://github.com/tas50))
+- Fix remote_directory does not obey removal of file specificity [#7551](https://github.com/chef/chef/pull/7551) ([thechile](https://github.com/thechile))
+- Update expeditor config to use subscriptions [#7632](https://github.com/chef/chef/pull/7632) ([tas50](https://github.com/tas50))
+- paludis_package: Make sure timeout property is an Integer [#7625](https://github.com/chef/chef/pull/7625) ([tas50](https://github.com/tas50))
+- Add locale resource more managing the system&#39;s locale [#7633](https://github.com/chef/chef/pull/7633) ([vincentaubert](https://github.com/vincentaubert))
+- windows_ad_join resource - add newname property [#7637](https://github.com/chef/chef/pull/7637) ([derekgroh](https://github.com/derekgroh))
+- Bump inspec-core to 2.2.101 [#7640](https://github.com/chef/chef/pull/7640) ([chef-ci](https://github.com/chef-ci))
+- windows_workgroup Resource for joining Windows Workgroups [#7564](https://github.com/chef/chef/pull/7564) ([derekgroh](https://github.com/derekgroh))
+- Update libarchive to 3.3.3 [#7641](https://github.com/chef/chef/pull/7641) ([tas50](https://github.com/tas50))
+- Rename windows_ad_join&#39;s newname to be new_hostname [#7643](https://github.com/chef/chef/pull/7643) ([tas50](https://github.com/tas50))
+- Bump ohai to 14.5.0 [#7644](https://github.com/chef/chef/pull/7644) ([chef-ci](https://github.com/chef-ci))
+- Fix resource descriptions for ohai_hint and rhsm_errata_level [#7645](https://github.com/chef/chef/pull/7645) ([tas50](https://github.com/tas50))
+- More Resource doc fixes [#7646](https://github.com/chef/chef/pull/7646) ([tas50](https://github.com/tas50))
+- Move subversion properties out of scm and into subversion [#7648](https://github.com/chef/chef/pull/7648) ([tas50](https://github.com/tas50))
+- Bump InSpec and Ohai to the latest [#7657](https://github.com/chef/chef/pull/7657) ([tas50](https://github.com/tas50))
+- Add Chef 14.5 release notes [#7652](https://github.com/chef/chef/pull/7652) ([tas50](https://github.com/tas50))
+- Pull in updated omnibus-software [#7658](https://github.com/chef/chef/pull/7658) ([tas50](https://github.com/tas50))
+- Update script resource deprecation waring [#7651](https://github.com/chef/chef/pull/7651) ([tas50](https://github.com/tas50))
+- Remove Bryant Lippert as a FreeBSD maintainer [#7654](https://github.com/chef/chef/pull/7654) ([tas50](https://github.com/tas50))
+- Wire up openssl_x509 [#7660](https://github.com/chef/chef/pull/7660) ([tas50](https://github.com/tas50))
+
+## [v14.4.56](https://github.com/chef/chef/tree/v14.4.56) (2018-08-29)
+
+#### Merged Pull Requests
+- [MSYS-843] - Add remove_account_right function to win32/security [#7445](https://github.com/chef/chef/pull/7445) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Add proper yard deprecated tags on methods [#7452](https://github.com/chef/chef/pull/7452) ([tas50](https://github.com/tas50))
+- Remove require mixlib/shellouts where not necessary [#7457](https://github.com/chef/chef/pull/7457) ([tas50](https://github.com/tas50))
+- Add knife config get/use-profile commands [#7455](https://github.com/chef/chef/pull/7455) ([coderanger](https://github.com/coderanger))
+- Fixing array args in some unix providers [#7379](https://github.com/chef/chef/pull/7379) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix duplicated query parameters to resolve Chef::HTTP::Simple regression [#7465](https://github.com/chef/chef/pull/7465) ([lamont-granquist](https://github.com/lamont-granquist))
+- Move all knife cookbook site plugin logic into knife supermarket [#7466](https://github.com/chef/chef/pull/7466) ([tas50](https://github.com/tas50))
+- Improve the error message when knife bootstrap windows isn&#39;t installed [#7470](https://github.com/chef/chef/pull/7470) ([tas50](https://github.com/tas50))
+- Remove require json_compat where not used [#7472](https://github.com/chef/chef/pull/7472) ([tas50](https://github.com/tas50))
+- Make gem_installer generate a valid Gemfile [#6168](https://github.com/chef/chef/pull/6168) ([oclaussen](https://github.com/oclaussen))
+- Bump version to 14.4.0 [#7476](https://github.com/chef/chef/pull/7476) ([tas50](https://github.com/tas50))
+- add back clean_array API [#7477](https://github.com/chef/chef/pull/7477) ([lamont-granquist](https://github.com/lamont-granquist))
+- ifconfig: Allow specifying VLAN on RHEL/Centos [#7478](https://github.com/chef/chef/pull/7478) ([tas50](https://github.com/tas50))
+- Allow specifying VLAN &amp; Gateway on RHEL/Centos [#6400](https://github.com/chef/chef/pull/6400) ([tomdoherty](https://github.com/tomdoherty))
+- group: convert to properties with descriptions and improve comma separated parsing [#7474](https://github.com/chef/chef/pull/7474) ([tas50](https://github.com/tas50))
+- Pull in the latest inspec-core and train-core [#7487](https://github.com/chef/chef/pull/7487) ([tas50](https://github.com/tas50))
+- ifconfig: Add gateway property on RHEL/Debian based systems [#7475](https://github.com/chef/chef/pull/7475) ([tas50](https://github.com/tas50))
+- Expand platform support for the route resource [#7480](https://github.com/chef/chef/pull/7480) ([tas50](https://github.com/tas50))
+- Handling Quotes in Windows Task Commands and Arguments [#7497](https://github.com/chef/chef/pull/7497) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Functional tests: Ensure we logon with the local security_user account [#7498](https://github.com/chef/chef/pull/7498) ([stuartpreston](https://github.com/stuartpreston))
+- Make sure knife descriptions all have periods [#7473](https://github.com/chef/chef/pull/7473) ([tas50](https://github.com/tas50))
+- Add minor version bumping to Expeditor config [#7501](https://github.com/chef/chef/pull/7501) ([tas50](https://github.com/tas50))
+- Bump version of ISO8601 to latest (0.11.0) [#7505](https://github.com/chef/chef/pull/7505) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- Assume credentials supplied are still valid if they cannot be validated due to a Windows account restriction [#7416](https://github.com/chef/chef/pull/7416) ([stuartpreston](https://github.com/stuartpreston))
+- Add support for setting task priority [#7464](https://github.com/chef/chef/pull/7464) ([Vasu1105](https://github.com/Vasu1105))
+- Add additional resource descriptions [#7506](https://github.com/chef/chef/pull/7506) ([tas50](https://github.com/tas50))
+- [SHACK-304] Deprecation checking turns up false positive [#7515](https://github.com/chef/chef/pull/7515) ([tyler-ball](https://github.com/tyler-ball))
+- MSYS-858 : added warning if allow_downgrade set to be false and tried to install older version [#7495](https://github.com/chef/chef/pull/7495) ([piyushawasthi](https://github.com/piyushawasthi))
+- Always update both the loaded sysctl value and the sysctl.d value on disk [#7519](https://github.com/chef/chef/pull/7519) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
+- [SHACK-290] Unpacking tarball paths suffer from URI error [#7523](https://github.com/chef/chef/pull/7523) ([tyler-ball](https://github.com/tyler-ball))
+- Prevent failures RHSM resources by using default_env in execute resources [#7520](https://github.com/chef/chef/pull/7520) ([tas50](https://github.com/tas50))
+- add chef_version API to provides lines [#7524](https://github.com/chef/chef/pull/7524) ([lamont-granquist](https://github.com/lamont-granquist))
+- Maybe hold off on rspec 3.8 [#7527](https://github.com/chef/chef/pull/7527) ([cheeseplus](https://github.com/cheeseplus))
+- stop parsing init script at the &quot;### END INIT INFO&quot; marker. [#7525](https://github.com/chef/chef/pull/7525) ([goblin23](https://github.com/goblin23))
+- Bump Ohai to 14.4 [#7532](https://github.com/chef/chef/pull/7532) ([tas50](https://github.com/tas50))
+- Update omnibus to 5.6.15 [#7536](https://github.com/chef/chef/pull/7536) ([tas50](https://github.com/tas50))
+- Update inspec to 2.2.61 [#7534](https://github.com/chef/chef/pull/7534) ([tas50](https://github.com/tas50))
+- osx_profile: Use the full path to /usr/bin/profiles [#7539](https://github.com/chef/chef/pull/7539) ([tas50](https://github.com/tas50))
+- Run rspec tests within a kitchen container on CentOS 7 [#7529](https://github.com/chef/chef/pull/7529) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix issue of setting comment for windows user [#7537](https://github.com/chef/chef/pull/7537) ([NAshwini](https://github.com/NAshwini))
+- Require mixlib-shellout 2.4 or later [#7543](https://github.com/chef/chef/pull/7543) ([tas50](https://github.com/tas50))
+- windows_package: Fix package sensitive error [#7353](https://github.com/chef/chef/pull/7353) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Add cron_d and cron_access resources [#7253](https://github.com/chef/chef/pull/7253) ([tas50](https://github.com/tas50))
+- Update to openssl 1.0.2p [#7546](https://github.com/chef/chef/pull/7546) ([tas50](https://github.com/tas50))
+- Add new openssl resources: ec_private_key, ec_public_key, certificate, and x509_request [#7513](https://github.com/chef/chef/pull/7513) ([tas50](https://github.com/tas50))
+- Fix failed RHEL6 32-bit functional tests [#7555](https://github.com/chef/chef/pull/7555) ([lamont-granquist](https://github.com/lamont-granquist))
+- Restart Python yum helper before each repo enable/disable [#7558](https://github.com/chef/chef/pull/7558) ([cosinusoidally](https://github.com/cosinusoidally))
+- Support for battery power options in windows_task resource [#7483](https://github.com/chef/chef/pull/7483) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- support repeated options in systemd_unit [#7560](https://github.com/chef/chef/pull/7560) ([dbresson](https://github.com/dbresson))
+- Validatorless bootstrap fix [#7562](https://github.com/chef/chef/pull/7562) ([coderanger](https://github.com/coderanger))
+- Pull in the latest InSpec and Ohai [#7563](https://github.com/chef/chef/pull/7563) ([tas50](https://github.com/tas50))
+- switch shell_out to shell_out! in func tests [#7565](https://github.com/chef/chef/pull/7565) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update to Ohai 14.4.2 [#7570](https://github.com/chef/chef/pull/7570) ([tas50](https://github.com/tas50))
+- lazy the default resource_name until after parsing [#7566](https://github.com/chef/chef/pull/7566) ([lamont-granquist](https://github.com/lamont-granquist))
+- Modernize our Rakefile / Version bumping system [#7574](https://github.com/chef/chef/pull/7574) ([tas50](https://github.com/tas50))
+- Fix rake task to build the correct gemspec on Chef [#7579](https://github.com/chef/chef/pull/7579) ([tas50](https://github.com/tas50))
+- Pull in latest omnibus definitions + new inspec/train [#7581](https://github.com/chef/chef/pull/7581) ([tas50](https://github.com/tas50))
+- Simplify / fix our yard doc Rake task [#7580](https://github.com/chef/chef/pull/7580) ([tas50](https://github.com/tas50))
+- Update the announcement rake task with kitchen examples [#7583](https://github.com/chef/chef/pull/7583) ([tas50](https://github.com/tas50))
+- Add openssl_x509_crl resource and fix default modes in x509_certificate / x509_request [#7586](https://github.com/chef/chef/pull/7586) ([tas50](https://github.com/tas50))
+- Add missing description to windows_feature_powershell [#7587](https://github.com/chef/chef/pull/7587) ([tas50](https://github.com/tas50))
+- Resolve new_resource error with cron_d resource [#7588](https://github.com/chef/chef/pull/7588) ([tas50](https://github.com/tas50))
+- openssl resources: Improve descriptions and fix provides for Chef 14.X [#7590](https://github.com/chef/chef/pull/7590) ([tas50](https://github.com/tas50))
+- Be more explicit in disabling provides for openssl_x509 [#7591](https://github.com/chef/chef/pull/7591) ([tas50](https://github.com/tas50))
+
+## [v14.3.37](https://github.com/chef/chef/tree/v14.3.37) (2018-07-11)
+
+#### Merged Pull Requests
+- Expand development docs with branch/backport + more [#7343](https://github.com/chef/chef/pull/7343) ([tas50](https://github.com/tas50))
+- Add skip_publisher_check property to powershell_package [#7259](https://github.com/chef/chef/pull/7259) ([Happycoil](https://github.com/Happycoil))
+- Support windows_feature_powershell on Windows 2008 R2 [#7349](https://github.com/chef/chef/pull/7349) ([tas50](https://github.com/tas50))
+- Bump the version to 14.3.0 [#7346](https://github.com/chef/chef/pull/7346) ([tas50](https://github.com/tas50))
+- Deprecated the Chef::Provider::Package::Freebsd::Pkg provider [#7350](https://github.com/chef/chef/pull/7350) ([tas50](https://github.com/tas50))
+- Make shell_out_compact automatically pull timeouts off the resource + remove uses of shell_out_compact_timeout [#7330](https://github.com/chef/chef/pull/7330) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add whyrun message when installing a local file on Windows [#7351](https://github.com/chef/chef/pull/7351) ([josh-barker](https://github.com/josh-barker))
+- Implement rfc107: NodeMap locking for resource and provider handlers [#7224](https://github.com/chef/chef/pull/7224) ([coderanger](https://github.com/coderanger))
+- Update help link in Add/Remove Programs on Windows [#7345](https://github.com/chef/chef/pull/7345) ([stuartpreston](https://github.com/stuartpreston))
+- Add ssh_known_hosts_entry resource from ssh_known_hosts cookbook [#7161](https://github.com/chef/chef/pull/7161) ([tas50](https://github.com/tas50))
+- Add kernel_module resource from the kernel_module cookbook [#7165](https://github.com/chef/chef/pull/7165) ([tas50](https://github.com/tas50))
+- Mount: Fix errors on Windows when using the mount_point property [#7284](https://github.com/chef/chef/pull/7284) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Update to the latest inspec and liblzma [#7355](https://github.com/chef/chef/pull/7355) ([tas50](https://github.com/tas50))
+- Add missing chef/resource requires in resource [#7364](https://github.com/chef/chef/pull/7364) ([tas50](https://github.com/tas50))
+- package: Make sure to use the package_name name properties [#7365](https://github.com/chef/chef/pull/7365) ([tas50](https://github.com/tas50))
+- windows_feature_dism: Fix errors when specifying the source [#7370](https://github.com/chef/chef/pull/7370) ([tas50](https://github.com/tas50))
+- Add more property descriptions to resources [#7358](https://github.com/chef/chef/pull/7358) ([tas50](https://github.com/tas50))
+- removing mwrock from client maintainers [#7369](https://github.com/chef/chef/pull/7369) ([mwrock](https://github.com/mwrock))
+- Pull in win32-taskscheduler 1.0.2 [#7371](https://github.com/chef/chef/pull/7371) ([tas50](https://github.com/tas50))
+- windows_task: Don&#39;t allow bad username/password to be provided to a task which will fail later [#7288](https://github.com/chef/chef/pull/7288) ([Vasu1105](https://github.com/Vasu1105))
+- windows_task: Fix for task is not idempotent when task name includes parent folder [#7293](https://github.com/chef/chef/pull/7293) ([Vasu1105](https://github.com/Vasu1105))
+- Remove awesome customers testing and update kitchen configs [#7377](https://github.com/chef/chef/pull/7377) ([tas50](https://github.com/tas50))
+- Silence deprecation warnings [#7375](https://github.com/chef/chef/pull/7375) ([coderanger](https://github.com/coderanger))
+- Remove the unused audit test cookbook [#7378](https://github.com/chef/chef/pull/7378) ([tas50](https://github.com/tas50))
+- Unification of shell_out APIs [#7372](https://github.com/chef/chef/pull/7372) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rework the credentials file system to support any config keys. [#7387](https://github.com/chef/chef/pull/7387) ([coderanger](https://github.com/coderanger))
+- deprecate old shell_out APIs [#7382](https://github.com/chef/chef/pull/7382) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add missing knife license headers [#7397](https://github.com/chef/chef/pull/7397) ([tas50](https://github.com/tas50))
+- Add chocolatey_config and chocolatey_source resources [#7388](https://github.com/chef/chef/pull/7388) ([tas50](https://github.com/tas50))
+- Remove the existing acceptance testing framework [#7399](https://github.com/chef/chef/pull/7399) ([tas50](https://github.com/tas50))
+- Remove sudo/gcc-c++ package installs from kitchen tests [#7398](https://github.com/chef/chef/pull/7398) ([tas50](https://github.com/tas50))
+- Add missing require knife [#7400](https://github.com/chef/chef/pull/7400) ([tas50](https://github.com/tas50))
+- Switch powershell_exec mixin to use FFI instead of COM [#7380](https://github.com/chef/chef/pull/7380) ([stuartpreston](https://github.com/stuartpreston))
+- Rename the kitchen base test suite to end-to-end [#7385](https://github.com/chef/chef/pull/7385) ([tas50](https://github.com/tas50))
+- Pull in new InSpec and win32-service [#7405](https://github.com/chef/chef/pull/7405) ([tas50](https://github.com/tas50))
+- Chefstyle fixes [#7414](https://github.com/chef/chef/pull/7414) ([lamont-granquist](https://github.com/lamont-granquist))
+- More chefstyle updates [#7415](https://github.com/chef/chef/pull/7415) ([lamont-granquist](https://github.com/lamont-granquist))
+- chefstyle: fix Style/MutableConstant [#7417](https://github.com/chef/chef/pull/7417) ([lamont-granquist](https://github.com/lamont-granquist))
+- knife config and a bunch of UX improvements [#7390](https://github.com/chef/chef/pull/7390) ([coderanger](https://github.com/coderanger))
+- fix some chefstyle offenses [#7427](https://github.com/chef/chef/pull/7427) ([lamont-granquist](https://github.com/lamont-granquist))
+- Don&#39;t require rubygems in our binaries [#7428](https://github.com/chef/chef/pull/7428) ([tas50](https://github.com/tas50))
+- bump chefstyle + inspec-core [#7431](https://github.com/chef/chef/pull/7431) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix dupe stdout_logger [#7401](https://github.com/chef/chef/pull/7401) ([nsdavidson](https://github.com/nsdavidson))
+- Prevent failures using windows_feature due to the platform helper [#7433](https://github.com/chef/chef/pull/7433) ([tas50](https://github.com/tas50))
+- Bump Ohai to 14.3.0 [#7437](https://github.com/chef/chef/pull/7437) ([tas50](https://github.com/tas50))
+- Enable Amazon Linux 2.0 tests again [#7442](https://github.com/chef/chef/pull/7442) ([tas50](https://github.com/tas50))
+- Add missing descriptions and add periods after resource the descriptions [#7444](https://github.com/chef/chef/pull/7444) ([tas50](https://github.com/tas50))
+- Attributes -&gt; Properties in a few more resources [#7448](https://github.com/chef/chef/pull/7448) ([tas50](https://github.com/tas50))
+
+## [v14.2.0](https://github.com/chef/chef/tree/v14.2.0) (2018-06-07)
+
+#### Merged Pull Requests
+- publish habitat packages [#7272](https://github.com/chef/chef/pull/7272) ([thommay](https://github.com/thommay))
+- improved regex accuracy lib/chef/resource/hostname.rb [#7262](https://github.com/chef/chef/pull/7262) ([bottkv488](https://github.com/bottkv488))
+- Add additional unit tests for resource actions/properties [#7266](https://github.com/chef/chef/pull/7266) ([tas50](https://github.com/tas50))
+- Add additional resource unit tests [#7275](https://github.com/chef/chef/pull/7275) ([tas50](https://github.com/tas50))
+- Add default_action to the resource inspector [#7276](https://github.com/chef/chef/pull/7276) ([tas50](https://github.com/tas50))
+- UID now starts at 501, uses createhomedir instead [#4903](https://github.com/chef/chef/pull/4903) ([nmcspadden](https://github.com/nmcspadden))
+- [MSYS-817] fix for windows_task does not parse backslashes in the commad property [#7281](https://github.com/chef/chef/pull/7281) ([Vasu1105](https://github.com/Vasu1105))
+- object validation for DataHandlerBase#normalize_hash [#7264](https://github.com/chef/chef/pull/7264) ([jeremymv2](https://github.com/jeremymv2))
+- Fix manifest entries for root files [#7270](https://github.com/chef/chef/pull/7270) ([thommay](https://github.com/thommay))
+- Fix systemd_unit user context [#7274](https://github.com/chef/chef/pull/7274) ([mal](https://github.com/mal))
+- Cleanup AIX and Solaris user resources. [#7249](https://github.com/chef/chef/pull/7249) ([lamont-granquist](https://github.com/lamont-granquist))
+- Cookbook Version: add host-&lt;fqdn&gt; to the error message for templates and files specificity [#7295](https://github.com/chef/chef/pull/7295) ([lamont-granquist](https://github.com/lamont-granquist))
+- add default_env flag to shell_out and execute resource [#7298](https://github.com/chef/chef/pull/7298) ([lamont-granquist](https://github.com/lamont-granquist))
+- Properly print path to config file to the screen in knife configure [#7325](https://github.com/chef/chef/pull/7325) ([tas50](https://github.com/tas50))
+- windows_ad_join: Ensure that reboot requests work [#7328](https://github.com/chef/chef/pull/7328) ([thommay](https://github.com/thommay))
+- Better errors with uninstalled official knife plugins [#7326](https://github.com/chef/chef/pull/7326) ([tas50](https://github.com/tas50))
+- convert a_to_s to shell_out_compact in DNF/yum [#7313](https://github.com/chef/chef/pull/7313) ([lamont-granquist](https://github.com/lamont-granquist))
+- Support signing with ssh-agent [#7324](https://github.com/chef/chef/pull/7324) ([coderanger](https://github.com/coderanger))
+- fix yum versionlock quoting [#7329](https://github.com/chef/chef/pull/7329) ([lamont-granquist](https://github.com/lamont-granquist))
+- resource_inspector: Add default values for properties [#7300](https://github.com/chef/chef/pull/7300) ([thommay](https://github.com/thommay))
+- Check local file exists before installing a windows package [#7299](https://github.com/chef/chef/pull/7299) ([josh-barker](https://github.com/josh-barker))
+- Fix :configure_startup action to configure delayed start [#7297](https://github.com/chef/chef/pull/7297) ([jasonwbarnett](https://github.com/jasonwbarnett))
+- Allow securable resource tests to work on Windows 10 machines connected to an Azure Active Directory [#7301](https://github.com/chef/chef/pull/7301) ([stuartpreston](https://github.com/stuartpreston))
+- Quote git remote_url property (PR 6249 + chefstyle fix) [#7014](https://github.com/chef/chef/pull/7014) ([tas50](https://github.com/tas50))
+- Use inspec-core, new ffi gem, and bump deps [#7332](https://github.com/chef/chef/pull/7332) ([lamont-granquist](https://github.com/lamont-granquist))
+- bump ohai to 14.2.0 [#7333](https://github.com/chef/chef/pull/7333) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v14.1.12](https://github.com/chef/chef/tree/v14.1.12) (2018-05-16)
+
+#### Merged Pull Requests
+- Remove redundant &quot;?&quot; in knife configure [#7235](https://github.com/chef/chef/pull/7235) ([alexymik](https://github.com/alexymik))
+- 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))
+- 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))
+- 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))
+- remote_directory: restore overwrite default [#7254](https://github.com/chef/chef/pull/7254) ([rmoriz](https://github.com/rmoriz))
+- apt_repository: Use the repo_name name property [#7244](https://github.com/chef/chef/pull/7244) ([tas50](https://github.com/tas50))
+- Update Habitat plan to correctly build [#6111](https://github.com/chef/chef/pull/6111) ([elliott-davis](https://github.com/elliott-davis))
+- Update Ohai to 14.1.3 [#7258](https://github.com/chef/chef/pull/7258) ([tas50](https://github.com/tas50))
+- Fix windows_task resource not handling commands with arguments [#7250](https://github.com/chef/chef/pull/7250) ([Vasu1105](https://github.com/Vasu1105))
+- Update win32-taskscheduler gem to fix creating tasks as the SYSTEM user [#7265](https://github.com/chef/chef/pull/7265) ([tas50](https://github.com/tas50))
+- Use some unique task names for windows_task functional tests [#7267](https://github.com/chef/chef/pull/7267) ([btm](https://github.com/btm))
+
+## [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))
+
+## [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.12.14](https://github.com/chef/chef/tree/v13.12.14) (2019-03-07)
+
+#### Merged Pull Requests
+- Update openssl to 1.0.2q and bring in latest omnibus-software [#8088](https://github.com/chef/chef/pull/8088) ([tas50](https://github.com/tas50))
+- Update Chef 13 to the latest gem deps [#8145](https://github.com/chef/chef/pull/8145) ([tas50](https://github.com/tas50))
+- Update omnibus Chef dep to 14.8 [#8146](https://github.com/chef/chef/pull/8146) ([tas50](https://github.com/tas50))
+- Fix for Property deprecations are broken in Chef 13 [#8132](https://github.com/chef/chef/pull/8132) ([kapilchouhan99](https://github.com/kapilchouhan99))
+- Update knife bootstrap template to use up to date omnitruck URL [#8208](https://github.com/chef/chef/pull/8208) ([tas50](https://github.com/tas50))
+- Update nokogiri to 1.10.1 [#8218](https://github.com/chef/chef/pull/8218) ([tas50](https://github.com/tas50))
+- Update Ohai to 13.12.6 [#8223](https://github.com/chef/chef/pull/8223) ([tas50](https://github.com/tas50))
+- Chef-13: add lazy module include to universal DSL [#8247](https://github.com/chef/chef/pull/8247) ([lamont-granquist](https://github.com/lamont-granquist))
+- Update libxml2 to 2.9.9 [#8239](https://github.com/chef/chef/pull/8239) ([tas50](https://github.com/tas50))
+- Update openssl to 1.0.2r and rubygems to 2.7.9 [#8280](https://github.com/chef/chef/pull/8280) ([tas50](https://github.com/tas50))
+- mount: Add proper new lines when on AIX to prevent failures [#8281](https://github.com/chef/chef/pull/8281) ([tas50](https://github.com/tas50))
+
+## [v13.12.3](https://github.com/chef/chef/tree/v13.12.3) (2018-11-01)
+
+#### Merged Pull Requests
+- Backport omnibus cleanup + MSI speedup logic from Chef 14 [#7739](https://github.com/chef/chef/pull/7739) ([tas50](https://github.com/tas50))
+- Bump dependencies / slim the package size [#7805](https://github.com/chef/chef/pull/7805) ([tas50](https://github.com/tas50))
+- Pin rake to 12.0 to prevent shipping 2 copies [#7812](https://github.com/chef/chef/pull/7812) ([tas50](https://github.com/tas50))
+- Update Ohai to 13.12.4 [#7817](https://github.com/chef/chef/pull/7817) ([tas50](https://github.com/tas50))
+- Backport: Throw better error on invalid resources actions [#7836](https://github.com/chef/chef/pull/7836) ([tas50](https://github.com/tas50))
+
+## [v13.11.3](https://github.com/chef/chef/tree/v13.11.3) (2018-09-26)
+
+#### Merged Pull Requests
+- Update to openssl 1.0.2p [#7547](https://github.com/chef/chef/pull/7547) ([tas50](https://github.com/tas50))
+- Use the existing helper method for package resource classes that don&#39;t support allow_downgrade [#7548](https://github.com/chef/chef/pull/7548) ([tas50](https://github.com/tas50))
+- windows_service: Remove potentially sensitive info from the log [#7688](https://github.com/chef/chef/pull/7688) ([tas50](https://github.com/tas50))
+- Improve the error message when knife bootstrap windows isn&#39;t installed [#7686](https://github.com/chef/chef/pull/7686) ([tas50](https://github.com/tas50))
+- Fix remote_directory does not obey removal of file specificity [#7687](https://github.com/chef/chef/pull/7687) ([tas50](https://github.com/tas50))
+- windows_package: Avoid exposing sensitive data during package install failures if sensitive property set [#7684](https://github.com/chef/chef/pull/7684) ([tas50](https://github.com/tas50))
+- osx_profile: Use the full path to /usr/bin/profiles [#7683](https://github.com/chef/chef/pull/7683) ([tas50](https://github.com/tas50))
+
+## [v13.10.4](https://github.com/chef/chef/tree/v13.10.4) (2018-08-08)
+
+#### Merged Pull Requests
+- Check local file exists before installing a windows package [#7341](https://github.com/chef/chef/pull/7341) ([josh-barker](https://github.com/josh-barker))
+- Backport for 13: scrub tempfile names [#7526](https://github.com/chef/chef/pull/7526) ([tyler-ball](https://github.com/tyler-ball))
+- Pin to rspec to &lt; 3.8 [#7528](https://github.com/chef/chef/pull/7528) ([cheeseplus](https://github.com/cheeseplus))
+- [SHACK-290] Unpacking tarball paths suffer from URI error [#7522](https://github.com/chef/chef/pull/7522) ([tyler-ball](https://github.com/tyler-ball))
+
+## [v13.10.0](https://github.com/chef/chef/tree/v13.10.0) (2018-07-11)
+
+#### Merged Pull Requests
+- Trying to use --recipe-url on Windows with local file fails [#7426](https://github.com/chef/chef/pull/7426) ([tyler-ball](https://github.com/tyler-ball))
+- Pull in latest win32-service gem [#7432](https://github.com/chef/chef/pull/7432) ([tas50](https://github.com/tas50))
+- Bump Ohai to 13.10.0 [#7438](https://github.com/chef/chef/pull/7438) ([tas50](https://github.com/tas50))
+- Backport duplicate logger fix [#7447](https://github.com/chef/chef/pull/7447) ([btm](https://github.com/btm))
+- Bump to 13.10 and add release notes [#7454](https://github.com/chef/chef/pull/7454) ([tas50](https://github.com/tas50))
+
+## [v13.9.4](https://github.com/chef/chef/tree/v13.9.4) (2018-06-07)
+
+#### Merged Pull Requests
+- Update nokogiri, ruby, and openssl for CVEs [#7232](https://github.com/chef/chef/pull/7232) ([tas50](https://github.com/tas50))
+- Backport Ubuntu 18.04 fixes [#7280](https://github.com/chef/chef/pull/7280) ([thommay](https://github.com/thommay))
+- Chef-13: Bump ffi to 1.9.25 along with the rest of things [#7337](https://github.com/chef/chef/pull/7337) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v13.9.1](https://github.com/chef/chef/tree/v13.9.1) (2018-05-08)
+
+#### Merged Pull Requests
+- Backport RFC-101/RFC-104 resource enhancements [#6964](https://github.com/chef/chef/pull/6964) ([thommay](https://github.com/thommay))
+- Pass pointer to LsaFreeMemory, not FFI::MemoryPointer [#6991](https://github.com/chef/chef/pull/6991) ([btm](https://github.com/btm))
+- Backport mount provider fixes to 13 [#7007](https://github.com/chef/chef/pull/7007) ([thommay](https://github.com/thommay))
+- [chef-13] support nils because of course [#7017](https://github.com/chef/chef/pull/7017) ([thommay](https://github.com/thommay))
+- Empty commit to trigger a release build [#7040](https://github.com/chef/chef/pull/7040) ([btm](https://github.com/btm))
+- partially revert 61e3d4bb: do not use properties for mount [#7031](https://github.com/chef/chef/pull/7031) ([thommay](https://github.com/thommay))
+- Bump dependencies to bring in Ohai 13.9 [#7135](https://github.com/chef/chef/pull/7135) ([tas50](https://github.com/tas50))
+- Windows MSI: files are now re-unzipped during repair mode (Backport to Chef 13) [#7112](https://github.com/chef/chef/pull/7112) ([stuartpreston](https://github.com/stuartpreston))
+- Don&#39;t always request lazy files [#7216](https://github.com/chef/chef/pull/7216) ([thommay](https://github.com/thommay))
+- 13.9 Release notes [#7218](https://github.com/chef/chef/pull/7218) ([thommay](https://github.com/thommay))
+- RFC-102: Deprecation warning in resources [#7219](https://github.com/chef/chef/pull/7219) ([thommay](https://github.com/thommay))
+
+## [v13.8.5](https://github.com/chef/chef/tree/v13.8.5) (2018-03-07)
+
+#### Merged Pull Requests
+- [knife] Don&#39;t crash when a deprecated cookbook has no replacement (#6853) [#6936](https://github.com/chef/chef/pull/6936) ([tas50](https://github.com/tas50))
+- lock ffi at 1.9.21 [#6960](https://github.com/chef/chef/pull/6960) ([thommay](https://github.com/thommay))
+
+## [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)
@@ -680,7 +3455,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
@@ -766,32 +3541,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
@@ -904,7 +3679,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):
@@ -942,7 +3717,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
@@ -1012,7 +3787,7 @@ of partial templates.
* windows_user: look up username instead of resource name (Issue #1705)
* Remove the unused bootstrap templates that install chef from rubygems
* Remove the Chef 10 functionality from bootstrap.
-* Deprecate --distro / --template_file options in favor of --boostrap-template
+* Deprecate --distro / --template_file options in favor of --bootstrap-template
* Add `:node_ssl_verify_mode` & `:node_verify_api_cert` options to bootstrap
to be able to configure these settings on the bootstrapped node.
* Add partial_search dsl method to Chef::Search::Query, add result filtering to search.
@@ -1195,5 +3970,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..e623e4a948 100644
--- a/CHEF_MVPS.md
+++ b/CHEF_MVPS.md
@@ -1,22 +1,78 @@
-### Chef is proud of our community!
+# Chef is proud of our community
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
+## Lifetime Community Chef Award
+
+The Lifetime Community Chef Award is a recognition of years of investment by a member of the community, and is awarded only occasionally.
+
+- [2020](https://blog.chef.io/congratulations-awesome-community-chefs-2020/)
+ - [Mandi Walls](https://github.com/lnxchk)
+- [2019](https://blog.chef.io/congratulations-to-our-2019-awesome-community-chefs/)
+ - [Nathen Harvey](https://github.com/NathenHarvey)
+
+## Awesome Community Chefs
+
+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.
+
+- [2020](https://blog.chef.io/congratulations-awesome-community-chefs-2020/)
+ - [Bastien Jove](https://github.com/tensibai)
+ - [Lance Albertson](https://github.com/ramereth)
+ - [Marc Chamberland](https://github.com/bobchaos)
+- [2019][USA](https://blog.chef.io/congratulations-to-our-2019-awesome-community-chefs/), [Europe](https://blog.chef.io/congratulations-to-our-chefconf-london-2019-award-winners/)
+ - [Graham Weldon](https://github.com/predominant)
+ - [Jason Field](https://github.com/xorima)
+ - [Joshua Basch](https://github.com/HT154)
+ - [Karl Fischer](https://github.com/kmf)
+ - [Nathen Harvey](https://github.com/NathenHarvey)
+ - [Robb Kidd](https://github.com/RobbKidd)
+ - [Tomas Heinen](https://github.com/tecracer-theinen)
+- [2018](https://blog.chef.io/2018/05/24/2018-awesome-community-chefs/)
+ - [Dan Webb](https://github.com/damacus)
+ - [Romain Sertelon](https://github.com/rsertelon)
+ - [Edmund Haselwanter](https://github.com/ehaselwanter)
+ - [Tim Smith](https://github.com/tas50)
+ - [Joshua Timberman](https://github.com/jtimberman)
+- [2017](https://blog.chef.io/2017/06/08/awesome-community-chefs-2017-award-winners/)
+ - [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)
+- [2016](https://blog.chef.io/2016/08/31/awesome-community-chefs-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)
+- 2015
+ - [Jon Cowie](https://github.com/jonlives)
+ - [Noah Kantrowitz](https://github.com/coderanger)
+ - [Matt Wrock](https://github.com/mwrock)
+- 2014
+ - [Ranjib Dey](https://github.com/ranjib)
+ - [Miah Johnson](https://github.com/miah)
+ - [Seth Vargo](https://github.com/sethvargo)
+ - [Eric Wolfe](https://github.com/atomic-penguin)
+- 2013
+ - [Bryan Berry](https://github.com/bryanwb)
+ - [Fletcher Nichol](https://github.com/fnichol)
+ - [Jamie Winsor](https://github.com/reset)
+
+## Hall of Fame
After receiving three MVP awards, we add someone to the hall of fame. We want to express our gratitude to their continuing participation and give newer community members the opportunity to be recognized.
-* Matthew Kent
-* Doug MacEachern
-* Tollef Fog Heen
-* Thom May
-* Bryan Berry
-* Bryan McLellan
-* Jeff Blaine
+- Matthew Kent
+- Doug MacEachern
+- Tollef Fog Heen
+- Thom May
+- Bryan Berry
+- Bryan McLellan
+- Jeff Blaine
-#### The MVP recipients
+## The MVP recipients
| Release | Date | MVP |
|---------|------|-----|
@@ -89,20 +145,3 @@ After receiving three MVP awards, we add someone to the hall of fame. We want to
| [Chef 0.5.4](https://www.chef.io/blog/2009/02/13/chef-0-5-4/) | 2009-02-13 | Arjuna Christensen |
| [Chef 0.5.2](https://www.chef.io/blog/2009/02/01/chef-0-5-2-and-ohai-0-1-4/) | 2009-02-01 | Bryan McLellan |
-#### 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.
-
-* 2013
- * [Bryan Berry](https://github.com/bryanwb)
- * [Fletcher Nichol](https://github.com/fnichol)
- * [Jamie Winsor](https://github.com/reset)
-* 2014
- * [Ranjib Dey](https://github.com/ranjib)
- * [Miah Johnson](https://github.com/miah)
- * [Seth Vargo](https://github.com/sethvargo)
- * [Eric Wolfe](https://github.com/atomic-penguin)
-* 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
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/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..980c1341a4
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1 @@
+Please refer to the Chef Community Code of Conduct at https://www.chef.io/code-of-conduct/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4f9f527381..2ccfb550ba 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,58 +1,102 @@
-# 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.
+
+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 [Chef GitHub organization](https://github.com/chef/). All projects include GitHub issue templates to help gather information needed for a thorough review.
+
+We ask you not to submit security concerns via GitHub. For details on submitting potential security issues please see <https://www.chef.io/security/>
+
+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://www.chef.io/feedback/>
## Contribution Process
-We have a 3 step process that utilizes **Github Issues**:
+We have a 3 step process for contributions:
-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.
+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.
-### Chef Pull Requests
+### Pull Request Requirements
-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:
+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:
-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.
+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 [Buildkite](https://buildkite.com/chef-oss) to test all pull requests. We require these test runs to succeed on every pull request before being merged.
-### Chef Code Review Process
+### Code Review Process
-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.
+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.
-Once you open 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:
+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:
-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.
+1. Two or more members of the owners, approvers, or reviewers groups must approve your PR. See the [Chef Infra OSS Project](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md) for a list of all members.
+2. Your change will be merged into the project's `master` branch
+3. Our Expeditor bot will automatically increment the version and update the project's changelog with your contribution. For projects that ship as a package, Expeditor will kick off a build which will publish the package to the project's `current` channel.
-If you would like to learn about when your code will be available in a release of Chef, read more about [Chef Release Cycles](#chef-release-cycles).
+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).
-### Contributor License Agreement (CLA)
+### Developer Certification of Origin (DCO)
Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired.
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.
-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.
+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.
+
+To make a good faith effort to ensure these criteria are met, Chef requires the Developer Certificate of Origin (DCO) process to be followed.
+
+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/>.
+
+```
+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.
+```
+
+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/)
+
+#### DCO Sign-Off Methods
-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.
+The DCO requires a sign-off message in the following format appear on each commit in the pull request:
-It only takes a few minutes to complete a CLA, and you retain the copyright to your contribution.
+```
+Signed-off-by: Julia Child <juliachild@chef.io>
+```
-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.
+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 are using the GitHub UI to make a change you can add the sign-off message directly to the commit message when creating the pull request. 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 Obvious Fix Policy
-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.
+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.
-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:
+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:
- Spelling / grammar fixes
- Typo correction, white space and formatting changes
@@ -67,40 +111,21 @@ As a rule of thumb, changes are obvious fixes if they do not introduce any new f
```
------------------------------------------------------------------------
commit 370adb3f82d55d912b0cf9c1d1e99b132a8ed3b5
-Author: danielsdeleo <dan@chef.io>
-Date: Wed Sep 18 11:44:40 2013 -0700
+Author: Julia Child <juliachild@chef.io>
+Date: Wed Sep 18 11:44:40 2015 -0700
- Fix typo in config file docs.
+ Fix typo in the README.
Obvious fix.
------------------------------------------------------------------------
```
-## Chef Issue Tracking
-
-Chef Issue Tracking is handled using Github Issues.
-
-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/).
-
-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).
-
-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.
-
-### 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)
-
-## Chef Release Cycles
+## 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)
+Our primary shipping vehicle is operating system specific packages that includes all the requirements of Chef. The packages are built with our [Omnibus](https://github.com/chef/omnibus) packing project.
-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.
+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 thoroughly.
Our version numbering roughly follows [Semantic Versioning](http://semver.org/) standard. Our standard version numbers look like X.Y.Z which mean:
@@ -108,9 +133,9 @@ Our version numbering roughly follows [Semantic Versioning](http://semver.org/)
- Y is a minor release, which adds both new features and bug fixes
- Z is a patch release, which adds just bug fixes
-After shipping a release of Chef we bump the `Minor` version by one to start development of the next minor releaae. 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.
+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 shipping 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 made to the [chef mailing list](https://discourse.chef.io/c/chef-release) 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 and are mirrored to the #announcements channel on the [Chef Community Slack](https://community-slack.chef.io/).
## Chef Community
@@ -123,4 +148,5 @@ 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 Software Inc. Website](https://www.chef.io/)
+- [Chef Project Website](https://www.chef.sh/)
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
deleted file mode 100644
index fadd382cbb..0000000000
--- a/DOC_CHANGES.md
+++ /dev/null
@@ -1,9 +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.13
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000..ae06e5ba0a
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+# About this Dockerfile:
+# When run without any arguments passed, this Docker file will build the latest "stable" release of Chef. The version
+# of that release is specified in the VERSION arg in this file, and is automatically updated as described below.
+#
+# Several processes occur using this file which are kicked off by our Expeditor pipeline tooling:
+#
+# When a build makes it through our internal CI system and is promoted to our "unstable" channel Expeditor will
+# trigger a Docker image build of that version and push it to Docker Hub.
+#
+# When tests of an unstable build pass within our CI system it will be promoted to the "current" channel and
+# Expeditor will tag that image as "current" on Docker Hub.
+#
+# When a build is promoted to our "stable" channel .expeditor/update_dockerfile.sh is run to update the version
+# in this file and also tag that image as "latest" on Docker Hub. Additionally major and minor tags will be
+# applied so 15.0.260 would be tagged as "latest", "stable", "15" and "15.0", as well as "15.0.260".
+
+FROM busybox
+LABEL maintainer="Chef Software, Inc. <docker@chef.io>"
+
+ARG CHANNEL=stable
+ARG VERSION=16.8.14
+
+RUN wget "http://packages.chef.io/files/${CHANNEL}/chef/${VERSION}/el/7/chef-${VERSION}-1.el7.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 003df8ee15..99e7cf3c6b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,3 @@
-# This buys us the ability to be included in other Gemfiles
-require_relative "tasks/gemfile_util"
-extend GemfileUtil
-
source "https://rubygems.org"
# Note we do not use the gemspec DSL which restricts to the
@@ -11,85 +7,80 @@ source "https://rubygems.org"
# 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", group: [ :default, :omnibus_package, :development ]
-gem "bundler"
-gem "cheffish"
+gem "ohai", git: "https://github.com/chef/ohai.git", branch: "master"
+
+gem "chef-utils", path: File.expand_path("chef-utils", __dir__) if File.exist?(File.expand_path("chef-utils", __dir__))
+gem "chef-config", path: File.expand_path("chef-config", __dir__) if File.exist?(File.expand_path("chef-config", __dir__))
+
+if File.exist?(File.expand_path("chef-bin", __dir__))
+ # bundling in a git checkout
+ gem "chef-bin", path: File.expand_path("chef-bin", __dir__)
+else
+ # bundling in omnibus
+ gem "chef-bin" # rubocop:disable Bundler/DuplicatedGem
+end
+
+gem "cheffish", ">= 14"
+
+gem "chef-telemetry", ">=1.0.8" # 1.0.8 removes the http dep
group(:omnibus_package) do
gem "appbundler"
gem "rb-readline"
- gem "nokogiri"
+ gem "inspec-core-bin", "~> 4.24" # need to provide the binaries for inspec
+ 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 "poise-boiler", git: "https://github.com/poise/poise-boiler"
- gem "knife-windows"
- gem "foodcritic"
-
- # We pin this so nobody brings in a cucumber-core incompatible with cucumber latest
- gem "cucumber", ">= 2.4.0"
- # We pin oc-chef-pedant to prevent it from updating out of lockstep with chef-zero
- gem "oc-chef-pedant", git: "https://github.com/chef/chef-server"
-end
-
-group(:docgen) do
- gem "yard"
-end
-
-group(:maintenance) do
- gem "tomlrb"
-
- # To sync maintainers with github
- gem "octokit"
- gem "netrc"
-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
- gem "ruby-prof"
+group(:ruby_prof) do
+ # ruby-prof 1.3.0 does not compile on our centos6 builders/kitchen testers
+ gem "ruby-prof", "< 1.3.0"
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 "simplecov"
+ gem "rake"
+ gem "rspec"
+ gem "webmock"
+ gem "fauxhai-ng" # for chef-utils gem
+end
+group(:chefstyle) do
# 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"
- 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("./Gemfile.local") if File.exist?("./Gemfile.local")
+
+# These lines added for Windows development only.
+# For FFI to call into PowerShell we need the binaries and assemblies located
+# in the Ruby bindir.
+# The Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
+# Every merge into that repo triggers a Habitat build and promotion. Running
+# the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
+# the built packages and copy the binaries to distro/ruby_bin_folder.
+#
+# We copy (and overwrite) these files every time "bundle <exec|install>" is
+# executed, just in case they have changed.
+if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
+ instance_eval do
+ ruby_exe_dir = RbConfig::CONFIG["bindir"]
+ assemblies = Dir.glob(File.expand_path("distro/ruby_bin_folder/#{ENV["PROCESSOR_ARCHITECTURE"]}", __dir__) + "**/*")
+ FileUtils.cp_r assemblies, ruby_exe_dir, verbose: false unless ENV["_BUNDLER_WINDOWS_DLLS_COPIED"]
+ ENV["_BUNDLER_WINDOWS_DLLS_COPIED"] = "1"
+ end
+end
diff --git a/Gemfile.lock b/Gemfile.lock
index a23bc64c8b..23c9512de6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,534 +1,374 @@
GIT
- remote: https://github.com/chef/chef-server
- revision: 8165dfd0524ee6076eda32727cbd0005c9348150
- specs:
- oc-chef-pedant (2.2.0)
- activesupport (~> 5.0)
- erubis (~> 2.7)
- mixlib-authentication (~> 1.4)
- mixlib-config (~> 2.0)
- mixlib-shellout (>= 1.1)
- net-http-spy (~> 0.2)
- rest-client (>= 1.6)
- rspec (~> 3.2)
- rspec-rerun (~> 1.0)
- rspec_junit_formatter (~> 0.2)
-
-GIT
remote: https://github.com/chef/chefstyle.git
- revision: c36dcbd6c2c21d2e19db77d9fbdf2402d0bacccf
+ revision: a226b7d33c2119003dc47839e8c5559a22bf5ab9
branch: master
specs:
- chefstyle (0.4.0)
- rubocop (= 0.42.0)
-
-GIT
- remote: https://github.com/poise/poise-boiler
- revision: b2faa2bcfd128d5fcd781c2b727d05f6b818d74b
- specs:
- poise-boiler (1.12.1.pre)
- bundler
- chefspec (~> 5.0)
- codeclimate-test-reporter (~> 0.4)
- codecov (~> 0.0, >= 0.0.2)
- foodcritic (~> 7.0)
- fuubar (~> 2.0)
- git (~> 1.2)
- halite (~> 1.2)
- kitchen-docker (>= 2.6.0.rc.0)
- kitchen-ec2 (~> 1.0)
- kitchen-sync (~> 2.1)
- kitchen-vagrant
- mixlib-shellout (>= 1.4, < 3.0)
- poise-profiler (~> 1.0)
- pry
- pry-byebug
- rake (>= 10.4, < 12.0)
- rspec (~> 3.2)
- rspec-its (~> 1.2)
- simplecov (~> 0.9)
- test-kitchen (~> 1.7, >= 1.7.1)
- travis (~> 1.8, >= 1.8.1)
- vagrant-wrapper
- winrm (~> 2.0)
- winrm-fs (~> 1.0)
- yard (~> 0.8)
- yard-classmethods (~> 1.0)
+ chefstyle (1.5.9)
+ rubocop (= 1.7.0)
GIT
- remote: https://github.com/rubysec/bundler-audit.git
- revision: b7123d7b294f244165d9469f22b37a559e235fc2
+ remote: https://github.com/chef/ohai.git
+ revision: b90f861bb77d0364f208947e57a6579c7eb78fae
+ branch: master
specs:
- bundler-audit (0.5.0)
- bundler (~> 1.2)
- thor (~> 0.18)
+ ohai (17.0.3)
+ chef-config (>= 12.8, < 18)
+ chef-utils (>= 16.0, < 18)
+ ffi (~> 1.9)
+ ffi-yajl (~> 2.2)
+ ipaddress
+ mixlib-cli (>= 1.7.0)
+ mixlib-config (>= 2.0, < 4.0)
+ mixlib-log (>= 2.0.1, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ plist (~> 3.1)
+ train-core
+ wmi-lite (~> 1.0)
PATH
remote: .
specs:
- chef (12.14.75)
+ chef (17.0.32)
addressable
- bundler (>= 1.10)
- chef-config (= 12.14.75)
- chef-zero (>= 4.8)
- diff-lcs (~> 1.2, >= 1.2.4)
+ bcrypt_pbkdf (= 1.1.0.rc2)
+ chef-config (= 17.0.32)
+ chef-utils (= 17.0.32)
+ chef-vault
+ chef-zero (>= 14.0.11)
+ diff-lcs (>= 1.2.4, < 1.4.0)
+ ed25519 (~> 1.2)
erubis (~> 2.7)
+ ffi (>= 1.9.25, < 1.14.0)
+ ffi-libarchive (~> 1.0, >= 1.0.3)
ffi-yajl (~> 2.2)
- highline (~> 1.6, >= 1.6.9)
+ highline (>= 1.6.9, < 3)
iniparse (~> 1.4)
- mixlib-archive (>= 0.2.0)
- mixlib-authentication (~> 1.4)
- mixlib-cli (~> 1.7)
- mixlib-log (~> 1.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)
+ inspec-core (~> 4.23)
+ license-acceptance (>= 1.0.5, < 3)
+ mixlib-archive (>= 0.4, < 2.0)
+ mixlib-authentication (>= 2.1, < 4)
+ mixlib-cli (>= 2.1.1, < 3.0)
+ mixlib-log (>= 2.0.3, < 4.0)
+ mixlib-shellout (>= 3.1.1, < 4.0)
+ net-sftp (>= 2.1.2, < 4.0)
+ net-ssh (>= 5.1, < 7)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 17.0)
+ pastel
plist (~> 3.2)
proxifier (~> 1.0)
- 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)
+ train-core (~> 3.2, >= 3.2.28)
+ train-winrm (>= 0.2.5)
+ tty-prompt (~> 0.21)
+ tty-screen (~> 0.6)
+ tty-table (~> 0.11)
uuidtools (~> 2.1.5)
- chef (12.14.75-universal-mingw32)
+ chef (17.0.32-universal-mingw32)
addressable
- bundler (>= 1.10)
- chef-config (= 12.14.75)
- chef-zero (>= 4.8)
- diff-lcs (~> 1.2, >= 1.2.4)
+ bcrypt_pbkdf (= 1.1.0.rc2)
+ chef-config (= 17.0.32)
+ chef-utils (= 17.0.32)
+ chef-vault
+ chef-zero (>= 14.0.11)
+ diff-lcs (>= 1.2.4, < 1.4.0)
+ ed25519 (~> 1.2)
erubis (~> 2.7)
- ffi (~> 1.9)
+ ffi (>= 1.9.25, < 1.14.0)
+ ffi-libarchive (~> 1.0, >= 1.0.3)
ffi-yajl (~> 2.2)
- highline (~> 1.6, >= 1.6.9)
+ highline (>= 1.6.9, < 3)
iniparse (~> 1.4)
- mixlib-archive (>= 0.2.0)
- mixlib-authentication (~> 1.4)
- mixlib-cli (~> 1.7)
- mixlib-log (~> 1.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)
+ inspec-core (~> 4.23)
+ iso8601 (>= 0.12.1, < 0.14)
+ license-acceptance (>= 1.0.5, < 3)
+ mixlib-archive (>= 0.4, < 2.0)
+ mixlib-authentication (>= 2.1, < 4)
+ mixlib-cli (>= 2.1.1, < 3.0)
+ mixlib-log (>= 2.0.3, < 4.0)
+ mixlib-shellout (>= 3.1.1, < 4.0)
+ net-sftp (>= 2.1.2, < 4.0)
+ net-ssh (>= 5.1, < 7)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 17.0)
+ pastel
plist (~> 3.2)
proxifier (~> 1.0)
- 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)
+ train-core (~> 3.2, >= 3.2.28)
+ train-winrm (>= 0.2.5)
+ tty-prompt (~> 0.21)
+ tty-screen (~> 0.6)
+ tty-table (~> 0.11)
uuidtools (~> 2.1.5)
win32-api (~> 1.5.3)
- win32-dir (~> 0.5.0)
+ win32-certstore (~> 0.3)
win32-event (~> 0.6.1)
win32-eventlog (= 0.6.3)
win32-mmap (~> 0.4.1)
win32-mutex (~> 0.4.2)
- win32-process (~> 0.8.2)
- win32-service (~> 0.8.7)
- windows-api (~> 0.4.4)
+ win32-process (~> 0.9)
+ win32-service (>= 2.1.5, < 3.0)
+ win32-taskscheduler (~> 2.0)
wmi-lite (~> 1.0)
PATH
+ remote: chef-bin
+ specs:
+ chef-bin (17.0.32)
+ chef (= 17.0.32)
+
+PATH
remote: chef-config
specs:
- chef-config (12.14.75)
+ chef-config (17.0.32)
addressable
+ chef-utils (= 17.0.32)
fuzzyurl
- mixlib-config (~> 2.0)
- mixlib-shellout (~> 2.0)
+ mixlib-config (>= 2.2.12, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ tomlrb (~> 1.2)
+
+PATH
+ remote: chef-utils
+ specs:
+ chef-utils (17.0.32)
GEM
remote: https://rubygems.org/
specs:
- activesupport (5.0.0.1)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (~> 0.7)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- addressable (2.4.0)
- appbundler (0.9.0)
- mixlib-cli (~> 1.4)
- artifactory (2.3.3)
- ast (2.3.0)
- aws-sdk (2.5.11)
- aws-sdk-resources (= 2.5.11)
- aws-sdk-core (2.5.11)
- jmespath (~> 1.0)
- aws-sdk-resources (2.5.11)
- aws-sdk-core (= 2.5.11)
- aws-sdk-v1 (1.66.0)
- json (~> 1.4)
- nokogiri (>= 1.4.4)
- backports (3.6.8)
- binding_of_caller (0.7.2)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
+ appbundler (0.13.2)
+ mixlib-cli (>= 1.4, < 3.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ ast (2.4.1)
+ bcrypt_pbkdf (1.1.0.rc2)
+ bcrypt_pbkdf (1.1.0.rc2-x64-mingw32)
+ bcrypt_pbkdf (1.1.0.rc2-x86-mingw32)
+ binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
- builder (3.2.2)
- byebug (9.0.5)
- chef-api (0.7.0)
- logify (~> 0.1)
- mime-types
- chef-provisioning (2.0.1)
- cheffish (~> 4.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-fs (~> 1.0)
- chef-provisioning-aws (2.0.0)
- aws-sdk (>= 2.1.26, < 3.0)
- aws-sdk-v1 (>= 1.59.0)
- chef-provisioning (~> 2.0)
- retryable (~> 2.0, >= 2.0.1)
- ubuntu_ami (~> 0.4, >= 0.4.1)
- chef-rewind (0.0.9)
- chef-sugar (3.4.0)
- chef-zero (5.1.0)
+ builder (3.2.4)
+ byebug (11.1.3)
+ chef-telemetry (1.0.14)
+ chef-config
+ concurrent-ruby (~> 1.0)
+ ffi-yajl (~> 2.2)
+ chef-vault (4.1.0)
+ chef-zero (15.0.3)
ffi-yajl (~> 2.2)
- hashie (>= 2.0, < 4.0)
- mixlib-log (~> 1.3)
- rack (~> 2.0)
+ hashie (>= 2.0, < 5.0)
+ mixlib-log (>= 2.0, < 4.0)
+ rack (~> 2.0, >= 2.0.6)
uuidtools (~> 2.1)
- cheffish (4.0.0)
- chef-zero (~> 5.0)
+ cheffish (16.0.12)
+ chef-zero (>= 14.0)
net-ssh
- chefspec (5.0.0)
- chef (>= 12.0)
- fauxhai (~> 3.6)
- rspec (~> 3.0)
- codeclimate-test-reporter (0.6.0)
- simplecov (>= 0.7.1, < 1.0.0)
- codecov (0.1.5)
- json
- simplecov
- url
- coderay (1.1.1)
- colorize (0.8.1)
- concurrent-ruby (1.0.2)
- cucumber (2.4.0)
- builder (>= 2.1.2)
- cucumber-core (~> 1.5.0)
- cucumber-wire (~> 0.0.1)
- diff-lcs (>= 1.1.3)
- gherkin (~> 4.0)
- multi_json (>= 1.7.5, < 2.0)
- multi_test (>= 0.1.2)
- cucumber-core (1.5.0)
- gherkin (~> 4.0)
- cucumber-wire (0.0.1)
- 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)
- domain_name (0.5.20160826)
- unf (>= 0.0.5, < 1.0.0)
+ coderay (1.1.3)
+ concurrent-ruby (1.1.7)
+ crack (0.4.4)
+ debug_inspector (0.0.3)
+ diff-lcs (1.3)
+ ed25519 (1.2.4)
+ erubi (1.10.0)
erubis (2.7.0)
- ethon (0.9.0)
- ffi (>= 1.3.0)
- excon (0.52.0)
- faraday (0.9.2)
+ faraday (1.0.1)
multipart-post (>= 1.2, < 3)
- faraday_middleware (0.10.0)
- faraday (>= 0.7.4, < 0.10)
- fauxhai (3.8.0)
+ fauxhai-ng (8.6.0)
net-ssh
- ffi (1.9.14)
- ffi (1.9.14-x86-mingw32)
- ffi-win32-extensions (1.0.3)
+ ffi (1.13.1)
+ ffi (1.13.1-x64-mingw32)
+ ffi (1.13.1-x86-mingw32)
+ ffi-libarchive (1.0.4)
+ ffi (~> 1.0)
+ ffi-win32-extensions (1.0.4)
ffi
- ffi-yajl (2.3.0)
+ ffi-yajl (2.3.4)
libyajl2 (~> 1.2)
- foodcritic (7.1.0)
- cucumber-core (>= 1.3)
- erubis
- nokogiri (>= 1.5, < 2.0)
- rake
- rufus-lru (~> 1.0)
- treetop (~> 1.4)
- yajl-ruby (~> 1.1)
- fuubar (2.2.0)
- rspec-core (~> 3.0)
- ruby-progressbar (~> 1.4)
fuzzyurl (0.9.0)
- gh (0.14.0)
- addressable
- backports
- faraday (~> 0.8)
- multi_json (~> 1.0)
- net-http-persistent (>= 2.7)
- net-http-pipeline
- gherkin (4.0.0)
- git (1.3.0)
- github_api (0.14.5)
- addressable (~> 2.4.0)
- descendants_tracker (~> 0.0.4)
- faraday (~> 0.8, < 0.10)
- hashie (>= 3.4)
- oauth2 (~> 1.0)
- github_changelog_generator (1.13.1)
- colorize (~> 0.7)
- github_api (~> 0.12)
- rake (>= 10.0)
- gssapi (1.2.0)
+ gssapi (1.3.1)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
- halite (1.3.0)
- bundler
- chef (~> 12.0)
- stove (~> 4.0)
- thor
- hashie (3.4.4)
- highline (1.7.8)
- http-cookie (1.0.2)
- domain_name (~> 0.5)
- httpclient (2.8.2.4)
- i18n (0.7.0)
- inifile (3.0.0)
- iniparse (1.4.2)
+ hashdiff (1.0.1)
+ hashie (3.6.0)
+ highline (2.0.3)
+ httpclient (2.8.3)
+ iniparse (1.5.0)
+ inspec-core (4.24.8)
+ addressable (~> 2.4)
+ chef-telemetry (~> 1.0)
+ faraday (>= 0.9.0, < 1.1)
+ hashie (~> 3.4)
+ license-acceptance (>= 0.2.13, < 3.0)
+ method_source (>= 0.8, < 2.0)
+ mixlib-log (~> 3.0)
+ multipart-post (~> 2.0)
+ parallel (~> 1.9)
+ parslet (~> 1.5)
+ pry (~> 0.13)
+ rspec (~> 3.9.0)
+ rspec-its (~> 1.2)
+ rubyzip (~> 1.2, >= 1.2.2)
+ semverse (~> 3.0)
+ sslshake (~> 1.2)
+ thor (>= 0.20, < 2.0)
+ tomlrb (~> 1.2.0)
+ train-core (~> 3.0)
+ tty-prompt (~> 0.17)
+ tty-table (~> 0.10)
+ inspec-core-bin (4.24.8)
+ inspec-core (= 4.24.8)
ipaddress (0.8.3)
- jmespath (1.3.1)
- json (1.8.3)
- jwt (1.5.4)
- kitchen-docker (2.6.0.rc.0)
- test-kitchen (>= 1.0.0)
- kitchen-ec2 (1.1.0)
- aws-sdk (~> 2)
- excon
- multi_json
- retryable (~> 2.0)
- test-kitchen (~> 1.4, >= 1.4.1)
- kitchen-sync (2.1.1)
- net-sftp
- test-kitchen (>= 1.0.0)
- kitchen-vagrant (0.20.0)
- test-kitchen (~> 1.4)
- knife-windows (1.6.0)
- winrm-elevated (~> 1.0)
- launchy (2.4.3)
- addressable (~> 2.3)
+ iso8601 (0.13.0)
+ json (2.4.1)
libyajl2 (1.2.0)
+ license-acceptance (2.1.13)
+ pastel (~> 0.7)
+ tomlrb (>= 1.2, < 3.0)
+ tty-box (~> 0.6)
+ tty-prompt (~> 0.20)
little-plugger (1.1.4)
- logging (2.1.0)
+ logging (2.3.0)
little-plugger (~> 1.1)
- multi_json (~> 1.10)
- logify (0.2.0)
- method_source (0.8.2)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mini_portile2 (2.1.0)
- minitest (5.9.0)
- mixlib-archive (0.2.0)
+ multi_json (~> 1.14)
+ method_source (1.0.0)
+ mixlib-archive (1.0.7)
mixlib-log
- mixlib-authentication (1.4.1)
+ mixlib-archive (1.0.7-universal-mingw32)
mixlib-log
- mixlib-cli (1.7.0)
- mixlib-config (2.2.4)
- mixlib-install (1.1.0)
- artifactory
- mixlib-shellout
- mixlib-versioning
- mixlib-log (1.7.1)
- mixlib-shellout (2.2.7)
- mixlib-shellout (2.2.7-universal-mingw32)
- win32-process (~> 0.8.2)
+ mixlib-authentication (3.0.7)
+ mixlib-cli (2.1.8)
+ mixlib-config (3.0.9)
+ tomlrb
+ mixlib-log (3.0.9)
+ mixlib-shellout (3.2.2)
+ chef-utils
+ mixlib-shellout (3.2.2-universal-mingw32)
+ chef-utils
+ ffi-win32-extensions (~> 1.0.3)
+ win32-process (~> 0.9)
wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- multi_json (1.12.1)
- multi_test (0.1.2)
- multi_xml (0.5.5)
- multipart-post (2.0.0)
- net-http-persistent (2.9.4)
- net-http-pipeline (1.0.1)
- net-http-spy (0.2.1)
- net-scp (1.2.1)
- net-ssh (>= 2.6.5)
- net-sftp (2.1.2)
- net-ssh (>= 2.6.5)
- net-ssh (3.2.0)
- net-ssh-gateway (1.2.0)
- net-ssh (>= 2.6.5)
+ multi_json (1.15.0)
+ multipart-post (2.1.1)
+ net-scp (3.0.0)
+ net-ssh (>= 2.6.5, < 7.0.0)
+ net-sftp (3.0.0)
+ net-ssh (>= 5.0.0, < 7.0.0)
+ net-ssh (6.1.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.8)
- mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
- nokogiri (1.6.8-x86-mingw32)
- mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
nori (2.6.0)
- oauth2 (1.2.0)
- faraday (>= 0.8, < 0.10)
- jwt (~> 1.0)
- 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.20.0)
- chef-config (>= 12.5.0.alpha.1, < 13)
- ffi (~> 1.9)
- ffi-yajl (~> 2.2)
- ipaddress
- mixlib-cli
- mixlib-config (~> 2.0)
- mixlib-log (>= 1.7.1, < 2.0)
- mixlib-shellout (~> 2.0)
- plist (~> 3.1)
- systemu (~> 2.6.4)
- wmi-lite (~> 1.0)
- parser (2.3.1.2)
- ast (~> 2.2)
- pkg-config (1.1.7)
- plist (3.2.0)
- poise (2.7.1)
- halite (~> 1.0)
- poise-profiler (1.0.1)
- halite (~> 1.0)
- polyglot (0.3.5)
- powerpack (0.1.1)
+ parallel (1.20.1)
+ parser (3.0.0.0)
+ ast (~> 2.4.1)
+ parslet (1.8.2)
+ pastel (0.8.0)
+ tty-color (~> 0.5)
+ plist (3.6.0)
proxifier (1.0.3)
- pry (0.10.4)
- coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.0)
- byebug (~> 9.0)
- pry (~> 0.10)
- pry-remote (0.1.8)
- pry (~> 0.9)
- slop (~> 3.0)
- pry-stack_explorer (0.4.9.2)
- binding_of_caller (>= 0.7)
- pry (>= 0.9.11)
- pusher-client (0.6.2)
- json
- websocket (~> 1.0)
- rack (2.0.1)
- rainbow (2.1.0)
- rake (11.2.2)
- rb-readline (0.5.3)
- rest-client (2.0.0)
- http-cookie (>= 1.0.2, < 2.0)
- mime-types (>= 1.16, < 4.0)
- netrc (~> 0.8)
- rest-client (2.0.0-x86-mingw32)
- ffi (~> 1.9)
- http-cookie (>= 1.0.2, < 2.0)
- mime-types (>= 1.16, < 4.0)
- netrc (~> 0.8)
- retryable (2.0.4)
- rspec (3.5.0)
- rspec-core (~> 3.5.0)
- rspec-expectations (~> 3.5.0)
- rspec-mocks (~> 3.5.0)
- rspec-core (3.5.3)
- rspec-support (~> 3.5.0)
- rspec-expectations (3.5.0)
+ pry (0.13.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ pry-byebug (3.9.0)
+ byebug (~> 11.0)
+ pry (~> 0.13.0)
+ pry-stack_explorer (0.6.0)
+ binding_of_caller (~> 1.0)
+ pry (~> 0.13)
+ public_suffix (4.0.6)
+ rack (2.2.3)
+ rainbow (3.0.0)
+ rake (13.0.3)
+ rb-readline (0.5.5)
+ regexp_parser (2.0.3)
+ rexml (3.2.4)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-core (3.9.3)
+ rspec-support (~> 3.9.3)
+ rspec-expectations (3.9.4)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-its (1.2.0)
+ rspec-support (~> 3.9.0)
+ rspec-its (1.3.0)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
- rspec-mocks (3.5.0)
+ rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-rerun (1.1.0)
- rspec (~> 3.0)
- rspec-support (3.5.0)
- rspec_junit_formatter (0.2.3)
- builder (< 4)
- rspec-core (>= 2, < 4, != 2.12.0)
- rubocop (0.42.0)
- parser (>= 2.3.1.1, < 3.0)
- powerpack (~> 0.1)
- rainbow (>= 1.99.1, < 3.0)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.4)
+ rubocop (1.7.0)
+ parallel (~> 1.10)
+ parser (>= 2.7.1.5)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml
+ rubocop-ast (>= 1.2.0, < 2.0)
ruby-progressbar (~> 1.7)
- unicode-display_width (~> 1.0, >= 1.0.1)
- ruby-prof (0.16.2)
- ruby-progressbar (1.8.1)
+ unicode-display_width (>= 1.4.0, < 2.0)
+ rubocop-ast (1.3.0)
+ parser (>= 2.7.1.5)
+ ruby-prof (1.2.0)
+ ruby-progressbar (1.11.0)
ruby-shadow (2.5.0)
- rubyntlm (0.6.0)
- rubyzip (1.2.0)
- rufus-lru (1.1.0)
- safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- serverspec (2.36.1)
- multi_json
- rspec (~> 3.0)
- rspec-its
- specinfra (~> 2.53)
- sfl (2.2)
- simplecov (0.12.0)
- docile (~> 1.1.0)
- json (>= 1.8, < 3)
- simplecov-html (~> 0.10.0)
- simplecov-html (0.10.0)
- slop (3.6.0)
- specinfra (2.61.3)
- net-scp
- net-ssh (>= 2.7, < 4.0)
- net-telnet
- sfl
- stove (4.1.1)
- chef-api (~> 0.5)
- logify (~> 0.2)
+ rubyntlm (0.6.2)
+ rubyzip (1.3.0)
+ semverse (3.0.0)
+ sslshake (1.3.1)
+ strings (0.2.0)
+ strings-ansi (~> 0.2)
+ unicode-display_width (~> 1.5)
+ unicode_utils (~> 1.4)
+ strings-ansi (0.2.0)
+ structured_warnings (0.4.0)
syslog-logger (1.6.8)
- systemu (2.6.5)
- test-kitchen (1.12.0)
- mixlib-install (~> 1.0, >= 1.0.4)
- mixlib-shellout (>= 1.2, < 3.0)
- net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-gateway (~> 1.2.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
- thor (0.19.1)
- thread_safe (0.3.5)
- tomlrb (1.2.3)
- travis (1.8.2)
- 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)
- treetop (1.6.8)
- polyglot (~> 0.3)
- typhoeus (0.8.0)
- ethon (>= 0.8.0)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
- ubuntu_ami (0.4.1)
- unf (0.1.4)
- unf_ext
- unf_ext (0.0.7.2)
- unf_ext (0.0.7.2-x86-mingw32)
- unicode-display_width (1.1.1)
- url (0.3.2)
+ thor (1.0.1)
+ tomlrb (1.2.9)
+ train-core (3.4.4)
+ addressable (~> 2.5)
+ ffi (!= 1.13.0)
+ json (>= 1.8, < 3.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ net-scp (>= 1.2, < 4.0)
+ net-ssh (>= 2.9, < 7.0)
+ train-winrm (0.2.11)
+ winrm (~> 2.0)
+ winrm-elevated (~> 1.2.2)
+ winrm-fs (~> 1.0)
+ tty-box (0.7.0)
+ pastel (~> 0.8)
+ strings (~> 0.2.0)
+ tty-cursor (~> 0.7)
+ tty-color (0.6.0)
+ tty-cursor (0.7.1)
+ tty-prompt (0.23.0)
+ pastel (~> 0.8)
+ tty-reader (~> 0.8)
+ tty-reader (0.9.0)
+ tty-cursor (~> 0.7)
+ tty-screen (~> 0.8)
+ wisper (~> 2.0)
+ tty-screen (0.8.1)
+ tty-table (0.12.0)
+ pastel (~> 0.8)
+ strings (~> 0.2.0)
+ tty-screen (~> 0.8)
+ unicode-display_width (1.7.0)
+ unicode_utils (1.4.0)
uuidtools (2.1.5)
- vagrant-wrapper (2.0.3)
- websocket (1.2.3)
+ webmock (3.11.0)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff (>= 0.4.0, < 2.0.0)
win32-api (1.5.3-universal-mingw32)
- win32-dir (0.5.1)
- ffi (>= 1.0.0)
+ win32-certstore (0.4.1)
+ ffi
+ mixlib-shellout
win32-event (0.6.3)
win32-ipc (>= 0.6.0)
win32-eventlog (0.6.3)
@@ -539,76 +379,62 @@ GEM
ffi
win32-mutex (0.4.3)
win32-ipc (>= 0.6.0)
- win32-process (0.8.3)
+ win32-process (0.9.0)
ffi (>= 1.0.0)
- win32-service (0.8.9)
+ win32-service (2.2.0)
ffi
ffi-win32-extensions
- windows-api (0.4.4)
- win32-api (>= 1.4.5)
- winrm (2.0.1)
+ win32-taskscheduler (2.0.4)
+ ffi
+ structured_warnings
+ winrm (2.3.5)
builder (>= 2.1.2)
- erubis (~> 2.7)
+ erubi (~> 1.8)
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-elevated (1.0.0)
+ rubyntlm (~> 0.6.0, >= 0.6.1)
+ winrm-elevated (1.2.3)
+ erubi (~> 1.8)
winrm (~> 2.0)
winrm-fs (~> 1.0)
- winrm-fs (1.0.0)
- erubis (~> 2.7)
+ winrm-fs (1.3.3)
+ erubi (~> 1.8)
logging (>= 1.6.1, < 3.0)
rubyzip (~> 1.1)
winrm (~> 2.0)
- wmi-lite (1.0.0)
- yajl-ruby (1.2.1)
- yard (0.9.5)
- yard-classmethods (1.0.0)
- yard
+ wisper (2.0.1)
+ wmi-lite (1.0.5)
PLATFORMS
ruby
+ x64-mingw32
x86-mingw32
DEPENDENCIES
appbundler
- bundler
- bundler-audit!
chef!
+ chef-bin!
chef-config!
- chef-provisioning
- chef-provisioning-aws
- chef-rewind
- chef-sugar
- cheffish
- chefspec
+ chef-telemetry (>= 1.0.8)
+ chef-utils!
+ chef-vault
+ cheffish (>= 14)
chefstyle!
- cucumber (>= 2.4.0)
- foodcritic
- github_changelog_generator
- halite
- knife-windows
- netrc
- nokogiri
- oc-chef-pedant!
- octokit
- poise
- poise-boiler!
+ fauxhai-ng
+ inspec-core-bin (~> 4.24)
+ ohai!
pry
pry-byebug
- pry-remote
pry-stack_explorer
rake
rb-readline
- ruby-prof
+ rspec
+ ruby-prof (< 1.3.0)
ruby-shadow
- simplecov
- tomlrb
- travis
- yard
+ webmock
BUNDLED WITH
- 1.12.5
+ 2.1.4
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
deleted file mode 100644
index d8fc022bb8..0000000000
--- a/MAINTAINERS.md
+++ /dev/null
@@ -1,291 +0,0 @@
-<!-- This is a generated file. Please do not edit directly -->
-
-# Maintainers
-
-This file lists how the Chef project is maintained. When making changes to the system, this
-file tells you who needs to review your patch - you need a simple majority of maintainers
-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/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
-
-* [Adam Jacob](https://github.com/adamhjk)
-
-## Components
-
-## 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.
-
-To mention the team, use @chef/client-core
-
-### Lieutenant
-
-* [Thom May](https://github.com/thommay)
-
-### Maintainers
-
-* [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)
-* [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)
-* [John Keiser](https://github.com/jkeiser)
-* [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)
-
-## Chef Provisioning
-
-Chef Provisioning and Drivers. Supported Drivers are listed in the [README](https://github.com/chef/chef-provisioning/blob/master/README.md#chef-provisioning).
-
-To mention the team, use @chef/provisioning
-
-### Lieutenant
-
-* [Tyler Ball](https://github.com/tyler-ball)
-
-### 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)
-* [Harley Alaniz](https://github.com/thehar)
-
-## Platform Specific Components
-
-The specific components of Chef related to a given platform - including (but not limited to) resources, providers, and the core DSL.
-
-## Enterprise Linux
-
-To mention the team, use @chef/client-enterprise-linux
-
-### Lieutenant
-
-* [Jon Cowie](https://github.com/jonlives)
-
-### Maintainers
-
-* [Phil Dibowitz](https://github.com/jaymzh)
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## Ubuntu
-
-To mention the team, use @chef/client-ubuntu
-
-### Lieutenant
-
-* [Ranjib Dey](https://github.com/ranjib)
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-* [Thom May](https://github.com/thommay)
-
-## Windows
-
-To mention the team, use @chef/client-windows
-
-### Lieutenant
-
-* [Bryan McLellan](https://github.com/btm)
-
-### Maintainers
-
-* [Jay Mundrawala](https://github.com/jaym)
-* [Kartik Cating-Subramanian](https://github.com/ksubrama)
-* [Steven Murawski](https://github.com/smurawski)
-* [Salim Alam](https://github.com/chefsalim)
-* [Matt Wrock](https://github.com/mwrock)
-
-## Solaris
-
-To mention the team, use @chef/client-solaris
-
-### Lieutenant
-
-* [Thom May](https://github.com/thommay)
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## AIX
-
-To mention the team, use @chef/client-aix
-
-### Lieutenant
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## Mac OS X
-
-To mention the team, use @chef/client-os-x
-
-### Lieutenant
-
-* [Joshua Timberman](https://github.com/jtimberman)
-
-### Maintainers
-
-* [Tyler Ball](https://github.com/tyler-ball)
-
-## Debian
-
-To mention the team, use @chef/client-debian
-
-### Lieutenant
-
-* [Thom May](https://github.com/thommay)
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## Cisco NX-OS
-
-To mention the team, use @chef/client-nxos
-
-### Lieutenant
-
-* [Adam Leff](https://github.com/adamleff)
-
-### Maintainers
-
-* [Adam Leff](https://github.com/adamleff)
-
-## Cisco IOS XR
-
-To mention the team, use @chef/client-iosxr
-
-### Lieutenant
-
-* [Adam Leff](https://github.com/adamleff)
-
-### Maintainers
-
-* [Adam Leff](https://github.com/adamleff)
-
-## Fedora
-
-To mention the team, use @chef/client-fedora
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## openSUSE
-
-To mention the team, use @chef/client-opensuse
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## SUSE Enterprise Linux Server
-
-To mention the team, use @chef/client-suse
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## FreeBSD
-
-To mention the team, use @chef/client-freebsd
-
-### Lieutenant
-
-* [Aaron Kalin](https://github.com/martinisoft)
-
-### Maintainers
-
-* [Cory Stephenson](https://github.com/Aevin1387)
-* [David Aronsohn](https://github.com/tbunnyman)
-* [Bryant Lippert](https://github.com/AgentMeerkat)
-
-## OpenBSD
-
-To mention the team, use @chef/client-openbsd
-
-### Lieutenant
-
-* [Joe Miller](https://github.com/joemiller)
-
-## Gentoo
-
-To mention the team, use @chef/client-gentoo
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-
-## OmniOS
-
-To mention the team, use @chef/client-omnios
-
-### Maintainers
-
-* [Thom May](https://github.com/thommay)
-
-## ArchLinux
-
-To mention the team, use @chef/client-archlinux
-
-### Maintainers
-
-* [Lamont Granquist](https://github.com/lamont-granquist)
-* [Ryan Cragun](https://github.com/ryancragun)
-
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
deleted file mode 100644
index ca90a72119..0000000000
--- a/MAINTAINERS.toml
+++ /dev/null
@@ -1,419 +0,0 @@
-#
-# This file is structured to be consumed by both humans and computers.
-# To update the generated Markdown, run `bundle exec rake maintainers:generate`
-# To synchronize the maintainers with the github teams, run `bundle exec rake maintainers:synchronize`
-# It is a TOML document containing Markdown
-#
-[Preamble]
- title = "Maintainers"
- text = """
-This file lists how the Chef project is maintained. When making changes to the system, this
-file tells you who needs to review your patch - you need a simple majority of maintainers
-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/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.
-"""
-
-[Org]
- [Org.Lead]
- title = "Project Lead"
- person = "adamhjk"
-
- [Org.Components]
- title = "Components"
-
- [Org.Components.Core]
- 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.
-"""
-
- lieutenant = "thommay"
-
- maintainers = [
- "btm",
- "coderanger",
- "danielsdeleo",
- "fujin",
- "jaymzh",
- "jaym",
- "jkeiser",
- "jonlives",
- "lamont-granquist",
- "mcquin",
- "smurawski",
- "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",
- "jkeiser",
- "jtimberman",
- "lamont-granquist",
- "stevendanna"
- ]
-
- [Org.Components.TestTools]
- title = "Test Tools"
- team = "client-test-tools"
- text = "ChefSpec"
-
- lieutenant = "sethvargo"
-
- maintainers = [
- "jtimberman",
- "lamont-granquist",
- "ranjib"
- ]
-
- [Org.Components.Provisioning]
- title = "Chef Provisioning"
- team = "provisioning"
- text = """
-Chef Provisioning and Drivers. Supported Drivers are listed in the [README](https://github.com/chef/chef-provisioning/blob/master/README.md#chef-provisioning).
-"""
-
- lieutenant = "tyler-ball"
-
- maintainers = [
- "jkeiser",
- "stuartpreston",
- "jjasghar",
- "joaogbcravo",
- "thehar"
- ]
-
- [Org.Components.Subsystems]
- title = "Platform Specific Components"
- text = """
-The specific components of Chef related to a given platform - including (but not limited to) resources, providers, and the core DSL.
-"""
-
- [Org.Components.Subsystems."Enterprise Linux"]
- title = "Enterprise Linux"
- team = "client-enterprise-linux"
-
- lieutenant = "jonlives"
-
- maintainers = [
- "jaymzh",
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.Ubuntu]
- title = "Ubuntu"
- team = "client-ubuntu"
-
- lieutenant = "ranjib"
-
- maintainers = [
- "lamont-granquist",
- "thommay"
- ]
-
- [Org.Components.Subsystems.Windows]
- title = "Windows"
- team = "client-windows"
-
- lieutenant = "btm"
- maintainers = [
- "jaym",
- "ksubrama",
- "smurawski",
- "chefsalim",
- "mwrock"
- ]
-
- [Org.Components.Subsystems.Solaris]
- title = "Solaris"
- team = "client-solaris"
-
- lieutenant = "thommay"
-
- maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.AIX]
- title = "AIX"
- team = "client-aix"
-
- lieutenant = "lamont-granquist"
-
- [Org.Components.Subsystems."Mac OS X"]
- title = "Mac OS X"
- team = "client-os-x"
-
- lieutenant = "jtimberman"
-
- maintainers = [
- "tyler-ball"
- ]
-
- [Org.Components.Subsystems.Debian]
- title = "Debian"
- team = "client-debian"
-
- 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"
- ]
-
- [Org.Components.Subsystems.Fedora]
- title = "Fedora"
- team = "client-fedora"
-
- maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.openSUSE]
- title = "openSUSE"
- team = "client-opensuse"
-
- maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems."SUSE Enterprise Linux"]
- title = "SUSE Enterprise Linux Server"
- team = "client-suse"
-
- maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.FreeBSD]
- title = "FreeBSD"
- team = "client-freebsd"
-
- lieutenant = "martinisoft"
-
- maintainers = [
- "Aevin1387",
- "tBunnyMan",
- "AgentMeerkat"
- ]
-
- [Org.Components.Subsystems.OpenBSD]
- title = "OpenBSD"
- team = "client-openbsd"
-
- lieutenant = "joemiller"
-
- [Org.Components.Subsystems.Gentoo]
- title = "Gentoo"
- team = "client-gentoo"
-
- maintainers = [
- "lamont-granquist"
- ]
-
- [Org.Components.Subsystems.OmniOS]
- title = "OmniOS"
- team = "client-omnios"
-
- maintainers = [
- "thommay"
- ]
-
- [Org.Components.Subsystems.ArchLinux]
- title = "ArchLinux"
- team = "client-archlinux"
-
- maintainers = [
- "lamont-granquist",
- "ryancragun"
- ]
-
-[people]
- [people.adamhjk]
- Name = "Adam Jacob"
- GitHub = "adamhjk"
-
- [people.adamleff]
- Name = "Adam Leff"
- GitHub = "adamleff"
-
- [people.Aevin1387]
- Name = "Cory Stephenson"
- GitHub = "Aevin1387"
-
- [people.AgentMeerkat]
- Name = "Bryant Lippert"
- GitHub = "AgentMeerkat"
-
- [people.btm]
- Name = "Bryan McLellan"
- GitHub = "btm"
-
- [people.danielsdeleo]
- Name = "Daniel DeLeo"
- GitHub = "danielsdeleo"
-
- [people.fujin]
- Name = "AJ Christensen"
- GitHub = "fujin"
-
- [people.jaymzh]
- Name = "Phil Dibowitz"
- GitHub = "jaymzh"
-
- [people.jaym]
- Name = "Jay Mundrawala"
- GitHub = "jaym"
-
- [people.jonlives]
- Name = "Jon Cowie"
- GitHub = "jonlives"
-
- [people.jtimberman]
- Name = "Joshua Timberman"
- GitHub = "jtimberman"
-
- [people.lamont-granquist]
- Name = "Lamont Granquist"
- GitHub = "lamont-granquist"
-
- [people.martinisoft]
- Name = "Aaron Kalin"
- GitHub = "martinisoft"
-
- [people.mcquin]
- Name = "Claire McQuin"
- GitHub = "mcquin"
-
- [people.ranjib]
- Name = "Ranjib Dey"
- GitHub = "ranjib"
-
- [people.sethvargo]
- Name = "Seth Vargo"
- GitHub = "sethvargo"
-
- [people.smurawski]
- Name = "Steven Murawski"
- GitHub = "smurawski"
-
- [people.stevendanna]
- Name = "Steven Danna"
- GitHub = "stevendanna"
-
- [people.tBunnyMan]
- Name = "David Aronsohn"
- GitHub = "tbunnyman"
- IRC = "tBunnyMan"
- Twitter = "OnlyHaveCans"
-
- [people.thommay]
- Name = "Thom May"
- GitHub = "thommay"
- IRC = "thom"
- Twitter = "thommay"
-
- [people.tyler-ball]
- Name = "Tyler Ball"
- GitHub = "tyler-ball"
-
- [people.ksubrama]
- Name = "Kartik Cating-Subramanian"
- GitHub = "ksubrama"
-
- [people.joemiller]
- Name = "Joe Miller"
- GitHub = "joemiller"
-
- [people.coderanger]
- Name = "Noah Kantrowitz"
- GitHub = "coderanger"
-
- [people.ryancragun]
- Name = "Ryan Cragun"
- GitHub = "ryancragun"
-
- [people.chefsalim]
- Name = "Salim Alam"
- GitHub = "chefsalim"
-
- [people.mwrock]
- Name = "Matt Wrock"
- GitHub = "mwrock"
-
- [people.tas50]
- Name = "Tim Smith"
- GitHub = "tas50"
-
- [people.jkeiser]
- Name = "John Keiser"
- GitHub = "jkeiser"
-
- [people.stuartpreston]
- Name = "Stuart Preston"
- GitHub = "stuartpreston"
-
- [people.jjasghar]
- Name = "JJ Asghar"
- GitHub = "jjasghar"
- Twitter = "jjasghar"
- IRC = "j^2"
-
- [people.joaogbcravo]
- Name = "João Cravo"
- GitHub = "joaogbcravo"
- Twitter = "joaogbcravo"
-
- [people.thehar]
- Name = "Harley Alaniz"
- GitHub = "thehar"
diff --git a/README.md b/README.md
index 6eb0c2705b..6c3373a2af 100644
--- a/README.md
+++ b/README.md
@@ -1,284 +1,54 @@
-# Chef
+# Chef Infra
[![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)
+[![Build Status](https://badge.buildkite.com/c82093430ceec7d27af05febb9dcafe3aa331fff9d74c0ab9d.svg?branch=master)](https://buildkite.com/chef-oss/chef-chef-master-verify)
+[![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/blob/master/docs/dev/design_documents/client_release_cadence.md)
-Want to try Chef? Get started with [learnchef](https://learn.chef.io)
+**Umbrella Project**: [Chef Infra](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md)
-* 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)
-* Slack: [Chef Community Slack](https://community-slack.chef.io/)
-* Mailing list: [https://discourse.chef.io](https://discourse.chef.io)
+**Project State**: [Active](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#active)
-Chef is a configuration management tool designed to bring automation to your
-entire infrastructure.
+**Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
-This README focuses on developers who want to modify Chef source code.
-If you just want to use Chef, check out these resources:
+**Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
-* [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
+## Getting Started
-## Installing From Git
+Chef Infra is a configuration management tool designed to bring automation to your entire infrastructure.
-**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.
+### Want to try Chef Infra?
-### Prerequisites
+For Chef Infra usage, please refer to [Learn Chef](https://learn.chef.io/), our self-paced, entirely free learning platform. Learn Chef also includes module-based training for Chef Infra, as well as Chef Automate, Chef Habitat, and Chef InSpec.
-Install these via your platform's preferred method (`apt`, `yum`, `ports`,
-`emerge`, etc.):
+Other useful resources for Chef Infra users:
-* git
-* C compiler, header files, etc. On Ubuntu/Debian, use the
- `build-essential` package.
-* ruby 2.1.0 or later
-* rubygems
-* bundler gem
-
-### 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
-```
-
-## 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).
-
-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.
-
-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.
+- 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/Forum: <https://discourse.chef.io>
## 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.
-
-## 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.
-
-```bash
-# Run All the Tests
-bundle exec rake spec
-
-# Run a Single Test File
-bundle exec rspec spec/PATH/TO/FILE_spec.rb
-
-# Run a Subset of Tests
-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.
-
-## 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!
-
-## 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).
-
-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.
-
-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.
-
-# How Chef Builds and Versions
-
-Chef is an amalgam of many components. These components update all the time, necessitating new builds. This is an overview of the process of versioning, building and releasing Chef.
-
-## Chef Packages
-
-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://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:
-
-1. Bumps the patch version in `lib/chef/version.rb` (e.g. 0.9.14 -> 0.9.15).
-2. Runs `rake bundle:install` to update the `Gemfile.lock` to include the new version.
-3. Pushes to `master` and submits a new build to Chef's Jenkins cluster.
-
-## Bumping the minor version of Chef
-
-After each "official" stable release we need to bump the minor version. To do this:
-
-1. Manually increment the minor version in the VERSION file that is in the root of this repo. and reset the patch version to 0. Assuming the current version is `12.10.57` you would edit `VERSION` to be `12.11.0`.
-2. Run `bundle exec rake version` which will copy the version to the respective `version.rb` files in chef and chef-config.
-3. Run `bundle exec rake bundle:install` to update the base Gemfile.lock
-
-Submit a PR with the changes made by the above.
+Note that this repository is primarily for reporting issues in the chef-client itself. For reporting issues against other Chef projects, please look up the appropriate repository. If you're unsure where to submit an issue, please ask in the #chef-dev channel in [Chef Community Slack](https://community-slack.chef.io/).
-## Component Versions
+## How We Build & Release Chef
-Chef has two sorts of component: ruby components like `berkshelf` and `test-kitchen`, and binary components like `openssl` and even `ruby` itself.
+For information on how a contribution goes from PR to released package, see [How Chef Infra Is Built](docs/dev/design_documents/how_chef_is_tested_and_built.md)
-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.
+To learn more about our monthly feature releases and yearly major releases, see [Chef Infra Release and Support Schedule](./docs/dev/policy/release_and_support_schedule.md).
-### Binary Components
+## Getting Involved
-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.
+We'd love to have your help developing Chef Infra. See our [Contributing Document](./CONTRIBUTING.md) for more information on getting started.
-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.
+## License and Copyright
-### Rubygems Components
-
-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.
-
-Our rubygems component versions are locked down with `Gemfile.lock`, and can be updated with `rake dependencies`.
-
-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`.
-
-**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.
-
-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.
-
-### Build Tooling Versions
-
-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.
-
-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.
-
-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.
-
-The omnibus tooling versions are locked down with `omnibus/Gemfile.lock`, and can be updated by running `rake dependencies`.
-
-### Test Versions
-
-chef is tested by the [chef-acceptance framework](https://github.com/chef/chef-acceptance), which contains suites that are run on the Jenkins test machines. The definitions of the tests are in the `acceptance` directory. The version of chef-acceptance and test-kitchen, are governed by `acceptance/Gemfile`.
-
-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):
+Copyright 2008-2020, Chef Software, Inc.
```
-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
-
-| | |
-|:---------------------|:-----------------------------------------|
-| **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
@@ -290,3 +60,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 8010770e04..0a23f8b596 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,44 +1,5574 @@
-*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/> for the official Chef release notes.
-# Chef Client Release Notes 12.14:
+## What's New in 16.9
-## Highlighted enhancements for this release:
+### Knife Improvements
+
+- The `knife bootstrap` command now properly formats the `trusted_certs_dir` configuration value on Windows hosts. Thanks for this fix [@axelrtgs](https://github.com/axelrtgs)!
+- The `knife bootstrap` command now only specifies the ssh option `-o IdentitiesOnly=yes` if keys are present. Thanks for this fix [@drbrain](https://github.com/drbrain)!
+- The `knife status` command with the `-F json` flag no longer fails if cloud nodes have no public IP.
+
+### Updated Resources
+
+#### cron_d
+
+The `cron_d` resource now respects the use of the `sensitive` property. Thanks for this fix [@axl89](https://github.com/axl89)!
+
+#### dnf
+
+The `dnf` resource has received a large number of improvements to provide improved idempotency and to better handle uses of the `version` and `arch` properties. Thanks for reporting these issues [@epilatow](https://github.com/epilatow) and [@Blorpy](https://github.com/Blorpy)!
+
+#### homebrew_cask
+
+The `homebrew_cask` resource has been updated to work with the latest command syntax requirements in the `brew` command. Thanks for reporting this issue [@bcg62](https://github.com/bcg62)!
+
+#### locale
+
+The allowed execution time for the `locale-gen` command in the `locale` resource has been extended to 1800 seconds to make sure the Chef Infra Client run doesn't fail before the command completes on slower systems. Thanks for reporting this issue [@janskarvall](https://github.com/janskarvall)!
+
+#### plist / macosx_service / osx_profile / macos_userdefaults
+
+Parsing of plist files has been improved in the `plist`, `macosx_service`, `osx_profile`, and `macos_userdefaults` resources thanks to updates to the plist gem by [@reitermarkus](https://github.com/reitermarkus) and [@tboyko](https://github.com/tboyko).
+
+#### user
+
+The `user` resource on Windows hosts now properly handles `uid` values passed as strings instead of integers. Thanks for reporting this issue [@jaymzh](https://github.com/jaymzh)!
+
+#### yum_repostiory
+
+The `yum_repository` resource has been updated with a new `reposdir` property to control the path where the Yum repository configuration files will be written. Thanks for suggesting this [@wildcrazyman](https://github.com/wildcrazyman)!
+
+### Security
+
+- The bundled Nokogiri Ruby gem has been updated to 1.11 resolve [CVE-2020-26247](https://nvd.nist.gov/vuln/detail/CVE-2020-26247).
+
+## What's New in 16.8.14
+
+- Updated openSSL to 1.0.2x to resolve [CVE-2020-1971](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1971).
+- Updated libarchive to 3.5.0, which powers the `archive_file` resource. This new release resolves extraction failures and better handles symlinks in archives.
+- `knife ssh` with the `--sudo` flag will no longer silently fail. Thanks for the fix [@rveznaver](https://github.com/rveznaver)!
+- Resolve failures running the Compliance Phase introduced in the 16.8.9 release. Thanks for reporting this issue [@axelrtgs](https://github.com/axelrtgs)!
+
+## What's New in 16.8.9
+
+### Chef InSpec 4.24
+
+Chef InSpec has been updated to 4.24.8 including the following improvements:
+
+- An unset `HOME` environment variable will not cause execution failures
+- You can use wildcards in `platform-name` and `release` in InSpec profiles
+- The support for arrays in the `WMI` resource, so it can return multiple objects
+- The `package` resource on Windows properly escapes package names
+- The `grub_conf` resource succeeds even if without a `menuentry` in the grub config
+- Loaded plugins won't try to re-load themselves
+
+### Updated Resources
+
+#### dsc_resource / dsc_script
+
+The `dsc_resource` and `dsc_script` resources have been updated to use the `powershell_exec` helper for significantly improved performance executing the PowerShell commands.
+
+#### hostname
+
+The `hostname` resource has been updated to prevent failures when the default system hostname is set on macOS hosts.
+
+#### remote_file
+
+The `remote_file` resource has been updated to use certificates located in Chef Infra Client's `trusted_certificates` directory. Thanks for reporting this issue [@carguel](https://github.com/carguel/)!
+
+#### windows_certificate
+
+The `windows_certificate` has been updated with a new `exportable` property that marks PFX files as exportable in the certificate store.
+
+### Ohai Improvements
+
+- A new optional `Grub2` plugin can be enabled to expose GRUB2 environment variables.
+- Linode cloud detection has been improved.
+
+### Platform Packages
+
+We are once again building packages for Solaris on Sparc and x86 platforms.
+
+## What's New in 16.7
+
+### Performance Enhancements
+
+In Chef Infra Client 16.7, we've put a particular focus on optimizing the performance of the client. We've created several dozen minor optimizations that increase performance and reduce overall memory usage across all platforms. On Windows, our work has been particularly pronounced as we've improved resource execution and Chef Infra Client installation. Chef Infra Client install times on Windows are now up to 3x faster than previous releases. Resources that use PowerShell to make changes now execute significantly faster. This improvement will be the most noticeable in Chef Infra Client runs that don't make actual system changes (no-op runs), where determining the current system state was previously resource-intensive.
+
+### Windows Bootstrap Improvements
+
+We've improved how Windows nodes are bootstrapped when using the `knife bootstrap` command. The `knife bootstrap` `--secret` flag is now respected on Windows hosts, allowing for the proper setup of nodes to use encrypted data bags. Thanks for reporting this issue [@AMC-7](https://github.com/AMC-7)! Additionally, during the bootstrap we now force connections to use TLS 1.2, preventing failures on Windows 2012-2016. Thanks for this improvement [@TimothyTitan](https://github.com/TimothyTitan)!
+
+### Chef Vault 4.1
+
+We've updated the release of `chef-vault` bundled with Chef Infra Client to 4.1. Chef Vault 4.1 properly handles escape strings in secrets and greatly improves performance for users with large numbers of secrets. Thanks for the performance work [@Annih](https://github.com/Annih)!
+
+### Updated Resources
+
+#### build_essential
+
+The `build_essential` resource has been updated to resolve idempotency issues and greatly improve performance on macOS hosts.
+
+#### chef_client_config
+
+The `chef_client_config` resource has been updated to no longer produce invalid `client.rb` content.
+
+#### group
+
+The `group` resource has been improved to provide log output of changes being made and on Windows now properly translates group SIDs to names in order to operate idempotently.
+
+Thanks for these improvements [@jaymzh](https://github.com/jaymzh)!
+
+#### homebrew_update
+
+The `homebrew_update` has been updated to resolve failures that would occur when running the resource.
+
+#### ifconfig
+
+The `ifconfig` resource has been updated to better support Linux distributions that are derivatives of either Ubuntu or Debian. Support for setting the `BRIDGE` property on RHEL-based systems has also been added.
+
+#### mount
+
+The `mount` resource has been updated to resolve several issues:
+
+- Idempotency failures when using labels on Linux hosts.
+- Idempotency failures when using network paths that end with a slash.
+- fstab entries being reordered instead of performing in-place updates.
+
+Thanks for reporting these issues [@limitusus](https://github.com/limitusus), [@axelrtgs](https://github.com/axelrtgs), and [@scarpe01](https://github.com/scarpe01)!
+
+#### powershell_package
+
+The `powershell_package` resource has been updated to better force connections to use TLS 1.2 when communicating with the PowerShell Gallery on Windows Server 2012-2016. Connections must be forced to use TLS 1.2 as the system default cipher suite because Windows 2012-2016 did not include TLS 1.2.
+
+#### powershell_script
+
+The `powershell_script` resource has been updated to not fail when using a `not_if` or `only_if` guard when specifying the `user` property. Thanks for reporting this issue [@Blorpy](https://github.com/Blorpy)!
+
+#### user
+
+The `user` resource has been improved to provide log output of changes being made.
+
+Thanks for this improvement [@jaymzh](https://github.com/jaymzh)!
+
+#### zypper_package
+
+The `zypper_package` resource has been refactored to improve idempotency when specifying a version of the package to either install or downgrade.
+
+### Ohai Improvements
+
+- The `Joyent` plugin has been removed as the Joyent public cloud was shutdown 11/2019
+- `pop_os` is now detected as having the `platform_family` of `debian`. Thanks for this improvement [@chasebolt](https://github.com/chasebolt)!
+- Recent `openindiana` releases are now properly detected.
+- The `Hostnamectl` plugin properly detects hostnames that contain a colon. Thanks for reporting this [@ziggythehamster](https://github.com/ziggythehamster)!
+- The `Zpool` plugin now properly detects ZFS zpools that include `nvme` or `xvd` drives. Thanks for reporting this [@ziggythehamster](https://github.com/ziggythehamster)!
+- The `Zpool` plugin now properly detects ZFS zpools that use disk labels/guids instead of traditional drive designations.
+- Performance of system configuration gathering on AIX systems has been improved
+- The `Virtualization` plugin on AIX systems now gathers a state `state` per WPAR and properly gathers LPAR names that include spaces
+
+## Whats New in 16.6
+
+### pwsh Support
+
+We've updated multiple parts of the Chef Infra Client to fully support Microsoft's `pwsh` (commonly known as PowerShell Core) in addition to our previous support for `PowerShell`.
+
+#### powershell_script resource
+
+The `powershell_script` resource includes a new `interpreter` property that accepts either `powershell` or `pwsh`.
+
+```ruby
+powershell_script 'check version table' do
+ code '$PSVersionTable'
+ interpreter 'pwsh'
+end
+```
+
+#### powershell_out / powershell_exec helpers
+
+The `powershell_out` and `powershell_exec` helpers for use in custom resources have been updated to support `pwsh` with a new argument that accepts either `:pwsh` or `:powershell`.
+
+```ruby
+powershell_exec('$PSVersionTable', :pwsh)
+```
+
+### Enhanced 32-bit Windows Support
+
+The `powershell_exec` helper now supports the 32-bit version of Windows. This ensures many of the newer PowerShell based resources in Chef Infra Client will function as expected on 32-bit systems.
+
+### New Resources
+
+#### chef_client_config
+
+The `chef_client_config` resource allows you to manage Chef Infra Client's `client.rb` file without the need for the `chef-client` cookbook.
+
+##### Example
+
+```ruby
+chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+end
+```
+
+##### chef-client Cookbook Future
+
+With the inclusion of the `chef_client_config` resource in Chef Infra Client 16.6, it is now possible to fully manage the Chef Infra Client without the need for the `chef-client` cookbook. We highly recommend using the `chef_client_config`, `chef_client_trusted_certificate`, and `chef_client_*` service resources to manage your clients instead of the `chef-client` cookbook. In the future we will mark that cookbook as deprecated, at which time it will no longer receive updates.
+
+Here's a sample of fully managing Linux hosts with the built-in resources:
+
+```ruby
+chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+end
+
+chef_client_trusted_certificate "chef.example.dmz" do
+ certificate <<~CERT
+ -----BEGIN CERTIFICATE-----
+ MIIDeTCCAmGgAwIBAgIJAPziuikCTox4MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
+ c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0x
+ OTEwMDkyMzQxNTJaFw0yMTEwMDgyMzQxNTJaMGIxCzAJBgNVBAYTAlVTMRMwEQYD
+ VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK
+ DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB
+ BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2
+ PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW
+ hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A
+ xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve
+ ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY
+ QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T
+ BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI
+ hvcNAQELBQADggEBAGlwCdbPxflZfYOaukZGCaxYK6gpincX4Lla4Ui2WdeQxE95
+ w7fChXvP3YkE3UYUE7mupZ0eg4ZILr/A0e7JQDsgIu/SRTUE0domCKgPZ8v99k3A
+ vka4LpLK51jHJJK7EFgo3ca2nldd97GM0MU41xHFk8qaK1tWJkfrrfcGwDJ4GQPI
+ iLlm6i0yHq1Qg1RypAXJy5dTlRXlCLd8ufWhhiwW0W75Va5AEnJuqpQrKwl3KQVe
+ wGj67WWRgLfSr+4QG1mNvCZb2CkjZWmxkGPuoP40/y7Yu5OFqxP5tAjj4YixCYTW
+ EVA0pmzIzgBg+JIe3PdRy27T0asgQW/F4TY61Yk=
+ -----END CERTIFICATE-----
+ CERT
+end
+
+chef_client_systemd_timer "Run chef-client as a systemd timer" do
+ interval "1hr"
+ cpu_quota 50
+end
+```
+
+### Target Mode Improvements
+
+Chef Infra Client 16 introduced an experimental Target Mode feature for executing resources remotely against hosts that do not have a Chef Infra Client or even Ruby installed. For Chef Infra Client 16.6 we've improved this functionality by converting the majority of the Ohai plugins to run remotely. This means when using Target Mode you'll have the majority of Ohai data as if the Chef Infra Client was installed on the node. Keep in mind this data collection can be time consuming over high latency network connections, and cloud plugins which fetch metadata cannot currently be run remotely. Ohai also now includes a `--target` option for remote data gathering, which accepts a Train URI: `ohai --target ssh://foobar.example.org/`. We still consider Target Mode to be an experimental feature, and we'd love your feedback on what works and what doesn't in your environment. A super huge thanks for the countless hours of work put in by [tecRacer](https://www.tecracer.de/), [@tecracer-theinen](https://github.com/tecracer-theinen), and [burtlo](https://github.com/burtlo) to make this a reality.
+
+### Updated Resources
+
+#### ifconfig
+
+The `ifconfig` resource has been updated to no longer add empty blank lines to the configuration files. Thanks for this improvement [@jmherbst](https://github.com/jmherbst/)!
+
+#### windows_audit_policy
+
+The `windows_audit_policy` resource has been updated to fix a bug on failure-only auditing.
+
+## Ohai Improvements
+
+#### Passwd Plugin For Windows
+
+The optional Ohai `Passwd` plugin now supports Windows hosts in addition to Unix-like systems. To collect user/group data on Windows hosts you can use the `ohai_optional_plugins` property in the new `chef_client_config` resource to enable this plugin.
+
+```ruby
+chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+ ohai_optional_plugins [:Passwd]
+end
+```
+
+Thanks for adding Windows support to this plugin [@jaymzh](https://github.com/jaymzh)!
+
+#### Improved Azure Detection
+
+The `Azure` plugin has been improved to better detect Windows hosts running on Azure. The plugin will now look for DHCP with the domain of `reddog.microsoft.com`. Thanks for this improvement [@jasonwbarnett](https://github.com/jasonwbarnett/)!
+
+#### EC2 IAM Role Data
+
+Ohai now collects IAM Role data on EC2 hosts including the role name and info. To address potential security concerns the data we collect is sanitized to ensure we don't report security credentials to the Chef Infra Server. Thanks for this improvement [@kcbraunschweig](https://github.com/kcbraunschweig)!
+
+### Security
+
+Ruby has been updated to 2.7.2, which includes a fix for [CVE-2020-25613](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-25613).
+
+## Whats New in 16.5.77
+
+- Added missing requires to prevent errors when loading `chef/policy_builder/dynamic`.
+- The `homebrew_package` resource will now check for the full and short package names. Both `homebrew_package 'homebrew/core/vim'` and `homebrew_package 'vim'` styles should now work correctly.
+- Resolved errors that occurred in cookbooks requiring `addressable/uri`.
+- Improved the license acceptance flow to give helpful information if the user passes an invalid value in the environment variable or command line argument.
+- Updated Chef InSpec to 4.23.11 in order to resolve issues when running the new `junit2` reporter.
+- Additional performance improvements to reduce the startup time of the `chef-client` and `knife` commands.
+- `knife vault` commands now output proper JSON or YAML when using the `-f json` or `-f yaml` flags.
+
+## What's New in 16.5
+
+### Performance Improvements
+
+We continue to reduce the size of the Chef Infra Client install and optimize the performance of the client. With Chef Infra Client 16.5 we've greatly reduced the startup time of the `chef-client` process. Startup times on macOS, Linux, and Windows hosts are now approximately 2x faster than the 16.4 release.
+
+### CLI Improvements
+
+- The client license acceptance logic has been improved to provide helpful error messages when an incorrect value is passed and to accept license values in any text case.
+- A new `chef-client` process exit code of 43 has been added to signal that an invalid configuration was specified. Thanks [@NaomiReeves](https://github.com/NaomiReeves)!
+- The `knife ssh` command no longer hangs when connecting to Windows nodes over SSH.
+- The `knife config` commands have been renamed to make them shorter and table output has been improved:
+ - knife config get-profile -> knife config use
+ - knife config use-profile [NAME] -> knife config use [NAME]
+ - knife config list-profiles -> knife config list
+ - knife config get -> knife config show
+
+### Chef InSpec 4.23.4
+
+Chef InSpec has been updated from 4.22.1 to 4.23.4. This new release includes the following improvements:
+
+- A new mechanism marks inputs as sensitive: true and replaces their values with `***`.
+- Use the `--no-diff` CLI option to suppress diff output for textual tests.
+- Control the order of controls in output, but not execution order, with the `--sort_results_by=none|control|file|random` CLI option.
+- Disable caching of inputs with a cache_inputs: true setting.
+
+### New Resources
+
+#### chef_client_launchd
+
+The `chef_client_launchd` resource allows you to configure Chef Infra Client to run as a global launchd daemon on macOS hosts. This resource mirrors the configuration of other `chef_client_*` resources and allows for simple out-of-the-box configuration of the daemon, while also providing advanced tunables. If you've used the `chef-client` cookbook in the past, you'll notice a number of improvements in the new resource including configuration update handling, splay times support, nice level support, and an out-of-the-box configuration of low IO priority execution. In order to handle restarting the Chef Infra Client launchd daemon when configuration changes occur, the resource also installs a new `com.chef.restarter` daemon. This daemon watches for daemon configuration changes and gracefully handles the restart to ensure the client process continues to run.
+
+```ruby
+chef_client_launchd 'Setup the Chef Infra Client to run every 30 minutes' do
+ interval 30
+ action :enable
+end
+```
+
+#### chef_client_trusted_certificate
+
+The `chef_client_trusted_certificate` resource allows you to add a certificate to Chef Infra Client's trusted certificate directory. The resource handles platform-specific locations and creates the trusted certificates directory if it doesn't already exist. Once a certificate is added, it will be used by the client itself to communicate with the Chef Infra Server and by resources such as `remote_file`.
+
+```ruby
+chef_client_trusted_certificate 'self-signed.badssl.com' do
+ certificate <<~CERT
+ -----BEGIN CERTIFICATE-----
+ MIIDeTCCAmGgAwIBAgIJAPziuikCTox4MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
+ c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0x
+ OTEwMDkyMzQxNTJaFw0yMTEwMDgyMzQxNTJaMGIxCzAJBgNVBAYTAlVTMRMwEQYD
+ VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK
+ DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB
+ BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2
+ PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW
+ hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A
+ xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve
+ ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY
+ QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T
+ BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI
+ hvcNAQELBQADggEBAGlwCdbPxflZfYOaukZGCaxYK6gpincX4Lla4Ui2WdeQxE95
+ w7fChXvP3YkE3UYUE7mupZ0eg4ZILr/A0e7JQDsgIu/SRTUE0domCKgPZ8v99k3A
+ vka4LpLK51jHJJK7EFgo3ca2nldd97GM0MU41xHFk8qaK1tWJkfrrfcGwDJ4GQPI
+ iLlm6i0yHq1Qg1RypAXJy5dTlRXlCLd8ufWhhiwW0W75Va5AEnJuqpQrKwl3KQVe
+ wGj67WWRgLfSr+4QG1mNvCZb2CkjZWmxkGPuoP40/y7Yu5OFqxP5tAjj4YixCYTW
+ EVA0pmzIzgBg+JIe3PdRy27T0asgQW/F4TY61Yk=
+ -----END CERTIFICATE-----
+ CERT
+end
+```
+
+### Resource Updates
+
+#### chef_client_cron
+
+The `chef_client_cron` resource has been updated with a new `nice` property that allows you to set the nice level for the `chef-client` process. Nice level changes only apply to the `chef-client` process and not any subprocesses like `ohai` or system utility calls. If you need to ensure that the `chef-client` process does not negatively impact system performance, we highly recommend instead using the `cpu_quota` property in the `chef_client_systemd_timer` resource which applies to all child processes.
+
+#### chef_client_systemd_timer
+
+The `chef_client_systemd_timer` resource has been updated with a new `cpu_quota` property that allows you to control the systemd `CPUQuota` value for the `chef-client` process. This allows you to ensure `chef-client` execution doesn't adversely impact performance on your systems.
+
+#### launchd
+
+The `launchd` resource has been updated to better validate inputs to the `nice` property so we can make sure these are acceptable nice values.
+
+#### mount
+
+The `mount` resource on Linux has new improved idempotency in some scenarios by switching to `findmnt` to determine the current state of the system. Thanks for reporting this issue [@pollosp](https://github.com/pollosp)!
+
+#### osx_profile
+
+The `osx_profile` resource will now allow you to remove profiles from macOS 11 (Big Sur) systems. Due to security changes in macOS 11, it is no longer possible to locally install profiles, but this will allow you to cleanup existing profiles left over after an upgrade from an earlier macOS release. The resource has been updated to resolve a regression introduced in Chef Infra Client 16.4 that caused the resource to attempt to update profiles on each converge. Thanks for reporting these issues [@chilcote](https://github.com/chilcote)!
+
+#### rhsm_register
+
+The `rhsm_register` resource has been updated to reduce the load on the RedHat Satellite server when checking if a system is already registered. Thanks for reporting this issue [@donwlewis](https://github.com/donwlewis)! A new `system_name` property has also been added to allow you to register a name other than the system's hostname. Thanks for this improvement [@jasonwbarnett](https://github.com/jasonwbarnett/)!
+
+#### windows_ad_join
+
+The `windows_ad_join` resource has been updated with a new `reboot_delay` property which allows you to control the delay time before restarting systems.
+
+#### windows_firewall_profile
+
+The `windows_firewall_profile` resource was updated to prevent NilClass errors from loading the firewall state.
+
+#### windows_user_privilege
+
+The `windows_user_privilege` resource has been updated to better validate the `privilege` property and to allow the `users` property to accept String values. Thanks for reporting this issue [@jeremyciak](https://github.com/jeremyciak)!
+
+#### Windows securable resources
+
+All Windows securable resources now support using SID in addition to user or group name when specifying `owner`, `group`, or `rights` principal. These resources include the `template`, `file`, `remote_file`, `cookbook_file`, `directory`, and `remote_directory` resources. When using a SID, you may use either the standard string representation of a SID (S-R-I-S-S) or one of the [SDDL string constants](https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-strings).
+
+### Ohai Improvements
+
+- Ohai now uses the same underlying code for shelling out to external commands as Chef Infra Client. This may resolve issues from determining the state on some non-English systems.
+- The `Packages` plugin has been updated to gather package installation information on macOS hosts.
+
+### Platform Packages
+
+- We are once again building Chef Infra Client packages for RHEL 7 / SLES 12 on the S390x architecture. In addition to these packages, we've also added S390x packages for RHEL 8 / SLES 15.
+- We now produce packages for Apple's upcoming macOS 11 Big Sur release.
+
+### Security
+
+OpenSSL has been updated to 1.0.2w which includes a fix for [CVE-2020-1968](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-1968).
+
+## What's New in 16.4
+
+### Resource Updates
+
+#### chef_client_systemd_timer
+
+The `chef_client_systemd_timer` resource has been updated to prevent failures running the `:remove` action.
+
+#### openssl resource
+
+The various openssl_* resources were refactored to better report the changed state of the resource to Automate or other handlers.
+
+#### osx_profile
+
+The `osx_profile` resource has been refactored as a custom resource internally. This update also better reports the changed state of the resource to Automate or other handlers and no longer silently continues if the attempts to shellout fail.
+
+#### powershell_package_source
+
+The `powershell_package_source` resource no longer requires the `url` property to be set when using the `:unregister` action. Thanks for this fix [@kimbernator](https://github.com/kimbernator)!
+
+#### powershell_script
+
+The `powershell_script` resource has been refactored to better report the changed state of the resource to Automate or other handlers.
+
+#### windows_feature
+
+The `windows_feature` resource has been updated to allow installing features that have been removed if a source location is provided. Thanks for reporting this [@stefanwb](https://github.com/stefanwb)!
+
+#### windows_font
+
+The `windows_font` resource will no longer fail on newer releases of Windows if a font is already installed. Thanks for reporting this [@bmiller08](https://github.com/bmiller08)!
+
+#### windows_workgroup
+
+The `windows_workgroup` resource has been updated to treat the `password` property as a sensitive property. The value of the `password` property will no longer be shown in logs or handlers.
+
+### Security
+
+#### CA Root Certificates
+
+The included `cacerts` bundle in Chef Infra Client has been updated to the 7-22-2020 release. This new release removes 4 legacy root certificates and adds 4 additional root certificates.
+
+#### Reduced Dependencies
+
+We've audited the included dependencies that we ship with Chef Infra Client to reduce the 3rd party code we ship. We've removed many of the embedded binaries that shipped with the client in the past, but were not directly used. We've also reduced the feature set built into many of the libraries that we depend on, and removed several Ruby gem dependencies that were no longer necessary. This reduces the future potential for CVEs in the product and reduces package size at the same time.
+
+## What's New in 16.3.45
+
+- Resolved failures negotiating protocol versions with the Chef Infra Server.
+- Improved log output on Windows systems in the `hostname` resource.
+- Added support to the `archive_file` resource for `pzstd` compressed files.
+
+## What's New in 16.3.38
+
+### Renamed Client Configuration Options
+
+We took a hard look at many of the terms we've historically used throughout the Chef Infra Client configuration sub-system and came to the realization that we weren't living up to the words of our [Community Code of Conduct](https://community.chef.io/code-of-conduct/). From the code of conduct: "Be careful in the words that you choose. Be kind to others. Practice empathy". Terms such as blacklist and sanity don't meet that bar so we've chosen to rename these configuration options:
+
+- `automatic_attribute_blacklist` -> `blocked_automatic_attributes`
+- `default_attribute_blacklist` -> `blocked_default_attributes`
+- `normal_attribute_blacklist` -> `blocked_normal_attributes`
+- `override_attribute_blacklist` -> `blocked_override_attributes`
+- `automatic_attribute_whitelist` -> `allowed_automatic_attributes`
+- `default_attribute_whitelist` -> `allowed_default_attributes`
+- `normal_attribute_whitelist` -> ``allowed_normal_attributes``
+- `override_attribute_whitelist` -> `allowed_override_attributes`
+- `enforce_path_sanity` -> `enforce_default_paths`
+
+Existing configuration options will continue to function for now, but will raise a deprecation warning and will be removed entirely from a future release of Chef Infra Client.
+
+### Chef InSpec 4.22.1
+
+Chef InSpec has been updated from 4.21.1 to 4.22.1. This new release includes the following improvements:
+
+- The `=` character is now allowed for command line inputs
+- `apt-cdrom` repositories are now skipped when parsing out the list of apt repositories
+- Faulty profiles are now reported instead of causing a crash
+- Errors are no longer logged to stdout with the `html2` reporter
+- macOS Big Sur is now correctly identified as macOS
+
+### New Resources
+
+#### windows_firewall_profile
+
+The `windows_firewall_profile` allows you to `enable`, `disable`, or `configure` Windows Firewall profiles. For example, you can now set up default actions and configure rules for the `Public` profile using this single resource instead of managing your own PowerShell code in a `powershell_script` resource:
+
+```ruby
+windows_firewall_profile 'Public' do
+ default_inbound_action 'Block'
+ default_outbound_action 'Allow'
+ allow_inbound_rules false
+ display_notification false
+ action :enable
+end
+```
+
+For a complete guide to all properties and additional examples, see the [windows_firewall_profile documentation](https://docs.chef.io/resources/windows_firewall_profile).
+
+### Resource Updates
+
+#### build_essential
+
+Log output has been improved in the `build_essential` resource when running on macOS systems.
+
+#### chef_client_scheduled_task
+
+The `chef_client_scheduled_task` resource no longer sets up the schedule task with invalid double quoting around the specified command. Thanks for reporting this issue [@tiobagio](https://github.com/tiobagio/).
+
+#### execute
+
+The `user` property in the `execute` resource can now accept user IDs as Integers.
+
+#### git
+
+The `git` resource will no longer fail if syncing a branch that already exists locally. Thanks for fixing this [@lotooo](https://github.com/lotooo/).
+
+#### macos_user_defaults
+
+The `macos_user_defaults` has received a ground-up refactoring with new actions, additional properties, and better overall reliability:
+
+- Improved idempotency by properly loading the current state of domains.
+- Improved how we set `dict` and `array` type data.
+- Improved logging to show the existing key/value pair that is changed, and improved the property state data that the resource sends to handlers and/or Chef Automate.
+- Fixed a failure when setting keys or values that included a space.
+- Replaced the existing non-functional `global` property with a new default for the `domain` property. To set a key/value pair on the `NSGlobalDomain` domain, you can either set that value explicitly or just skip the `domain` property entirely and Chef Infra Client will default to `NSGlobalDomain`. The existing property has been marked as deprecated and we will ship a Cookstyle rule to detect cookbooks using this property in the future.
+- Fixed the `type` property to only accept valid inputs. Previously typos or otherwise incorrect values would just be ignored resulting in unexpected behavior. This may cause failures in your codebase if you previously used incorrect values. We will be shipping a Cookstyle rule to detect and correct these values in the future.
+- Added a new `delete` action to allow users to remove a key from a domain.
+- Added a new `host` property that lets you set per-host values. If you set this to `:current` it sets the -currentHost flag.
+
+#### windows_dns_record
+
+The `windows_dns_record` resource includes a new optional property, `dns_server`, allowing you to make changes against remote servers. Thanks for this addition [@jeremyciak](https://github.com/jeremyciak/).
+
+#### windows_package
+
+A Chef Infra Client 16 regression within `windows_package` that prevented specifying `path` in the `remote_file_attributes` property has been resolved. Thanks for reporting this issue [@asvinours](https://github.com/asvinours/).
+
+#### windows_security_policy
+
+The `windows_security_policy` resource has been refactored to improve idempotency and improve log output when changes are made. You'll now see more complete change information in logs and any handler consuming this data will also receive more detailed change information.
+
+### Knife Improvements
+
+- Ctrl-C can now be used to exit knife even when being prompted for input.
+- `knife bootstrap` will now properly error if attempting to bootstrap an AIX system using an account with an expired password.
+- `knife profile` commands will no longer error if an invalid profile was previously set.
+- The `-o` flag for `knife cookbook upload` can now be used on Windows systems.
+- `knife ssh` now once again accepts legacy DSS host keys although we highly recommend upgrading to a more secure key algorithm if possible.
+- Several changes were made to knife to that may prevent intermittent failures running cookbook commands
+
+### Habitat Package Improvements
+
+Habitat packages for Windows, Linux and Linux2 are now built and tested against each pull request to Chef Infra Client. Additionally we've improved how these packages are built to reduce the size of the package, which reduces network utilization when using the Effortless deployment pattern.
+
+## What's New in 16.2.72
+
+- Habitat packages for Chef Infra Client 16 are now published with full support for the `powershell_exec` helper now added.
+- Added a new `clear` action to the `windows_user_privilege` resource.
+- Resolved a regression in Chef Infra Client 16.1 and later that caused failures running on FIPS enabled systems.
+- Resolved failures in the `archive_file` resource when running on Windows hosts.
+- Resolved a failure when running `chef-apply` with the `-j` option. Thanks [@komazarari](https://github.com/komazarari).
+- Chef Infra Client running within GitHub Actions is now properly identified as running in a Docker container. Thanks [@jaymzh](http://github.com/jaymzh).
+- SSH connections are now reused, improving the speed of knife bootstrap and remote resources on slow network links. Thanks [@tecracer-theinen](https://github.com/tecracer-theinen).
+- `node['network']['interfaces']` data now correctly identifies IPv6 next hops for IPv4 routes. Thanks [@cooperlees](https://github.com/cooperlees).
+- Updated InSpec from 4.20.10 to 4.21.1.
+
+## What's New in 16.2.50
+
+- Correctly identify the new macOS Big Sur (11.0) beta as platform "mac_os_x".
+- Fix `knife config use-profile` to fail if an invalid profile is provided.
+- Fix failures running the `windows_security_policy` resource.
+- Update InSpec from 4.20.6 to 4.20.10.
+
+## What's New in 16.2.44
+
+### Breaking Change in Resources
+
+In Chef Infra Client 16.0, we changed the way that custom resource names are applied in order to resolve some longstanding edge-cases. This change had several unintended side effects, so we're further changing how custom names are set in this release of Chef Infra Client.
+
+Previously you could set a custom name for a resource via `resource_name` and under the hood this would also magically set the `provides` for the resource. Magic is great when it works, but is confusing when it doesn't. We've decided to remove some of this magic and instead rely on more explicit `provides` statements in resources. For cookbooks that support just Chef Infra Client 16 and later, you should change any `resource_name` calls to `provides` instead. If you need to support older releases of Chef Infra Client as well as 16+, you'll want to include both `resource_name` and `provides` for full compatibility.
+
+**Pre-16 code:**
+
+```ruby
+resource_name :foo
+```
+
+**Chef Infra Client 16+ code**
+
+```ruby
+provides :foo
+```
+
+**Chef Infra Client < 16 backwards compatible code**
+
+```ruby
+resource_name :foo
+provides :foo
+```
+
+We've introduced several Cookstyle rules to detect both custom resources and legacy HWRPs that need to be updated for this change:
+
+**[ChefDeprecations/ResourceUsesOnlyResourceName](https://github.com/chef/cookstyle/blob/master/docs/cops_chefdeprecations.md#chefdeprecationsresourceusesonlyresourcename)**: detects resources that only set resource_name and automatically adds a provides call as well.
+
+**[ChefDeprecations/HWRPWithoutProvides](https://github.com/chef/cookstyle/blob/master/docs/cops_chefdeprecations.md#chefdeprecationshwrpwithoutprovides)**: detects legacy HWRPs that don't include the necessary provides and resource_name calls for Chef Infra Client 16.
+
+### Chef InSpec 4.20.6
+
+Chef InSpec has been updated from 4.18.114 to 4.2.0.6. This new release includes the following improvements:
+
+- Develop your own Chef InSpec Reporter plugins to control how Chef InSpec will report result data.
+- The `inspec archive` command packs your profile into a `tar.gz` file that includes the profile in JSON form as the inspec.json file.
+- Certain substrings within a `.toml` file no longer cause unexpected crashes.
+- Accurate InSpec CLI input parsing for numeric values and structured data, which were previously treated as strings. Numeric values are cast to an `integer` or `float` and `YAML` or `JSON` structures are converted to a hash or an array.
+- Suppress deprecation warnings on inspec exec with the `--silence-deprecations` option.
+
+### New Resources
+
+#### windows_audit_policy
+
+The `windows_audit_policy` resource is used to configure system-level and per-user Windows advanced audit policy settings. See the [windows_audit_policy Documentation](/resources/windows_audit_policy/) for complete usage information.
+
+For example, you can enable auditing of successful credential validation:
+
+```ruby
+windows_audit_policy "Set Audit Policy for 'Credential Validation' actions to 'Success'" do
+ subcategory 'Credential Validation'
+ success true
+ failure false
+ action :set
+end
+```
+
+#### homebrew_update
+
+The `homebrew_update` resource is used to update the available package cache for the Homebrew package system similar to the behavior of the `apt_update` resource. See the [homebrew_update Documentation](/resources/homebrew_update/) for complete usage information. Thanks for adding this new resource, [@damacus](http://github.com/damacus).
+
+### Resource Updates
+
+#### All resources now include umask property
+
+All resources, including custom resources, now have a `umask` property which allows you to specify a umask for file creation. If not specified the system default will continue to be used.
+
+#### archive_file
+
+The `archive_file` resource has been updated with two important fixes. The resource will no longer fail with uninitialized constant errors under some scenarios. Additionally, the behavior of the `mode` property has been improved to prevent incorrect file modes from being applied to the decompressed files. Due to how file modes and Integer values are processed in Ruby, this resource will now produce a deprecation warning if integer values are passed. Using string values lets us accurately pass values such as '644' or '0644' without ambiguity as to the user's intent. Thanks for reporting these issues [@sfiggins](http://github.com/sfiggins) and [@hammerhead](http://github.com/hammerhead).
+
+#### chef_client_scheduled_task
+
+The `chef_client_scheduled_task` resource has been updated to default the `frequency_modifier` property to `30` if the `frequency` property is set to `minutes`, otherwise it still defaults to `1`. This provides a more predictable schedule behavior for users.
+
+#### cron / cron_d
+
+The `cron` and `cron_d` resources have been updated using the new Custom Resource Partials functionality introduced in Chef Infra Client 16. This has allowed us to standardize the properties used to declare cron job timing between the two resources. The timing properties in both resources all accept the same types and ranges, and include the same validation, which makes moving from `cron` to `cron_d` seamless.
+
+#### cron_access
+
+The `cron_access` resource has been updated to support Solaris and AIX systems. Thanks [@aklyachkin](http://github.com/aklyachkin).
+
+#### execute
+
+The `execute` resource has a new `input` property which allows you to pass `stdin` input to the command being executed.
+
+#### powershell_package
+
+The `powershell_package` resource has been updated to use TLS 1.2 when communicating with the PowerShell Gallery on Windows Server 2012-2016. Previously this resource used the system default cipher suite which did not include TLS 1.2. The PowerShell Gallery now requires TLS 1.2 for all communication, which caused failures on Windows Server 2012-2016. Thanks for reporting this issue [@Xorima](http://github.com/Xorima).
+
+#### remote_file
+
+The `remote_file` resource has a new property `ssl_verify_mode` which allows you to control SSL validation at the property level. This can be used to verify certificates (Chef Infra Client's defaults) with `:verify_peer` or to skip verification in the case of a self-signed certificate with `:verify_none`. Thanks [@jaymzh](http://github.com/jaymzh).
+
+#### script
+
+The various `script` resources such as `bash` or `ruby` now pass the provided script content to the interpreter using system pipes instead of writing to a temporary file and executing it. Executing script content using pipes is faster, more secure as potentially sensitive scripts aren't written to disk, and bypasses issues around user privileges.
+
+#### snap_package
+
+Multiple issues with the `snap_package` resource have been resolved, including an infinite wait that occurred, and issues with specifying the package version or channel. Thanks [@jaymzh](http://github.com/jaymzh).
+
+#### zypper_repository
+
+The `zypper_repository` resource has been updated to work with the newer release of GPG in openSUSE 15 and SLES 15. This prevents failures when importing GPG keys in the resource.
+
+### Knife bootstrap updates
+
+- Knife bootstrap will now warn when bootstrapping a system using a validation key. Users should instead use `validatorless bootstrapping` with `knife bootstrap` which generates node and client keys using the client key of the user bootstrapping the node. This method is far more secure as an org-wide validation key does not not need to be distributed or rotated. Users can switch to `validatorless bootstrapping` by removing any `validation_key` entries in their `config.rb (knife.rb)` file.
+- Resolved an error bootstrapping Linux nodes from Windows hosts
+- Improved information messages during the bootstrap process
+
+### Platform Packages
+
+- Debian 8 packages are no longer being produced as Debian 8 is now end-of-life.
+- We now produce Windows 8 packages
+
+## What's New in 16.1.16
+
+This release resolves high-priority bugs in the 16.1 release of Chef Infra Client:
+
+- Resolved a critical performance regression in the Rubygems release within Ruby 2.7, which was discovered by a Chef engineer.
+- Resolved several Ruby 2.7 deprecation warnings.
+- Added `armv6l` and `armv7l` architectures to the `arm?` and `armhf?` helpers
+- Resolved failures in the Windows bootstrap script
+- Resolved incorrect paths when bootstrapping Windows nodes
+
+### Security Updates
+
+#### openSSL
+
+openSSL has been updated from 1.0.2u to 1.0.2v which does not address any particular CVEs, but includes multiple security hardening updates.
-* Upgraded Ruby version from 2.1.9 to 2.3.1 which adds several performance and functionality enhancements.
-* Added a small patch to Ruby 2.3.1 and improvements to the Ohai Network plugin in order to support chef client runs on Windows Nano Server.
-* Added the ability to mark a property of a custom resource as "sensitive." This will suppress the property's value when it's used in other outputs, such as messages used by the [Data Collector](https://github.com/chef/chef-rfc/blob/master/rfc077-mode-agnostic-data-collection.md). To use, add `sensitive: true` when definine the property. Example:
+## What's New in 16.1
- ```ruby
- property :db_password, String, sensitive: true
- ```
+### Ohai 16.1
-* Ported the yum_repository resource from the yum cookbook to core chef. With this change you can create and remove repositories without depending on the yum cookbook. Example:
+Ohai 16.1 includes a new `Selinux` plugin which exposes `node['selinux']['status']`, `node['selinux']['policy_booleans']`, `node['selinux']['process_contexts']`, and `node['selinux']['file_contexts']`. Thanks [@davide125](http://github.com/davide125) for this contribution. This new plugin is an optional plugin which is disabled by default. It can be enabled within your `client.rb`:
- ```ruby
- yum_repository 'OurCo' do
- description 'OurCo yum repository'
- mirrorlist 'http://artifacts.ourco.org/mirrorlist?repo=ourco-6&arch=$basearch'
- gpgkey 'http://artifacts.ourco.org/pub/yum/RPM-GPG-KEY-OURCO-6'
- action :create
+```ruby
+ohai.optional_plugins = [ :Selinux ]
+```
+
+### Chef InSpec 4.18.114
+
+InSpec has been updated from 4.18.111 to 4.18.114. This update adds new `--reporter_message_truncation` and `--reporter_backtrace_inclusion` reporter options to truncate messages and suppress backtraces.
+
+### Debian 10 aarch64
+
+Chef Infra Client packages are now produced for Debian 10 on the aarch64 architecture. These packages are available at [downloads.chef.io](https://downloads.chef.io/chef/).
+
+### Bug Fixes
+
+- Resolved a regression in the `launchd` resource that prevented it from converging.
+- The `:disable` action in the `launchd` resource no longer fails if the plist was not found.
+- Several Ruby 2.7 deprecation warnings have been resolved.
+
+## What's New in 16.0.287
+
+The Chef Infra Client 16.0.287 release includes important bug fixes for the Chef Infra Client 16 release:
+
+- Fixes the failure to install Windows packages on the 2nd convergence of the Chef Infra Client.
+- Resolves several failures in the `launchd` resource.
+- Removes an extra `.java` file on Windows installations that would cause a failure in the IIS 8.5 Server Security Technical Implementation Guide audit.
+- Updates the `windows_printer` resource so that the driver property will only be required when using the `:create` action.
+- Fixes the incorrectly spelled `knife user invite recind` command to be `knife user invite rescind`.
+- Update Chef InSpec to 4.8.111 with several minor improvements.
+
+## What's New in 16.0.275
+
+The Chef Infra Client 16.0.275 release includes important regression fixes for the Chef Infra Client 16 release:
+
+- Resolved failures when using the `windows_package` resource. Thanks for reporting this issue [@cookiecurse](https://github.com/cookiecurse).
+- Resolved log warnings when running `execute` resources.
+- The appropriate `cron` or `cron_d` resource call is now called when using the `:delete` action in chef_client_cron. Thanks for reporting this issue [jimwise](https://github.com/jimwise).
+- The `chef_client_cron` resource now creates the log directory with `750` permissions not `640`. Thanks for this fix [DhaneshRaghavan](https://github.com/DhaneshRaghavan).
+- The `knife yaml convert` command now correctly converts symbol values.
+- The `sysctl`, `apt_preference`, and `cron_d` remove actions no longer fail with missing property warnings.
+
+## What's New in 16.0
+
+### Breaking Changes
+
+#### Log Resource Notification Behavior
+
+The `log` resource in a recipe or resource will no longer trigger notifications by default. This allows authors to more liberally use `log` resources without impacting the updated resources count or impacting reporting to Chef Automate. This change will impact users that used the `log` resource to aggregate notifications from other resources, so they could limit the number of times a notification would fire. If you used the `log` resource to aggregate multiple notifications, you should convert to using the `notify group` resource, which was introduced in Chef Infra Client 15.8.
+
+Example of notification aggregation with `log` resource:
+
+```ruby
+template '/etc/foo' do
+ source 'foo.erb'
+ notifies :write, 'log[Aggregate notifications using a single log resource]', :immediately
+end
+
+template '/etc/bar' do
+ source 'bar.erb'
+ notifies :write, 'log[Aggregate notifications using a single log resource]', :immediately
+end
+
+log 'Aggregate notifications using a single log resource' do
+ notifies :restart, 'service[foo]', :delayed
+end
+```
+
+Example of notification aggregation with `notify_group` resource:
+
+```ruby
+template '/etc/foo' do
+ source 'foo.erb'
+ notifies :run, 'notify_group[Aggregate notifications using a single notify_group resource]', :immediately
+end
+
+template '/etc/bar' do
+ source 'bar.erb'
+ notifies :run, 'notify_group[Aggregate notifications using a single notify_group resource]', :immediately
+end
+
+notify_group 'Aggregate notifications using a single notify_group resource' do
+ notifies :restart, 'service[foo]', :delayed
+end
+```
+
+The `ChefDeprecations/LogResourceNotifications` cop in Cookstyle 6.0 and later detects using the `log` resource for notifications in cookbooks.
+
+To restore the previous behavior, set `count_log_resource_updates true` in your `client.rb`.
+
+#### HWRP Style Resources Now Require resource_name / provides
+
+Legacy HWRP-style resources, written as Ruby classes in the libraries directory of a cookbook, will now require either the use of `resource_name` or `provides` methods to define the resource names. Previously, Chef Infra Client would infer the desired resource name from the class, but this magic was problematic and has been removed.
+
+The `ChefDeprecations/ResourceWithoutNameOrProvides` cop in Cookstyle 6.0 and later detects this deprecation.
+
+#### build_essential GCC Updated on Solaris
+
+On Solaris systems, we no longer constrain the version of GCC to 4.8.2 in the `build_essential` resource to allow for GCC 5 installations.
+
+#### git Resource Branch Checkout Changes
+
+The `git` resource no longer checks out to a new branch named `deploy` by default. Many users found this branching behavior confusing and unexpected so we've decided to implement a more predictable default. The resource will now default to either checking out the branch specified with the `checkout_branch` property or a detached HEAD state. If you'd like to revert to the previous behavior you can set the `checkout_branch` to `deploy`.
+
+#### s390x Packaging
+
+As outlined in our blog post at <https://blog.chef.io/chef-infra-end-of-life-announcement-for-linux-client-on-ibm-s390x-architecture/>, we will no longer be producing s390x platform packages for Chef Infra Client.
+
+#### filesystem2 Node Data Replaces filesystem on FreeBSD / AIX / Solaris
+
+In Chef Infra Client 14 we introduced a modernized filesystem layout of Ohai data on FreeBSD, AIX, and Solaris at `node['fileystem2']`. With the release of 16.0, we are now replacing the existing data at `node['filesystem']` with this updated filesystem data. This data has a standardized format that matches Linux and macOS data to make it easier to write cross-platform cookbooks. In a future release of Chef Infra Client we'll remove the `node['filesystem2']` as we complete this migration.
+
+#### required: true on Properties Now Behaves As Expected
+
+The behavior of `required: true` has been changed to better align with the expected behavior. Previously, if you set a property `required: true` on a custom resource property and did not explicitly reference the property in an action, then Chef Infra Client would not raise an exception. This meant many users would add their own validation to raise for resources they wanted to ensure they were always set. `required: true` will now properly raise if a property has not been set.
+
+We have also expanded the `required` field for added flexibility in defining exactly which actions a property is required for. See [Improved property require behavior](#improved-property-require-behavior) below for more details.
+
+#### Removal of Legacy metadata.rb depends Version Constraints
+
+Support for the `<<` and `>>` version constraints in metadata.rb has been removed. This was an undocumented feature from the Chef 0.10 era, which is not used in any cookbooks on the Supermarket. We are mentioning it since it is technically a breaking change, but it unlikely that this change will be impacting.
+
+Examples:
+
+```ruby
+depends 'windows', '<< 1.0'
+depends 'windows', '>> 1.0'
+```
+
+#### Logging Improvements May Cause Behavior Changes
+
+We've made low-level changes to how logging behaves in Chef Infra Client that resolves many complaints we've heard of the years. With these change you'll now see the same logging output when you run `chef-client` on the command line as you will in logs from a daemonized client run. This also corrects often confusing behavior where running `chef-client` on the command line would log to the console, but not to the log file location defined your `client.rb`. In that scenario you'll now see logs in your console and in your log file. We believe this is the expected behavior and will mean that your on-disk log files can always be the source of truth for changes that were made by Chef Infra Client. This may cause unexpected behavior changes for users that relied on using the command line flags to override the `client.rb` log location - in this case logging will be sent to *both- the locations in `client.rb` and on the command line. If you have daemons running that log using the command line options you want to make sure that `client.rb` log location either matches or isn't defined.
+
+#### Red Hat / CentOS 6 Systems Require C11 GCC for Some Gem Installations
+
+The included release of Ruby in Chef Infra Client 16 now requires a [C99](https://en.wikipedia.org/wiki/C99) compliant compiler when using the `chef_gem` resource with gems that require compilation. Some systems, such as RHEL 6, do not ship with a C99 compiler and will fail if the gems they're attempting to install require compilation. If it is necessary to install compiled gems into the Chef Infra Client installation on one of these systems you can upgrade to a modern GCC release.
+
+CentOS:
+
+```bash
+yum install centos-release-scl
+yum install devtoolset-7
+scl enable devtoolset-7 bash
+```
+
+Red Hat:
+
+```bash
+yum-config-manager --enable rhel-server-rhscl-7-rpms
+yum install devtoolset-7
+scl enable devtoolset-7 bash
+```
+
+#### Changes to Improve Gem Source behavior
+
+We've improved the behavior for those that use custom rubygem sources, particularly those operating in air-gapped installations. These improvements involved changes to many of the default `client.rb` values and `gem_package`/`chef_gem` properties that require updating your usage of `chef_gem` and `gem_package` resources
+
+The default value of the `clear_sources` property of `gem_package` and `chef_gem` resources has been changed to `nil`. The possible behaviors for clear_sources are now:
+
+- `true`: Always clear sources.
+- `false`: Never clear sources.
+- `nil`: Clear sources if `source` property is set, but don't clear sources otherwise.
+
+The default value of the `include_default_source` property of `gem_package` and `chef_gem` resources has been changed to `nil`. The possible behaviors for include_default_source are now:
+
+- `true`: Always include the default source.
+- `false`: Never include the default source.
+- `nil`: Include the default source if `rubygems_url` `client.rb` value is set or if `source` and `clear_sources` are not set on the resource.
+
+The default values of the `rubygems_url` `client.rb` config option has been changed to `nil`. Setting to nil previously had similar behavior to setting `clear_sources` to true, but with some differences. The new behavior is to always use `https://rubygems.org` as the default rubygems repo unless explicitly changed, and whether to use this value is determined by `clear_sources` and `include_default_source`.
+
+#### Behavior Changes in Knife
+
+**knife status --long uses cloud attribute**
+
+The `knife status --long` resource now uses Ohai's cloud data instead of ec2 specific data. This improves, but changes, the data output for users on non-AWS clouds.
+
+**knife download role/environment format update**
+
+The `knife download role` and `knife download environment` commands now include all possible data fields including those without any data set. This new output behavior matches the behavior of other commands such as `knife role show` or `knife environment show`
+
+**Deprecated knife cookbook site command removed**
+
+The previously deprecated `knife cookbook site` commands have been removed. Use the `knife supermarket` commands instead.
+
+**Deprecated knife data bag create -s short option removed**
+
+The deprecated `knife data bag create -s` option that was not properly honored has been removed. Use the `--secret` option instead to set a data bag secret file during data bag creation.
+
+**sites-cookbooks directory no longer in cookbook_path**
+
+The legacy `sites-cookbooks` directory is no longer added to the default `cookbook_path` value. With this change, any users with a legacy `sites-cookbooks` directory will need to use the `-O` flag to override the cookbook directory when running commands such as `knife cookbook upload`.
+
+If you have a repository that contains a `site-cookbooks` directory, we highly recommend using Policyfiles or Berkshelf to properly resolve these external cookbook dependencies without the need to copy them locally. Alternatively, you can move the contents of this folder into your main cookbook directory and they will continue to be seen by knife commands.
+
+### New Resources
+
+#### alternatives
+
+Use the `alternatives` resource to manage symbolic links to specify default command versions on Linux hosts. See the [alternatives documentation](https://docs.chef.io/resources/alternatives/) for full usage information. Thanks [@vkhatri](https://github.com/vkhatri) for the original cookbook alternatives resource.
+
+#### chef_client resources
+
+We've added new resources to Chef Infra Client for setting the client to run on an interval using native system schedulers. We believe that these native schedulers provide a more flexible and reliable method for running the client than the traditional method of running as a full service. Using the native schedulers reduces hung clients and eases upgrades. This is the first of many steps towards removing the need for the `chef-client` cookbook and allowing Chef Infra Client to configure itself out of the box.
+
+**chef_client_cron**
+
+Use the `chef_client_cron` resource to setup the Chef Infra Client to run on a schedule using cron on Linux, Solaris, and AIX systems. See the [chef_client_cron documentation](https://docs.chef.io/resources/chef_client_cron/) for full usage information.
+
+**chef_client_systemd_timer**
+
+Use the `chef_client_systemd_timer` resource to setup the Chef Infra Client to run on a schedule using a systemd timer on systemd based Linux systems (RHEL 7+, Debian 8+, Ubuntu 16.04+ SLES 12+). See the [chef_client_systemd_timer documentation](https://docs.chef.io/resources/chef_client_systemd_timer/) for full usage information.
+
+**chef_client_scheduled_task**
+
+Use the `chef_client_scheduled_task` resource to setup the Chef Infra Client to run on a schedule using Windows Scheduled Tasks. See the [chef_client_scheduled_task documentation](https://docs.chef.io/resources/chef_client_scheduled_task) for full usage information.
+
+#### plist
+
+Use the `plist` resource to generate plist files on macOS hosts. See the [plist documentation](https://docs.chef.io/resources/plist/) for full usage information. Thanks Microsoft and [@americanhanko](https://github.com/americanhanko) for the original work on this resource in the [macos cookbook](https://supermarket.chef.io/cookbooks/macos).
+
+#### user_ulimit
+
+Use the `user_ulimit` resource to set per user ulimit values on Linux systems. See the [user_ulimit documentation](https://docs.chef.io/resources/user_ulimit/) for full usage information. Thanks [@bmhatfield](https://github.com/bmhatfield) for the original work on this resource in the [ulimit cookbook](https://supermarket.chef.io/cookbooks/ulimit).
+
+#### windows_security_policy
+
+Use the `windows_security_policy` resource to modify location security policies on Windows hosts. See the [windows_security_policy documentation](https://docs.chef.io/resources/windows_security_policy/) for full usage information.
+
+#### windows_user_privilege
+
+Use the `windows_user_privilege` resource to add users and groups to the specified privileges on Windows hosts. See the [windows_user_privilege documentation](https://docs.chef.io/resources/windows_user_privilege/) for full usage information.
+
+### Improved Resources
+
+#### compile_time on all resources
+
+The `compile_time` property is now available for all resources so that they can be set to run at compile time without the need to force the action.
+
+Set the `compile_time` property instead of forcing the resource to run at compile time:
+
+```ruby
+ my_resource "foo" do
+ action :nothing
+ end.run_action(:run)
+```
+
+With the simpler `compile_time` property:
+
+```ruby
+ my_resource "foo" do
+ compile_time true
end
+```
+
+#### build_essential
+
+The `build_essential` resource includes a new `:upgrade` action for macOS systems that allows you to install updates to the Xcode Command Line Tools available via Software Update.
+
+#### cron
+
+The `cron` resource has been updated to use the same property validation for cron times that the `cron_d` resource uses. This improves failure messages when invalid inputs are set and also allows for `jan`-`dec` values to be used in the `month` property.
+
+#### dnf_package
+
+The `dnf_package` resource, which provides `package` under the hood on any system shipping with DNF, has been greatly refactored to resolve multiple issues. The version behavior and overall resource capabilities now match that of the `yum_package` resource.
+
+- The `:lock` action now works on RHEL 8.
+- Fixes to prevent attempting to install the same package during each Chef Infra Client run.
+- Resolved several idempotency issues.
+- Resolved an issue where installing a package with `options '--enablerepo=foo'` would fail.
+
+#### git
+
+The `git` resource now fully supports why-run mode and no longer checks out the `deploy` branch by default as mentioned in the breaking changes section.
+
+#### locale
+
+The `locale` resource now supports setting the system locale on Windows hosts.
+
+#### msu_package resource improvements
+
+The `msu_package` resource has been improved to work better with Microsoft's cumulative update packages. Newer releases of these cumulative update packages will not correctly install over the previous versions. We also extended the default timeout for installing MSU packages to 60 minutes. Thanks for reporting the timeout issue, [@danielfloyd](https://github.com/danielfloyd).
+
+#### package
+
+The `package` resource on macOS and Arch Linux systems now supports passing multiple packages into a single package resource via an array. This allows you to collapse multiple resources into a single resource for simpler cookbook authoring, which is significantly faster as it requires fewer calls to the packaging systems. Thanks for the Arch Linux support, [@ingobecker](https://github.com/ingobecker)!
+
+Using multiple resources to install a package:
+
+```ruby
+package 'git'
+package 'curl'
+package 'packer'
+```
+
+or
+
+```ruby
+%w(git curl packer).each do |pkg|
+ package pkg
+end
+```
+
+can now be simplified to:
+
+```ruby
+package %w(git curl packer)
+```
+
+#### service
+
+The `service` resource has been updated to support newer releases of `update-rc.d` so that it properly disables sys-v init services on Debian Linux distributions. Thanks [@robuye](https://github.com/robuye)!
+
+#### windows_firewall_rule
+
+The `windows_firewall_rule` resource has been greatly improved thanks to work by [@pschaumburg](https://github.com/pschaumburg) and [@tecracer-theinen](https://github.com/tecracer-theinen).
+
+- New `icmp_type` property, which allows setting the ICMP type when setting up ICMP protocol rules.
+- New `displayname` property, which allows defining the display name of the firewall rule.
+- New `group` property, which allows you to specify that only matching firewall rules of the indicated group association are copied.
+- The `description` property will now update if changed.
+- Fixed setting rules with multiple profiles.
+
+#### windows_package
+
+The `windows_package` resource now considers `3010` to be a valid exit code by default. The `3010` exit code means that a package has been successfully installed, but requires a reboot.
+
+**knife-acl is now built-in**
+
+The `knife-acl` gem is now part of Chef Infra Client. This gives you the ability to manage Chef organizations and ACLs directly.
+
+### YAML Recipes
+
+We added support for writing recipes in YAML to provide a low-code syntax for simple use cases. To write recipes in YAML, Chef resources and any user-defined parameters can be added as elements in a `resources` hash, such as the example below:
+
+```yaml
+---
+resources:
+ - type: "package"
+ name: "httpd"
+ - type: "template"
+ name: "/var/www/html/index.html"
+ source: "index.html.erb"
+ - type: "service"
+ name: "httpd"
+ action:
+ - enable
+ - start
+```
+
+This implementation is restrictive and does not support arbitrary Ruby code, helper functions, or attributes. However, if the need for additional customization arises, YAML recipes can be automatically converted into the DSL via the `knife yaml convert` command.
+
+### Custom Resource Improvements
+
+#### Improved property require behavior
+
+As noted in the breaking changes above, we improved how the required value is set on custom resource properties, in order to give a more predictable behavior. This new behavior now allows you to specify actions where individual properties are required. This is especially useful when `:create` actions require certain properties that may not be required for a `:remove` type property.
+
+Example required field defining specific actions:
+
+```ruby
+property :password, String, required: [:create]
+
+action :create do
+ # code to create something
+end
+
+action :remove do
+ # code to remove it that doesn't need a password
+end
+```
+
+#### Resource Partials
+
+Resource partials allow you to define reusable portions of code that can be included in multiple custom resources. This feature is particularly useful when there are common properties, such as authentication properties, that you want to define in a single location, but use for multiple resources. Internally in the Chef Infra Client codebase, we have already used this feature to remove duplicate properties from our `subversion` and `git` resources and make them easier to maintain.
+
+Resource partials are stored in a cookbook's `/resources` directory just like existing custom resources, but they start with the `_` prefix. They're then called using a new `use` helper within the resource where they're needed:
+
+`resources/_api_auth_properties.rb:`
+
+```ruby
+property :api_endpoint, String
+property :api_key, String
+property :api_retries, Integer
+```
+
+`resources/mything.rb`:
+
+```ruby
+property :another_property, String
+property :yet_another_property, String
+
+use 'api_auth_properties'
+
+action :create do
+ # some create logic
+end
+```
+
+The example above shows a resource partial that contains properties for use in multiple resources. You can also use resource partials to define helper methods that you want to use in your actions instead of defining the same helper methods in each action_class.
+
+`resources/_api_auth_helpers.rb:`
+
+```ruby
+def make_api_call(endpoint, value)
+ # API call code here
+end
+```
+
+`resources/mything.rb`:
+
+```ruby
+property :another_property, String
+property :yet_another_property, String
+
+action :create do
+ # some create logic
+end
+
+action_class do
+ use 'api_auth_helpers'
+end
+```
+
+#### after_resource
+
+A new `after_resource` state has been added to resources that allows you to better control the resource state information reported to Chef Automate when a resource converges. If your custom resource uses the `load_current_value` helper, then this after state is calculated automatically. If you don't utilize the `load_current_value` helper and would like fine grained control over the state information sent to Chef Automate, you can use a new `load_after_resource` helper to load the state of each property for reporting.
+
+#### identity Improvements
+
+A resource's name property is now set to be the identity property by default and to have `desired_state: false` set by default. This eliminates the need to set `identity: true, desired_state: false` on these properties and better exposes identity data to handler and reporting.
+
+#### compile_time property
+
+The `compile_time` property is now defined for all custom resources, so there is no need to add your own compile-time logic to your resource.
+
+### Other Improvements
+
+#### Up to 33% smaller on disk
+
+We optimized the files that ship with Chef Infra Client and eliminated many unnecessary files from the installation, reducing the on-disk size of Chef Infra Client by up to 33%.
+
+#### Windows Performance Improvements
+
+We've optimized the Chef Infra Client for modern Windows releases and improved the performance on these systems.
+
+#### Simpler Version Comparisons with node['platform_version']
+
+The `node['platform_version']` attribute returned from Ohai can now be intelligently compared as a version instead of as a String or Integer. Previously, to compare the platform_version, many users would first convert the version String to a Float with `node['platform_version']`. This introduced problems on many platforms, such as macOS, where macOS 10.9 would appear to be a greater version number than 10.15. You can now directly compare the version without converting it first.
+
+Greater than or equal comparison:
+
+```ruby
+node['platform_version'] >= '10.15'
+```
+
+Comparison using Ruby's pessimistic operator:
+
+```ruby
+node['platform_version'] =~ '~> 10.15'
+```
+
+#### New helpers for recipes and resources
+
+Several helpers introduced in Chef Infra Client 15.5 are now available for use in any resource or recipe. These helpers include:
+
+`sanitized_path`
+
+`sanitize_path` is a cross-platform method that returns the system's path along with the Chef Infra Client Ruby bin dir / gem bin dir and common system paths such as `/sbin` and `/usr/local/bin`.
+
+`which(foo)`
+
+The `which` helper searches the system's path and returns the first occurrence of a binary, similar to the `which` command on *nix systems. It also allows you to pass an `extra_path` value for additional directories to search.
+
+```ruby
+which('systemctl')
+```
+
+```ruby
+which('my_app', extra_path: '/opt/my_app/bin')
+```
+
+#### eager_load_libraries metadata.rb setting
+
+By default, Chef Infra Client eagerly loads all ruby files in each cookbook's libraries directory at runtime. A new metadata.rb option `eager_load_libraries` has been introduced and allows you to control if and when a cookbook library is loaded. Depending on the construction of your libraries, this new option may greatly improve the runtime performance of your cookbook. With eager loading disabled, you may manually load libraries included in your cookbook using Ruby's standard `require` method. Metadata.rb configuration options:
+
+```ruby
+eager_load_libraries false # disable eager loading all libraries
+eager_load_libraries 'helper_library.rb' # eager load just the file helper_library.rb
+eager_load_libraries %w(helper_library_1.rb helper_library_2.rb) # eager load both helper_library_1.rb and helper_library_2.rb files
+```
+
+Note: Unless you are experiencing performance issues in your libraries, we advise against changing the loading behavior.
+
+#### always_dump_stacktrace client.rb option
+
+A new `always_dump_stacktrace` client.rb configuration option and command line option allows you to have any Ruby stacktraces from Chef Infra Client logged directly to the log file. This may help troubleshooting when used in conjunction with centralized logging systems such as Splunk. To enable this new option, run `chef-client --always-dump-stacktrace` or add the following to your `client.rb`:
+
+```ruby
+always_dump_stacktrace true
+```
+
+#### Chef Vault Functionality Out of the Box
+
+Chef Infra Client now ships with built-in Chef Vault functionality, so there's no need to depend on the `chef-vault` cookbook or gem. Chef Vault helpers `chef_vault_item`, `chef_vault`, and `chef_vault_item_for_environment` are included, as well as the `chef_vault_secret` resource. Additionally, the Chef Vault knife commands are also available out of the box. We do not recommend new users adopt the Chef Vault workflow due to limitations with autoscaling new systems, so these resources should only be consumed by existing Chef Vault users.
+
+#### Ruby 2.7
+
+Chef Infra Client's ruby installation has been updated to from Ruby 2.6 to Ruby 2.7, which includes many features available for use in resources and libraries.
+
+See <https://medium.com/rubyinside/whats-new-in-ruby-2-7-79c98b265502> for details on many of the new features.
+
+#### Ohai 16 Improvements
+
+Ohai has been improved to gather additional system configuration information for use when authoring recipes and resources.
+
+**filesystem2 Node Data available on Windows**
+
+In previous Chef Infra Clients we've introduced a modernized filesystem layout of Ohai data for many platforms. In Chef Infra Client 16.0, Windows now has this layout available in `node['filesystem2']`. In Chef Infra Client 17, it will replace `node['filesystem']` to match all other platforms.
+
+**Extended Azure Metadata**
+
+The `Azure` Ohai plugin now gathers the latest version of the metadata provided by the Azure metadata endpoint. This greatly expands the information available on Azure instances. See [Ohai PR 1427](https://github.com/chef/ohai/pull/1427) for an example of the new data gathered.
+
+**New Ohai Plugins**
+
+New `IPC` and `Interupts` plugins have been added to Ohai. The IPC plugin exposes SysV IPC shmem information and interupts plugin exposes data from `/proc/interrupts` and `/proc/irq`. Thanks [@jsvana](https://github.com/jsvana) and [@davide125](https://github.com/davide125) for these new plugins.
+
+Note: Both `IPC` and `Interupts` plugins are optional plugins, which are disabled by default. They can be enabled via your `client.rb`:
+
+```ruby
+ohai.optional_plugins = [
+ :IPC,
+ :Interupts
+]
+```
+
+**Improved Linux Network Plugin Data**
+
+The Linux Network plugin has been improved to gather additional information from the `ethtool` utility. This includes the number of queues (`ethtool -l`), the coalesce parameters (`ethtool -c`), and information about the NIC driver (`ethtool -i`). Thanks [@matt-c-clark](https://github.com/matt-c-clark) for these improvements.
+
+**Windows DMI plugin**
+
+Windows systems now include a new `DMI` plugin which presents data in a similar format to the `DMI` plugin on *nix systems. This makes it easier to detect system information like manufacturer, serial number, or asset tag number in a cross-platform way.
+
+### New Platforms
+
+Over the last quarter, we worked to greatly expand the platforms that we support with the addition of Chef Infra Client packages for Ubuntu 20.04 amd64, Amazon Linux 2 x86_64/aarch64, and Debian 10 amd64. With the release of Chef Infra Client 16, we expanded our platform support again with the following new platforms:
+
+- RHEL 8 aarch64
+- Ubuntu 20.04 aarch64
+- SLES 16 aarch64
+
+### Newly Introduced Deprecations
+
+Several legacy Windows helpers have been deprecated as they will always return true when running on Chef Infra Client's currently supported platforms. The helpers previously detected systems prior to Windows 2012 and systems running Windows Nano, which has been discontinued by Microsoft. These helpers were never documented externally so their usage is most likely minimal. A new Cookstyle rule has been introduced to detect the usage of `older_than_win_2012_or_8?`: [ChefDeprecations/DeprecatedWindowsVersionCheck](https://github.com/chef/cookstyle/blob/master/docs/cops_chefdeprecations.md#chefdeprecationsdeprecatedwindowsversioncheck).
+
+- Chef::Platform.supports_msi?
+- Chef::Platform.older_than_win_2012_or_8?
+- Chef::Platform.supports_powershell_execution_bypass?
+- Chef::Platform.windows_nano_server?
+
+## What's new in 15.15
+
+### Chef InSpec 4.24.8
+
+Chef InSpec has been updated from 4.22.22 to 4.24.8 with the following improvements:
+
+- An unset `HOME environment variable will not cause execution failures
+- You can use wildcards in `platform-name` and `release` in InSpec profiles
+- The support for arrays in the `WMI` resource, so it can return multiple objects
+- The `package` resource on Windows properly escapes package names
+- The `grub_conf` resource succeeds even if without a `menuentry` in the grub config
+- Loaded plugins won't try to re-load themselves
+- A new mechanism marks inputs as sensitive: true and replaces their values with `***`.
+- Use the `--no-diff` CLI option to suppress diff output for textual tests.
+- Control the order of controls in output, but not execution order, with the `--sort_results_by=none|control|file|random` CLI option.
+- Disable caching of inputs with a cache_inputs: true setting.
+
+### Chef Vault 4.1
+
+We've updated the release of `chef-vault` bundled with Chef Infra Client to 4.1. Chef Vault 4.1 properly handles escape strings in secrets and greatly improves performance for users with large numbers of secrets. Thanks for the performance work [@Annih](https://github.com/Annih)!
+
+### Resource Improvements
+
+#### cron_d
+
+The `cron_d` resource now respects the use of the `sensitive` property. Thanks for this fix [@axl89](https://github.com/axl89)!
+
+#### homebrew_cask
+
+The `homebrew_cask` resource has been updated to work with the latest command syntax requirements in the `brew` command. Thanks for reporting this issue [@bcg62](https://github.com/bcg62)!
+
+#### locale
+
+The allowed execution time for the `locale-gen` command in the `locale` resource has been extended to 1800 seconds to make sure the Chef Infra Client run doesn't fail before the command completes on slower systems. Thanks for reporting this issue [@janskarvall](https://github.com/janskarvall)!
+
+#### plist / macosx_service / osx_profile / macos_userdefaults
+
+Parsing of plist files has been improved in the `plist`, `macosx_service`, `osx_profile`, and `macos_userdefaults` resources thanks to updates to the plist gem by [@reitermarkus](https://github.com/reitermarkus) and [@tboyko](https://github.com/tboyko).
+
+### Security
+
+- The bundled Nokogiri Ruby gem has been updated to 1.11 resolve [CVE-2020-26247](https://nvd.nist.gov/vuln/detail/CVE-2020-26247).
+- openSSL has been updated to 1.0.2x to resolve [CVE-2020-1971](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1971).
+
+## What's New In 15.14
+
+### Chef InSpec 4.22.22
+
+Chef InSpec has been updated from 4.22.1 to 4.22.22. This new release includes the following improvements:
+
+- Fix mysql_session stdout, stderr and exit_status parameters. Thanks [@ramereth](https://github.com/ramereth)!
+- Add new windows_firewall and windows_firewall_rule resources. Thanks [@tecracer-theinen](https://github.com/tecracer-theinen)!
+
+### Fixes and Improvements
+
+- The `knife ssh` command no longer hangs when connecting to Windows nodes over SSH.
+- Resolved several failures that could occur in the included chef-vault gem.
+
+### Resource Updates
+
+#### hostname
+
+The `hostname` resource has been updated to improve logging on Windows systems.
+
+#### windows_feature
+
+The `windows_feature` resource has been updated to allow installing features that have been removed if a source location is provided. Thanks for reporting this [@stefanwb](https://github.com/stefanwb)!
+
+#### windows_font
+
+The `windows_font` resource will no longer fail on newer releases of Windows if a font is already installed. Thanks for reporting this [@bmiller08](https://github.com/bmiller08)!
+
+### Platform Packages
+
+- We are once again building Chef Infra Client packages for RHEL 7 / SLES 12 on the S390x architecture. In addition to these packages, we've also added S390x packages for SLES 15.
+- We now produce packages for Apple's upcoming macOS 11 Big Sur release.
+
+### Security
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2w which includes a fix for [CVE-2020-1968](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-1968).
+
+#### CA Root Certificates
+
+The included `cacerts` bundle in Chef Infra Client has been updated to the 7-22-2020 release. This new release removes 4 legacy root certificates and adds 4 additional root certificates.
+
+## What's New In 15.13
+
+### Chef InSpec 4.22.1
+
+Chef InSpec has been updated from 4.20.6 to 4.22.1. This new release includes the following improvements:
+
+- `apt-cdrom` repositories are now skipped when parsing out the list of apt repositories
+- Faulty profiles are now reported instead of causing a crash
+- Errors are no longer logged to stdout with the `html2` reporter
+- macOS Big Sur is now correctly identified as macOS
+- macOS/BSD support added to the interface resource along with new `ipv4_address`, `ipv4_addresses`, `ipv4_addresses_netmask`, `ipv4_cidrs`, `ipv6_addresses`, and `ipv6_cidrs` properties
+
+### Fixes and Improvements
+
+- Support for legacy DSA host keys has been restored in `knife ssh` and `knife bootstrap` commands.
+- The collision warning error message when a cookbook includes a resource that now ships in Chef Infra Client has been improved to better explain the issue.
+- Package sizes have been reduced with fewer installed files on disk.
+- The `archive_file` resource now supports `pzstd` compressed files.
+
+### New Deprecations
+
+Chef Infra Client 16.2 and later require `provides` when assigning a name to a custom resource. In order to prepare for Chef Infra Client 16, make sure to include both `resource_name` and `provides` in resources when specifying a custom name.
+
+## What's New In 15.12
+
+### Chef InSpec 4.20.6
+
+Chef InSpec has been updated from 4.18.114 to 4.2.0.6. This new release includes the following improvements:
+
+- Develop your own Chef InSpec Reporter plugins to control how Chef InSpec will report result data.
+- The `inspec archive` command packs your profile into a `tar.gz` file that includes the profile in JSON form as the inspec.json file.
+- Certain substrings within a `.toml` file no longer cause unexpected crashes.
+- Accurate InSpec CLI input parsing for numeric values and structured data, which were previously treated as strings. Numeric values are cast to an `integer` or `float` and `YAML` or `JSON` structures are converted to a hash or an array.
+- Suppress deprecation warnings on `inspec exec` with the `--silence-deprecations` option.
- yum 'Oldrepo' do
- action :delete
+### Resource Updates
+
+#### archive_file
+
+The `archive_file` resource has been updated with two important fixes. The resource will no longer fail with uninitialized constant errors under some scenarios. Additionally, the behavior of the `mode` property has been improved to prevent incorrect file modes from being applied to the decompressed files. Due to how file modes and Integer values are processed in Ruby, this resource will now produce a deprecation warning if integer values are passed. Using string values lets us accurately pass values such as '644' or '0644' without ambiguity as to the user's intent. Thanks for reporting these issues [@sfiggins](http://github.com/sfiggins) and [@hammerhead](http://github.com/hammerhead).
+
+#### cron_access
+
+The `cron_access` resource has been updated to support Solaris and AIX systems. Thanks [@aklyachkin](http://github.com/aklyachkin).
+
+#### msu_package resource improvements
+
+The `msu_package` resource has been improved to work better with Microsoft's cumulative update packages. Newer releases of these cumulative update packages will not correctly install over the previous versions. We also extended the default timeout for installing MSU packages to 60 minutes. Thanks for reporting the timeout issue [@danielfloyd](https://github.com/danielfloyd).
+
+#### powershell_package
+
+The `powershell_package` resource has been updated to use TLS 1.2 when communicating with the PowerShell Gallery on Windows Server 2012-2016. Previously, this resource used the system default cipher suite which did not include TLS 1.2. The PowerShell Gallery now requires TLS 1.2 for all communication, which caused failures on Windows Server 2012-2016. Thanks for reporting this issue [@Xorima](http://github.com/Xorima).
+
+#### snap_package
+
+Multiple issues with the `snap_package` resource have been resolved, including an infinite wait that occurred and issues with specifying the package version or channel. Thanks [@jaymzh](http://github.com/jaymzh).
+
+#### zypper_repository
+
+The `zypper_repository` resource has been updated to work with the newer release of GPG in openSUSE 15 and SLES 15. This prevents failures when importing GPG keys in the resource.
+
+### Knife bootstrap updates
+
+- Knife bootstrap will now warn when bootstrapping a system using a validation key. Users should instead use `validatorless bootstrapping` with `knife bootstrap` which generates node and client keys using the client key of the user bootstrapping the node. This method is far more secure as an org-wide validation key does not not need to be distributed or rotated. Users can switch to `validatorless bootstrapping` by removing any `validation_key` entries in their `config.rb (knife.rb)` file.
+- Resolved an error bootstrapping Linux nodes from Windows hosts
+- Improved information messages during the bootstrap process
+
+### SSH Improvements
+
+The `net-ssh` library used by the `knife ssh` and `knife bootstrap` commands has been updated bringing improvements to SSH connectivity:
+
+- Support for additional key exchange and transport algorithms
+- Support algorithm subtraction syntax in the `ssh_config` file
+- Support empty lines and comments in `known_hosts` file
+
+### Initial macOS Big Sur Support
+
+Chef Infra Client now correctly detects macOS Big Sur (11.0) beta as being platform "mac_os_x". Chef Infra Client 15.12 has not been fully qualified for macOS Big Sur, but we will continue to validate against this release and provide any additional support updates.
+
+### Platform Packages
+
+- Debian 8 packages are no longer being produced as Debian 8 is now end-of-life.
+- We now produce Windows 8 packages
+
+## What's New In 15.11
+
+### Bootstrapping Bugfixes
+
+This release of Chef Infra Client resolves multiple issues when using `knife bootstrap` to bootstrap new nodes to a Chef Infra Server:
+
+- Bootstrapping from a Windows host to a Linux host with an ED25519 ssh key no longer fails
+- Resolved failures in the Windows bootstrap script
+- Incorrect paths when bootstrapping Windows nodes have been resolved
+
+### Chef InSpec 4.18.114
+
+Chef InSpec was updated from 4.18.104 to 4.18.114 with the following improvements:
+
+- Added new `--reporter_message_truncation` and `--reporter_backtrace_inclusion` reporter options to truncate messages and suppress backtraces.
+- Fixed a warning when an input is provided
+- Inputs and controls can now have the same name
+
+### Resource Improvements
+
+#### windows_firewall
+
+The `windows_firewall` resource has been updated to support firewall rules that are associated with more than one profile. Thanks [@tecracer-theinen](https://github.com/tecracer-theinen).
+
+#### chocolatey_package
+
+The `chocolatey_package` resource has been updated to properly handle quotes within the `options` property. Thanks for reporting this issue [@dave-q](https://github.com/dave-q).
+
+### Platform Support
+
+#### Additional aarch64 Builds
+
+Chef Infra Client is now tested on Debian 10, SLES 15, and Ubuntu 20.04 on the aarch64 architecture with packages available on the [Chef Downloads Page](https://downloads.chef.io/chef).
+
+### Security Updates
+
+#### openSSL
+
+openSSL has been updated from 1.0.2u to 1.0.2v which does not address any particular CVEs, but includes multiple security hardening updates.
+
+## What's New in 15.10
+
+### Improvements
+
+- The `systemd_unit` resource now respects the `sensitive` property and will no longer output the contents of the unit file to logs if this is set.
+- A new `arm?` helper has been added which can be used in recipes and resources to determine if a system is on the ARM architecture.
+
+### Bug Fixes
+
+- Resolved a bug that prevented users from bootstrapping nodes using knife when specifying the `--use_sudo_password`.
+- Resolved a bug that prevented the `--bootstrap-version` flag from being honored when bootstrapping in knife.
+
+### Chef InSpec 4.18.104
+
+- Resolved a regression that prevented the `service` resource from working correctly on Windows. Thanks [@Axuba](https://github.com/Axuba)
+- Implemented VMware and Hyper-V detection on Linux systems
+- Implemented VMware, Hyper-V, Virtualbox, KVM and Xen detection on Windows systems
+- Added helpers `virtual_system?` and `physical_system?`. Thanks [@tecracer-theinen](https://github.com/tecracer-theinen)
+
+### Ohai 15.9
+
+- Improve the resiliency of the `Shard` plugin when `dmidecode` cannot be found on a system. Thanks [@jaymzh](https://github.com/jaymzh)
+- Fixed detection of Openstack guests via DMI data. Thanks [@ramereth](https://github.com/ramereth)
+
+### Platform Support
+
+#### Amazon Linux 2
+
+Chef Infra Client is now tested on Amazon Linux 2 running on x86_64 and aarch64 with packages available on the [Chef Downloads Page](https://downloads.chef.io/chef).
+
+## What's New in 15.9
+
+### Chef InSpec 4.18.100
+
+Chef InSpec has been updated from 4.18.85 to 4.18.100:
+
+- Resolved several failures in executing resources
+- Fixed `auditd` resource processing of action and list
+- Fixed platform detection when running in Habitat
+- "inspec schema" has been revised to be in the JSON Schema draft 7 format
+- Improved the functionality of the `oracledb_session` resource
+
+### Ohai 15.8
+
+Ohai has been updated to 15.8.0 which includes a fix for failures that occurred in the OpenStack plugin (thanks [@sawanoboly](https://github.com/sawanoboly/)) and improved parsing of data in the `optional_plugins` config option (thanks [@salzig](https://github.com/salzig/)).
+
+### Resource Improvements
+
+#### build_essential
+
+The `build_essential` resource has been updated to better detect if the Xcode CLI Tools package needs to be installed on macOS. macOS 10.15 (Catalina) is now supported with this update. Thank you [@w0de](https://github.com/w0de/) for kicking this work off, [@jazaval](https://github.com/jazaval/) for advice on macOS package parsing, and Microsoft for their work in the macOS cookbook.
+
+#### rhsm_errata / rhsm_errata_level
+
+The `rhsm_errata` and `rhsm_errata_level` resources have been updated to properly function on RHEL 8 systems.
+
+#### rhsm_register
+
+The `rhsm_register` resource has a new property `https_for_ca_consumer` that enables using https connections during registration. Thanks for this improvement [@jasonwbarnett](https://github.com/jasonwbarnett/). This resource has also been updated to properly function on RHEL 8.
+
+#### windows_share
+
+Resolved failures in the `windows_share` resource when setting the `path` property. Thanks for reporting this issue [@Kundan22](https://github.com/Kundan22/).
+
+### Platform Support
+
+#### Ubuntu 20.04
+
+Chef Infra Client is now tested on Ubuntu 20.04 (AMD64) with packages available on the [Chef Downloads Page](https://downloads.chef.io/chef).
+
+#### Ubuntu 18.04 aarch64
+
+Chef Infra Client is now tested on Ubuntu 18.04 aarch64 with packages available on the [Chef Downloads Page](https://downloads.chef.io/chef).
+
+#### Windows 10
+
+Our Windows 10 Chef Infra Client packages now receive an additional layer of testing to ensure they function as expected.
+
+### Security Updates
+
+#### Ruby
+
+Ruby has been updated from 2.6.5 to 2.6.6 to resolve the following CVEs:
+
+ - [CVE-2020-16255](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/): Unsafe Object Creation Vulnerability in JSON (Additional fix)
+ - [CVE-2020-10933](https://www.ruby-lang.org/en/news/2020/03/31/heap-exposure-in-socket-cve-2020-10933/): Heap exposure vulnerability in the socket library
+
+#### libarchive
+
+libarchive has been updated from 3.4.0 to 3.4.2 to resolve multiple security vulnerabilities including the following CVEs:
+
+ - [CVE-2019-19221](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-19221): archive_wstring_append_from_mbs in archive_string.c has an out-of-bounds read because of an incorrect mbrtowc or mbtowc call
+ - [CVE-2020-9308](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-9308): archive_read_support_format_rar5.c in libarchive before 3.4.2 attempts to unpack a RAR5 file with an invalid or corrupted header
+
+## What's New in 15.8
+
+### New notify_group functionality
+
+Chef Infra Client now includes a new `notify_group` feature that can be used to extract multiple common notifies out of individual resources to reduce duplicate code in your cookbooks and custom resources. Previously cookbook authors would often use a `log` resource to achieve a similar outcome, but using the log resource results in unnecessary Chef Infra Client log output. The `notify_group` method produces no additional logging, but fires all defined notifications when the `:run` action is set.
+
+Example notify_group that stops, sleeps, and then starts service when a service config is updated:
+
+```ruby
+service "crude" do
+ action [ :enable, :start ]
+end
+
+chef_sleep "60" do
+ action :nothing
+end
+
+notify_group "crude_stop_and_start" do
+ notifies :stop, "service[crude]", :immediately
+ notifies :sleep, "chef_sleep[60]", :immediately
+ notifies :start, "service[crude]", :immediately
+end
+
+template "/etc/crude/crude.conf" do
+ source "crude.conf.erb"
+ variables node["crude"]
+ notifies :run, "notify_group[crude_stop_and_start]", :immediately
+end
+```
+
+### Chef InSpec 4.18.85
+
+Chef InSpec has been updated from 4.18.39 to 4.18.85. This release includes a large number of bug fixes in addition to some great resource enhancements:
+
+- The service resource features new support for yocto-based linux distributions. Thank you to [@michaellihs](https://github.com/michaellihs) for this addition!
+- The package resource now includes support for FreeBSD. Thank you to [@fzipi](https://github.com/fzipi) for this work!
+- We standardized the platform for the etc_hosts, virtualization, ini, and xml resources.
+- The oracledb_session resource works again due to a missing quote fix.
+- The groups resource on macOS no longer reports duplicates anymore.
+command.exist? now conforms to POSIX standards. Thanks to [@PiQuer](https://github.com/PiQuer)!
+- Changed the postfix_conf resource's supported platform to the broader unix. Thank you to [@fzipi](https://github.com/fzipi) for this fix!
+
+### New Cookbook Helpers
+
+New helpers have been added to make writing cookbooks easier.
+
+#### Platform Version Helpers
+
+New helpers for checking platform versions have been added. These helpers return parsed version strings so there's no need to convert the returned values to Integers or Floats before comparing them. Additionally, comparisons with version objects properly understand the order of versions so `5.11` will compare as larger than `5.9`, whereas converting those values to Floats would result in `5.9` being larger than `5.11`.
+
+- `windows_nt_version` returns the NT kernel version which often differs from Microsoft's marketing versions. This helper offers a good way to find desktop and server releases that are based on the same codebase. For example, NT 6.3 is both Windows 8.1 and Windows 2012 R2.
+- `powershell_version` returns the version of PowerShell installed on the system.
+- `platform_version` returns the value of node['platform_version'].
+
+Example comparison using windows_nt_version:
+
+```ruby
+if windows_nt_version >= 10
+ some_modern_windows_things
+end
+```
+
+#### Cloud Helpers
+
+The cloud helpers from chef-sugar have been ported to Chef Infra Client:
+
+- `cloud?` - if the node is running in any cloud, including internal clouds
+- `ec2?` - if the node is running in ec2
+- `gce?` - if the node is running in gce
+- `rackspace?` - if the node is running in rackspace
+- `eucalyptus?` - if the node is running under eucalyptus
+- `linode?` - if the node is running in linode
+- `openstack?` - if the node is running under openstack
+- `azure?` - if the node is running in azure
+- `digital_ocean?` - if the node is running in digital ocean
+- `softlayer?` - if the node is running in softlayer
+
+#### Virtualization Helpers
+
+The virtualization helpers from chef-sugar have been ported to Chef Infra Client and extended with helpers to detect hypervisor hosts, physical, and guest systems.
+
+- `kvm?` - if the node is a kvm guest
+- `kvm_host?` - if the node is a kvm host
+- `lxc?` - if the node is an lxc guest
+- `lxc_host?` - if the node is an lxc host
+- `parallels?`- if the node is a parallels guest
+- `parallels_host?`- if the node is a parallels host
+- `vbox?` - if the node is a virtualbox guest
+- `vbox_host?` - if the node is a virtualbox host
+- `vmware?` - if the node is a vmware guest
+- `vmware_host?` - if the node is a vmware host
+- `openvz?` - if the node is an openvz guest
+- `openvz_host?` - if the node is an openvz host
+- `guest?` - if the node is detected as any kind of guest
+- `hypervisor?` - if the node is detected as being any kind of hypervisor
+- `physical?` - the node is not running as a guest (may be a hypervisor or may be bare-metal)
+- `vagrant?` - attempts to identify the node as a vagrant guest (this check may be error-prone)
+
+#### include_recipe? helper
+
+chef-sugar's `include_recipe?` has been added to Chef Infra Client providing a simple way to see if a recipe has been included on a node already.
+
+Example usage in a not_if conditional:
+
+```ruby
+execute 'install my_app'
+ command '/tmp/my_app_install.sh'
+ not_if { include_recipe?('my_app::install') }
+end
+```
+
+### Updated Resources
+
+#### ifconfig
+
+The `ifconfig` resource now supports the newer `ifconfig` release that ships in Debian 10.
+
+#### mac_user
+
+The `mac_user` resource, used when creating a user on Mac systems, has been improved to work better with macOS Catalina (10.15). The resource now properly looks up the numeric GID when creating a user, once again supports the `system` property, and includes a new `hidden` property which prevents the user from showing on the login screen. Thanks [@chilcote](https://github.com/chilcote) for these fixes and improvements.
+
+#### sysctl
+
+The `sysctl` resource has been updated to allow the inclusion of descriptive comments. Comments may be passed as an array or as a string. Any comments provided are prefixed with '#' signs and precede the `sysctl` setting in generated files.
+
+An example:
+
+```ruby
+sysctl 'vm.swappiness' do
+ value 10
+ comment [
+ "define how aggressively the kernel will swap memory pages.",
+ "Higher values will increase aggressiveness",
+ "lower values decrease the amount of swap.",
+ "A value of 0 instructs the kernel not to initiate swap",
+ "until the amount of free and file-backed pages is less",
+ "than the high water mark in a zone.",
+ "The default value is 60."
+ ]
+end
+```
+
+which results in `/etc/sysctl.d/99-chef-vm.swappiness.conf` as follows:
+
+```
+# define how aggressively the kernel will swap memory pages.
+# Higher values will increase aggressiveness
+# lower values decrease the amount of swap.
+# A value of 0 instructs the kernel not to initiate swap
+# until the amount of free and file-backed pages is less
+# than the high water mark in a zone.
+# The default value is 60.
+vm.swappiness = 10
+```
+
+### Platform Support
+
+- Chef Infra Clients packages are now validated for Debian 10.
+
+### macOS Binary Signing
+
+Each binary in the macOS Chef Infra Client installation is now signed to improve the integrity of the installation and ensure compatibility with macOS Catalina security requirements.
+
+## What's New in 15.7
+
+### Updated Resources
+
+#### archive_file
+
+The `archive_file` resource will now only change ownership on files and directories that were part of the archive itself. This prevents changing permissions on important high level directories such as /etc or /bin when you extract a file into those directories. Thanks for this fix, [@bobchaos](https://github.com/bobchaos/).
+
+#### cron and cron_d
+
+The `cron` and `cron_d` resources now include a `timeout` property, which allows you to configure actions to perform when a job times out. This property accepts a hash of timeout configuration options:
+
+- `preserve-status`: `true`/`false` with a default of `false`
+- `foreground`: `true`/`false` with a default of `false`
+- `kill-after`: `Integer` for the timeout in seconds
+- `signal`: `String` or `Integer` to send to the process such as `HUP`
+
+#### launchd
+
+The `launchd` resource has been updated to properly capitalize `HardResourceLimits`. Thanks for this fix, [@rb2k](https://github.com/rb2k/).
+
+#### sudo
+
+The `sudo` resource no longer fails on the second Chef Infra Client run when using a `Cmnd_Alias`. Thanks for reporting this issue, [@Rudikza](https://github.com/Rudikza).
+
+#### user
+
+The `user` resource on AIX no longer forces the user to change the password after Chef Infra Client modifies the password. Thanks for this fix, [@Triodes](https://github.com/Triodes).
+
+The `user` resource on macOS 10.15 has received several important fixes to improve logging and prevent failures.
+
+#### windows_task
+
+The `windows_task` resource is now idempotent when a system is joined to a domain and the job runs under a local user account.
+
+#### x509_certificate
+
+The `x509_certificate` resource now includes a new `renew_before_expiry` property that allows you to auto renew certificates a specified number of days before they expire. Thanks [@julienhuon](https://github.com/julienhuon/) for this improvement.
+
+### Additional Recipe Helpers
+
+We have added new helpers for identifying Windows releases that can be used in any part of your cookbooks.
+
+#### windows_workstation?
+
+Returns `true` if the system is a Windows Workstation edition.
+
+#### windows_server?
+
+Returns `true` if the system is a Windows Server edition.
+
+#### windows_server_core?
+
+Returns `true` if the system is a Windows Server Core edition.
+
+### Notable Changes and Fixes
+
+- `knife upload` and `knife cookbook upload` will now generate a metadata.json file from metadata.rb when uploading a cookbook to the Chef Infra Server.
+- A bug in `knife bootstrap` behavior that caused failures when bootstrapping Windows hosts from non-Windows hosts and vice versa has been resolved.
+- The existing system path is now preserved when bootstrapping Windows nodes. Thanks for this fix, [@Xorima](https://github.com/Xorima/).
+- Ohai now properly returns the drive name on Windows and includes new drive_type fields to allow you to determine the type of attached disk. Thanks for this improvement [@sshock](https://github.com/sshock/).
+- Ohai has been updated to properly return DMI data to Chef Infra Client. Thanks for troubleshooting this, [@zmscwx](https://github.com/zmscwx) and [@Sliim](https://github.com/Sliim).
+
+### Platform Support
+
+- Chef Infra Clients packages are no longer produced for Windows 2008 R2 as this release reached its end of life on Jan 14th, 2020.
+- Chef Infra Client packages are no longer produced for RHEL 6 on the s390x platform. Builds will continue to be published for RHEL 7 on the s390x platform.
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2u to resolve [CVE-2019-1551](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1551)
+
+## What's New in 15.6
+
+### Updated Resources
+
+### apt_repository
+
+The `apt_repository` resource now properly escapes repository URIs instead of quoting them. This prevents failures when using the `apt-file` command, which was unable to parse the quoted URIs. Thanks for reporting this [@Seb-Solon](https://github.com/Seb-Solon)
+
+### file
+
+The `file` resource now shows the output of any failures when running commands specified in the `verify` property. This means you can more easily validate config files before potentially writing an incorrect file to disk. Chef Infra Client will shellout to any specified command and will show the results of failures for further troubleshooting.
+
+### user
+
+The `user` resource on Linux systems now continues successfully when `usermod` returns an exit code of 12. Exit code 12 occurs when a user's home directory is changed and the underlying directory already exists. Thanks [@skippyj](https://github.com/skippyj) for this fix.
+
+### yum_repository
+
+The `yum_repository` now properly formats the repository configuration when multiple `baseurl` values are present. Thanks [@bugok](https://github.com/bugok) for this fix.
+
+### Performance Improvements
+
+This release of Chef Infra Client ships with several optimizations to our Ruby installation to improve the performance of loading the chef-client and knife commands. These improvements are particularly noticeable on non-SSD hosts and on Windows.
+
+### Smaller Install Footprint
+
+We've further optimized our install footprint and reduced the size of `/opt/chef` by ~7% by removing unnecessary test files and libraries that shipped in previous releases.
+
+### filesystem2 Ohai Data on Windows
+
+Ohai 15.6 includes new `node['filesystem2']` data on Windows hosts. Filesystem2 presents filesystem data by both mountpoint and by device name. This data structure matches that of the filesystem plugin on Linux and other *nix operating systems. Thanks [@jaymzh](https://github.com/jaymzh) for this new data structure.
+
+## What's New in 15.5.15
+
+The Chef Infra Client 15.5.15 release includes fixes for two regressions. A regression in the `build_essential` resource caused failures on `rhel` platforms and a second regression caused Chef Infra Client to fail when starting with `enforce_path_sanity` enabled. As part of this fix we've added a new property, `raise_if_unsupported`, to the `build-essential` resource. Instead of silently continuing, this property will fail a Chef Infra Client run if an unknown platform is encountered.
+
+We've also updated the `windows_package` resource. The resource will now provide better error messages if invalid options are passed to the `installer_type` property and the `checksum` property will now accept uppercase SHA256 checksums.
+
+## What's New in 15.5.9
+
+### New Cookbook Helpers
+
+Chef Infra Client now includes a new `chef-utils` gem, which ships with a large number of helpers to make writing cookbooks easier. Many of these helpers existed previously in the `chef-sugar` gem. We have renamed many of the named helpers for consistency, while providing backwards compatibility with existing `chef-sugar` names. Existing cookbooks written with `chef-sugar` should work unmodified with any of these new helpers. Expect a Cookstyle rule in the near future to help you update existing `chef-sugar` code to use the newer built-in helpers.
+
+For more information all all of the new helpers available, see the [chef-utils readme](https://github.com/chef/chef/blob/master/chef-utils/README.md)
+
+### Chefignore Improvements
+
+We've reworked how chefignore files are handled in `knife`, which has allowed us to close out a large number of long outstanding bugs. `knife` will now traverse all the way up the directory structure looking for a chefignore file. This means you can place a chefignore file in each cookbook or any parent directory in your repository structure. Additionally, we have made fixes that ensure that commands like `knife diff` and `knife cookbook upload` always honor your chefignore files.
+
+### Windows Habitat Plan
+
+Official Habitat packages of Chef Infra Client are now available for Windows. It has all the executables of the traditional omnibus packages, but in Habitat form. You can find it in the Habitat Builder under [chef/chef-infra-client](https://bldr.habitat.sh/#/pkgs/chef/chef-infra-client/latest/windows).
+
+### Performance Improvements
+
+This release of Chef Infra Client ships with several optimizations to our Ruby installation that improve the performance of the chef-client and knife commands, especially on Windows systems. Expect to see more here in future releases.
+
+### Chef InSpec 4.18.39
+
+Chef InSpec has been updated from 4.17.17 to 4.18.38. This release includes a large number of bug fixes in addition to some great resource enhancements:
+
+- Inputs can now be used within a `describe.one` block
+- The `service` resource now includes a `startname` property for Windows and systemd services
+- The `interface` resource now includes a `name` property
+- The `user` resource now better supports Windows with the addition of `passwordage`, `maxbadpasswords`, and `badpasswordattempts` properties
+- The `nginx` resource now includes parsing support for wildcard, dot prefix, and regex
+- The `iis_app_pool` resource now handles empty app pools
+- The `filesystem` resource now supports devices with very long names
+- The `apt` better handles URIs and supports repos with an `arch`
+- The `oracledb_session` has received multiple fixes to make it work better
+- The `npm` resource now works under sudo on Unix and on Windows with a custom PATH
+
+### New Resources
+
+#### chef_sleep
+
+The `chef_sleep` resource can be used to sleep for a specified number of seconds during a Chef Infra Client run. This may be helpful to use with other commands that return a completed status before they are actually ready. In general, do not use this resource unless you truly need it.
+
+Using with a Windows service that starts, but is not immediately ready:
+
+```ruby
+service 'Service that is slow to start and reports as started' do
+ service_name 'my_database'
+ action :start
+ notifies :sleep, chef_sleep['wait for service start']
+end
+
+chef_sleep 'wait for service start' do
+ seconds 30
+ action :nothing
+end
+```
+
+### Updated Resources
+
+### systemd_unit / service
+
+The `systemd_unit` and `service` resources (when on systemd) have been updated to not re-enable services with an indirect status. Thanks [@jaymzh](https://github.com/jaymzh) for this fix.
+
+### windows_firewall
+
+The `windows_firewall` resource has been updated to support passing in an array of profiles in the `profile` property. Thanks [@Happycoil](https://github.com/Happycoil) for this improvement.
+
+### Security Updates
+
+#### libxslt
+
+libxslt has been updated to 1.1.34 to resolve [CVE-2019-13118](https://nvd.nist.gov/vuln/detail/CVE-2019-13118).
+
+## What's New in 15.4
+
+### converge_if_changed Improvements
+
+Chef Infra Client will now take into account any `default` values specified in custom resources when making converge determinations with the `converge_if_changed` helper. Previously, default values would be ignored, which caused necessary changes to be skipped. Note: This change may cause behavior changes for some users, but we believe this original behavior is an impacting bug for enough users to make it outside of a major release. Thanks [@ jakauppila](https://github.com/jakauppila) for reporting this.
+
+### Bootstrap Improvements
+
+Several improvements have been made to the `knife bootstrap` command to make it more reliable and secure:
+
+- File creation is now wrapped in a umask to avoid potential race conditions
+- `NameError` and `RuntimeError` failures during bootstrap have been resolved
+- `Undefined method 'empty?' for nil:NilClass` during bootstrap have been resolved
+- Single quotes in attributes during bootstrap no longer result in bootstrap failures
+- The bootstrap command no longer appears in PS on the host while bootstrapping is running
+
+### knife supermarket list Improvements
+
+The `knife supermarket list` command now includes two new options:
+
+- `--sort-by [recently_updated recently_added most_downloaded most_followed]`: Sort cookbooks returned from the Supermarket API
+- `--owned_by`: Limit returned cookbooks to a particular owner
+
+### Updated Resources
+
+#### chocolatey_package
+
+The `chocolatey_package` resource no longer fails when passing options with the `options` property. Thanks for reporting this issue [@kenmacleod](https://github.com/kenmacleod).
+
+#### kernel_module
+
+The `kernel_module` resource includes a new `options` property, which allows users to set module specific parameters and settings. Thanks [@ramereth](https://github.com/ramereth) for this new feature.
+
+Example of a kernel_module resource using the new options property:
+
+```ruby
+ kernel_module 'loop' do
+ options [ 'max_loop=4', 'max_part=8' ]
end
- ```
+```
-* Support for Solaris releases before 10u11 has been removed
-* Upgraded Ohai to 8.20 with new / enhanced plugins. See the [ohai changelog](https://github.com/chef-cookbooks/ohai/blob/master/CHANGELOG.md)
+#### remote_file
-## Highlighted bug fixes for this release:
+The `remote_file` resource has been updated to better display progress when using the `show_progress` resource. Thanks for reporting this issue [@isuftin](https://github.com/isuftin).
+
+#### sudo
+
+The `sudo` resource now runs sudo config validation against all of the sudo configuration files on the system instead of only the file being written. This allows us to detect configuration errors that occur when configs conflict with each other. Thanks for reporting this issue [@drzewiec](https://github.com/drzewiec).
+
+#### windows_ad_join
+
+The `windows_ad_join` has a new `:leave` action for leaving an Active Directory domain and rejoining a workgroup. This new action also has a new `workgroup_name` property for specifying the workgroup to join upon leaving the domain. Thanks [@jasonwbarnett](https://github.com/jasonwbarnett) for adding this new action.
+
+Example of leaving a domain
+
+```ruby
+windows_ad_join 'Leave the domain' do
+ workgroup_name 'local'
+ action :leave
+end
+```
+
+#### windows_package
+
+The `windows_package` resource no longer updates environmental variables before installing the package. This prevents potential modifications that may cause a package installation to fail. Thanks [@jeremyhage](https://github.com/jeremyhage) for this fix.
+
+#### windows_service
+
+The `windows_service` resource no longer updates the service and triggers notifications if the case of the `run_as_user` property does not match the user set on the service. Thanks [@jasonwbarnett](https://github.com/jasonwbarnett) for this fix.
+
+#### windows_share
+
+The `windows_share` resource is now fully idempotent by better validating the provided `path` property from the user. Thanks [@Happycoil](https://github.com/Happycoil) for this fix.
+
+### Security Updates
+
+#### Ruby
+
+Ruby has been updated from 2.6.4 to 2.6.5 in order to resolve the following CVEs:
+
+- [CVE-2019-16255](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16255): A code injection vulnerability of Shell#[] and Shell#test
+- [CVE-2019-16254](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16254): HTTP response splitting in WEBrick (Additional fix)
+- [CVE-2019-15845](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-15845): A NUL injection vulnerability of File.fnmatch and File.fnmatch?
+- [CVE-2019-16201](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16201): Regular Expression Denial of Service vulnerability of WEBrick's Digest access authentication
+
+## What's New in 15.3
+
+### Custom Resource Unified Mode
+
+Chef Infra Client 15.3 introduces an exciting new way to easily write custom resources that mix built-in Chef Infra resources with Ruby code. Previously custom resources would use Chef Infra's standard compile and converge phases, which meant that Ruby would be evaluated first and then the resources would be converged. This often results in confusing and undesirable behavior when you are trying to mix resources with Ruby logic. Many custom resource authors would attempt to get around this by forcing resources to run at compile time so that all the code in their resource would execute during the compile phase.
+
+An example of forcing a resource to run at compile time:
+
+```ruby
+resource_name 'foo' do
+ action :nothing
+end.run_action(:some_action)
+```
+
+With unified mode, you opt in to a single phase per resource where all Ruby and Chef Infra resources are executed at once. This makes it far easier to determine how your code will be evaluated and run. Additionally, you no longer need to force any resources to run at compile time, as all code is run in the compile phase. To enable this new mode just add `unified_mode true` to your resources like this:
+
+```ruby
+property :Some_property, String
+
+unified_mode true
+
+action :create do
+ # some code
+end
+```
+
+### Interval Mode Now Fails on Windows
+
+Chef Infra Client 15.3 will now raise an error if you attempt to keep the chef-client process running long-term by enabling interval runs. Interval runs have already raised failures on non-Windows platforms and we've suggested that users move away from them on Windows for many years. The long-running chef-client process on Windows will load and reload cookbooks over each other in memory. This could produce a running state which is not a representation of the cookbook code that the authors wrote or tested, and behavior that may be wildly different depending on how long the chef-client process has been running and on the sequence that the cookbooks were uploaded.
+
+### Updated Resources
+
+#### ifconfig
+
+The `ifconfig` resource has been updated to properly support interfaces with a hyphen in their name. This is most commonly encountered with bridge interfaces that are named `br-1234`.
+
+#### archive_file
+
+The `archive_file` resource now supports archives in the RAR 5.0 format as well as zip files compressed using xz, lzma, ppmd8 and bzip2 compression.
+
+#### user
+
+**macOS 10.14 / 10.15 support**
+
+The `user` resource now supports the creation of users on macOS 10.14 and 10.15 systems. The updated resource now complies with macOS TCC policies by using a user with admin privileges to create and modify users. The following new properties have been added for macOS user creation:
+
+- `admin` sets a user to be an admin.
+
+- `admin_username` and `admin_password` define the admin user credentials required for toggling SecureToken for a user. The value of 'admin_username' must correspond to a system user that is part of the 'admin' with SecureToken enabled in order to toggle SecureToken.
+
+- `secure_token` is a boolean property that sets the desired state for SecureToken. FileVault requires a SecureToken for full disk encryption.
+
+- `secure_token_password` is the plaintext password required to enable or disable `secure_token` for a user. If no salt is specified we assume the 'password' property corresponds to a plaintext password and will attempt to use it in place of secure_token_password if it is not set.
+
+**Password property is now sensitive**
+
+The `password` property is now set to sensitive to prevent the password from being shown in debug or failure logs.
+
+**gid property can now be a string**
+
+The `gid` property now allows specifying the user's gid as a string. For example:
+
+```ruby
+user 'tim' do
+ gid '123'
+end
+```
+
+### Platform Support Updates
+
+#### macOS 10.15 Support
+
+Chef Infra Client is now validated against macOS 10.15 (Catalina) with packages now available at [downloads.chef.io](https://downloads.chef.io/) and via the [Omnitruck API](https://docs.chef.io/api_omnitruck/). Additionally, Chef Infra Client will no longer be validated against macOS 10.12.
+
+#### AIX 7.2
+
+Chef Infra Client is now validated against AIX 7.2 with packages now available at [downloads.chef.io](https://downloads.chef.io/) and via the [Omnitruck API](https://docs.chef.io/api_omnitruck/).
+
+### Chef InSpec 4.16
+
+Chef InSpec has been updated from 4.10.4 to 4.16.0 with the following changes:
+
+- A new `postfix_conf` has been added for inspecting Postfix configuration files.
+- A new `plugins` section has been added to the InSpec configuration file which can be used to pass secrets or other configurations into Chef InSpec plugins.
+- The `service` resource now includes a new `startname` property for determining which user is starting the Windows services.
+- The `groups` resource now properly gathers membership information on macOS hosts.
+
+### Security Updates
+
+#### Ruby
+
+Ruby has been updated from 2.6.3 to 2.6.4 in order to resolve [CVE-2012-6708](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6708) and [CVE-2015-9251](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9251).
+
+#### openssl
+
+openssl has been updated from 1.0.2s to 1.0.2t in order to resolve [CVE-2019-1563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1563) and [CVE-2019-1547](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547).
+
+#### nokogiri
+
+nokogiri has been updated from 1.10.2 to 1.10.4 in order to resolve [CVE-2019-5477](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5477)
+
+## What's New in 15.2
+
+### Updated Resources
+
+#### dnf_package
+
+The `dnf_package` resource has been updated to fully support RHEL 8.
+
+#### kernel_module
+
+The `kernel_module` now supports a `:disable` action. Thanks [@tomdoherty](https://github.com/tomdoherty).
+
+#### rhsm_repo
+
+The `rhsm_repo` resource has been updated to support passing a repo name of `*` in the `:disable` action. Thanks for reporting this issue [@erinn](https://github.com/erinn).
+
+#### windows_task
+
+The `windows_task` resource has been updated to allow the `day` property to accept an `Integer` value.
+
+#### zypper_package
+
+The `zypper_package` package has been updated to properly upgrade packages if necessary based on the version specified in the resource block. Thanks [@foobarbam](https://github.com/foobarbam) for this fix.
+
+### Platform Support Updates
+
+#### RHEL 8 Support Added
+
+Chef Infra Client 15.2 now includes native packages for RHEL 8 with all builds now validated on RHEL 8 hosts.
+
+#### SLES 11 EOL
+
+Packages will no longer be built for SUSE Linux Enterprise Server (SLES) 11 as SLES 11 exited the 'General Support' phase on March 31, 2019. See Chef's [Platform End-of-Life Policy](https://docs.chef.io/platforms/#platform-end-of-life-policy) for more information on when Chef ends support for an OS release.
+
+#### Ubuntu 14.04 EOL
+
+Packages will no longer be built for Ubuntu 14.04 as Canonical ended maintenance updates on April 30, 2019. See Chef's [Platform End-of-Life Policy](https://docs.chef.io/platforms/#platform-end-of-life-policy) for more information on when Chef ends support for an OS release.
+
+### Ohai 15.2
+
+Ohai has been updated to 15.2 with the following changes:
+
+- Improved detection of Openstack including proper detection of Windows nodes running on Openstack when fetching metadata. Thanks [@jjustice6](https://github.com/jjustice6).
+- A new `other_versions` field has been added to the Packages plugin when the node is using RPM. This allows you to see all installed versions of packages, not just the latest version. Thanks [@jjustice6](https://github.com/jjustice6).
+- The Linux Network plugin has been improved to not mark interfaces down if `stp_state` is marked as down. Thanks [@josephmilla](https://github.com/josephmilla).
+- Arch running on ARM processors is now detected as the `arm` platform. Thanks [@BackSlasher](https://github.com/BackSlasher).
+
+### Chef InSpec 4.10.4
+
+Chef InSpec has been updated from 4.6.4 to 4.10.4 with the following changes:
+
+- Fix handling multiple triggers in the `windows_task` resource
+- Fix exceptions when resources are used with incompatible transports
+- Un-deprecate the `be_running` matcher on the `service` resource
+- Add resource `sys_info.manufacturer` and `sys_info.model`
+- Add `ip6tables` resource
+
+### Security Updates
+
+#### bzip2
+
+bzip2 has been updated from 1.0.6 to 1.0.8 to resolve [CVE-2016-3189](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3189) and [CVE-2019-12900](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12900).
+
+## What's New in 15.1
+
+### New Resources
+
+#### chocolatey_feature
+
+The `chocolatey_feature` resource allows you to enable and disable Chocolatey features. See the [chocolatey_feature documentation](https://docs.chef.io/resources/chocolatey_feature/) for full usage information. Thanks [@gep13](https://github.com/gep13) for this new resource.
+
+### Updated Resources
+
+#### chocolatey_source
+
+The `chocolatey_source` resource has been updated with new `enable` and `disable` actions, as well as `admin_only` and `allow_self_service` properties. Thanks [@gep13](https://github.com/gep13) for this enhancement.
+
+#### launchd
+
+The `launchd` resource has been updated with a new `launch_events` property, which allows you to specify higher-level event types to be used as launch-on-demand event sources. Thanks [@chilcote](https://github.com/chilcote) for this enhancement.
+
+#### yum_package
+
+The `yum_package` resource's helper for interacting with the yum subsystem has been updated to always close out the rpmdb lock, even during failures. This may prevent the rpmdb becoming locked in some rare conditions. Thanks for reporting this issue, [@lytao](https://github.com/lytao).
+
+#### template
+
+The `template` resource now provides additional information on failures, which is especially useful in ChefSpec tests. Thanks [@brodock](https://github.com/brodock) for this enhancement.
+
+### Target Mode Improvements
+
+Our experimental Target Mode received a large number of updates in Chef Infra Client 15.1. Target Mode now reuses the connection to the remote system, which greatly speeds up the remote Chef Infra run. There is also now support for Target Mode in the `systemd_unit`, `log`, `ruby_block`, and `breakpoint` resources. Keep in mind that when using `ruby_block` with Target Mode that the Ruby code in the block will execute locally as there is not necessarily a Ruby runtime on the remote host.
+
+### Ohai 15.1
+
+Ohai has been updated to 15.1 with the following changes:
+
+- The `Shard` plugin properly uses the machine's `machinename`, `serial`, and `uuid` attributes to generate the shard value. The plugin also no longer throws an exception on macOS hosts. Thanks [@michel-slm](https://github.com/michel-slm) for these fixes.
+- The `Virtualbox` plugin has been enhanced to gather information on running guests, storage, and networks when VirtualBox is installed on a node. Thanks [@freakinhippie](https://github.com/freakinhippie) for this new capability.
+- Ohai no longer fails to gather interface information on Solaris in some rare conditions. Thanks [@devoptimist](https://github.com/devoptimist) for this fix.
+
+### Chef InSpec 4.6.4
+
+Chef InSpec has been updated from 4.3.2 to 4.6.4 with the following changes:
+
+- InSpec `Attributes` have now been renamed to `Inputs` to avoid confusion with Chef Infra attributes.
+- A new InSpec plugin type of `Input` has been added for defining new input types. See the [InSpec Plugins documentation](https://github.com/inspec/inspec/blob/master/docs/dev/plugins.md#implementing-input-plugins) for more information on writing these plugins.
+- InSpec no longer prints errors to the stdout when passing `--format json`.
+- When fetching profiles from GitHub, the URL can now include periods.
+- The performance of InSpec startup has been improved.
+
+## What's New in 15.0.300
+
+This release includes critical bugfixes for the 15.0 release:
+- Fix `knife bootstrap` over SSH when `requiretty` is configured on the host.
+- Added the `--chef-license` CLI flag to `chef-apply` and `chef-solo` commands.
+
+## What's New in 15.0.298
+
+This release includes critical bugfixes for the 15.0 release:
+
+- Allow accepting the license on non-interactive Windows sessions
+- Resolve license acceptance failures on Windows 2012 R2
+- Improve some `knife` and `chef-client` help text
+- Properly handle session_timeout default value in `knife bootstrap`
+- Avoid failures due to Train::Transports::SSHFailed class not being loaded in `knife bootstrap`
+- Resolve failures using the ca_trust_file option with `knife bootstrap`
+
+## What's New in 15.0.293
+
+### Chef Client is now Chef Infra Client
+
+Chef Client has a new name, but don't worry, it's the same Chef Client you've grown used to. You'll notice new branding throughout the application, help, and documentation but the command line name of `chef-client` remains the same.
+
+### Chef EULA
+
+Chef Infra Client requires an EULA to be accepted by users before it can run. Users can accept the EULA in a variety of ways:
+
+- `chef-client --chef-license accept`
+- `chef-client --chef-license accept-no-persist`
+- `CHEF_LICENSE="accept" chef-client`
+- `CHEF_LICENSE="accept-no-persist" chef-client`
+
+Finally, if users run `chef-client` without any of these options, they will receive an interactive prompt asking for license acceptance. If the license is accepted, a marker file will be written to the filesystem unless `accept-no-persist` is specified. Once this marker file is persisted, users no longer need to set any of these flags.
+
+See our [Frequently Asked Questions document](https://www.chef.io/bmc-faq/) for more information on the EULA and license acceptance.
+
+### New Features / Functionality
+
+#### Target Mode Prototype
+
+Chef Infra Client 15 adds a prototype for a new method of executing resources called Target Mode. Target Mode allows a Chef Infra Client run to manage a remote system over SSH or another protocol supported by the Train library. This support includes platforms that we currently support like Ubuntu Linux, but also allows for configuring other architectures and platforms, such as switches that do not have native builds of Chef Infra Client. Target Mode maintains a separate node object for each target and allows you to manage that node using existing patterns that you currently use.
+
+As of this release, only the `execute` resource and guards are supported, but modifying existing resources or writing new resources to support Target Mode is relatively easy. Using Target Mode is as easy as running `chef-client --target hostname`. The authentication credentials should be stored in your local `~/.chef/credentials` file with the hostname of the target node as the profile name. Each key/value pair is passed to Train for authentication.
+
+#### Data Collection Ground-Up Refactor
+
+Chef Infra Client's Data Collection subsystem is used to report node changes during client runs to Chef Automate or other reporting systems. For Chef Infra Client 15, we performed a ground-up rewrite of this subsystem, which greatly improves the data reported to Chef Automate and ensures data is delivered even in the toughest of failure conditions.
+
+#### copy_properties_from in Custom Resources
+
+A new `copy_properties_from` method for custom resources allows you copy properties from your custom resource into other resources you are calling, so you can avoid unnecessarily repeating code.
+
+To inherit all the properties of another resource:
+
+```ruby
+resource_name :my_resource
+
+property :mode, String, default: '777'
+property :owner, String, default: 'app_user'
+property :group, String, default: 'admins'
+
+directory '/etc/myapp' do
+ copy_properties_from new_resource
+ recursive true
+end
+```
+
+To selectively inherit certain properties from a resource:
+
+```ruby
+resource_name :my_resource
+
+property :mode, String, default: '777'
+property :owner, String, default: 'app_user'
+property :group, String, default: 'admins'
+
+directory '/etc/myapp' do
+ copy_properties_from(new_resource, :owner, :group, :mode)
+ mode '755'
+ recursive true
+end
+```
+
+#### ed25519 SSH key support
+
+Our underlying SSH implementation has been updated to support the new ed25519 SSH key format. This means you will be able to use `knife bootstrap` and `knife ssh` on hosts that only support this new key format.
+
+#### Allow Using --delete-entire-chef-repo in Chef Local Mode
+
+Chef Solo's `--delete-entire-chef-repo` option has been extended to work in Local Mode as well. Be warned that this flag does exactly what it states, and when used incorrectly, can result in loss of work.
+
+### New Resources
+
+#### archive_file resource
+
+Use the `archive_file` resource to decompress multiple archive formats without the need for compression tools on the host.
+
+See the [archive_file](https://docs.chef.io/resources/archive_file/) documentation for more information.
+
+#### windows_uac resource
+
+Use the `windows_uac` resource to configure UAC settings on Windows hosts.
+
+See the [windows_uac](https://docs.chef.io/resources/windows_uac) documentation for more information.
+
+#### windows_dfs_folder resource
+
+Use the `windows_dfs_folder` resource to create and delete Windows DFS folders.
+
+See the [windows_dfs_folder](https://docs.chef.io/resources/windows_dfs_folder) documentation for more information.
+
+#### windows_dfs_namespace resources
+
+Use the `windows_dfs_namespace` resource to create and delete Windows DFS namespaces.
+
+See the [windows_dfs_namespace](https://docs.chef.io/resources/windows_dfs_namespace) documentation for more information.
+
+#### windows_dfs_server resources
+
+Use the `windows_dfs_server` resource to configure Windows DFS server settings.
+
+See the [windows_dfs_server](https://docs.chef.io/resources/windows_dfs_server) documentation for more information.
+
+#### windows_dns_record resource
+
+Use the `windows_dns_record` resource to create or delete DNS records.
+
+See the [windows_dns_record](https://docs.chef.io/resources/windows_dns_record) documentation for more information.
+
+#### windows_dns_zone resource
+
+Use the `windows_dns_zone` resource to create or delete DNS zones.
+
+See the [windows_dns_zone](https://docs.chef.io/resources/windows_dns_zone) documentation for more information.
+
+#### snap_package resource
+
+Use the `snap_package` resource to install snap packages on Ubuntu hosts.
+
+See the [snap_package](https://docs.chef.io/resources/snap_package) documentation for more information.
+
+### Resource Improvements
+
+#### windows_task
+
+The `windows_task` resource now supports the Start When Available option with a new `start_when_available` property.
+
+#### locale
+
+The `locale` resource now allows setting all possible LC_* environmental variables.
+
+#### directory
+
+The `directory` resource now property supports passing `deny_rights :write` on Windows nodes.
+
+#### windows_service
+
+The `windows_service` resource has been improved to prevent accidentally reverting a service back to default settings in a subsequent definition.
+
+This example will no longer result in the MyApp service reverting to default RunAsUser:
+```ruby
+windows_service 'MyApp' do
+ run_as_user 'MyAppsUser'
+ run_as_password 'MyAppsUserPassword'
+ startup_type :automatic
+ delayed_start true
+ action [:configure, :start]
+end
+
+...
+
+windows_service 'MyApp' do
+ startup_type :automatic
+ action [:configure, :start]
+end
+```
+
+#### Ruby 2.6.3
+
+Chef now ships with Ruby 2.6.3. This new version of Ruby improves performance and includes many new features to make more advanced Chef usage easier. See <https://www.rubyguides.com/2018/11/ruby-2-6-new-features/> for a list of some of the new functionality.
+
+### Ohai Improvements
+
+#### Improved Linux Platform / Platform Family Detection
+
+`Platform` and `platform_family` detection on Linux has been rewritten to utilize the latest config files on modern Linux distributions before falling back to slower and fragile legacy detection methods. Ohai will now begin by parsing the contents of `/etc/os-release` for OS information if available. This feature improves the reliability of detection on modern distros and allows detection of new distros as they are released.
+
+With this change, we now detect `sles_sap` as a member of the `suse` `platform_family`. Additionally, this change corrects our detection of the `platform_version` on Cisco Nexus switches where previously the build number was incorrectly appended to the version string.
+
+#### Improved Virtualization Detection
+
+Hypervisor detection on multiple platforms has been updated to use DMI data and a single set of hypervisors. This greatly improves the detection of hypervisors on Windows, BSD and Solaris platforms. It also means that as new hypervisor detection is added in the future, we will automatically support the majority of platforms.
+
+#### Fix Windows 2016 FQDN Detection
+
+Ohai 14 incorrectly detected a Windows 2016 node's `fqdn` as the node's `hostname`. Ohai 15 now correctly reports the FQDN value.
+
+#### Improved Memory Usage
+
+Ohai now uses less memory due to internal optimization of how we track plugin information.
+
+#### FIPS Detection Improvements
+
+The FIPS plugin now uses the built-in FIPS detection in Ruby for improved detection.
+
+### New Deprecations
+
+#### knife cookbook site deprecated in favor of knife supermarket
+
+The `knife cookbook site` command has been deprecated in favor of the `knife supermarket` command. `knife cookbook site` will now produce a warning message. In Chef Infra Client 16, we will remove the `knife cookbook site` command entirely.
+
+#### locale LC_ALL property
+
+The `LC_ALL` property in the `locale` resource has been deprecated as the usage of this environmental variable is not recommended by distribution maintainers.
+
+### Breaking Changes
+
+#### Knife Bootstrap
+
+Knife bootstrap has been entirely rewritten. Native support for Windows bootstrapping is now a part of the main `knife bootstrap` command. This marks the deprecation of the `knife-windows` plugin's `bootstrap` behavior. This change also addresses [CVE-2015-8559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-8559): *The `knife bootstrap` command in chef leaks the validator.pem private RSA key to /var/log/messages*.
+
+**Important**: `knife bootstrap` can bootstrap all supported versions of Chef Infra Client. Older versions may continue to work as far back as 12.20.
+
+In order to accommodate a combined bootstrap that supports both SSH and WinRM, some CLI flags have been added, removed, or changed. Using the changed options will result in deprecation warnings, but `knife bootstrap` will accept those options unless otherwise noted. Using removed options will cause the command to fail.
+
+**New Flags**
+
+| Flag | Description |
+|-----:|:------------|
+| --max-wait SECONDS | Maximum time to wait for initial connection to be established. |
+| --winrm-basic-auth-only | Perform only Basic Authentication to the target WinRM node. |
+| --connection-protocol PROTOCOL| Connection protocol to use. Valid values are 'winrm' and 'ssh'. Default is 'ssh'. |
+| --connection-user | User to authenticate as, regardless of protocol. |
+| --connection-password| Password to authenticate as, regardless of protocol. |
+| --connection-port | Port to connect to, regardless of protocol. |
+| --ssh-verify-host-key VALUE | Verify host key. Default is 'always'. Valid values are 'accept', 'accept\_new', 'accept\_new\_or\_local\_tunnel', and 'never'. |
+
+**Changed Flags**
+
+| Flag | New Option | Notes |
+|-----:|:-----------|:------|
+| --[no-]host-key-verify |--ssh-verify-host-key VALUE | See above for valid values. |
+| --forward-agent | --ssh-forward-agent| |
+| --session-timeout MINUTES | --session-timeout SECONDS|New for ssh, existing for winrm. The unit has changed from MINUTES to SECONDS for consistency with other timeouts. |
+| --ssh-password | --connection-password | |
+| --ssh-port | --connection-port | `knife[:ssh_port]` config setting remains available.
+| --ssh-user | --connection-user | `knife[:ssh_user]` config setting remains available.
+| --ssl-peer-fingerprint | --winrm-ssl-peer-fingerprint | |
+| --prerelease |--channel CHANNEL | This now allows you to specify the channel that Chef Infra Client gets installed from. Valid values are _stable_, _current_, and _unstable_. 'current' has the same effect as using the old --prerelease. |
+| --winrm-authentication-protocol=PROTO | --winrm-auth-method=AUTH-METHOD | Valid values: _plaintext_, _kerberos_, _ssl_, _negotiate_ |
+| --winrm-password| --connection-password | |
+| --winrm-port| --connection-port | `knife[:winrm_port]` config setting remains available.|
+| --winrm-ssl-verify-mode MODE | --winrm-no-verify-cert | Mode is not accepted. When flag is present, SSL cert will not be verified. Same as original mode of 'verify\_none'. [1] |
+| --winrm-transport TRANSPORT | --winrm-ssl | Use this flag if the target host is accepts WinRM connections over SSL. [1] |
+| --winrm-user | --connection-user | `knife[:winrm_user]` config setting remains available.|
+| --winrm-session-timeout | --session-timeout | Now available for bootstrapping over SSH as well |
+
+[1] These flags do not have an automatic mapping of old flag -> new flag. The new flag must be used.
+
+**Removed Flags**
+
+| Flag | Notes |
+|-----:|:------|
+|--kerberos-keytab-file| This option existed but was not implemented. |
+|--winrm-codepage| This was used under `knife-windows` because bootstrapping was performed over a `cmd` shell. It is now invoked from `powershell`, so this option is no longer used. |
+|--winrm-shell| This option was ignored for bootstrap. |
+|--install-as-service| Installing Chef Client as a service is not supported. |
+
+**Usage Changes**
+
+Instead of specifying protocol with `-o`, it is also possible to prefix the target hostname with the protocol in URL format. For example:
+
+```
+knife bootstrap example.com -o ssh
+knife bootstrap ssh://example.com
+knife bootstrap example.com -o winrm
+knife bootstrap winrm://example.com
+```
+
+#### Chef Infra Client packages remove /opt/chef before installation
+
+Upon upgrading Chef Infra Client packages, the `/opt/chef` directory is removed. This ensures any `chef_gem` installed gem versions and other modifications to `/opt/chef` will removed to prevent upgrade issues. Due to technical details with rpm script execution order, the implementation involves a a pre-installation script that wipes `/opt/chef` before every install, and is done consistently this way on every package manager.
+
+Users who are properly managing customizations to `/opt/chef` through Chef recipes would not be affected, because their customizations will still be installed by the new package.
+
+You will see a warning that the `/opt/chef` directory will be removed during the package installation process.
+
+#### powershell_script now allows overriding the default flags
+
+We now append `powershell_script` user flags to the default flags rather than the other way around, which made user flags override the defaults. This is the correct behavior, but it may cause scripts to execute differently than in previous Chef Client releases.
+
+#### Package provider allow_downgrade is now true by default
+
+We reversed the default behavior to `allow_downgrade true` for our package providers. To override this setting to prevent downgrades, use the `allow_downgrade false` flag. This behavior change will mostly affect users of the rpm and zypper package providers.
+
+In this example, the code below should now read as asserting that the package `foo` must be version `1.2.3` after that resource is run.:
+
+```
+package "foo" do
+ version "1.2.3"
+end
+```
+
+The code below is now what is necessary to specify that `foo` must be version `1.2.3` or higher. Note that the yum provider supports syntax like `package "foo > 1.2.3"`, which should be used and is preferred over using allow_downgrade.
+
+```
+package "foo" do
+ allow_downgrade false
+ version "1.2.3"
+end
+```
+
+#### Node Attributes deep merge nil values
+
+Writing a `nil` to a precedence level in the node object now acts like any other value and can be used to override values back to `nil`.
+
+For example:
+
+```
+chef (15.0.53)> node.default["foo"] = "bar"
+ => "bar"
+chef (15.0.53)> node.override["foo"] = nil
+ => nil
+chef (15.0.53)> node["foo"]
+ => nil
+```
+
+In prior versions of `chef-client`, the `nil` set in the override level would be completely ignored and the value of `node["foo"]` would have been "bar".
+
+#### http_disable_auth_on_redirect now enabled
+
+The Chef config ``http_disable_auth_on_redirect`` has been changed from `false` to `true`. In Chef Infra Client 16, this config option will be removed altogether and Chef Infra Client will always disable auth on redirect.
+
+#### knife cookbook test removal
+
+The `knife cookbook test` command has been removed. This command would often report non-functional cookbooks as functional, and has been superseded by functionality in other testing tools such as `cookstyle`, `foodcritic`, and `chefspec`.
+
+#### ohai resource's ohai_name property removal
+
+The `ohai` resource contained a non-functional `ohai_name` property, which has been removed.
+
+#### knife status --hide-healthy flag removal
+
+The `knife status --hide-healthy` flag has been removed. Users should run `knife status --hide-by-mins MINS` instead.
+
+#### Cookbook shadowing in Chef Solo Legacy Mode Removed
+
+Previously, if a user provided multiple cookbook paths to Chef Solo that contained cookbooks with the same name, Chef Solo would combine these into a single cookbook. This merging of two cookbooks often caused unexpected outcomes and has been removed.
+
+#### Removal of unused route resource properties
+
+The `route` resource contained multiple unused properties that have been removed. If you previously set `networking`, `networking_ipv6`, `hostname`, `domainname`, or `domain`, they would be ignored. In Chef Infra Client 15, setting these properties will throw an error.
+
+#### FreeBSD pkg provider removal
+
+Support for the FreeBSD `pkg` package system in the `freebsd_package` resource has been removed. FreeBSD 10 replaced the `pkg` system with `pkg-ng` system, so this removal only impacts users of EOL FreeBSD releases.
+
+#### require_recipe removal
+
+The legacy `require_recipe` method in recipes has been removed. This method was replaced with `include_recipe` in Chef Client 10, and a FoodCritic rule has been warning to update cookbooks for multiple years.
+
+#### Legacy shell_out methods removed
+
+In Chef Client 14, many of the more obscure `shell_out` methods used in LWRPs and custom resources were combined into the standard `shell_out` and `shell_out!` methods. The legacy methods were infrequently used and Chef Client 14/Foodcritic both contained deprecation warnings for these methods. The following methods will now throw an error: `shell_out_compact`, `shell_out_compact!`, `shell_out_compact_timeout`, `shell_out_compact_timeout!`, `shell_out_with_systems_locale`, and `shell_out_with_systems_locale!`.
+
+#### knife bootstrap --identity_file removal
+
+The `knife bootstrap --identity_file` flag has been removed. This flag was deprecated in Chef Client 12, and users should now use the `--ssh-identity-file` flag instead.
+
+### knife user support for Chef Infra Server < 12 removed
+
+The `knife user` command no longer supports the open source Chef Infra Server version prior to 12.
+
+#### attributes in metadata.rb
+
+Chef Infra Client no longer processes attributes in the `metadata.rb` file. Attributes could be defined in the `metadata.rb` file as a form of documentation, which would be shown when running `knife cookbook show COOKBOOK_NAME`. Often, these attribute definitions would become out of sync with the attributes in the actual attributes files. Chef Infra Client 15 will no longer show these attributes when running `knife cookbook show COOKBOOK_NAME` and will instead throw a warning message upon upload. Foodcritic has warned against the use of attributes in the `metadata.rb` file since April 2017.
+
+#### Node attributes array bugfix
+
+Chef Infra Client 15 includes a bugfix for incorrect node attribute behavior involving a rare usage of arrays, which may impact users who depend on the incorrect behavior.
+
+Previously, you could set an attribute like this:
+
+```
+node.default["foo"] = []
+node.default["foo"] << { "bar" => "baz }
+```
+
+This would result in a Hash, instead of a VividMash, inserted into the AttrArray, so that:
+
+```
+node.default["foo"][0]["bar"] # gives the correct result
+node.default["foo"][0][:bar] # does not work due to the sub-Hash not
+ # converting keys
+```
+
+The new behavior uses a Mash so that the attributes will work as expected.
+
+#### Ohai's system_profile plugin for macOS removed
+
+We removed the `system_profile` plugin because it incorrectly returned data on modern macOS systems. If you relied on this plugin, you'll want to update recipes to use `node['hardware']` instead, which correctly returns the same data, but in a more easily consumed format. Removing this plugin speeds up Ohai and Chef Infra Client by ~3 seconds, and dramatically reduces the size of the node object on the Chef Infra Server.
+
+#### Ohai's Ohai::Util::Win32::GroupHelper class has been removed
+
+We removed the `Ohai::Util::Win32::GroupHelper` helper class from Ohai. This class was intended for use internally in several Windows plugins, but it was never marked private in the codebase. If any of your Ohai plugins rely on this helper class, you will need to update your plugins for Ohai 15.
+
+#### Audit Mode
+
+Chef Client's Audit mode was introduced in 2015 as a beta that needed to be enabled via `client.rb`. Its functionality has been superseded by Chef InSpec and has been removed.
+
+#### Ohai system_profiler plugin removal
+
+The `system_profiler` plugin, which ran on macOS systems, has been removed. This plugin took longer to run than all other plugins on macOS combined, and no longer produced usable information on modern macOS releases. If you're looking for similar information, it can now be found in the `hardware` plugin.
+
+#### Ohai::Util::Win32::GroupHelper helper removal
+
+The deprecated `Ohai::Util::Win32::GroupHelper` helper has been removed from Ohai. Any custom Ohai plugins using this helper will need to be updated.
+
+#### Ohai::System.refresh_plugins method removal
+
+The `refresh_plugins` method in the `Ohai::System` class has been removed as it has been unused for multiple major Ohai releases. If you are programmatically using Ohai in your own Ruby application, you will need to update your code to use the `load_plugins` method instead.
+
+#### Ohai Microsoft VirtualPC / VirtualServer detection removal
+
+The `Virtualization` plugin will no longer detect systems running on the circa ~2005 VirtualPC or VirtualServer hypervisors. These hypervisors were long ago deprecated by Microsoft and support can no longer be tested.
+
+## What's New in 14.15
+
+### Updated Resources
+
+#### ifconfig
+
+The `ifconfig` resource has been updated to properly support interfaces with a hyphen in their name. This is most commonly encountered with bridge interfaces that are named `br-1234`. Additionally, the `ifconfig` resource now supports the latest ifconfig binaries found in OS releases such as Debian 10.
+
+#### windows_task
+
+The `windows_task` resource now supports the Start When Available option with a new `start_when_available` property. Issues that prevented the resource from being idempotent on Windows 2016 and 2019 hosts have also been resolved.
+
+### Platform Support
+
+#### New Platforms
+
+Chef Infra Client is now tested against the following platforms with packages available on [downloads.chef.io](https://downloads.chef.io):
+
+- Ubuntu 20.04
+- Ubuntu 18.04 aarch64
+- Debian 10
+
+#### Retired Platforms
+
+- Chef Infra Clients packages are no longer produced for Windows 2008 R2 as this release reached its end of life on Jan 14th, 2020.
+- Chef Infra Client packages are no longer produced for RHEL 6 on the s390x platform.
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2u to resolve [CVE-2019-1551](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1551)
+
+#### Ruby
+
+Ruby has been updated from 2.5.7 to 2.5.8 to resolve the following CVEs:
+
+- [CVE-2020-16255](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/): Unsafe Object Creation Vulnerability in JSON (Additional fix)
+- [CVE-2020-10933](https://www.ruby-lang.org/en/news/2020/03/31/heap-exposure-in-socket-cve-2020-10933/): Heap exposure vulnerability in the socket library
+
+## What's New in 14.14.29
+
+### Bug Fixes
+
+ - Fixed an error with the `service` and `systemd_unit` resources which would try to re-enable services with an indirect status.
+ - The `systemd_unit` resource now logs at the info level.
+ - Fixed knife config when it returned a `TypeError: no implicit conversion of nil into String` error.
+
+### Security Updates
+
+#### libxslt
+
+libxslt has been updated to 1.1.34 to resolve [CVE-2019-13118](https://nvd.nist.gov/vuln/detail/CVE-2019-13118).
+
+## What's New in 14.14.25
+
+### Bug Fixes
+
+- Resolved a regression introduced in Chef Infra Client 14.14.14 that broke installation of gems in some scenarios
+- Fixed Habitat packaging of `chef-client` artifacts
+- Fixed crash in knife when displaying a missing profile error message
+- Fixed knife subcommand --help not working as intended for some commands
+- Fixed knife ssh interactive mode exit error
+- Fixed for `:day` option not accepting integer value in the `windows_task` resource
+- Fixed for `user` resource not handling a GID if it is specified as a string
+- Fixed the `ifconfig` resource to support interfaces with a `-` in the name
+
+## What's New in 14.14.14
+
+### Platform Updates
+
+#### Newly Supported Platforms
+
+The following platforms are now packaged and tested for Chef Infra Client:
+
+- Red Hat 8
+- FreeBSD 12
+- macOS 10.15
+- Windows 2019
+- AIX 7.2
+
+#### Deprecated Platforms
+
+The following platforms have reached EOL status and are no longer packaged or tested for Chef Infra Client:
+
+- FreeBSD 10
+- macOS 10.12
+- SUSE Linux Enterprise Server (SLES) 11
+- Ubuntu 14.04
+
+See Chef's [Platform End-of-Life Policy](/platforms/#platform-end-of-life-policy) for more information on when Chef ends support for an OS release.
+
+### Updated Resources
+
+#### dnf_package
+
+The `dnf_package` resource has been updated to fully support RHEL 8.
+
+#### zypper_package
+
+The `zypper_package` resource has been updated to properly update packages when using the `:upgrade` action.
+
+#### remote_file
+
+The `remote_file` resource now properly shows download progress when the `show_progress` property is set to true.
+
+### Improvements
+
+### Custom Resource Unified Mode
+
+Chef Infra Client 14.14 introduces an exciting new way to easily write custom resources that mix built-in Chef Infra resources with Ruby code. Previously, custom resources would use Chef Infra's standard compile and converge phases, which meant that Ruby would be evaluated first and then the resources would be converged. This often results in confusing and undesirable behavior when you are trying to mix resources with Ruby logic. Many custom resource authors would attempt to get around this by forcing resources to run at compile time so that all the code in their resource would execute during the compile phase.
+
+An example of forcing a resource to run at compile time:
+
+```ruby
+resource_name 'foo' do
+ action :nothing
+end.run_action(:some_action)
+```
+
+With unified mode, you opt in to a single phase per resource where all Ruby and Chef Infra resources are executed at once. This makes it far easier to determine how your code will be evaluated and run. Additionally, you no longer need to force any resources to run at compile time, as all code is run in the compile phase. To enable this new mode just add `unified_mode true` to your resources like this:
+
+```ruby
+property :Some_property, String
+
+unified_mode true
+
+action :create do
+ # some code
+end
+```
+
+#### New Options for installing Ruby Gems From metadata.rb
-Fixed `chef_gem` for local gems with remote dependencies. A recent chef release introduced a breaking change which added the `--local` argument to `gem installs` for local gems prohibiting remote dependencies from being installed. Users who want to ensure that gem installs remain completely local should add `--local` to the `options` property:
+Chef Infra Client allows gems to be specified in the cookbook metadata.rb, which can be problematic in some environments. When a cookbook is running in an airgapped environment, Chef Infra Client attempts to connect to rubygems.org even if the gem is already on the system. There are now two additional configuration options that can be set in your `client.rb` config:
+ - `gem_installer_bundler_options`: This allows setting additional bundler options for the install such as --local to install from local cache. Example: ["--local", "--clean"].
+ - `skip_gem_metadata_installation`: If set to true skip gem metadata installation if all gems are already installed.
+#### SLES / openSUSE 15 detection
+
+Ohai now properly detects SLES and openSUSE 15.x. Thanks for this fix [@balasankarc](https://gitlab.com/balasankarc).
+
+#### Performance Improvements
+
+We have improved the performance of Chef Infra Client by resolving bundler errors in our packaging.
+
+#### Bootstrapping Chef Infra Client 15 will no fail
+
+Knife now fails with a descriptive error message when attempting to bootstrap nodes with Chef Infra Client 15. You will need to bootstrap these nodes using Knife from Chef Infra Client 15.x. We recommend performing this bootstrap from Chef Workstation, which includes the Knife CLI in addition to other useful tools for managing your infrastructure with Chef Infra.
+
+### Security Updates
+
+#### Ruby
+
+Ruby has been updated from 2.5.5 to 2.5.7 in order to resolve the following CVEs:
+
+- [CVE-2012-6708](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6708)
+- [CVE-2015-9251](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9251).
+- [CVE-2019-16201](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-15845).
+- [CVE-2019-15845](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9251).
+- [CVE-2019-16254](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16254).
+- [CVE-2019-16255](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16255).
+
+#### openssl
+
+openssl has been updated from 1.0.2s to 1.0.2t in order to resolve [CVE-2019-1563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1563) and [CVE-2019-1547](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547).
+
+#### nokogiri
+
+nokogiri has been updated from 1.10.2 to 1.10.4 in order to resolve [CVE-2019-5477](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5477).
+
+## What's New in 14.13
+
+### Updated Resources
+
+#### directory
+
+The `directory` has been updated to properly set the `deny_rights` permission on Windows. Thanks [@merlinjim](https://github.com/merlinjim) for reporting this issue.
+
+#### service
+
+The `service` resource is now idempotent on SLES 11 systems. Thanks [@gsingla294](https://github.com/gsingla294) for reporting this issue.
+
+#### cron
+
+The `cron` resource has been updated to advise users to use the specify properties rather than passing values in as part of the `environment` property. This avoids a situation where a user could pass the differing values in both locations and receive unexpected results.
+
+#### link
+
+The `link` resource includes improved logging upon failure to help you debug what has failed. Thanks [@jaymzh](https://github.com/jaymzh) for this improvement.
+
+#### template
+
+The `template` resource now includes additional information when templating failures, which is particularly useful in ChefSpec. Thanks [@brodock](https://github.com/brodock) for this improvement.
+
+### delete_resource Fix
+
+The `delete_resource` helper now works properly when the resource you are attempting to delete has multiple providers. Thanks [@artem-sidorenko](https://github.com/artem-sidorenko) for this fix.
+
+### Helpers Help Everywhere
+
+Various helpers have been moved into Chef Infra Client's `universal` class, which makes them available anywhere in your cookbook, not just recipes. If you've ever been confused why something like `search`, `powershell_out`, or `data_bag_item` didn't work somewhere in your code, that should be resolved now.
+
+### Deprecations
+
+The `CHEF-25` deprecation for resource collisions between cookbooks and resources in Chef Infra Client has been removed. Instead you will see a log warning that a collision has occurred, which advises you to update your run_list or cookbooks.
+
+### Updated Components
+
+- openssl 1.0.2r -> 1.0.2s (bugfix only release)
+- cacerts 2019-01-23 -> 2019-05-15
+
+## What's New in 14.12.9
+
+### License Acceptance Placeholder Flag
+
+In preparation for Chef Infra Client 15.0 we've added a placeholder `--chef-license` flag to the chef-client command. This allows you to use the new `--chef-license` flag on both Chef Infra Client 14.12.9+ and 15+ notes without producing errors on Chef Infra Client 14.
+
+### Important Bug Fixes
+
+- Blacklisting and whitelisting default and override level attributes is once again possible.
+- You may now encrypt a previously unencrypted data bag.
+- Resolved a regression introduced in Chef Infra Client 14.12.3 that resulted in errors when managing Windows services
+
+## What's New in 14.12.3
+
+### Updated Resources
+
+#### windows_service
+
+The windows_service resource no longer resets credentials on a service when using the :start action without the :configure action. Thanks [@jasonwbarnett](https://github.com/jasonwbarnett) for fixing this.
+
+#### windows_certificate
+
+The windows_certificate resource now imports nested certificates while importing P7B certs.
+
+### Updated Components
+
+- nokogiri 1.10.1 -> 1.10.2
+- ruby 2.5.3 -> 2.5.5
+- InSpec 3.7.1 -> 3.9.0
+- The unused windows-api gem is no longer bundled with Chef on Windows hosts
+
+## What's New in 14.11
+
+### Updated Resources
+
+#### chocolatey_package
+
+The chocolatey_package resource now uses the provided options to fetch information on available packages, which allows installation packages from private sources. Thanks [@astoltz](https://github.com/astoltz) for reporting this issue.
+
+#### openssl_dhparam
+
+The openssl_dhparam resource now supports updating the dhparam file's mode on subsequent chef-client runs. Thanks [@anewb](https://github.com/anewb) for the initial work on this fix.
+
+#### mount
+
+The mount resource now properly adds a blank line between entries in fstab to prevent mount failures on AIX.
+
+#### windows_certificate
+
+The windows_certificate resource now supports importing Base64 encoded CER certificates and nested P7B certificates. Additionally, private keys in PFX certificates are now imported along with the certificate.
+
+#### windows_share
+
+The windows_share resource has improved logic to compare the desired share path vs. the current path, which prevents the resource from incorrectly converging during each Chef run. Thanks [@Xorima](https://github.com/xorima) for this fix.
+
+#### windows_task
+
+The windows_task resource now properly clears out arguments that are no longer present when updating a task. Thanks [@nmcspadden](https://github.com/nmcspadden) for reporting this.
+
+### InSpec 3.7.1
+
+InSpec has been updated from 3.4.1 to 3.7.1. This new release contains improvements to the plugin system, a new config file system, and improvements to multiple resources. Additionally, profile attributes have also been renamed to inputs to prevent confusion with Chef attributes, which weren't actually related in any way.
+
+### Updated Components
+
+- bundler 1.16.1 -> 1.17.3
+- libxml2 2.9.7 -> 2.9.9
+- ca-certs updated to 2019-01-22 for new roots
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2r in order to resolve [CVE-2019-1559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1559)
+
+#### RubyGems
+
+RubyGems has been updated to 2.7.9 in order to resolve the following CVEs:
+
+- [CVE-2019-8320](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8320): Delete directory using symlink when decompressing tar
+- [CVE-2019-8321](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8321): Escape sequence injection vulnerability in verbose
+- [CVE-2019-8322](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8322): Escape sequence injection vulnerability in gem owner
+- [CVE-2019-8323](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8323): Escape sequence injection vulnerability in API response handling
+- [CVE-2019-8324](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8324): Installing a malicious gem may lead to arbitrary code execution
+- [CVE-2019-8325](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8325): Escape sequence injection vulnerability in errors
+
+## What's New in 14.10
+
+### Updated Resources
+
+#### windows_certificate
+
+The windows_certificate resource is now fully idempotent and properly imports private keys. Thanks [@Xorima](https://github.com/Xorima) for reporting these issues.
+
+#### apt_repository
+
+The apt_repository resource no longer creates .gpg directory in the user's home directory owned by root when installing repository keys. Thanks [@omry](http://github.com/omry) for reporting this issue.
+
+#### git
+
+The git resource no longer displays the URL of the repository if the `sensitive` property is set.
+
+### InSpec 3.4.1
+
+InSpec has been updated from 3.2.6 to 3.4.1. This new release adds new `aws_billing_report` / `aws_billing_reports` resources, resolves multiple bugs, and includes tons of under the hood improvements.
+
+### New Deprecations
+
+#### knife cookbook site
+
+Since Chef 13, `knife cookbook site` has actually called the `knife supermarket` command under the hood. In Chef 16 (April 2020), we will remove the `knife cookbook site` command in favor of `knife supermarket`.
+
+#### Audit Mode
+
+Chef's Audit mode was introduced in 2015 as a beta that needed to be enabled via client.rb. Its functionality has been superseded by InSpec and we will be removing this beta feature in Chef Infra Client 15 (April 2019).
+
+#### Cookbook Shadowing
+
+Cookbook shadowing was deprecated in 0.10 and will be removed in Chef Infra Client 15 (April 2019). Cookbook shadowing allowed combining cookbooks within a mono-repo, so long as the cookbooks in question had the same name and were present in both the cookbooks directory and the site-cookbooks directory.
+
+## What's New in 14.9
+
+### Updated Resources
+
+#### group
+
+On Windows hosts, the group resource now supports setting the comment field via a new `comment` property.
+
+#### homebrew_cask
+
+Two issues, which caused homebrew_cask to converge on each Chef run, have been resolved. Thanks [@jeroenj](https://github.com/jeroenj) for this fix. Additionally, the resource will no longer fail if the `cask_name` property is specified.
+
+#### homebrew_tap
+
+The homebrew_tap resource no longer fails if the `tap_name` property is specified.
+
+#### openssl_x509_request
+
+The openssl_x509_request resource now properly writes out the CSR file if the `path` property is specified. Thank you [@cpjones](https://github.com/cpjones) for reporting this issue.
+
+#### powershell_package_source
+
+powershell_package_source now suppresses warnings, which prevented properly loading the resource state, and resolves idempotency issues when both the `name` and `source_name` properties were specified. Thanks [@Happycoil](https://github.com/Happycoil) for this fix.
+
+#### sysctl
+
+The sysctl resource now allows slashes in the key or block name. This allows keys such as `net/ipv4/conf/ens256.401/rp_filter` to be used with this resource.
+
+#### windows_ad_join
+
+Errors joining the domain are now properly suppressed from the console and logs if the `sensitive` property is set to true. Thanks [@Happycoil](https://github.com/Happycoil) for this improvement.
+
+#### windows_certificate
+
+The delete action now longer fails if a certificate does not exist on the system. Additionally, certificates with special characters in their passwords will no longer fail. Thank you for reporting this [@chadmccune](https://github.com/chadmccune).
+
+#### windows_printer
+
+The windows_printer resource no longer fails when creating or deleting a printer if the `device_id` property is specified.
+
+#### windows_task
+
+Non-system users can now run tasks without a password being specified.
+
+### Minimal Ohai Improvements
+
+The ohai `init_package` plugin is now included as part of the `minimal_ohai` plugins set, which allows resources such as timezone to continue to function if Chef is running with the minimal number of ohai plugins.
+
+### Ruby 2.6 Support
+
+Chef 14.9 now supports Ruby 2.6.
+
+### InSpec 3.2.6
+
+InSpec has been updated from 3.0.64 to 3.2.6 with improved resources for auditing. See the [InSpec changelog](https://github.com/inspec/inspec/blob/master/CHANGELOG.md#v326-2018-12-20) for additional details on this new version.
+
+### powershell_exec Runtimes Bundled
+
+The necessary VC++ runtimes for the powershell_exec helper are now bundled with Chef to prevent failures on hosts that lacked the runtimes.
+
+## What's New in 14.8
+
+### Updated Resources
+
+#### apt_package
+
+The apt_package resource now supports using the `allow_downgrade` property to enable downgrading of packages on a node in order to meet a specified version. Thank you [@whiteley](https://github.com/whiteley) for requesting this enhancement.
+
+#### apt_repository
+
+An issue was resolved in the apt_repository resource that caused the resource to fail when importing GPG keys on newer Debian releases. Thank you [@EugenMayer](https://github.com/EugenMayer) for this fix.
+
+#### dnf_package / yum_package
+
+Initial support has been added for Red Hat Enterprise Linux 8. Thank you [@pixdrift](https://github.com/pixdrift) for this fix.
+
+#### gem_package
+
+gem_package now supports installing gems into Ruby 2.6 or later installations.
+
+#### windows_ad_join
+
+windows_ad_join now uses the UPN format for usernames, which prevents some failures authenticating to the domain.
+
+#### windows_certificate
+
+An issue was resolved in the :acl_add action of the windows_certificate resource, which caused the resource to fail. Thank you [@shoekstra](https://github.com/shoekstra) for reporting this issue.
+
+#### windows_feature
+
+The windows_feature resource now allows for the installation of DISM features that have been fully removed from a system. Thank you [@zanecodes](https://github.com/zanecodes) for requesting this enhancement.
+
+#### windows_share
+
+Multiple issues were resolved in windows_share, which caused the resource to either fail or update the share state on every Chef Client run. Thank you [@chadmccune](https://github.com/chadmccune) for reporting several of these issues and [@derekgroh](https://github.com/derekgroh) for one of the fixes.
+
+#### windows_task
+
+A regression was resolved that prevented ChefSpec from testing the windows_task resource in Chef Client 14.7. Thank you [@jjustice6](https://github.com/jjustice6) for reporting this issue.
+
+### Ohai 14.8
+
+#### Improved Virtualization Detection
+
+**Hyper-V Hypervisor Detection**
+
+Detection of Linux guests running on Hyper-V has been improved. In addition, Linux guests on Hyper-V hypervisors will also now detect their hypervisor's hostname. Thank you [@safematix](https://github.com/safematix) for contributing this enhancement.
+
+Example `node['virtualization']` data:
+
+```json
+{
+ "systems": {
+ "hyperv": "guest"
+ },
+ "system": "hyperv",
+ "role": "guest",
+ "hypervisor_host": "hyper_v.example.com"
+}
+```
+
+**LXC / LXD Detection**
+
+On Linux systems running lxc or lxd containers, the lxc/lxd virtualization system will now properly populate the `node['virtualization']['systems']` attribute.
+
+**BSD Hypervisor Detection**
+
+BSD-based systems can now detect guests running on KVM and Amazon's hypervisor without the need for the dmidecode package.
+
+#### New Platform Support
+
+- Ohai now properly detects the openSUSE 15.X platform. Thank you [@megamorf](https://github.com/megamorf) for reporting this issue.
+- SUSE Linux Enterprise Desktop now identified as platform_family 'suse'
+- XCP-NG is now identified as platform 'xcp' and platform_family 'rhel'. Thank you [@heyjodom](http://github.com/heyjodom) for submitting this enhancement.
+- Mangeia Linux is now identified as platform 'mangeia' and platform_family 'mandriva'
+- Antergos Linux now identified as platform_family 'arch'
+- Manjaro Linux now identified as platform_family 'arch'
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2q in order to resolve:
+
+- Microarchitecture timing vulnerability in ECC scalar multiplication [CVE-2018-5407](https://nvd.nist.gov/vuln/detail/CVE-2018-5407)
+- Timing vulnerability in DSA signature generation ([CVE-2018-0734](https://nvd.nist.gov/vuln/detail/CVE-2018-0734))
+
+## What's New in 14.7
+
+### New Resources
+
+#### windows_firewall_rule
+
+Use the `windows_firewall_rule` resource create or delete Windows Firewall rules.
+
+See the [windows_firewall_rule](https://docs.chef.io/resources/windows_firewall_rule) documentation for more information.
+
+Thank you [Schuberg Philis](https://schubergphilis.com/) for transferring us the [windows_firewall cookbook](https://supermarket.chef.io/cookbooks/windows_firewall) and to [@Happycoil](https://github.com/Happycoil) for porting it to chef-client with a significant refactoring.
+
+#### windows_share
+
+Use the `windows_share` resource create or delete Windows file shares.
+
+See the [windows_share](https://docs.chef.io/resources/windows_share) documentation for more information.
+
+#### windows_certificate
+
+Use the `windows_certificate` resource add, remove, or verify certificates in the system or user certificate stores.
+
+See the [windows_certificate](https://docs.chef.io/resources/windows_certificate) documentation for more information.
+
+### Updated Resources
+
+#### dmg_package
+
+The dmg_package resource has been refactored to improve idempotency and properly support accepting a DMG's EULA with the `accept_eula` property.
+
+#### kernel_module
+
+Kernel_module now only runs the `initramfs` update once per Chef run to greatly speed up chef-client runs when multiple kernel_module resources are used. Thank you [@tomdoherty](https://github.com/tomdoherty) for this improvement.
+
+#### mount
+
+The `supports` property once again allows passing supports data as an array. This matches the behavior present in Chef 12.
+
+#### timezone
+
+macOS support has been added to the timezone resource.
+
+#### windows_task
+
+A regression in Chef 14.6's windows_task resource which resulted in tasks being created with the "Run only when user is logged on" option being set when created with a specific user other than SYSTEM, has been resolved.
+
+## What's New in 14.6
+
+### Smaller Package and Install Size
+
+Both Chef packages and on disk installations have been greatly reduced in size by trimming unnecessary installation files. This has reduced our package size on macOS/Linux by ~50% and Windows by ~12%. With this change Chef 14 is now smaller than a legacy Chef 10 package.
+
+### New Resources
+
+#### timezone
+
+Chef now includes the `timezone` resource from [@dragonsmith](http://github.com/dragonsmith)'s `timezone_lwrp` cookbook. This resource supports setting a Linux node's timezone. Thank you [@dragonsmith](http://github.com/dragonsmith) for allowing us to include this out of the box in Chef.
+
+Example:
+
+```ruby
+timezone 'UTC'
```
-chef_gem 'my-gem' do
- source '/tmp/gems/my-gem.gem'
- options '--local'
- action :install
+
+### Updated Resources
+
+#### windows_task
+
+The `windows_task` resource has been updated to support localized system users and groups on non-English nodes. Thanks [@jugatsu](http://github.com/jugatsu) for making this possible.
+
+#### user
+
+The `user` resource now includes a new `full_name` property for Windows hosts, which allows specifying a user's full name.
+
+Example:
+
+```ruby
+user 'jdoe' do
+ full_name 'John Doe'
end
```
+
+#### zypper_package
+
+The `zypper_package` resource now includes a new `global_options` property. This property can be used to specify one or more options for the zypper command line that are global in context.
+
+Example:
+
+```ruby
+package 'sssd' do
+ global_options '-D /tmp/repos.d/'
+end
+```
+
+### InSpec 3.0
+
+Inspec has been updated to version 3.0 with addition resources, exception handling, and a new plugin system. See <https://blog.chef.io/2018/10/16/announcing-inspec-3-0/> for details.
+
+### macOS Mojave (10.14)
+
+Chef is now tested against macOS Mojave, and packages are now available at downloads.chef.io.
+
+### Important Bugfixes
+
+- Multiple bugfixes in Chef Vault have been resolved by updating chef-vault to 3.4.2
+- Invalid yum package names now gracefully fail
+- `windows_ad_join` now properly executes. Thank you [@cpjones01](https://github.com/cpjones01) for reporting this.
+- `rhsm_errata_level` now properly executes. Thank you [@freakinhippie](https://github.com/freakinhippie) for this fix.
+- `registry_key` now properly writes out the correct value when `sensitive` is specified. Thank you [@josh-barker](https://github.com/josh-barker) for this fix.
+- `locale` now properly executes on RHEL 6 and Amazon Linux 201X.
+
+### Ohai 14.6
+
+#### Filesystem Plugin on AIX and Solaris
+
+AIX and Solaris now ship with a filesystem2 plugin that updates the filesystem data to match that of Linux, macOS, and BSD hosts. This new data structure makes accessing filesystem data in recipes easier and especially improves the layout and depth of data on ZFS filesystems. In Chef Infra Client 15 (April 2019) we will begin writing this same format of data to the existing `node['filesystem']` namespace. In Chef 16 (April 2020) we will remove the `node['filesystem2']` namespace, completing the transition to the new format. Thank you [@jaymzh](https://github.com/jaymzh) for continuing the updates to our filesystem plugins with this change.
+
+#### macOS Improvements
+
+The system_profile plugin has been improved to skip over unnecessary data, which reduces macOS node sizes on the Chef Server. Additionally the CPU plugin has been updated to limit what sysctl values it polls, which prevents hanging on some system configurations.
+
+#### SLES 15 Detection
+
+SLES 15 is now correctly detected as the platform "suse" instead of "sles". This matches the behavior of SLES 11 and 12 hosts.
+
+### New Deprecations
+
+#### system_profile Ohai plugin removal
+
+The system_profile plugin will be removed from Chef/Ohai 15 in April 2019. This plugin does not correctly return data on modern Mac systems. Additionally the same data is provided by the hardware plugin, which has a format that is simpler to consume. Removing this plugin will reduce Ohai return by ~3 seconds and greatly reduce the size of the node object on the Chef server.
+
+### Security Updates
+
+#### Ruby 2.5.3
+
+Ruby has been updated to from 2.5.1 to 2.5.3 to resolve multiple CVEs and bugs:
+
+- [CVE-2018-16396](https://www.ruby-lang.org/en/news/2018/10/17/not-propagated-taint-flag-in-some-formats-of-pack-cve-2018-16396/)
+- [CVE-2018-16395](https://www.ruby-lang.org/en/news/2018/10/17/openssl-x509-name-equality-check-does-not-work-correctly-cve-2018-16395/)
+
+## What's New in 14.5.33
+
+This release resolves a regression that caused the ``windows_ad_join`` resource to fail to run. It also makes the following additional fixes:
+
+ - The ``ohai`` resource's unused ``ohai_name`` property has been deprecated. This will be removed in Chef Infra Client 15.0.
+ - Error messages in the ``windows_feature`` resources have been improved.
+ - The ``windows_service`` resource will no longer log potentially sensitive information if the ``sensitive`` property is used.
+
+Thanks to @cpjones01, @kitforbes, and @dgreeninger for their help with this release.
+
+## What's New in 14.5.27
+
+### New Resources
+
+We've added new resources to Chef 14.5. Cookbooks using these resources will continue to take precedent until the Chef Infra Client 15.0 release
+
+#### windows_workgroup
+
+Use the `windows_workgroup` resource to join or change a Windows host workgroup.
+
+See the [windows_workgroup](https://docs.chef.io/resources/windows_workgroup) documentation for more information.
+
+Thanks [@derekgroh](https://github.com/derekgroh) for contributing this new resource.
+
+#### locale
+
+Use the `locale` resource to set the system's locale.
+
+See the [locale](https://docs.chef.io/resources/locale) documentation for more information.
+
+Thanks [@vincentaubert](https://github.com/vincentaubert) for contributing this new resource.
+
+### Updated Resources
+
+#### windows_ad_join
+
+`windows_ad_join` now includes a `new_hostname` property for setting the hostname for the node upon joining the domain.
+
+Thanks [@derekgroh](https://github.com/derekgroh) for contributing this new property.
+
+### InSpec 2.2.102
+
+InSpec has been updated from 2.2.70 to 2.2.102. This new version includes the following improvements:
+
+ - Support for using ERB templating within the .yml files
+ - HTTP basic auth support for fetching dependent profiles
+ - A new global attributes concept
+ - Better error handling with Automate reporting
+ - Vendor command now vendors profiles when using path://
+
+### Ohai 14.5
+
+#### Windows Improvements
+
+Detection for the `root_group` attribute on Windows has been simplified and improved to properly support non-English systems. With this change, we've also deprecated the `Ohai::Util::Win32::GroupHelper` helper, which is no longer necessary. Thanks to [@jugatsu](https://github.com/jugatsu) for putting this together.
+
+We've also added a new `encryption_status` attribute to volumes on Windows. Thanks to [@kmf](https://github.com/kmf) for suggesting this new feature.
+
+#### Configuration Improvements
+
+The timeout period for communicating with OpenStack metadata servers can now be configured with the `openstack_metadata_timeout` config option. Thanks to [@sawanoboly](https://github.com/sawanoboly) for this improvement.
+
+Ohai now properly handles relative paths to config files when running on the command line. This means commands like `ohai -c ../client.rb` will now properly use your config values.
+
+### Security updates
+
+#### Rubyzip
+
+The rubyzip gem has been updated to 1.2.2 to resolve [CVE-2018-1000544](https://www.cvedetails.com/cve/CVE-2018-1000544/)
+
+## What's New in 14.4
+
+### Knife configuration profile management commands
+
+Several new commands have been added under `knife config` to help manage multiple
+profiles in your `credentials` file.
+
+`knife config get-profile` displays the active profile.
+
+`knife config use-profile PROFILE` sets the workstation-level default
+profile. You can still override this setting with the `--profile` command line
+option or the `$CHEF_PROFILE` environment variable.
+
+`knife config list-profiles` displays all your available profiles along with
+summary information on each.
+
+```bash
+$ knife config get-profile
+staging
+$ knife config use-profile prod
+Set default profile to prod
+$ knife config list-profiles
+ Profile Client Key Server
+-----------------------------------------------------------------------------
+ staging myuser ~/.chef/user.pem https://example.com/organizations/staging
+*prod myuser ~/.chef/user.pem https://example.com/organizations/prod
+```
+
+Thank you [@coderanger](https://github.com/coderanger) for this contribution.
+
+### New Resources
+
+The following new previous resources were added to Chef 14.4. Cookbooks with the same resources will continue to take precedent until the Chef Infra Client 15.0 release
+
+#### cron_d
+
+Use the [cron_d](https://docs.chef.io/resources/cron_d) resource to manage cron definitions in /etc/cron.d. This is similar to the `cron` resource, but it does not use the monolithic `/etc/crontab`. file.
+
+#### cron_access
+
+Use the [cron_access](https://docs.chef.io/resources/cron_access) resource to manage the `/etc/cron.allow` and `/etc/cron.deny` files. This resource previously shipped in the `cron` community cookbook and has fully backwards compatibility with the previous `cron_manage` definition in that cookbook.
+
+#### openssl_x509_certificate
+
+Use the [openssl_x509_certificate](https://docs.chef.io/resources/openssl_x509_certificate) resource to generate signed or self-signed, PEM-formatted x509 certificates. If no existing key is specified, the resource automatically generates a passwordless key with the certificate. If a CA private key and certificate are provided, the certificate will be signed with them. This resource previously shipped in the `openssl` cookbook as `openssl_x509` and is fully backwards compatible with the legacy resource name.
+
+Thank you [@juju482](https://github.com/juju482) for updating this resource!
+
+#### openssl_x509_request
+
+Use the [openssl_x509_request](https://docs.chef.io/resources/openssl_x509_request) resource to generate PEM-formatted x509 certificates requests. If no existing key is specified, the resource automatically generates a passwordless key with the certificate.
+
+Thank you [@juju482](https://github.com/juju482) for contributing this resource.
+
+#### openssl_x509_crl
+
+Use the [openssl_x509_crl](https://docs.chef.io/resources/openssl_x509_crl)l resource to generate PEM-formatted x509 certificate revocation list (CRL) files.
+
+Thank you [@juju482](https://github.com/juju482) for contributing this resource.
+
+#### openssl_ec_private_key
+
+Use the [openssl_ec_private_key](https://docs.chef.io/resources/openssl_ec_private_key) resource to generate ec private key files. If a valid ec key file can be opened at the specified location, no new file will be created.
+
+Thank you [@juju482](https://github.com/juju482) for contributing this resource.
+
+#### openssl_ec_public_key
+
+Use the [openssl_ec_public_key](https://docs.chef.io/resources/openssl_ec_public_key) resource to generate ec public key files given a private key.
+
+Thank you [@juju482](https://github.com/juju482) for contributing this resource.
+
+### Resource improvements
+
+#### windows_package
+
+The windows_package resource now supports setting the `sensitive` property to avoid showing errors if a package install fails.
+
+#### sysctl
+
+The sysctl resource will now update the on-disk `sysctl.d` file even if the current sysctl value matches the desired value.
+
+#### windows_task
+
+The windows_task resource now supports setting the task priority of the scheduled task with a new `priority` property. Additionally windows_task now supports managing the behavior of task execution when a system is on battery using new `disallow_start_if_on_batteries` and `stop_if_going_on_batteries` properties.
+
+#### ifconfig
+
+The ifconfig resource now supports setting the interface's VLAN via a new `vlan` property on RHEL `platform_family` and setting the interface's gateway via a new `gateway` property on RHEL/Debian `platform_family`.
+
+Thank you [@tomdoherty](https://github.com/tomdoherty) for this contribution.
+
+#### route
+
+The route resource now supports additional RHEL platform_family systems as well as Amazon Linux.
+
+#### systemd_unit
+
+The [systemd_unit](https://docs.chef.io/resources/systemd_unit) resource now supports specifying options multiple times in the content hash. Instead of setting the value to a string you can now set it to an array of strings.
+
+Thank you [@dbresson](https://github.com/dbresson) for this contribution.
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL updated to 1.0.2p to resolve:
+
+- Client DoS due to large DH parameter ([CVE-2018-0732](https://nvd.nist.gov/vuln/detail/CVE-2018-0732))
+- Cache timing vulnerability in RSA Key Generation ([CVE-2018-0737](https://nvd.nist.gov/vuln/detail/CVE-2018-0737))
+
+## What's New in 14.3
+
+### New Preview Resources Concept
+
+This release of Chef introduces the concept of Preview Resources. Preview resources behave the same as a standard resource built into Chef, except Chef will load a resource with the same name from a cookbook instead of the built-in preview resource.
+
+What does this mean for you? It means we can introduce new resources in Chef without breaking existing behavior in your infrastructure. For instance if you have a cookbook with a resource named `manage_everything` and a future version of Chef introduced a preview resource named `manage_everything` you will continue to receive the resource from your cookbook. That way outside of a major release your won't experience a potentially breaking behavior change from the newly included resource.
+
+Then when we perform our yearly major release we'll remove the preview designation from all resources, and the built in resources will take precedence over resources with the same names in cookbooks.
+
+### New Resources
+
+#### chocolatey_config
+
+Use the chocolatey_config resource to add or remove Chocolatey configuration keys."
+
+**Actions**
+
+- `set` - Sets a Chocolatey config value.
+- `unset` - Unsets a Chocolatey config value.
+
+**Properties**
+
+- `config_key` - The name of the config. We'll use the resource's name if this isn't provided.
+- `value` - The value to set.
+
+#### chocolatey_source
+
+Use the chocolatey_source resource to add or remove Chocolatey sources.
+
+**Actions**
+
+- `add` - Adds a Chocolatey source.
+- `remove` - Removes a Chocolatey source.
+
+**Properties**
+
+- `source_name` - The name of the source to add. We'll use the resource's name if this isn't provided.
+- `source` - The source URL.
+- `bypass_proxy` - Whether or not to bypass the system's proxy settings to access the source.
+- `priority` - The priority level of the source.
+
+#### powershell_package_source
+
+Use the `powershell_package_source` resource to register a PowerShell package repository.
+
+#### Actions
+
+- `register` - Registers and updates the PowerShell package source.
+- `unregister` - Unregisters the PowerShell package source.
+
+**Properties**
+
+- `source_name` - The name of the package source.
+- `url` - The url to the package source.
+- `trusted` - Whether or not to trust packages from this source.
+- `provider_name` - The package management provider for the source. It supports the following providers: 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl' and 'chocolatey'.
+- `publish_location` - The url where modules will be published to for this source. Only valid if the provider is 'PowerShellGet'.
+- `script_source_location` - The url where scripts are located for this source. Only valid if the provider is 'PowerShellGet'.
+- `script_publish_location` - The location where scripts will be published to for this source. Only valid if the provider is 'PowerShellGet'.
+
+#### kernel_module
+
+Use the kernel_module resource to manage kernel modules on Linux systems. This resource can load, unload, blacklist, install, and uninstall modules.
+
+**Actions**
+
+- `install` - Load kernel module, and ensure it loads on reboot.
+- `uninstall` - Unload a kernel module and remove module config, so it doesn't load on reboot.
+- `blacklist` - Blacklist a kernel module.
+- `load` - Load a kernel module.
+- `unload` - Unload kernel module
+
+**Properties**
+
+- `modname` - The name of the kernel module.
+- `load_dir` - The directory to load modules from.
+- `unload_dir` - The modprobe.d directory.
+
+#### ssh_known_hosts_entry
+
+Use the ssh_known_hosts_entry resource to add an entry for the specified host in /etc/ssh/ssh_known_hosts or a user's known hosts file if specified.
+
+**Actions**
+
+- `create` - Create an entry in the ssh_known_hosts file.
+- `flush` - Immediately flush the entries to the config file. Without this the actual writing of the file is delayed in the Chef run so all entries can be accumulated before writing the file out.
+
+**Properties**
+
+- `host` - The host to add to the known hosts file.
+- `key` - An optional key for the host. If not provided this will be automatically determined.
+- `key_type` - The type of key to store.
+- `port` - The server port that the ssh-keyscan command will use to gather the public key.
+- `timeout` - The timeout in seconds for ssh-keyscan.
+- `mode` - The file mode for the ssh_known_hosts file.
+- `owner`- The file owner for the ssh_known_hosts file.
+- `group` - The file group for the ssh_known_hosts file.
+- `hash_entries` - Hash the hostname and addresses in the ssh_known_hosts file for privacy.
+- `file_location` - The location of the ssh known hosts file. Change this to set a known host file for a particular user.
+
+### New `knife config get` command
+
+The `knife config get` command has been added to help with debugging configuration issues with `knife` and other tools that use the `knife.rb` file.
+
+With no arguments, it will display all options you've set:
+
+```bash
+$ knife config get
+Loading from configuration file /Users/.../.chef/knife.rb
+chef_server_url: https://...
+client_key: /Users/.../.chef/user.pem
+config_file: /Users/.../.chef/knife.rb
+log_level: warn
+log_location: STDERR
+node_name: ...
+validation_key:
+```
+
+You can also pass specific keys to only display those `knife config get node_name client_key`, or use `--all` to display everything (including options that are using the default value).
+
+### Simplification of `shell_out` APIs
+
+The following helper methods have been deprecated in favor of the single shell_out helper:
+
+- `shell_out_with_systems_locale`
+- `shell_out_with_timeout`
+- `shell_out_compact`
+- `shell_out_compact_timeout`
+- `shell_out_with_systems_locale!`
+- `shell_out_with_timeout!`
+- `shell_out_compact!`
+- `shell_out_compact_timeout!`
+
+The functionality of `shell_out_with_systems_locale` has been implemented using the `default_env: false` option that removes the PATH and locale mangling that has been the default behavior of `shell_out`.
+
+The functionality of `shell_out_compact` has been folded into `shell_out`. The `shell_out` API when called with varargs has its arguments flatted, compacted and coerced to strings. This style of calling is encouraged over using strings and building up commands using `join(" ")` since it avoids shell interpolation and edge conditions in the construction of spaces between arguments. The varargs form is still not supported on Windows.
+
+The functionality of `shell_out*timeout` has also been folded into `shell_out`. Users writing Custom Resources should be explicit for Chef-14: `shell_out!("whatever", timeout: new_resource.timeout)` which will become automatic in Chef-15.
+
+### Silencing deprecation warnings
+
+While deprecation warnings have been great for the Chef community to ensure cookbooks are kept up-to-date and to prepare for major version upgrades, sometimes you just can't fix a deprecation right now. This is often compounded by the recommendation to enable `treat_deprecation_warnings_as_errors` mode in your Test Kitchen integration tests, which doesn't understand the difference between deprecations from community cookbooks and those from your own code.
+
+Two new options are provided for silencing deprecation warnings: `silence_deprecation_warnings` and inline `chef:silence_deprecation` comments.
+
+The `silence_deprecation_warnings` configuration value can be set in your `client.rb` or `solo.rb` config file, either to `true` to silence all deprecation warnings or to an array of deprecations to silence. You can specify which to silence either by the deprecation key name (e.g. `"internal_api"`), the numeric deprecation ID (e.g. `25` or `"CHEF-25"`), or by specifying the filename and line number where the deprecation is being raised from (e.g. `"default.rb:67"`).
+
+An example of setting the `silence_deprecation_warnings` option in your `client.rb` or `solo.rb`:
+
+```ruby
+silence_deprecation_warnings %w{deploy_resource chef-23 recipes/install.rb:22}
+```
+
+or in your `kitchen.yml`:
+
+```yaml
+provisioner:
+ name: chef_solo
+ solo_rb:
+ treat_deprecation_warnings_as_errors: true
+ silence_deprecation_warnings:
+ - deploy_resource
+ - chef-23
+ - recipes/install.rb:22
+```
+
+You can also silence deprecations using a comment on the line that is raising the warning:
+
+```ruby
+erl_call 'something' do # chef:silence_deprecation
+```
+
+We advise caution in the use of this feature, as excessive or prolonged silencing can lead to difficulty upgrading when the next major release of Chef comes out.
+
+### Misc Windows improvements
+
+- A new `skip_publisher_check` property has been added to the `powershell_package` resource
+- `windows_feature_powershell` now supports Windows 2008 R2
+- The `mount` resource now supports the `mount_point` property on Windows
+- `windows_feature_dism` no longer errors when specifying the source
+- Resolved idempotency issues in the `windows_task` resource and prevented setting up a task with bad credentials
+- `windows_service` no longer throws Ruby deprecation warnings
+
+### Newly Introduced Deprecations
+
+#### CHEF-26: Deprecation of old shell_out APIs
+
+As noted above, this release of Chef unifies our shell_out helpers into just shell_out and shell_out!. Previous helpers are now deprecated and will be removed in Chef Infra Client 15.
+
+See [CHEF-26 Deprecation Page](https://docs.chef.io/deprecations_shell_out) for details.
+
+#### Legacy FreeBSD pkg provider
+
+Chef Infra Client 15 will remove support for the legacy FreeBSD pkg format. We will continue to support the pkgng format introduced in FreeBSD 10.
+
+## What's New in 14.2
+
+### `ssh-agent` support for user keys
+
+You can now use `ssh-agent` to hold your user key when using knife. This allows storing your user key in an encrypted form as well as using `ssh -A` agent forwarding for running knife commands from remote devices.
+
+You can enable this by adding `ssh_agent_signing true` to your `knife.rb` or `ssh_agent_signing = true` in your `credentials` file.
+
+To encrypt your existing user key, you can use OpenSSL:
+
+```
+( openssl rsa -in user.pem -pubout && openssl rsa -in user.pem -aes256 ) > user_enc.pem
+chmod 600 user_enc.pem
+```
+
+This will prompt you for a passphrase for to use to encrypt the key. You can then load the key into your `ssh-agent` by running `ssh-add user_enc.pem`. Make sure you add the `ssh_agent_signing` to your configuration, and update your `client_key` to point at the new, encrypted key (and once you've verified things are working, remember to delete your unencrypted key file).
+
+### default_env Property in Execute Resource
+
+The shell_out helper has been extended with a new option `default_env` to allow disabling Chef from modifying PATH and LOCALE environmental variables as it shells out. This new option defaults to true (modify the env), preserving the previous behavior of the helper.
+
+The execute resource has also been updated with a new property `default_env` that allows utilizing this the ENV sanity functionality in shell_out. The new property defaults to false, but it can be set to true in order to ensure a sane PATH and LOCALE when shelling out. If you find that binaries cannot be found when using the execute resource, `default_env` set to true may resolve those issues.
+
+### Small Size on Disk
+
+Chef now bundles the inspec-core and train-core gems, which omit many cloud dependencies not needed within the Chef client. This change reduces the install size of a typical system by ~22% and the number of files within that installation by ~20% compared to Chef 14.1\. Enjoy the extra disk space.
+
+### Virtualization detection on AWS
+
+Ohai now detects the virtualization hypervisor `amazonec2` when running on Amazon's new C5/M5 instances.
+
+## What's New in 14.1.12
+
+This release resolves a number of regressions in 14.1.1:
+
+- `git` resource: don't use `--prune-tags` as it's really new.
+- `rhsm_repo` resource: now works
+- `apt_repository` resource: use the `repo_name` property to name files
+- `windows_task` resource: properly handle commands with arguments
+- `windows_task` resource: handle creating tasks as the SYSTEM user
+- `remote_directory` resource: restore the default for the `overwrite` property
+
+### Ohai 14.1.3
+
+- Properly detect FIPS environments
+- `shard` plugin: work in FIPS compliant environments
+- `filesystem` plugin: Handle BSD platforms
+
+## What's New in 14.1.1
+
+### Platform Additions
+
+Enable Ubuntu-18.04 and Debian-9 tested chef-client packages.
+
+## What's New in 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.
+
+### Security Updates
+
+#### ffi Gem
+
+- CVE-2018-1000201: DLL loading issue which can be hijacked on Windows OS
+
+## 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.
+
+## What's New in 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
+
+## What's New in 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 or remove 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.
+
+#### windows_workgroup
+
+Use the windows_workgroup resource to join a Windows Workgroup and reboot the node. This resource is based on the `windows_ad_join` resource.
+
+### 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://docs.chef.io/inspec/> 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 Solo `-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) 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" ]
+```
+
+## What's New in 13.12.14
+
+### Bugfixes
+
+- The mount provider now properly adds blank lines between fstab entries on AIX
+- Ohai now reports itself as Ohai well communicating with GCE metadata endpoints
+- Property deprecations in custom resources no longer result in an error. Thanks for reporting this [martinisoft](https://github.com/martinisoft)
+- mixlib-archive has been updated to prevent corruption of archives on Windows systems
+
+### Updated Components
+
+- libxml2 2.9.7 -> 2.9.9
+- ca-certs updated to 2019-01-22 for new roots
+- nokogiri 1.8.5 -> 1.10.1
+
+### Security Updates
+
+#### OpenSSL
+
+OpenSSL has been updated to 1.0.2r in order to resolve [CVE-2019-1559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1559) and [CVE-2018-5407](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5407)
+
+#### RubyGems
+
+RubyGems has been updated to 2.7.9 in order to resolve the following CVEs:
+
+- [CVE-2019-8320](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8320): Delete directory using symlink when decompressing tar
+- [CVE-2019-8321](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8321): Escape sequence injection vulnerability in verbose
+- [CVE-2019-8322](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8322): Escape sequence injection vulnerability in gem owner
+- [CVE-2019-8323](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8323): Escape sequence injection vulnerability in API response handling
+- [CVE-2019-8324](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8324): Installing a malicious gem may lead to arbitrary code execution
+- [CVE-2019-8325](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8325): Escape sequence injection vulnerability in errors
+
+## What's New in 13.12.3
+
+### Smaller Package and Install Size
+
+We trimmed unnecessary installation files, greatly reducing the sizes of both Chef packages and on disk installations. MacOS/Linux/FreeBSD packages are ~50% smaller and Windows are ~12% smaller. Chef 13 is now smaller than a legacy Chef 10 package.
+
+### macOS Mojave (10.14)
+
+Chef is now tested against macOS Mojave and packages are now available at downloads.chef.io.
+
+### SUSE Linux Enterprise Server 15
+
+- Ohai now properly detects SLES 15
+- The Chef package will no longer remove symlinks to chef-client and ohai when upgrading on SLES 15
+
+### Updated Chef-Vault
+
+Updating chef-vault to 3.4.2 resolved multiple bugs.
+
+### Faster Windows Installations
+
+Improved Windows installation speed by skipping unnecessary steps when Windows Installer 5.0 or later is available.
+
+### Ohai Release Notes 13.12
+
+#### macOS Improvements
+
+- sysctl commands have been modified to gather only the bare minimum required data, which prevents sysctl hanging in some scenarios
+- Extra data has been removed from the system_profile plugin, reducing the amount of data stored on the chef-server for each node
+
+### New Deprecations
+
+#### system_profile Ohai plugin removal
+
+The system_profile plugin will be removed from Chef/Ohai 15 in April, 2019. This plugin incorrectly returns data on modern Mac systems. Further, the hardware plugin returns the same data in a more readily consumable format. Removing this plugin reduces the speed of the Ohai return by ~3 seconds and also greatly reduces the node object size on the Chef server
+
+#### ohai_name property in ohai resource
+
+The ``ohai`` resource's unused ``ohai_name`` property has been deprecated. This will be removed in Chef Infra Client 15.0.
+
+### Security Updates
+
+#### Ruby 2.4.5
+
+Ruby has been updated to from 2.4.4 to 2.4.5 to resolve multiple CVEs as well as bugs:
+
+- [CVE-2018-16396](https://www.ruby-lang.org/en/news/2018/10/17/not-propagated-taint-flag-in-some-formats-of-pack-cve-2018-16396/)
+- [CVE-2018-16395](https://www.ruby-lang.org/en/news/2018/10/17/openssl-x509-name-equality-check-does-not-work-correctly-cve-2018-16395/)
+
+## What's New in 13.11
+
+### Sensitive Properties on Windows
+
+- `windows_service` no longer logs potentially sensitive information when a service is setup
+- `windows_package` now respects the `sensitive` property to avoid logging sensitive data in the event of a package installation failure
+
+### Other Fixes
+
+- `remote_directory` now properly loads files in the root of a cookbook's `files` directory
+- `osx_profile` now uses the full path the profiles CLI tool to avoid running other binaries of the same name in a users path
+- `package` resources that don't support the `allow_downgrade` property will no longer fail
+- `knife bootstrap windows` error messages have been improved
+
+### Security Updates
+
+#### OpenSSL
+
+- OpenSSL has been updated to 1.0.2p to resolve [CVE-2018-0732](https://nvd.nist.gov/vuln/detail/CVE-2018-0732) and [CVE-2018-0737](https://nvd.nist.gov/vuln/detail/CVE-2018-0737)
+
+#### Rubyzip
+
+- Updated Rubyzip to 1.2.2 to resolve [CVE-2018-1000544](https://nvd.nist.gov/vuln/detail/CVE-2018-1000544)
+
+## What's New in 13.10
+
+### Bugfixes
+
+- Resolves a duplicate logging getting created when redirecting stdout
+- Using --recipe-url with a local file on Windows no longer fails
+- Service resource no longer throws Ruby deprecation warnings on Windows
+
+### Ohai 13.10 Improvements
+
+- Correctly identify the platform_version on the final release of Amazon Linux 2.0
+- Detect nodes with the DMI data of "OpenStack Compute" as being OpenStack nodes
+
+### Security Updates
+
+#### ffi Gem
+
+- CVE-2018-1000201: DLL loading issue which can be hijacked on Windows OS
+
+## What's New in 13.9.4
+
+### Platform Updates
+
+As Debian 7 is now end of life we will no longer produce Debian 7 chef-client packages.
+
+### Ifconfig on Ubuntu 18.04
+
+Incompatibilities with Ubuntu 18.04 in the ifconfig resource have been resolved.
+
+### Ohai Updated to 13.9.2
+
+#### Virtualization detection on AWS
+
+Ohai now detects the virtualization hypervisor `amazonec2` when running on Amazon's new C5/M5 instances.
+
+#### Configurable DMI Whitelist
+
+The whitelist of DMI IDs is now user configurable using the `additional_dmi_ids` configuration setting, which takes an Array.
+
+#### Filesystem2 on BSD
+
+The Filesystem2 functionality has been backported to BSD systems to provide a consistent filesystem format.
+
+### Security Updates
+
+#### Ruby updated to 2.4.4
+
+- [CVE-2017-17742](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-17742/): HTTP response splitting in WEBrick
+- [CVE-2018-6914](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6914/): Unintentional file and directory creation with directory traversal in tempfile and tmpdir
+- [CVE-2018-8777](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8777/): DoS by large request in WEBrick
+- [CVE-2018-8778](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8778/): Buffer under-read in String#unpack
+- [CVE-2018-8779](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8779/): Unintentional socket creation by poisoned NUL byte in UNIXServer and UNIXSocket
+- [CVE-2018-8780](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8780/): Unintentional directory traversal by poisoned NUL byte in Dir
+- Multiple vulnerabilities in RubyGems
+
+#### Nokogiri updated to 1.8.2
+
+- Behavior in libxml2 has been reverted which caused CVE-2018-8048 (loofah gem), CVE-2018-3740 (sanitize gem), and CVE-2018-3741 (rails-html-sanitizer gem).
+
+#### OpenSSL updated to 1.0.2o
+
+- CVE-2018-0739: Constructed ASN.1 types with a recursive definition could exceed the stack.
+
+## What's New in 13.9.1
+
+## Platform Additions
+
+Enable Ubuntu-18.04 and Debian-9 tested chef-client packages.
+
+## What's New in 13.9.0
+
+- On Windows, the installer now correctly re-extracts files during repair mode
+- The mount resource will now not create duplicate entries when the device type differs
+- Ensure we don't request every remote file when running with lazy loading enabled
+- Don't crash when getting the access rights for Windows system accounts
+
+### 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 13 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.'
+```
+
+### 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
+```
+
+### Ohai Improvements
+
+- Fix uptime parsing on AIX
+- Fix Softlayer cloud detection
+- Use the current Azure metadata endpoint
+- Correctly detect macOS guests on VMware and VirtualBox
+- Please see the [Ohai Changelog](https://github.com/chef/ohai/blob/master/CHANGELOG.md) for the complete list of changes.
+
+## What's New in 13.8.5
+
+This is a small bug fix release to resolve two issues we found in the
+13.8 release:
+
+- chef-client run failures due to a failure in a newer version of the FFI gem on RHEL 6.x and 7.x
+- knife failures when running `knife cookbook site install` to install a deprecated cookbook that has no replacement
+
+## What's New in 13.8.3
+
+This is a small bug fix release that updates Ohai to properly detect and
+poll SoftLayer metadata now that SoftLayer no longer supports TLS
+1.0/1.1. This update is only necessary if you're running on Softlayer.
+
+## What's New in 13.8.0
+
+### 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](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15412)
+
+## What's New in 13.7.16
+
+### 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, the `knife` CLI 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(http://www.foodcritic.io/#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](http://www.foodcritic.io/#FC107) has been introduced to detect usage of epic_fail.
+
+#### Legacy Mixins
+
+In Chef 14 several 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](http://www.foodcritic.io/#FC097), [FC098](http://www.foodcritic.io/#FC098), [FC099](http://www.foodcritic.io/#FC099), [FC100](http://www.foodcritic.io/#FC100), and [FC102](http://www.foodcritic.io/#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](http://www.foodcritic.io/#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](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3738), [CVE-2017-3737](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3737), [CVE-2017-3736](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3736), and [CVE-2017-3735](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3735).
+- Ruby has been upgraded to 2.4.3 to resolve [CVE-2017-17405](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-17405)
+
+### Ohai 13.7
+
+#### 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
+
+## What's New in 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](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3735) and [CVE-2017-3736](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3736)
+- RubyGems has been upgraded to 2.6.14 to resolve [CVE-2017-0903](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0903)
+
+## What's New in 13.6.0
+
+### `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) 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
+
+#### 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:
+
+```ruby
+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 Infra Client 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.
+
+## What's New in 13.5
+
+- **The mount resource's password property is now marked as **sensitive** 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 support only the MM/DD/YYYY format, in keeping with the Windows cookbook.
+- **InSpec updated to 1.39.1**
+
+### Ohai 13.5
+
+### 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.
+
+## What's New in 13.4.24
+
+### Security
+
+This release includes Ruby 2.4.2 to fix the following CVEs:
+
+- [CVE-2017-0898](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0898)
+- [CVE-2017-10784](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-10784)
+- [CVE-2017-14033](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-14033)
+- [CVE-2017-14064](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-14064)
+
+## What's New in 13.4.19
+
+### Security release of RubyGems
+
+Chef Client 13.4 includes RubyGems 2.6.13 to fix the following CVEs:
+
+- [CVE-2017-0899](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0899)
+- [CVE-2017-0900](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0900)
+- [CVE-2017-0901](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0901)
+- [CVE-2017-0902](https://cve.mitre.org/cgi-bin/cvename.cgi?name=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 specified 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 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 Packages plugin has been updated to include package information on Arch Linux systems.
+
+## What's New in 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
+
+# What's New in 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>
+
+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>
+
+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>
+
+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) 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 fallback 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 descending from StandardError (e.g. LoadError, SecurityError, SystemExit) will no longer trigger a retry if they are raised during the execution 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 behavior 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.
+
+# What's New in 12.22:
+
+## Security Updates
+
+### Ruby
+
+Ruby has been updated to 2.3.6 to resolve CVE-2017-17405
+
+### LibXML2
+
+Libxml2 has been updated to 2.9.7 to resolve CVE-2017-15412
+
+## Ohai 8.26.1
+
+### EC2 detection on C5/M5
+
+Ohai now provides EC2 metadata configuration information on the new C5/M5 instance types running on Amazon's new hypervisor.
+
+### LsPci Plugin
+
+The new LsPci plugin provides a node[:pci] hash with information about the PCI bus based on lspci. Only runs on Linux.
+
+### Docker Detection
+
+The virtualization plugin has been updated to properly detect when running on Docker CE
+
+# What's New in 12.21:
+
+## Security Fixes
+
+This release of Chef Client contains Ruby 2.3.5, fixing 4 CVEs:
+
+ - CVE-2017-0898
+ - CVE-2017-10784
+ - CVE-2017-14033
+ - CVE-2017-14064
+
+It also contains a new version of Rubygems, fixing 4 CVEs:
+
+ - CVE-2017-0899
+ - CVE-2017-0900
+ - CVE-2017-0901
+ - CVE-2017-0902
+
+This release also contains a new version of zlib, fixing 4
+CVEs:
+
+ - [CVE-2016-9840](https://www.cvedetails.com/cve/CVE-2016-9840/)
+ - [CVE-2016-9841](https://www.cvedetails.com/cve/CVE-2016-9841/)
+ - [CVE-2016-9842](https://www.cvedetails.com/cve/CVE-2016-9842/)
+ - [CVE-2016-9843](https://www.cvedetails.com/cve/CVE-2016-9843/)
+
+## On Debian prefer Systemd to Upstart
+
+On Debian systems, packages that support systemd will often ship both an
+old style init script and a systemd unit file. When this happened, Chef
+would incorrectly choose Upstart rather than Systemd as the service
+provider. Chef will now prefer systemd where available.
+
+## Handle the supports pseudo-property more gracefully
+
+Chef 13 removed the `supports` property from core resources. However,
+many cookbooks also have a property named support, and Chef 12 was
+incorrectly giving a deprecation notice in that case, preventing users
+from properly testing their cookbooks for upgrades.
+
+## Don't crash if downgrading from Chef 13 to 12
+
+On systems where Chef 13 had been run, Chef 12 would crash as the
+on-disk cookbook format has changed. Chef 12 now correctly ignores the
+unexpected files.
+
+## Provide better system information when Chef crashes
+
+When Chef crashes, the output now includes details about the platform
+and version of Chef that was running, so that a bug report has more
+detail from the off.
+
+# What's New in 12.19:
+
+## Highlighted enhancements for this release:
+
+- Systemd unit files are now verified before being installed.
+- Added support for windows alternate user identity in execute resources.
+- Added ed25519 key support for for ssh connections.
+
+### Windows alternate user identity execute support
+
+The `execute` resource and similar resources such as `script`, `batch`, and `powershell_script` now support the specification of credentials on Windows so that the resulting process is created with the security identity that corresponds to those credentials.
+
+**Note**: When Chef is running as a service, this feature requires that the user that Chef runs as has 'SeAssignPrimaryTokenPrivilege' (aka 'SE_ASSIGNPRIMARYTOKEN_NAME') user right. By default only LocalSystem and NetworkService have this right when running as a service. This is necessary even if the user is an Administrator.
+
+This right can be added and checked in a recipe using this example:
+
+```ruby
+# Add 'SeAssignPrimaryTokenPrivilege' for the user
+Chef::ReservedNames::Win32::Security.add_account_right('<user>', 'SeAssignPrimaryTokenPrivilege')
+
+# Check if the user has 'SeAssignPrimaryTokenPrivilege' rights
+Chef::ReservedNames::Win32::Security.get_account_right('<user>').include?('SeAssignPrimaryTokenPrivilege')
+```
+
+#### Properties
+
+The following properties are new or updated for the `execute`, `script`, `batch`, and `powershell_script` resources and any resources derived from them:
+
+- `user`<br>
+ **Ruby types:** String<br>
+ The user name of the user identity with which to launch the new process. Default value: `nil`. The user name may optionally be specified 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 `domain` attribute. On Windows only, if this property is specified, the `password` property **must*- be specified.
+
+- `password`<br>
+ **Ruby types*- String<br>
+ _Windows only:_ The password of the user specified by the `user` property. Default value: `nil`. This property is mandatory if `user` is specified on Windows and may only be specified if `user` is specified. The `sensitive` property for this resource will automatically be set to `true` if `password` is specified.
+
+- `domain`<br>
+ **Ruby types*- String<br>
+ _Windows only:_ The domain of the user user specified by the `user` property. Default value: `nil`. If not specified, the user name and password specified by the `user` and `password` properties will be used to resolve that user against the domain in which the system running Chef client is joined, or if that system is not joined to a domain it will resolve 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 `user` property.
+
+#### Usage
+
+The following examples explain how alternate user identity properties can be used in the execute resources:
+
+```ruby
+powershell_script 'create powershell-test file' do
+ code <<-EOH
+ $stream = [System.IO.StreamWriter] "#{Chef::Config[:file_cache_path]}/powershell-test.txt"
+ $stream.WriteLine("In #{Chef::Config[:file_cache_path]}...word.")
+ $stream.close()
+ EOH
+ user 'username'
+ password 'password'
+end
+
+execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+ domain "domain-name"
+ user "user"
+ password "password"
+end
+
+script 'create test_dir' do
+ interpreter "bash"
+ code "mkdir test_dir"
+ cwd Chef::Config[:file_cache_path]
+ user "domain-name\\username"
+ password "password"
+end
+
+batch 'create test_dir' do
+ code "mkdir test_dir"
+ cwd Chef::Config[:file_cache_path]
+ user "username@domain-name"
+ password "password"
+end
+```
+
+## Highlighted bug fixes for this release:
+
+- Ensure that the Windows Administrator group can access the chef-solo nodes directory
+- When loading a cookbook in Chef Solo, use `metadata.json` in preference to `metadata.rb`
+
+## Deprecation Notice
+
+- As of version 12.19, chef client will no longer be build or tested on the Cisco NX-OS and IOS XR platforms.
+
+# Ohai Release Notes 8.23:
+
+## Cumulus Linux Platform
+
+Cumulus Linux will now be detected as platform `cumulus` instead of `debian` and the `platform_version` will be properly set to the Cumulus Linux release.
+
+## Virtualization Detection
+
+Windows / Linux / BSD guests running on the Veertu hypervisors will now be detected
+
+Windows guests running on Xen and Hyper-V hypervisors will now be detected
+
+## New Sysconf Plugin
+
+A new plugin parses the output of the sysconf command to provide information on the underlying system.
+
+## AWS Account ID
+
+The EC2 plugin now fetches the AWS Account ID in addition to previous instance metadata
+
+## GCC Detection
+
+GCC detection has been improved to collect additional information, and to not prompt for the installation of Xcode on macOS systems
+
+## New deprecations introduced in this release:
+
+### Ohai::Config removed
+
+- **Deprecation ID**: OHAI-1
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_legacy_config>
+- **Expected Removal**: Ohai 13 (April 2017)
+
+### sigar gem based plugins removed
+
+- **Deprecation ID**: OHAI-2
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_sigar_plugins>
+- **Expected Removal**: Ohai 13 (April 2017)
+
+### run_command and popen4 helper methods removed
+
+- **Deprecation ID**: OHAI-3
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_run_command_helpers>
+- **Expected Removal**: Ohai 13 (April 2017)
+
+### libvirt plugin attributes moved
+
+- **Deprecation ID**: OHAI-4
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_libvirt_plugin>
+- **Expected Removal**: Ohai 13 (April 2017)
+
+### Windows CPU plugin attribute changes
+
+- **Deprecation ID**: OHAI-5
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_windows_cpu>
+- **Expected Removal**: Ohai 13 (April 2017)
+
+### DigitalOcean plugin attribute changes
+
+- **Deprecation ID**: OHAI-6
+- **Remediation Docs**: <https://docs.chef.io/deprecations_ohai_digitalocean/>
+- **Expected Removal**: Ohai 13 (April 2017)
diff --git a/ROADMAP.md b/ROADMAP.md
deleted file mode 100644
index eeec513a3f..0000000000
--- a/ROADMAP.md
+++ /dev/null
@@ -1,11 +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.
-
-## 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 74a30d45b0..2500c93ea0 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,25 +17,62 @@
# limitations under the License.
#
-VERSION = IO.read(File.expand_path("../VERSION", __FILE__)).strip
-
-require "rubygems"
-require "chef/version"
-require "chef-config/package_task"
-require "rdoc/task"
-require_relative "tasks/rspec"
-require_relative "tasks/maintainers"
-require_relative "tasks/cbgb"
-require_relative "tasks/dependencies"
-require_relative "tasks/changelog"
-
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef", "chef") do |package|
- package.component_paths = ["chef-config"]
- package.generate_version_class = true
+# we need this to resolve files required by lib/chef/dist
+$LOAD_PATH.unshift(File.expand_path("chef-config/lib", __dir__))
+
+begin
+ require_relative "tasks/rspec"
+ require_relative "tasks/dependencies"
+ require_relative "tasks/docs"
+ require_relative "tasks/spellcheck"
+ require_relative "chef-utils/lib/chef-utils/dist" unless defined?(ChefUtils::Dist)
+rescue LoadError => e
+ puts "Skipping missing rake dep: #{e}"
+end
+
+require "bundler/gem_helper"
+
+ENV["CHEF_LICENSE"] = "accept-no-persist"
+
+namespace :pre_install do
+ desc "Runs 'rake install' for the gems that live in subdirectories in this repo"
+ task :install_gems_from_dirs do
+ %w{chef-utils chef-config}.each do |gem|
+ path = ::File.join(::File.dirname(__FILE__), gem)
+ Dir.chdir(path) do
+ sh("rake install")
+ end
+ end
+ end
+
+ desc "Renders the powershell extensions with distro flavoring"
+ task :render_powershell_extension do
+ require "erb"
+ template_file = ::File.join(::File.dirname(__FILE__), "distro", "templates", "powershell", "chef", "chef.psm1.erb")
+ psm1_path = ::File.join(::File.dirname(__FILE__), "distro", "powershell", "chef")
+ FileUtils.mkdir_p psm1_path
+ template = ERB.new(IO.read(template_file))
+ chef_psm1 = template.result
+ File.open(::File.join(psm1_path, "chef.psm1"), "w") { |f| f.write(chef_psm1) }
+ end
+
+ task all: ["pre_install:install_gems_from_dirs", "pre_install:render_powershell_extension"]
+end
+
+# hack in all the preinstall tasks to occur before the traditional install task
+task install: "pre_install:all"
+
+# make sure we build the correct gemspec on windows
+gemspec = Gem.win_platform? ? "chef-universal-mingw32" : "chef"
+Bundler::GemHelper.install_tasks name: gemspec
+
+# this gets appended to the normal bundler install helper
+task :install do
+ chef_bin_path = ::File.join(::File.dirname(__FILE__), "chef-bin")
+ Dir.chdir(chef_bin_path) do
+ sh("rake install:force")
+ end
end
-# Add a conservative dependency update to version:bump (which was created by PackageTask)
-task "version:bump" => %w{version:bump_patch version:update}
-task "version:bump" => %w{version:bump_patch version:update}
task :pedant, :chef_zero_spec
@@ -51,6 +88,25 @@ task :register_eventlog do
end
end
+desc "Copies powershell_exec related binaries from the latest built Habitat Packages"
+task :update_chef_exec_dll do
+ raise "This task must be run on Windows since we are installing a Windows targeted package!" unless Gem.win_platform?
+
+ require "mkmf"
+ raise "Unable to locate Habitat cli. Please install Habitat cli before invoking this task!" unless find_executable "hab"
+
+ sh("hab pkg install chef/chef-powershell-shim")
+ sh("hab pkg install chef/chef-powershell-shim-x86")
+ x64 = `hab pkg path chef/chef-powershell-shim`.chomp.tr('\\', "/")
+ x86 = `hab pkg path chef/chef-powershell-shim-x86`.chomp.tr('\\', "/")
+ FileUtils.rm_rf(Dir["distro/ruby_bin_folder/AMD64/*"])
+ FileUtils.rm_rf(Dir["distro/ruby_bin_folder/x86/*"])
+ puts "Copying #{x64}/bin/* to distro/ruby_bin_folder/AMD64"
+ FileUtils.cp_r(Dir["#{x64}/bin/*"], "distro/ruby_bin_folder/AMD64")
+ puts "Copying #{x86}/bin/* to distro/ruby_bin_folder/x86"
+ FileUtils.cp_r(Dir["#{x86}/bin/*"], "distro/ruby_bin_folder/x86")
+end
+
begin
require "chefstyle"
require "rubocop/rake_task"
@@ -58,21 +114,5 @@ begin
task.options += ["--display-cop-names", "--no-color"]
end
rescue LoadError
- puts "chefstyle/rubocop is not available. gem install chefstyle to do style checking."
-end
-
-begin
- require "yard"
- DOC_FILES = [ "README.rdoc", "LICENSE", "spec/tiny_server.rb", "lib/**/*.rb" ]
- namespace :yard do
- desc "Create YARD documentation"
-
- YARD::Rake::YardocTask.new(:html) do |t|
- t.files = DOC_FILES
- t.options = ["--format", "html"]
- end
- end
-
-rescue LoadError
- puts "yard is not available. (sudo) gem install yard to generate yard documentation."
+ puts "chefstyle/rubocop is not available. bundle install first to make sure all dependencies are installed."
end
diff --git a/VERSION b/VERSION
index 6a0c5ba561..914bcb5625 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.14.75 \ No newline at end of file
+17.0.32 \ No newline at end of file
diff --git a/acceptance/.gitignore b/acceptance/.gitignore
deleted file mode 100644
index 4b0b151d75..0000000000
--- a/acceptance/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.acceptance_logs
-.acceptance_data
-data-collector/Berksfile.lock
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.digitalocean.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.digitalocean.yml
deleted file mode 100644
index d49d4b9cf0..0000000000
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.digitalocean.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-# Not quite ready yet
-
-driver:
- name: digitalocean
- digitalocean_access_token: <%= ENV['DIGITALOCEAN_API_TOKEN'] %>
- region: <%= ENV['DIGITALOCEAN_REGION'] %>
- size: 2gb
- ssh_key_ids: <%= ENV['DIGITALOCEAN_SSH_KEYS'] %>
- transport:
- ssh_key: <%= ENV['DIGITALOCEAN_SSH_KEY_PATH'] %>
-
-provisioner:
- name: chef_zero
- product_name: chef
- product_version: latest
- channel: current
-
-# busser installation relies on this
-<% if ENV["KITCHEN_CHEF_PRODUCT"] %>
-verifier:
- chef_omnibus_root: "/opt/<%= ENV["KITCHEN_CHEF_PRODUCT"] %>"
-<% end %>
-
-platforms:
-<% %w(centos-6.5 centos-7.0
- fedora-21
- debian-8.1
- ubuntu-12.04 ubuntu-14.04 ubuntu-15.10
-).each do |platform| %>
- - name: #{platform}
- driver_config:
- image: <%= "#{platform.gsub('.', '-')}-x64" %>
-<% end %>
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
deleted file mode 100644
index 209b7fa979..0000000000
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
+++ /dev/null
@@ -1,280 +0,0 @@
-# Not quite ready yet
-
-<%
-def file_if_exists(path)
- path = File.expand_path(path)
- File.exist?(path) ? path : nil
-end
-%>
-
-driver:
- name: ec2
- tags:
- X-Project: chef-ci-acceptance
- aws_ssh_key_id: <%= ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME'] %>
- # test-specific stuff
- region: us-west-2
- subnet_id: subnet-19ac017c
- security_group_ids: ["sg-e401eb83", "sg-96274af3"]
- instance_type: m3.large
-# associate_public_ip: true # Don't enable public IP, as subnet specified is behind VPN
-
-# busser installation relies on this
-<% if ENV["KITCHEN_CHEF_PRODUCT"] %>
-verifier:
- chef_omnibus_root: "/opt/<%= ENV["KITCHEN_CHEF_PRODUCT"] %>"
-<% end %>
-
-transport:
- ssh_key: <%= file_if_exists("~/.ssh/#{ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME']}.pem") ||
- file_if_exists("~/.ssh/#{ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME']}") ||
- file_if_exists("~/.ssh/id_rsa") %>
-
-provisioner:
- name: chef_zero
- product_name: <%= ENV["KITCHEN_CHEF_PRODUCT"] %>
- product_version: <%= ENV["KITCHEN_CHEF_VERSION"] %>
- channel: <%= ENV["KITCHEN_CHEF_CHANNEL"] %>
- client_rb:
- audit_mode: :enabled
- attributes:
- chef_acceptance: "true"
- use_system_chef: "true"
-
-platforms:
- #
- # AIX
- #
- # - name: aix-6.1
- # - name: aix-7.1
- #
- # Debian
- #
- - name: debian-8
- driver:
- image_search:
- name: debian-jessie-*
- owner-id: "379101102735"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- 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
- #
- - name: ubuntu-15.10
- driver:
- image_search:
- name: ubuntu/images/*/ubuntu-*-15.10-amd64-server-*
- owner-id: "099720109477"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ubuntu
- - name: ubuntu-14.04
- driver:
- image_search:
- name: ubuntu/images/*/ubuntu-*-14.04-*-server-*
- owner-id: "099720109477"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ubuntu
- - name: ubuntu-12.04
- driver:
- image_search:
- name: ubuntu/images/*/ubuntu-*-12.04-*-server-*
- owner-id: "099720109477"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ubuntu
- #
- # Red Hat Enterprise Linux
- #
- - name: el-7
- driver:
- image_search:
- name: RHEL-7.*
- owner-id: "309956199498"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ec2-user
- - name: el-6
- driver:
- image_search:
- name: RHEL-6.*
- owner-id: "309956199498"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ec2-user
- - name: el-5
- driver:
- image_search:
- name: RHEL-5.*
- owner-id: "309956199498"
- architecture: x86_64
- virtualization-type: paravirtual
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ec2-user
- #
- # FreeBSD
- #
- - name: freebsd-10
- driver:
- image_search:
- name: FreeBSD/EC2 10.*-RELEASE*
- owner-id: "118940168514"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ec2-user
- - name: freebsd-9
- driver:
- image_search:
- name: FreeBSD/EC2 9.*-RELEASE*
- owner-id: "118940168514"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: ec2-user
- - name: freebsd-8
- driver:
- image_search:
- name: FreeBSD/EC2 8.*-RELEASE*
- owner-id: "118940168514"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: standard
- image-type: machine
- transport:
- username: ec2-user
- #
- # OS/X
- #
- # - name: mac_os_x-10.11
- # - name: mac_os_x-10.10
- # - name: mac_os_x-10.9
- # - name: mac_os_x-10.8
- #
- # Nexus???
- #
- # - name: nexus-7
- #
- # Solaris
- #
- # - name: solaris-11
- # - name: solaris-10
- #
- # Windows
- #
- - name: windows-2012r2
- provisioner:
- architecture: <%= ENV["KITCHEN_CHEF_WIN_ARCHITECTURE"] %>
- driver:
- image_search:
- name: Windows_Server-2012-R2*-English-*-Base-*
- owner-alias: amazon
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- user_data: |
- <powershell>
- & 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>
- transport:
- username: administrator
-
- - name: windows-2012
- driver:
- image_search:
- name: Windows_Server-2012-RTM*-English-*-Base-*
- owner-alias: amazon
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: administrator
- - name: windows-2008r2
- driver:
- image_search:
- name: Windows_Server-2008-R2*-English-*-Base-*
- owner-alias: amazon
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
- transport:
- username: administrator
- #
- # Centos
- #
- - name: centos-7
- driver:
- image_search:
- name: CentOS Linux 7 *
- owner-alias: aws-marketplace
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: standard
- image-type: machine
- transport:
- username: root
- - name: centos-6
- driver:
- image_search:
- name: CentOS-6.5-GA-*
- owner-alias: aws-marketplace
- architecture: x86_64
- virtualization-type: paravirtual
- block-device-mapping.volume-type: standard
- image-type: machine
- transport:
- username: root
- #
- # Fedora
- #
- - name: fedora-21
- driver:
- image_search:
- name: Fedora-Cloud-Base-21-*
- owner-id: "125523088429"
- architecture: x86_64
- virtualization-type: hvm
- block-device-mapping.volume-type: gp2
- image-type: machine
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml
deleted file mode 100644
index 954f5e9eb1..0000000000
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.vagrant.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-driver:
- name: vagrant
- forward_agent: yes
- customize:
- cpus: 2
- memory: 1024
-
-provisioner:
- name: chef_zero
- product_name: <%= ENV["KITCHEN_CHEF_PRODUCT"] %>
- product_version: <%= ENV["KITCHEN_CHEF_VERSION"] %>
- channel: <%= ENV["KITCHEN_CHEF_CHANNEL"] %>
- client_rb:
- audit_mode: :enabled
- attributes:
- chef_acceptance: "true"
- use_system_chef: "true"
-
-# busser installation relies on this
-<% if ENV["KITCHEN_CHEF_PRODUCT"] %>
-verifier:
- chef_omnibus_root: "/opt/<%= ENV["KITCHEN_CHEF_PRODUCT"] %>"
-<% end %>
-
-platforms:
-<% %w(
-debian-8
-debian-7
-debian-6
-ubuntu-15.10
-ubuntu-14.04
-el-7
-el-6
-el-5
-freebsd-10
-freebsd-9
-fedora-21
-).each do |platform| %>
- - name: <%= platform %>
- driver:
- box: opscode-<%= platform %>
- box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_<%= platform %>_chef-provisionerless.box
-<% end %>
-# freebsd-8
-# ubuntu-12.04
-# centos-7
- - name: centos-6
- driver:
- box: bento/centos-6.7
-<% %w(
-2012r2
-2012
-2008r2
-).each do |version| %>
- - name: windows-<%= version %>
- driver:
- box: chef/windows-server-<%= version %>-standard
-# URL is atlas
-<% end %>
diff --git a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
deleted file mode 100644
index d5d2e1380b..0000000000
--- a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'chef/mixin/shell_out'
-
-module KitchenAcceptance
- class Kitchen < Chef::Resource
- resource_name :kitchen
-
- property :command, String, name_property: true
- property :driver, %w(ec2 vagrant), coerce: proc { |v| v.to_s }, default: lazy { ENV["KITCHEN_DRIVER"] || :ec2 }
- property :instances, String, default: lazy { ENV["KITCHEN_INSTANCES"] ? ENV["KITCHEN_INSTANCES"] : "" }
- property :kitchen_dir, String, default: Chef.node['chef-acceptance']['suite-dir']
- property :chef_product, String, default: lazy {
- ENV["KITCHEN_CHEF_PRODUCT"] || begin
- # Figure out if we're in chefdk or chef
- if ::File.exist?(::File.expand_path("../../chef-dk.gemspec", node['chef-acceptance']['suite-dir']))
- "chefdk"
- else
- "chef"
- end
- end
- }
- property :chef_channel, String, default: lazy {
- ENV["KITCHEN_CHEF_CHANNEL"] ||
- # Pick up current if we can't connect to artifactory
- (ENV["ARTIFACTORY_USERNAME"] ? "unstable" : "current")
- }
- property :chef_version, String, default: lazy {
- ENV["KITCHEN_CHEF_VERSION"] ||
- # If we're running the chef or chefdk projects in jenkins, pick up the project name.
- (ENV["PROJECT_NAME"] == chef_product ? ENV["OMNIBUS_BUILD_VERSION"] : nil) ||
- "latest"
- }
- 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" }
-
- action :run do
-
- 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
-end
diff --git a/acceptance/.shared/kitchen_acceptance/metadata.rb b/acceptance/.shared/kitchen_acceptance/metadata.rb
deleted file mode 100644
index 70dc342d09..0000000000
--- a/acceptance/.shared/kitchen_acceptance/metadata.rb
+++ /dev/null
@@ -1 +0,0 @@
-name "kitchen_acceptance"
diff --git a/acceptance/Gemfile b/acceptance/Gemfile
deleted file mode 100644
index 83f9f7dcfc..0000000000
--- a/acceptance/Gemfile
+++ /dev/null
@@ -1,13 +0,0 @@
-source "https://rubygems.org"
-
-gem "chef-acceptance", github: "chef/chef-acceptance"
-gem "kitchen-ec2"
-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 "kitchen-inspec"
-gem "test-kitchen"
-gem "winrm-elevated"
-gem "berkshelf", "4.3.5"
diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock
deleted file mode 100644
index 265f8eebf8..0000000000
--- a/acceptance/Gemfile.lock
+++ /dev/null
@@ -1,247 +0,0 @@
-GIT
- remote: git://github.com/chef/chef-acceptance.git
- revision: e92ddae46d2126864698b9c8e4fc4ec2dcc46c55
- specs:
- chef-acceptance (0.2.0)
- mixlib-shellout (~> 2.0)
- thor (~> 0.19)
-
-GEM
- remote: https://rubygems.org/
- specs:
- addressable (2.4.0)
- artifactory (2.5.0)
- aws-sdk (2.6.1)
- aws-sdk-resources (= 2.6.1)
- aws-sdk-core (2.6.1)
- jmespath (~> 1.0)
- aws-sdk-resources (2.6.1)
- aws-sdk-core (= 2.6.1)
- 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-io (~> 0.16.1)
- cleanroom (~> 1.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.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)
- celluloid (0.16.0)
- timers (~> 4.0.0)
- celluloid-io (0.16.2)
- celluloid (>= 0.16.0)
- nio4r (>= 1.1.0)
- chef-config (12.14.60)
- 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.31.0)
- excon (>= 0.38.0)
- json
- erubis (2.7.0)
- excon (0.52.0)
- faraday (0.9.2)
- multipart-post (>= 1.2, < 3)
- ffi (1.9.14)
- fuzzyurl (0.9.0)
- gssapi (1.2.0)
- ffi (>= 1.0.1)
- gyoku (1.3.1)
- builder (>= 2.1.2)
- hashie (3.4.6)
- hitimes (1.2.4)
- httpclient (2.7.2)
- inspec (0.34.1)
- hashie (~> 3.4)
- json (>= 1.8, < 3.0)
- method_source (~> 0.8)
- mixlib-log
- parallel (~> 1.9)
- pry (~> 0)
- rainbow (~> 2)
- rspec (~> 3)
- rspec-its (~> 1.2)
- rubyzip (~> 1.1)
- sslshake (~> 1)
- thor (~> 0.19)
- train (>= 0.19.0, < 1.0)
- jmespath (1.3.1)
- json (2.0.2)
- kitchen-ec2 (1.2.0)
- aws-sdk (~> 2)
- excon
- multi_json
- retryable (~> 2.0)
- test-kitchen (~> 1.4, >= 1.4.1)
- kitchen-inspec (0.15.1)
- inspec (>= 0.22.0, < 1.0.0)
- test-kitchen (~> 1.6)
- kitchen-vagrant (0.20.0)
- test-kitchen (~> 1.4)
- little-plugger (1.1.4)
- logging (2.1.0)
- little-plugger (~> 1.1)
- multi_json (~> 1.10)
- method_source (0.8.2)
- minitar (0.5.4)
- mixlib-archive (0.2.0)
- mixlib-log
- mixlib-authentication (1.4.1)
- mixlib-log
- mixlib-config (2.2.4)
- mixlib-install (1.2.0)
- artifactory
- mixlib-shellout
- mixlib-versioning
- mixlib-log (1.7.1)
- mixlib-shellout (2.2.7)
- mixlib-versioning (1.1.0)
- molinillo (0.4.5)
- multi_json (1.12.1)
- multipart-post (2.0.0)
- net-scp (1.2.1)
- net-ssh (>= 2.6.5)
- net-ssh (3.2.0)
- net-ssh-gateway (1.2.0)
- net-ssh (>= 2.6.5)
- nio4r (1.2.1)
- nori (2.6.0)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- parallel (1.9.0)
- pry (0.10.4)
- coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- rainbow (2.1.0)
- retryable (2.0.4)
- ridley (4.6.1)
- addressable
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-ignore (~> 1.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 (3.5.0)
- rspec-core (~> 3.5.0)
- rspec-expectations (~> 3.5.0)
- rspec-mocks (~> 3.5.0)
- rspec-core (3.5.3)
- rspec-support (~> 3.5.0)
- rspec-expectations (3.5.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-its (1.2.0)
- rspec-core (>= 3.0.0)
- rspec-expectations (>= 3.0.0)
- rspec-mocks (3.5.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-support (3.5.0)
- rubyntlm (0.6.1)
- rubyzip (1.2.0)
- safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- semverse (1.2.1)
- slop (3.6.0)
- solve (2.0.3)
- molinillo (~> 0.4.2)
- semverse (~> 1.1)
- sslshake (1.0.12)
- test-kitchen (1.13.0)
- mixlib-install (~> 1.2)
- mixlib-shellout (>= 1.2, < 3.0)
- net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-gateway (~> 1.2.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
- thor (0.19.1)
- timers (4.0.4)
- hitimes
- train (0.19.1)
- docker-api (~> 1.26)
- json (>= 1.8, < 3.0)
- mixlib-shellout (~> 2.0)
- net-scp (~> 1.2)
- net-ssh (>= 2.9, < 4.0)
- winrm (~> 2.0)
- winrm-fs (~> 1.0)
- varia_model (0.4.1)
- buff-extensions (~> 1.0)
- hashie (>= 2.0.2, < 4.0.0)
- windows_chef_zero (2.0.0)
- test-kitchen (>= 1.2.1)
- winrm (2.0.2)
- 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, >= 0.6.1)
- winrm-elevated (1.0.0)
- winrm (~> 2.0)
- winrm-fs (~> 1.0)
- winrm-fs (1.0.0)
- erubis (~> 2.7)
- logging (>= 1.6.1, < 3.0)
- rubyzip (~> 1.1)
- winrm (~> 2.0)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- berkshelf (= 4.3.5)
- chef-acceptance!
- inspec
- kitchen-ec2
- kitchen-inspec
- kitchen-vagrant
- test-kitchen
- windows_chef_zero
- winrm-elevated
-
-BUNDLED WITH
- 1.12.5
diff --git a/acceptance/README.md b/acceptance/README.md
deleted file mode 100644
index fa4bab2806..0000000000
--- a/acceptance/README.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# Acceptance Testing for Chef Client
-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.
-
-### Important Note!
-Before running chef-acceptance, you *MUST* do the following on your current session:
-
-```
-export APPBUNDLER_ALLOW_RVM=true
-```
-
-## Pre-requisites / One time set up
-
-### 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).
-
-Set up the KITCHEN_DRIVER environment variable appropriately (value should be "vagrant"). E.g.:
-```
-export KITCHEN_DRIVER=vagrant
-```
-Add this to your shell profile or startup script as needed.
-
-### Set up for cloud VM (EC2)
-
-If you intend to run the acceptance tests on a cloud VM, the supported solution is to use EC2.
-
-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`.
-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.
-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`).
-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
- ```
-5. Set the private key to only be readable by root
-
- ```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.
-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
-```
-
-To restrict which OS's will run, use the KITCHEN_INSTANCES environment variable:
-
-```shell
-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).
-
-## 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.
-
-### 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".
-
-```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.
-
-```shell
-export KITCHEN_CHEF_VERSION=version-of-chef-client
-```
-
-### ARTIFACTORY_USERNAME / ARTIFACTORY_PASSWORD
-
-If the desired channel to get the Chef client from is "unstable", the following settings need to be exported:
-
-```shell
-export ARTIFACTORY_USERNAME=username
-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).
-
-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/.acceptance/acceptance-cookbook/.gitignore b/acceptance/basics/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/basics/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/basics/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/basics/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 2b7547b70d..0000000000
--- a/acceptance/basics/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-name 'acceptance-cookbook'
-
-depends "kitchen_acceptance"
diff --git a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/basics/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index e2d663ac2f..0000000000
--- a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "destroy"
diff --git a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/basics/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index 5726c0e7b5..0000000000
--- a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "converge"
diff --git a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/basics/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 05ac94ce66..0000000000
--- a/acceptance/basics/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "verify"
diff --git a/acceptance/basics/.kitchen.yml b/acceptance/basics/.kitchen.yml
deleted file mode 100644
index 4b7a516396..0000000000
--- a/acceptance/basics/.kitchen.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-suites:
- - name: chef-current-install
- includes: [ubuntu-14.04, windows-server-2012r2]
- run_list:
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
deleted file mode 100644
index dd0e0e4e34..0000000000
--- a/acceptance/basics/test/integration/chef-current-install/serverspec/chef_client_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-
-require "spec_helper"
-
-gem_path = "/opt/chef/embedded/bin/gem"
-white_list = %w{chef-config json rake}
-
-describe "gem list" do
- it "should not have non-whitelisted duplicate gems" do
- gems = command("#{gem_path} list").stdout
-
- duplicate_gems = gems.lines().select { |l| l.include?(",") }.collect { |l| l.split(" ").first }
- puts "Duplicate gems found: #{duplicate_gems}" if duplicate_gems.length > 0
-
- non_whitelisted_duplicates = duplicate_gems.select { |l| !white_list.include?(l) }
- puts "Non white listed duplicates: #{non_whitelisted_duplicates}" if non_whitelisted_duplicates.length > 0
-
- (non_whitelisted_duplicates.length).should be == 0
- end
-end
diff --git a/acceptance/basics/test/integration/chef-current-install/serverspec/spec_helper.rb b/acceptance/basics/test/integration/chef-current-install/serverspec/spec_helper.rb
deleted file mode 100644
index 7189ddb13b..0000000000
--- a/acceptance/basics/test/integration/chef-current-install/serverspec/spec_helper.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require "serverspec"
-require "pathname"
-
-set :backend, :exec
-
-set :path, "/bin:/usr/local/bin:$PATH"
diff --git a/acceptance/basics/test/integration/helpers/serverspec/Gemfile b/acceptance/basics/test/integration/helpers/serverspec/Gemfile
deleted file mode 100644
index b56d1e1298..0000000000
--- a/acceptance/basics/test/integration/helpers/serverspec/Gemfile
+++ /dev/null
@@ -1,8 +0,0 @@
-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/data-collector/.acceptance/acceptance-cookbook/.gitignore b/acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/data-collector/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 68fc3af2dd..0000000000
--- a/acceptance/data-collector/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index 7f4be2c7f7..0000000000
--- a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index c707e874f0..0000000000
--- a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index e4a547272b..0000000000
--- a/acceptance/data-collector/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-log "Running 'verify' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
-kitchen "verify"
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/.gitignore b/acceptance/data-collector/.acceptance/data-collector-test/.gitignore
deleted file mode 100644
index ec2a890bd3..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-.vagrant
-Berksfile.lock
-*~
-*#
-.#*
-\#*#
-.*.sw[a-z]
-*.un~
-
-# Bundler
-Gemfile.lock
-bin/*
-.bundle/*
-
-.kitchen/
-.kitchen.local.yml
diff --git a/acceptance/data-collector/.acceptance/data-collector-test/Berksfile b/acceptance/data-collector/.acceptance/data-collector-test/Berksfile
deleted file mode 100644
index 34fea2166b..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/Berksfile
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index 3fb2c730b0..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/api.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-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
- def self.reset
- @@counters = Hash.new { |h, k| h[k] = 0 }
- end
-
- def self.increment(payload)
- counter_name = ApiHelpers.payload_type(payload)
- @@counters[counter_name] += 1
- end
-
- def self.to_json
- @@counters.to_json
- end
-end
-
-class MessageCache
- include ApiHelpers
-
- def self.reset
- @@message_cache = {}
- end
-
- def self.store(payload)
- cache_key = ApiHelpers.payload_type(payload)
-
- @@message_cache[cache_key] = payload
- end
-
- def self.fetch(cache_key)
- @@message_cache[cache_key].to_json
- 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/data-collector/.acceptance/data-collector-test/files/default/apigemfile b/acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile
deleted file mode 100644
index 94fc334d88..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/apigemfile
+++ /dev/null
@@ -1,3 +0,0 @@
-source "https://rubygems.org"
-
-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
deleted file mode 100644
index 89f3555be1..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-both-mode.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 8e3f0c4845..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-client-mode.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index f8374107ea..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-no-endpoint.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index 904e952e85..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/client-rb-solo-mode.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 81cf29d9fb..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/files/default/config.ru
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index dbd376aa83..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/metadata.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-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
deleted file mode 100644
index 20b945db9b..0000000000
--- a/acceptance/data-collector/.acceptance/data-collector-test/recipes/default.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-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
-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}"
- not_if { running_pid.nil? }
-end
-
-execute "start API" do
- command "bin/rackup -D -P #{pid_file}"
- 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
deleted file mode 100644
index f719e3ea69..0000000000
--- a/acceptance/data-collector/.kitchen.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644
index b8f003071b..0000000000
--- a/acceptance/data-collector/Berksfile
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index f9d365ac58..0000000000
--- a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
+++ /dev/null
@@ -1,212 +0,0 @@
-#
-# 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}" 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
-
- it "does not have any extra fields" do
- payload = JSON.load(command("curl http://localhost:9292/cache/run_start").stdout)
- extra_fields = payload.keys.select { |key| !required_fields.include?(key) && !optional_fields.include?(key) }
- expect(extra_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
- resources
- run_id
- run_list
- source
- start_time
- status
- total_resource_count
- updated_resource_count
- }
- 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
-
- it "does not have any extra fields" do
- payload = JSON.load(command("curl http://localhost:9292/cache/run_converge.success").stdout)
- extra_fields = payload.keys.select { |key| !required_fields.include?(key) && !optional_fields.include?(key) }
- expect(extra_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
- resources
- run_id
- run_list
- source
- start_time
- status
- total_resource_count
- updated_resource_count
- }
- 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
deleted file mode 100644
index b56d1e1298..0000000000
--- a/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile
+++ /dev/null
@@ -1,8 +0,0 @@
-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/fips/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/fips/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
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/Gemfile b/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
deleted file mode 100644
index d297c4314d..0000000000
--- a/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-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"
-gem "mixlib-shellout"
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 2a087f8029..0000000000
--- a/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb
+++ /dev/null
@@ -1,52 +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
- skip
- #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 03c7a9e657..0000000000
--- a/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
+++ /dev/null
@@ -1,7 +0,0 @@
-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 "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/omnitruck/.acceptance/acceptance-cookbook/.gitignore b/acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 4c7c42d9bd..0000000000
--- a/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1 +0,0 @@
-name 'acceptance-cookbook'
diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index f890b597fe..0000000000
--- a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-log "NOOP 'destroy' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index 64ef7581ac..0000000000
--- a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-log "NOOP 'provision' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'"
diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 7db51450e1..0000000000
--- a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-control_group "omnitruck" do
- require 'chef/http'
- require 'chef/json_compat'
-
- # We do this to be able to reference 'rest' both inside and outside example
- # blocks
- rest = Chef::HTTP.new("https://omnitruck.chef.io/chef/metadata", headers: {"Accept" => "application/json"})
- let(:rest) { rest }
-
- def request(url)
- Chef::JSONCompat.parse(rest.get(url))["sha256"]
- end
-
- shared_examples "32 matches 64" do |version|
- it "only returns 32-bit packages" do
- sha32 = request("?p=windows&pv=2012r2&v=#{version}&m=i386")
- sha64 = request("?p=windows&pv=2012r2&v=#{version}&m=x86_64")
- expect(sha32).to eq(sha64)
- end
- end
-
- context "from the current channel" do
- it "returns both 32-bit and 64-bit packages" do
- # We cannot verify from the returned URL if the package is 64 or 32 bit because
- # it is often lying, so we just make sure they are different.
- # The current channel is often cleaned so only the latest builds are in
- # it, so we just request the latest version instead of trying to check
- # old versions
- sha32 = request("?p=windows&pv=2012r2&m=i386&prerelease=true")
- sha64 = request("?p=windows&pv=2012r2&m=x86_64&prerelease=true")
- expect(sha32).to_not eq(sha64)
- end
- end
-
- context "from the stable channel" do
- %w{11 12.3 12.4.2 12.6.0 12.8.1}.each do |version|
- describe "with version #{version}" do
- include_examples "32 matches 64", version
- end
- end
-
- begin
- rest.get("?p=windows&pv=2012r2&v=12.9")
- describe "with version 12.9" do
- it "returns both 32-bit and 64-bit packages" do
- sha32 = request("?p=windows&pv=2012r2&v=12.9&m=i386")
- sha64 = request("?p=windows&pv=2012r2&v=12.9&m=x86_64")
- expect(sha32).to_not eq(sha64)
- end
- end
- rescue Net::HTTPServerException => e
- # Once 12.9 is released this will stop 404ing and the example
- # will be executed
- unless e.response.code == "404"
- raise
- end
- end
-
- end
-
-end
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/.gitignore b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/cookbook_kitchen.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/cookbook_kitchen.rb
deleted file mode 100644
index 5d851a6ac6..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/cookbook_kitchen.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-class CookbookKitchen < KitchenAcceptance::Kitchen
- resource_name :cookbook_kitchen
-
- property :command, default: lazy { name.split(" ")[0] }
- property :kitchen_dir, default: lazy { ::File.join(repository_root, cookbook_relative_dir) }
- property :test_cookbook, String, default: lazy { name.split(" ")[1] }
- property :repository, String, default: lazy { "chef-cookbooks/#{test_cookbook}" },
- coerce: proc { |v|
- # chef-cookbooks/runit -> https://github.com/chef-cookbooks/runit.git
- if !v.include?(':')
- "https://github.com/#{v}.git"
- else
- v
- end
- }
- property :repository_root, String, default: lazy { ::File.join(Chef.node["chef-acceptance"]["suite-dir"], "test_run", test_cookbook) }
- property :branch, String, default: "master"
- property :cookbook_relative_dir, String, default: ""
- property :env, default: lazy {
- {
- "BUNDLE_GEMFILE" => ::File.expand_path("../Gemfile", Chef.node["chef-acceptance"]["suite-dir"]),
-# "KITCHEN_GLOBAL_YAML" => ::File.join(kitchen_dir, ".kitchen.yml"),
- "KITCHEN_YAML" => ::File.join(node["chef-acceptance"]["suite-dir"], ".kitchen.#{test_cookbook}.yml")
- }
- }
-
- action :run do
- # Ensure the parent directory exists
- directory ::File.expand_path("..", repository_root) do
- recursive true
- end
-
- # Grab the cookbook
- # TODO Grab the source URL from supermarket
- # TODO get git to include its kitchen tests in the cookbook.
- git repository_root do
- repository new_resource.repository
- branch new_resource.branch
- end
-
- super()
- 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
deleted file mode 100644
index 73f5151bca..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-class TopCookbooks < Chef::Resource
- resource_name :top_cookbooks
-
- 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
-
- 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
-
- cookbook_kitchen "#{command} iis" do
- end
-
- cookbook_kitchen "#{command} sql_server" do
- end
-
- cookbook_kitchen "#{command} winbox" do
- repository "adamedx/winbox"
- end
-
- # cookbook_kitchen "#{command} windows" do
- # end
-
- # cookbook_kitchen "#{command} chocolatey" do
- # repository "chocolatey/chocolatey-cookbook"
- # end
- end
-end
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 26cdab4e99..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-name "acceptance-cookbook"
-
-depends "kitchen_acceptance"
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index 63d10e86e4..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-top_cookbooks "destroy"
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index 7b16f8e66f..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-top_cookbooks "converge"
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 8d00a2e301..0000000000
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1 +0,0 @@
-top_cookbooks "verify"
diff --git a/acceptance/top-cookbooks/.gitignore b/acceptance/top-cookbooks/.gitignore
deleted file mode 100644
index 306f0cce57..0000000000
--- a/acceptance/top-cookbooks/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-test_run/
diff --git a/acceptance/top-cookbooks/.kitchen.chocolatey.yml b/acceptance/top-cookbooks/.kitchen.chocolatey.yml
deleted file mode 100644
index 7d7529d69f..0000000000
--- a/acceptance/top-cookbooks/.kitchen.chocolatey.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-suites:
- - name: default
- run_list:
- - recipe[chocolatey::default]
- - recipe[chocolatey_test::default]
- includes: [windows-2012r2]
diff --git a/acceptance/top-cookbooks/.kitchen.docker.yml b/acceptance/top-cookbooks/.kitchen.docker.yml
deleted file mode 100644
index 41864e3608..0000000000
--- a/acceptance/top-cookbooks/.kitchen.docker.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-suites:
- - name: docker-default
- attributes:
- docker:
- version: 1.10.0
- run_list:
- - recipe[apt]
- - recipe[chef-apt-docker]
- 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/top-cookbooks/.kitchen.iis.yml b/acceptance/top-cookbooks/.kitchen.iis.yml
deleted file mode 100644
index ffbc23caba..0000000000
--- a/acceptance/top-cookbooks/.kitchen.iis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-suites:
- - name: iis
- run_list: ["recipe[iis::default]"]
- includes: [windows-2012r2]
diff --git a/acceptance/top-cookbooks/.kitchen.learn-the-basics-rhel.yml b/acceptance/top-cookbooks/.kitchen.learn-the-basics-rhel.yml
deleted file mode 100644
index 6cbf1b50fa..0000000000
--- a/acceptance/top-cookbooks/.kitchen.learn-the-basics-rhel.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-suites:
- - name: learn-the-basics-rhel-default
- run_list:
- - recipe[learn-the-basics-rhel::setup]
- - recipe[learn-the-basics-rhel::lesson1]
- - recipe[learn-the-basics-rhel::lesson2]
- includes: [el-6]
diff --git a/acceptance/top-cookbooks/.kitchen.learn-the-basics-ubuntu.yml b/acceptance/top-cookbooks/.kitchen.learn-the-basics-ubuntu.yml
deleted file mode 100644
index 8d54ff94cb..0000000000
--- a/acceptance/top-cookbooks/.kitchen.learn-the-basics-ubuntu.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-suites:
- - name: learn-the-basics-ubuntu-default
- run_list:
- - recipe[learn-the-basics-ubuntu::setup]
- - recipe[learn-the-basics-ubuntu::lesson1]
- - recipe[learn-the-basics-ubuntu::lesson2]
- includes: [ubuntu-14.04]
diff --git a/acceptance/top-cookbooks/.kitchen.learn-the-basics-windows.yml b/acceptance/top-cookbooks/.kitchen.learn-the-basics-windows.yml
deleted file mode 100644
index 2704577537..0000000000
--- a/acceptance/top-cookbooks/.kitchen.learn-the-basics-windows.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-suites:
- - name: learn-the-basics-windows-default
- run_list:
- - recipe[learn-the-basics-windows::setup]
- - recipe[learn-the-basics-windows::lesson1]
- - recipe[learn-the-basics-windows::lesson2]
- includes: [windows-2012r2]
diff --git a/acceptance/top-cookbooks/.kitchen.powershell.yml b/acceptance/top-cookbooks/.kitchen.powershell.yml
deleted file mode 100644
index 6fad2364bb..0000000000
--- a/acceptance/top-cookbooks/.kitchen.powershell.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-suites:
- - name: powershell
- run_list: ["recipe[powershell::powershell2]"]
- includes: [windows-2012r2]
diff --git a/acceptance/top-cookbooks/.kitchen.sql_server.yml b/acceptance/top-cookbooks/.kitchen.sql_server.yml
deleted file mode 100644
index 51a6bd6616..0000000000
--- a/acceptance/top-cookbooks/.kitchen.sql_server.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-suites:
- - name: sql_server
- run_list: ["recipe[sql_server::default]"]
- attributes: { sql_server: { accept_eula: true } }
- includes: [windows-2012r2]
diff --git a/acceptance/top-cookbooks/.kitchen.winbox.yml b/acceptance/top-cookbooks/.kitchen.winbox.yml
deleted file mode 100644
index 9cf39a0d5b..0000000000
--- a/acceptance/top-cookbooks/.kitchen.winbox.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-suites:
- - name: default
- run_list:
- - recipe[winbox::default]
- includes: [windows-2012r2]
-
-verifier:
- name: dummy
diff --git a/acceptance/top-cookbooks/.kitchen.windows.yml b/acceptance/top-cookbooks/.kitchen.windows.yml
deleted file mode 100644
index 38c86ff322..0000000000
--- a/acceptance/top-cookbooks/.kitchen.windows.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-suites:
- # Waiting on https://github.com/chef-cookbooks/windows/pull/350
- # - name: tasks
- # run_list:
- # - recipe[windows::default]
- # - recipe[minimal::tasks]
- # includes: [windows-2012r2]
- - name: path
- run_list:
- - recipe[windows::default]
- - recipe[minimal::path]
- includes: [windows-2012r2]
- - name: certificate
- run_list:
- - recipe[windows::default]
- - recipe[minimal::certificate]
- includes: [windows-2012r2]
- # Package is deprecated
- # - name: package
- # run_list:
- # - recipe[windows::default]
- # - recipe[minimal::package]
- # includes: [windows-2012r2]
- - name: feature
- run_list:
- - recipe[windows::default]
- - recipe[minimal::feature]
- includes: [windows-2012r2]
- - name: http_acl
- run_list:
- - recipe[windows::default]
- - recipe[minimal::http_acl]
- includes: [windows-2012r2]
- - name: font
- run_list:
- - recipe[windows::default]
- - recipe[minimal::font]
- includes: [windows-2012r2]
diff --git a/acceptance/trivial/.acceptance/acceptance-cookbook/.gitignore b/acceptance/trivial/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/trivial/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/trivial/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/trivial/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 6c754560f0..0000000000
--- a/acceptance/trivial/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-name "acceptance-cookbook"
-depends "kitchen_acceptance"
diff --git a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index e2d663ac2f..0000000000
--- a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "destroy"
diff --git a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index a6f148f7ad..0000000000
--- a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "setup"
diff --git a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 05ac94ce66..0000000000
--- a/acceptance/trivial/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1 +0,0 @@
-kitchen "verify"
diff --git a/acceptance/trivial/.kitchen.yml b/acceptance/trivial/.kitchen.yml
deleted file mode 100644
index 0db67c468f..0000000000
--- a/acceptance/trivial/.kitchen.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-verifier:
- name: inspec
-
-suites:
- - name: chef-current-install
- includes: [ubuntu-14.04, windows-server-2012r2]
- run_list:
diff --git a/acceptance/trivial/test/integration/chef-current-install/inspec/chef_client_spec.rb b/acceptance/trivial/test/integration/chef-current-install/inspec/chef_client_spec.rb
deleted file mode 100644
index 05c1331744..0000000000
--- a/acceptance/trivial/test/integration/chef-current-install/inspec/chef_client_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-chef_version = ENV["KITCHEN_CHEF_VERSION"].split("+")[0]
-describe command("chef-client -v") do
- its("exit_status") { should eq 0 }
- its(:stdout) { should match /Chef: #{chef_version}/ } if chef_version != "latest"
-end
diff --git a/acceptance/vendor/bundle/bundler/gems/chef-acceptance-47e931cec100 b/acceptance/vendor/bundle/bundler/gems/chef-acceptance-47e931cec100
deleted file mode 160000
-Subproject 47e931cec100dce8efae4369a5b03443eaf2b73
diff --git a/acceptance/vendor/bundle/bundler/gems/chef-acceptance-e92ddae46d21 b/acceptance/vendor/bundle/bundler/gems/chef-acceptance-e92ddae46d21
deleted file mode 160000
-Subproject e92ddae46d2126864698b9c8e4fc4ec2dcc46c5
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/.gitignore b/acceptance/windows-service/.acceptance/acceptance-cookbook/.gitignore
deleted file mode 100644
index 041413b040..0000000000
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-nodes/
-tmp/
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/metadata.rb
deleted file mode 100644
index 6c754560f0..0000000000
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/metadata.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-name "acceptance-cookbook"
-depends "kitchen_acceptance"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
deleted file mode 100644
index e12f938cf3..0000000000
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ /dev/null
@@ -1 +0,0 @@
-#kitchen "destroy"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
deleted file mode 100644
index cec9de4e5d..0000000000
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ /dev/null
@@ -1 +0,0 @@
-#kitchen "converge"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
deleted file mode 100644
index 52e3560cf6..0000000000
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ /dev/null
@@ -1 +0,0 @@
-#kitchen "verify"
diff --git a/acceptance/windows-service/.kitchen.yml b/acceptance/windows-service/.kitchen.yml
deleted file mode 100644
index 5270e81487..0000000000
--- a/acceptance/windows-service/.kitchen.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-verifier:
- name: inspec
-
-suites:
- - name: chef-windows-service
- includes: [windows-2012r2]
- run_list:
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
deleted file mode 100644
index 75383b69bf..0000000000
--- a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-only_if do
- os["family"] == "windows"
-end
-
-describe command("chef-service-manager") do
- it { should exist }
- its("exit_status") { should eq 0 }
-end
-
-describe service("chef-client") do
- it { should_not be_enabled }
- it { should_not be_installed }
- it { should_not be_running }
-end
-
-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
-
-describe service("chef-client") do
- it { should be_enabled }
- it { should be_installed }
- it { should_not be_running }
-end
-
-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
-
-describe service("chef-client") do
- it { should be_enabled }
- it { should be_installed }
- it { should be_running }
-end
-
-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
-
-describe service("chef-client") do
- it { should be_enabled }
- it { should be_installed }
- it { should_not be_running }
-end
-
-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
-
-describe service("chef-client") do
- it { should_not be_enabled }
- it { should_not be_installed }
- it { should_not be_running }
-end
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 831ecf67e5..0000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-version: "master-{build}"
-
-os: Windows Server 2012 R2
-platform:
- - x64
-
-environment:
- matrix:
- - ruby_version: "23-x64"
- - ruby_version: "23"
-
-clone_folder: c:\projects\chef
-clone_depth: 1
-skip_tags: true
-branches:
- only:
- - master
-
-install:
- - systeminfo
- - winrm quickconfig -q
- - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- - echo %PATH%
- - 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
-
-test_script:
- - SET SPEC_OPTS=--format progress
- - bundle exec rake spec
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000000..c87781b1e1
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,91 @@
+# End-to-End Test of Chef in macOS
+
+variables:
+ FORCE_FFI_YAJL: 'ext'
+ CHEF_LICENSE: 'accept-no-persist'
+
+trigger:
+- master
+# TODO: 20190528 - should we be testing end_to_end on chef-15?
+#- chef-15
+
+pool:
+ vmImage: $(imageName)
+
+jobs:
+ - job:
+ strategy:
+ matrix:
+ mac_kitchen_tests:
+ imageName: 'macos-latest'
+
+ steps:
+ - script: |
+ curl -L https://omnitruck.chef.io/install.sh | sudo bash -s -- -c current
+ /opt/chef/bin/chef-client -v
+ /opt/chef/bin/ohai -v
+ /opt/chef/embedded/bin/rake --version
+ /opt/chef/embedded/bin/bundle -v
+ displayName: 'Install Chef/Ohai from Omnitruck'
+
+ - script: |
+ OHAI_VERSION=$(sed -n '/ohai .[0-9]/{s/.*(//;s/)//;p;}' Gemfile.lock)
+ sudo /opt/chef/embedded/bin/gem install appbundler appbundle-updater --no-doc
+ sudo /opt/chef/embedded/bin/appbundle-updater chef ohai v${OHAI_VERSION} --tarball --github chef/ohai
+ sudo /opt/chef/embedded/bin/appbundle-updater chef chef $BUILD_SOURCEVERSION --tarball --github chef/chef
+ echo "Installed Chef / Ohai release:"
+ /opt/chef/bin/chef-client -v
+ /opt/chef/bin/ohai -v
+ displayName: 'Upgrade Chef/Ohai via Appbundler'
+
+ - script: |
+ cd kitchen-tests
+ sudo /opt/chef/embedded/bin/bundle config set without 'omnibus_package ruby_prof'
+ sudo /opt/chef/embedded/bin/bundle install --jobs=3 --retry=3 --path=vendor/bundle
+ sudo /opt/chef/embedded/bin/gem install berkshelf --no-doc
+ sudo /opt/chef/embedded/bin/berks vendor cookbooks
+ sudo /opt/chef/bin/chef-client -z -o end_to_end --chef-license accept-no-persist
+ displayName: 'Run end_to_end::default recipe'
+
+ - job:
+ strategy:
+ matrix:
+ windows_kitchen_tests:
+ imageName: 'windows-latest'
+
+ steps:
+ - powershell: |
+ . { Invoke-WebRequest -useb https://omnitruck.chef.io/install.ps1 } | Invoke-Expression; Install-Project -project chef -channel current
+ $env:PATH = "C:\opscode\chef\bin;C:\opscode\chef\embedded\bin;" + $env:PATH
+ chef-client -v
+ ohai -v
+ rake --version
+ bundle -v
+ displayName: 'Install Chef/Ohai from Omnitruck'
+
+ - powershell: |
+ $env:PATH = "C:\opscode\chef\bin;C:\opscode\chef\embedded\bin;" + $env:PATH
+ $env:OHAI_VERSION = ( Select-String -Path .\Gemfile.lock -Pattern '(?<=ohai \()\d.*(?=\))' | ForEach-Object { $_.Matches[0].Value } )
+ gem install appbundler appbundle-updater --no-doc
+ appbundle-updater chef ohai v$env:OHAI_VERSION --tarball --github chef/ohai
+ appbundle-updater chef chef $env:BUILD_SOURCEVERSION --tarball --github chef/chef
+ Write-Output "Installed Chef / Ohai release:"
+ chef-client -v
+ ohai -v
+ displayName: 'Upgrade Chef/Ohai via Appbundler'
+
+ - powershell: |
+ cd kitchen-tests
+ $env:PATH = "C:\opscode\chef\bin;C:\opscode\chef\embedded\bin;" + $env:PATH
+ bundle config set without 'omnibus_package ruby_prof'
+ bundle install --jobs=3 --retry=3 --path=vendor/bundle
+ gem install berkshelf --no-doc
+ # berks emits a ruby warning when it loads net/http due to a previously
+ # defined constant. Even though it is just a warning, powershell immediately
+ # exits 1. I'm not sure why but this just suppresses the warnings.
+ $env:RUBYOPT="-W0"
+ berks vendor cookbooks
+ # restore the default warning level
+ $env:RUBYOPT="-W1"
+ chef-client -z -o end_to_end --chef-license accept-no-persist
+ displayName: 'Run end_to_end::default recipe'
diff --git a/bin/chef-apply b/bin/chef-apply
deleted file mode 100755
index 39c6682b78..0000000000
--- a/bin/chef-apply
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-#
-# ./chef-apply - Run a single chef recipe
-#
-# Author:: Bryan W. Berry (<bryan.berry@gmail.com>)
-# Copyright:: Copyright 2012-2016, Bryan W. Berry
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-require "rubygems"
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-require "chef/application/apply"
-
-Chef::Application::Apply.new.run
diff --git a/bin/chef-client b/bin/chef-client
deleted file mode 100755
index 207bb8941f..0000000000
--- a/bin/chef-client
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env ruby
-#
-# ./chef-client - Run the chef client
-#
-# Author:: AJ Christensen (<aj@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 "rubygems"
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-require "chef"
-require "chef/application/client"
-
-Chef::Application::Client.new.run
diff --git a/bin/chef-service-manager b/bin/chef-service-manager
deleted file mode 100755
index c894b10f4f..0000000000
--- a/bin/chef-service-manager
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env ruby
-#
-# ./chef-service-manager - Control chef-service on Windows platforms.
-#
-# 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 "rubygems"
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-require "chef"
-require "chef/application/windows_service_manager"
-
-if Chef::Platform.windows?
- chef_client_service = {
- :service_name => "chef-client",
- :service_display_name => "Chef Client Service",
- :service_description => "Runs Chef Client on regular, configurable intervals.",
- :service_file_path => File.expand_path("../chef-windows-service", $PROGRAM_NAME),
- :delayed_start => true,
- :dependencies => ["Winmgmt"],
- }
- Chef::Application::WindowsServiceManager.new(chef_client_service).run
-else
- puts "chef-service-manager is only available on Windows platforms."
-end
diff --git a/bin/chef-shell b/bin/chef-shell
deleted file mode 100755
index 4d9300ebb7..0000000000
--- a/bin/chef-shell
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env ruby
-#
-# ./chef-shell - Run the Chef REPL (Shell)
-#
-# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright (c) 2009
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-begin
- require "rubygems"
-rescue LoadError
-end
-
-require "irb"
-require "irb/completion"
-require "irb/ext/save-history"
-
-$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
-
-require "chef/shell"
-
-Shell.start
diff --git a/bin/chef-solo b/bin/chef-solo
deleted file mode 100755
index 23def86a11..0000000000
--- a/bin/chef-solo
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-#
-# ./chef-solo - Run the chef client, in stand-alone mode
-#
-# Author:: AJ Christensen (<aj@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 "rubygems"
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-require "chef/application/solo"
-
-Chef::Application::Solo.new.run
diff --git a/bin/chef-windows-service b/bin/chef-windows-service
deleted file mode 100755
index 184c3d7cd6..0000000000
--- a/bin/chef-windows-service
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env ruby
-#
-# Author:: Jay Mundrawala (<jdm@chef.io>)
-#
-# Copyright:: 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.
-#
-
-# Note:
-# The file is used by appbundler to generate a ruby file that
-# we can execute using the correct gems. The batch file we
-# generate will call that file, and will be registered as
-# a windows service.
-
-require "rubygems"
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-require "chef"
-require "chef/application/windows_service"
-
-if Chef::Platform.windows?
- Chef::Application::WindowsService.mainloop
-else
- puts "chef-windows-service is only available on Windows platforms."
-end
diff --git a/bin/knife b/bin/knife
index b8983d6d5c..85ac3b91e9 100755
--- a/bin/knife
+++ b/bin/knife
@@ -3,7 +3,7 @@
# ./knife - Chef CLI interface
#
# 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");
@@ -18,8 +18,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "rubygems"
-$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
+$:.unshift(File.expand_path(File.join(__dir__, "..", "lib")))
require "chef/application/knife"
Chef::Application::Knife.new.run
diff --git a/chef-bin/Gemfile b/chef-bin/Gemfile
new file mode 100644
index 0000000000..05b93d9230
--- /dev/null
+++ b/chef-bin/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in chef-config.gemspec
+gemspec
+
+group(:development, :test) do
+ gem "rake"
+end \ No newline at end of file
diff --git a/chef-bin/LICENSE b/chef-bin/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/chef-bin/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the 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.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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/chef-bin/Rakefile b/chef-bin/Rakefile
new file mode 100644
index 0000000000..7fe4f1e9d4
--- /dev/null
+++ b/chef-bin/Rakefile
@@ -0,0 +1,17 @@
+# we need to force the install in order to overwrite the binstubs from
+# old chef gems.
+
+require "bundler/gem_helper"
+
+Bundler::GemHelper.install_tasks
+
+# this is necessary to use to overwrite any chef-14 or earlier era gem which has the bin files in
+# the chef gem itself
+desc "force install the chef-bin gem"
+task "install:force" do
+ sh "gem build -V chef-bin.gemspec"
+ built_gem_path = Dir["chef-bin-*.gem"].max_by { |f| File.mtime(f) }
+ FileUtils.mkdir_p("pkg") unless Dir.exist?("pkg")
+ FileUtils.mv(built_gem_path, "pkg")
+ sh "gem install -f pkg/#{built_gem_path}"
+end
diff --git a/chef-bin/bin/chef-apply b/chef-bin/bin/chef-apply
new file mode 100755
index 0000000000..92c350b85b
--- /dev/null
+++ b/chef-bin/bin/chef-apply
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# ./chef-apply - Run a single chef recipe
+#
+# Author:: Bryan W. Berry (<bryan.berry@gmail.com>)
+# Copyright:: Copyright 2012-2016, Bryan W. Berry
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$:.unshift(File.join(__dir__, "..", "lib"))
+require "chef/application/apply"
+
+Chef::Application::Apply.new.run(enforce_license: true)
diff --git a/chef-bin/bin/chef-client b/chef-bin/bin/chef-client
new file mode 100755
index 0000000000..69436d4182
--- /dev/null
+++ b/chef-bin/bin/chef-client
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+#
+# ./chef-client - Run the chef client
+#
+# Author:: AJ Christensen (<aj@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.
+
+$:.unshift(File.join(__dir__, "..", "lib"))
+require "chef"
+require "chef/application/client"
+
+Chef::Application::Client.new.run(enforce_license: true)
diff --git a/chef-bin/bin/chef-resource-inspector b/chef-bin/bin/chef-resource-inspector
new file mode 100755
index 0000000000..e5f4100441
--- /dev/null
+++ b/chef-bin/bin/chef-resource-inspector
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+#
+# ./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");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+Encoding.default_external = Encoding::UTF_8
+
+$:.unshift(File.expand_path(File.join(__dir__, "..", "lib")))
+
+require "chef/resource_inspector"
+
+Chef::ResourceInspector.start
diff --git a/chef-bin/bin/chef-service-manager b/chef-bin/bin/chef-service-manager
new file mode 100755
index 0000000000..dcaae80141
--- /dev/null
+++ b/chef-bin/bin/chef-service-manager
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+#
+# ./chef-service-manager - Control chef-service on Windows platforms.
+#
+# Author:: Serdar Sutay (serdar@chef.io)
+# 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.
+
+$:.unshift(File.join(__dir__, "..", "lib"))
+require "chef"
+require "chef/application/windows_service_manager"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+if Chef::Platform.windows?
+ chef_client_service = {
+ service_name: ChefUtils::Dist::Infra::CLIENT,
+ service_display_name: "#{ChefUtils::Dist::Infra::PRODUCT} Service",
+ service_description: "Runs #{ChefUtils::Dist::Infra::PRODUCT} on regular, configurable intervals.",
+ service_file_path: File.expand_path("../chef-windows-service", $PROGRAM_NAME),
+ delayed_start: true,
+ dependencies: ["Winmgmt"],
+ }
+ Chef::Application::WindowsServiceManager.new(chef_client_service).run
+else
+ puts "chef-service-manager is only available on Windows platforms."
+end
diff --git a/chef-bin/bin/chef-shell b/chef-bin/bin/chef-shell
new file mode 100755
index 0000000000..48263dd7ea
--- /dev/null
+++ b/chef-bin/bin/chef-shell
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# ./chef-shell - Run the Chef REPL (Shell)
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# 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.
+
+Encoding.default_external = Encoding::UTF_8
+
+require "irb"
+require "irb/completion"
+require "irb/ext/save-history"
+
+$:.unshift(File.expand_path(File.join(__dir__, "..", "lib")))
+
+require "chef/shell"
+
+Shell.start
diff --git a/chef-bin/bin/chef-solo b/chef-bin/bin/chef-solo
new file mode 100755
index 0000000000..379aa89485
--- /dev/null
+++ b/chef-bin/bin/chef-solo
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# ./chef-solo - Run the chef client, in stand-alone mode
+#
+# Author:: AJ Christensen (<aj@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.
+
+$:.unshift(File.join(__dir__, "..", "lib"))
+require "chef/application/solo"
+
+Chef::Application::Solo.new.run(enforce_license: true)
diff --git a/chef-bin/bin/chef-windows-service b/chef-bin/bin/chef-windows-service
new file mode 100755
index 0000000000..ce1a30baae
--- /dev/null
+++ b/chef-bin/bin/chef-windows-service
@@ -0,0 +1,34 @@
+#!/usr/bin/env ruby
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+#
+# Copyright:: 2014-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.
+#
+
+# Note:
+# The file is used by appbundler to generate a ruby file that
+# we can execute using the correct gems. The batch file we
+# generate will call that file, and will be registered as
+# a windows service.
+
+$:.unshift(File.join(__dir__, "..", "lib"))
+require "chef"
+require "chef/application/windows_service"
+
+if Chef::Platform.windows?
+ Chef::Application::WindowsService.mainloop
+else
+ puts "chef-windows-service is only available on Windows platforms."
+end
diff --git a/chef-bin/chef-bin.gemspec b/chef-bin/chef-bin.gemspec
new file mode 100644
index 0000000000..bbf57f3d21
--- /dev/null
+++ b/chef-bin/chef-bin.gemspec
@@ -0,0 +1,25 @@
+lib = File.expand_path("lib", __dir__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require "chef-bin/version"
+
+Gem::Specification.new do |spec|
+ spec.name = "chef-bin"
+ spec.version = ChefBin::VERSION
+ spec.authors = ["Adam Jacob"]
+ spec.email = ["adam@chef.io"]
+
+ spec.summary = %q{Chef-branded binstubs for chef-client}
+ spec.homepage = "https://github.com/chef/chef"
+ spec.license = "Apache-2.0"
+
+ spec.require_paths = ["lib"]
+
+ spec.add_dependency "chef", "= #{ChefBin::VERSION}"
+ spec.add_development_dependency "rake"
+
+ spec.files = %w{Gemfile Rakefile LICENSE} + Dir.glob("*.gemspec") +
+ Dir.glob("{lib}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
+
+ spec.bindir = "bin"
+ spec.executables = %w{ chef-apply chef-client chef-resource-inspector chef-service-manager chef-shell chef-solo chef-windows-service }
+end
diff --git a/chef-bin/lib/chef-bin.rb b/chef-bin/lib/chef-bin.rb
new file mode 100644
index 0000000000..4a7b42eb6d
--- /dev/null
+++ b/chef-bin/lib/chef-bin.rb
@@ -0,0 +1,20 @@
+#
+# Copyright:: Copyright (c) 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 ChefBin
+
+end
diff --git a/chef-bin/lib/chef-bin/version.rb b/chef-bin/lib/chef-bin/version.rb
new file mode 100644
index 0000000000..fa2b88de7f
--- /dev/null
+++ b/chef-bin/lib/chef-bin/version.rb
@@ -0,0 +1,34 @@
+# Copyright:: Copyright (c) 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 is generated by running `rake version` in the top level of
+# this repo. Do not edit this manually. Edit the VERSION file and run the rake
+# task instead.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+module ChefBin
+ CHEFBIN_ROOT = File.expand_path("..", __dir__)
+ VERSION = "17.0.32".freeze
+end
+
+#
+# NOTE: the Chef::Version class is defined in version_class.rb
+#
+# NOTE: DO NOT Use the Chef::Version class on ChefConfig::VERSIONs. The
+# Chef::Version class is for _cookbooks_ only, and cannot handle
+# pre-release versions like "10.14.0.rc.2". Please use Rubygem's
+# Gem::Version class instead.
+#
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/.rspec b/chef-config/.rspec
deleted file mode 100644
index eb3ef03653..0000000000
--- a/chef-config/.rspec
+++ /dev/null
@@ -1,2 +0,0 @@
---color
--fd
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/Gemfile b/chef-config/Gemfile
index 96ab544690..4498154338 100644
--- a/chef-config/Gemfile
+++ b/chef-config/Gemfile
@@ -1,4 +1,10 @@
source "https://rubygems.org"
-# Specify your gem's dependencies in chef-config.gemspec
+gem "chef-utils", path: File.expand_path("../chef-utils", __dir__) if File.exist?(File.expand_path("../chef-utils", __dir__))
+
gemspec
+
+group(:development, :test) do
+ gem "rake"
+ gem "rspec"
+end
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 fd6497a287..4bb4605e0c 100644
--- a/chef-config/Rakefile
+++ b/chef-config/Rakefile
@@ -1,15 +1,12 @@
-require "chef-config/package_task"
+require "bundler/gem_tasks"
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig", "chef-config") do |package|
- package.module_path = "chef-config"
-end
-
-task :default => :spec
+task default: :spec
begin
require "rspec/core/rake_task"
desc "Run standard specs"
RSpec::Core::RakeTask.new(:spec) do |t|
+ t.verbose = false
t.pattern = FileList["spec/**/*_spec.rb"]
end
rescue LoadError
diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec
index 9e40528fba..08086ff25b 100644
--- a/chef-config/chef-config.gemspec
+++ b/chef-config/chef-config.gemspec
@@ -1,5 +1,4 @@
-# coding: utf-8
-lib = File.expand_path("../lib", __FILE__)
+lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "chef-config/version"
@@ -9,24 +8,30 @@ Gem::Specification.new do |spec|
spec.authors = ["Adam Jacob"]
spec.email = ["adam@chef.io"]
- spec.summary = %q{Chef's default configuration and config loading}
+ spec.summary = %q{Chef Infra's default configuration and config loading library}
spec.homepage = "https://github.com/chef/chef"
spec.license = "Apache-2.0"
+ spec.required_ruby_version = ">= 2.6.0"
+
+ spec.metadata = {
+ "bug_tracker_uri" => "https://github.com/chef/chef/issues",
+ "changelog_uri" => "https://github.com/chef/chef/blob/master/CHANGELOG.md",
+ "documentation_uri" => "https://github.com/chef/chef/tree/master/chef-config/README.md",
+ "homepage_uri" => "https://github.com/chef/chef/tree/master/chef-config",
+ "source_code_uri" => "https://github.com/chef/chef/tree/master/chef-config",
+ }
+
spec.require_paths = ["lib"]
- spec.add_dependency "mixlib-shellout", "~> 2.0"
- spec.add_dependency "mixlib-config", "~> 2.0"
+ spec.add_dependency "chef-utils", "= #{ChefConfig::VERSION}"
+ spec.add_dependency "mixlib-shellout", ">= 2.0", "< 4.0"
+ spec.add_dependency "mixlib-config", ">= 2.2.12", "< 4.0"
spec.add_dependency "fuzzyurl"
spec.add_dependency "addressable"
+ spec.add_dependency "tomlrb", "~> 1.2"
- spec.add_development_dependency "rake", "~> 10.0"
-
- %w{rspec-core rspec-expectations rspec-mocks}.each do |rspec|
- 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.rb b/chef-config/lib/chef-config.rb
index 3c52462ab6..1a762a21f8 100644
--- a/chef-config/lib/chef-config.rb
+++ b/chef-config/lib/chef-config.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index f2db54aa17..2f261b45a7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,19 +19,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "mixlib/config"
-require "pathname"
+require "mixlib/config" unless defined?(Mixlib::Config)
+autoload :Pathname, "pathname"
+autoload :ChefUtils, "chef-utils"
-require "chef-config/fips"
-require "chef-config/logger"
-require "chef-config/windows"
-require "chef-config/path_helper"
-require "chef-config/mixin/fuzzy_hostname_matcher"
+require_relative "fips"
+require_relative "logger"
+require_relative "windows"
+require_relative "path_helper"
+require_relative "mixin/fuzzy_hostname_matcher"
-require "mixlib/shellout"
-require "uri"
-require "addressable/uri"
-require "openssl"
+module Mixlib
+ autoload :ShellOut, "mixlib/shellout"
+end
+autoload :URI, "uri"
+module Addressable
+ autoload :URI, "addressable/uri"
+end
+autoload :OpenSSL, "openssl"
+autoload :YAML, "yaml"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
module ChefConfig
@@ -44,32 +51,140 @@ 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?
+ if ChefUtils.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
+ # On *nix, /etc/chef, on Windows C:\chef
+ #
+ # @param windows [Boolean] optional flag to force to windows or unix-style
+ # @return [String] the platform-specific path
+ #
+ def self.etc_chef_dir(windows: ChefUtils.windows?)
+ path = windows ? c_chef_dir : PathHelper.join("/etc", ChefUtils::Dist::Infra::DIR_SUFFIX, windows: windows)
+ PathHelper.cleanpath(path, windows: windows)
+ end
+
+ # On *nix, /var/chef, on Windows C:\chef
+ #
+ # @param windows [Boolean] optional flag to force to windows or unix-style
+ # @return [String] the platform-specific path
+ #
+ def self.var_chef_dir(windows: ChefUtils.windows?)
+ path = windows ? c_chef_dir : PathHelper.join("/var", ChefUtils::Dist::Infra::DIR_SUFFIX, windows: windows)
+ PathHelper.cleanpath(path, windows: windows)
+ end
+
+ # On *nix, /var, on Windows C:\
+ #
+ # @param windows [Boolean] optional flag to force to windows or unix-style
+ # @return [String] the platform-specific path
+ #
+ def self.var_root_dir(windows: ChefUtils.windows?)
+ path = windows ? "C:\\" : "/var"
+ PathHelper.cleanpath(path, windows: windows)
+ end
+
+ # On windows, C:/chef/
+ #
+ # (should only be called in a windows-context)
+ #
+ # @return [String] the platform-specific path
+ #
+ def self.c_chef_dir(windows: ChefUtils.windows?)
+ drive = windows_installation_drive || "C:"
+ PathHelper.join(drive, ChefUtils::Dist::Infra::DIR_SUFFIX, windows: windows)
+ end
+
+ # On windows, C:/opscode
+ #
+ # (should only be called in a windows-context)
+ #
+ # @return [String] the platform-specific path
+ #
+ def self.c_opscode_dir(windows: ChefUtils.windows?)
+ drive = windows_installation_drive || "C:"
+ PathHelper.join(drive, ChefUtils::Dist::Org::LEGACY_CONF_DIR, ChefUtils::Dist::Infra::DIR_SUFFIX, windows: windows)
+ 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
+ #
+ # (should only be called in a windows-context)
+ #
+ # @return [String] the drive letter
+ #
+ def self.windows_installation_drive
+ if ChefUtils.windows?
+ drive = File.expand_path(__FILE__).split("/", 2)[0]
+ drive = ENV["SYSTEMDRIVE"] if drive.to_s == ""
+ drive
+ end
+ end
+
+ # @param name [String]
+ # @param file_path [String]
def self.add_formatter(name, file_path = nil)
formatters << [name, file_path]
end
+ # @param logger [String]
def self.add_event_logger(logger)
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
+ set_extra_config_options(extra_parsed_options)
+ end
+ end
+
+ # We use :[]= assignment here to not bypass any coercions that happen via mixlib-config writes_value callbacks
+ def self.set_extra_config_options(extra_parsed_options)
+ extra_parsed_options.each do |key, value|
+ self[key.to_sym] = value
+ end
+ end
+
# Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.)
configurable(:config_file)
@@ -77,25 +192,29 @@ module ChefConfig
if config_file
PathHelper.dirname(PathHelper.canonical_path(config_file, false))
else
- PathHelper.join(user_home, ".chef", "")
+ PathHelper.join(PathHelper.cleanpath(user_home), ChefUtils::Dist::Infra::USER_CONF_DIR, "")
end
end
default :formatters, []
+ # @param uri [String] the URI to validate
+ #
+ # @return [Boolean] is the URL valid
def self.is_valid_url?(uri)
url = uri.to_s.strip
- /^http:\/\// =~ url || /^https:\/\// =~ url || /^chefzero:/ =~ url
+ %r{^http://} =~ url || %r{^https://} =~ url || /^chefzero:/ =~ url
end
+
# Override the config dispatch to set the value of multiple server options simultaneously
#
- # === Parameters
- # url<String>:: String to be set for all of the chef-server-api URL's
+ # @param [String] url String to be set for all of the chef-server-api URL's
#
configurable(:chef_server_url).writes_value do |uri|
unless is_valid_url? uri
- raise ConfigurationError, "#{uri} is an invalid chef_server_url."
+ raise ConfigurationError, "#{uri} is an invalid chef_server_url. The URL must start with http://, https://, or chefzero://."
end
+
uri.to_s.strip
end
@@ -105,22 +224,36 @@ module ChefConfig
# properly.
configurable(:daemonize).writes_value { |v| v }
+ def self.expand_relative_paths(path)
+ unless path.nil?
+ if path.is_a?(String)
+ File.expand_path(path)
+ else
+ Array(path).map { |path| File.expand_path(path) }
+ end
+ end
+ end
+
+ configurable(:cookbook_path).writes_value { |path| expand_relative_paths(path) }
+
+ configurable(:chef_repo_path).writes_value { |path| expand_relative_paths(path) }
+
# The root where all local chef object data is stored. cookbooks, data bags,
# environments are all assumed to be in separate directories under this.
# chef-solo uses these directories for input data. knife commands
# 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].is_a?(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
@@ -138,12 +271,13 @@ module ChefConfig
end
path = new_path
end
- ChefConfig.logger.info("Auto-discovered chef repository at #{path}")
+ ChefConfig.logger.info("Auto-discovered #{ChefUtils::Dist::Infra::SHORT} repository at #{path}")
path
end
+ # @param child_path [String]
def self.derive_path_from_chef_repo_path(child_path)
- if chef_repo_path.kind_of?(String)
+ if chef_repo_path.is_a?(String)
PathHelper.join(chef_repo_path, child_path)
else
chef_repo_path.uniq.map { |path| PathHelper.join(path, child_path) }
@@ -152,74 +286,66 @@ module ChefConfig
# Location of acls on disk. String or array of strings.
# Defaults to <chef_repo_path>/acls.
- default(:acl_path) { derive_path_from_chef_repo_path("acls") }
+ default(:acl_path) { derive_path_from_chef_repo_path("acls") }.writes_value { |path| expand_relative_paths(path) }
# Location of clients on disk. String or array of strings.
# Defaults to <chef_repo_path>/clients.
- default(:client_path) { derive_path_from_chef_repo_path("clients") }
+ default(:client_path) { derive_path_from_chef_repo_path("clients") }.writes_value { |path| expand_relative_paths(path) }
# Location of client keys on disk. String or array of strings.
# Defaults to <chef_repo_path>/client_keys.
- default(:client_key_path) { derive_path_from_chef_repo_path("client_keys") }
+ default(:client_key_path) { derive_path_from_chef_repo_path("client_keys") }.writes_value { |path| expand_relative_paths(path) }
# Location of containers on disk. String or array of strings.
# Defaults to <chef_repo_path>/containers.
- default(:container_path) { derive_path_from_chef_repo_path("containers") }
+ default(:container_path) { derive_path_from_chef_repo_path("containers") }.writes_value { |path| expand_relative_paths(path) }
# Location of cookbook_artifacts on disk. String or array of strings.
# Defaults to <chef_repo_path>/cookbook_artifacts.
- default(:cookbook_artifact_path) { derive_path_from_chef_repo_path("cookbook_artifacts") }
+ default(:cookbook_artifact_path) { derive_path_from_chef_repo_path("cookbook_artifacts") }.writes_value { |path| expand_relative_paths(path) }
# Location of cookbooks on disk. String or array of strings.
# 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]
- derive_path_from_chef_repo_path("cookbooks")
- else
- Array(derive_path_from_chef_repo_path("cookbooks")).flatten +
- Array(derive_path_from_chef_repo_path("site-cookbooks")).flatten
- end
- end
+ # is not specified, this is set to /var/chef/cookbooks.
+ default(:cookbook_path) { derive_path_from_chef_repo_path("cookbooks") }
# Location of data bags on disk. String or array of strings.
# Defaults to <chef_repo_path>/data_bags.
- default(:data_bag_path) { derive_path_from_chef_repo_path("data_bags") }
+ default(:data_bag_path) { derive_path_from_chef_repo_path("data_bags") }.writes_value { |path| expand_relative_paths(path) }
# Location of environments on disk. String or array of strings.
# Defaults to <chef_repo_path>/environments.
- default(:environment_path) { derive_path_from_chef_repo_path("environments") }
+ default(:environment_path) { derive_path_from_chef_repo_path("environments") }.writes_value { |path| expand_relative_paths(path) }
# Location of groups on disk. String or array of strings.
# Defaults to <chef_repo_path>/groups.
- default(:group_path) { derive_path_from_chef_repo_path("groups") }
+ default(:group_path) { derive_path_from_chef_repo_path("groups") }.writes_value { |path| expand_relative_paths(path) }
# Location of nodes on disk. String or array of strings.
# Defaults to <chef_repo_path>/nodes.
- default(:node_path) { derive_path_from_chef_repo_path("nodes") }
+ default(:node_path) { derive_path_from_chef_repo_path("nodes") }.writes_value { |path| expand_relative_paths(path) }
# Location of policies on disk. String or array of strings.
# Defaults to <chef_repo_path>/policies.
- default(:policy_path) { derive_path_from_chef_repo_path("policies") }
+ default(:policy_path) { derive_path_from_chef_repo_path("policies") }.writes_value { |path| expand_relative_paths(path) }
# Location of policy_groups on disk. String or array of strings.
# Defaults to <chef_repo_path>/policy_groups.
- default(:policy_group_path) { derive_path_from_chef_repo_path("policy_groups") }
+ default(:policy_group_path) { derive_path_from_chef_repo_path("policy_groups") }.writes_value { |path| expand_relative_paths(path) }
# Location of roles on disk. String or array of strings.
# Defaults to <chef_repo_path>/roles.
- default(:role_path) { derive_path_from_chef_repo_path("roles") }
+ default(:role_path) { derive_path_from_chef_repo_path("roles") }.writes_value { |path| expand_relative_paths(path) }
# Location of users on disk. String or array of strings.
# Defaults to <chef_repo_path>/users.
- default(:user_path) { derive_path_from_chef_repo_path("users") }
+ default(:user_path) { derive_path_from_chef_repo_path("users") }.writes_value { |path| expand_relative_paths(path) }
- # Location of policies on disk. String or array of strings.
- # Defaults to <chef_repo_path>/policies.
- default(:policy_path) { derive_path_from_chef_repo_path("policies") }
+ # DEPRECATED
+ default :enforce_path_sanity, false
- # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
- default :enforce_path_sanity, true
+ # Enforce default paths by default for all APIs, not just the default internal shell_out
+ default :enforce_default_paths, false
# Formatted Chef Client output is a beta feature, disabled by default:
default :formatter, "null"
@@ -236,32 +362,35 @@ module ChefConfig
if local_mode
PathHelper.join(config_dir, "local-mode-cache")
else
- primary_cache_root = platform_specific_path("/var")
- primary_cache_path = platform_specific_path("/var/chef")
+ primary_cache_root = var_root_dir
+ primary_cache_path = var_chef_dir
# Use /var/chef as the cache path only if that folder exists and we can read and write
# into it, or /var exists and we can read and write into it (we'll create /var/chef later).
# Otherwise, we'll create .chef under the user's home directory and use that as
# the cache path.
unless path_accessible?(primary_cache_path) || path_accessible?(primary_cache_root)
- secondary_cache_path = PathHelper.join(user_home, ".chef")
- ChefConfig.logger.info("Unable to access cache at #{primary_cache_path}. Switching cache to #{secondary_cache_path}")
+ secondary_cache_path = PathHelper.join(user_home, ChefUtils::Dist::Infra::USER_CONF_DIR)
+ secondary_cache_path = target_mode? ? PathHelper.join(secondary_cache_path, target_mode.host) : secondary_cache_path
+ ChefConfig.logger.trace("Unable to access cache at #{primary_cache_path}. Switching cache to #{secondary_cache_path}")
secondary_cache_path
else
- primary_cache_path
+ target_mode? ? PathHelper.join(primary_cache_path, target_mode.host) : primary_cache_path
end
end
end
# Returns true only if the path exists and is readable and writeable for the user.
+ #
+ # @param path [String]
def self.path_accessible?(path)
- File.exists?(path) && File.readable?(path) && File.writable?(path)
+ File.exist?(path) && File.readable?(path) && File.writable?(path)
end
# Where cookbook files are stored on the server (by content checksum)
default(:checksum_path) { PathHelper.join(cache_path, "checksums") }
# Where chef's cache files should be stored
- default(:file_cache_path) { PathHelper.join(cache_path, "cache") }
+ default(:file_cache_path) { PathHelper.join(cache_path, "cache") }.writes_value { |path| expand_relative_paths(path) }
# Where backups of chef-managed files should go
default(:file_backup_path) { PathHelper.join(cache_path, "backup") }
@@ -271,7 +400,7 @@ module ChefConfig
# If your `file_cache_path` resides on a NFS (or non-flock()-supporting
# fs), it's recommended to set this to something like
# '/tmp/chef-client-running.pid'
- default(:lockfile) { PathHelper.join(file_cache_path, "chef-client-running.pid") }
+ default(:lockfile) { PathHelper.join(file_cache_path, "#{ChefUtils::Dist::Infra::CLIENT}-running.pid") }
## Daemonization Settings ##
# What user should Chef run as?
@@ -280,6 +409,7 @@ module ChefConfig
default :umask, 0022
# Valid log_levels are:
+ # * :trace
# * :debug
# * :info
# * :warn
@@ -293,7 +423,7 @@ module ChefConfig
default :log_level, :auto
# Logging location as either an IO stream or string representing log file path
- default :log_location, STDOUT
+ default :log_location, nil
# Using `force_formatter` causes chef to default to formatter output when STDOUT is not a tty
default :force_formatter, false
@@ -301,6 +431,9 @@ module ChefConfig
# Using `force_logger` causes chef to default to logger output when STDOUT is a tty
default :force_logger, false
+ # When set to true always print the stacktrace even if we haven't done -l debug
+ default :always_dump_stacktrace, false
+
# Using 'stream_execute_output' will have Chef always stream the execute output
default :stream_execute_output, false
@@ -312,6 +445,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, true
+
default :interval, nil
default :once, nil
default :json_attribs, nil
@@ -321,6 +459,11 @@ module ChefConfig
default :diff_disabled, false
default :diff_filesize_threshold, 10000000
default :diff_output_threshold, 1000000
+
+ # This is true for "local mode" which uses a chef-zero server listening on
+ # localhost one way or another. This is true for both `chef-solo` (without
+ # the --legacy-mode flag) or `chef-client -z` methods of starting a client run.
+ #
default :local_mode, false
# Configures the mode of operation for ChefFS, which is applied to the
@@ -338,7 +481,7 @@ module ChefConfig
default :repo_mode do
if local_mode && !chef_zero.osc_compat
"hosted_everything"
- elsif chef_server_url =~ /\/+organizations\/.+/
+ elsif %r{/+organizations/.+}.match?(chef_server_url)
"hosted_everything"
else
"everything"
@@ -350,10 +493,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
@@ -370,55 +510,85 @@ module ChefConfig
# 11 (true) or Chef Server 12 (false). Chef Zero can still serve
# policyfile objects in Chef 11 mode, as long as `repo_mode` is set to
# "hosted_everything". The primary differences are:
- # * Chef 11 mode doesn't support multi-tennant, so there is no
+ # * Chef 11 mode doesn't support multi-tenant, so there is no
# distinction between global and org-specific objects (since there are
# no orgs).
# * Chef 11 mode doesn't expose RBAC objects
default :osc_compat, false
end
+
+ # RFCxxx Target Mode support, value is the name of a remote device to Chef against
+ # --target exists as a shortcut to enabling target_mode and setting the host
+ configurable(:target)
+
+ config_context :target_mode do
+ config_strict_mode false # we don't want to have to add all train configuration keys here
+ default :enabled, false
+ default :protocol, "ssh"
+ # typical additional keys: host, user, password
+ end
+
+ def self.target_mode?
+ target_mode.enabled
+ end
+
default :chef_server_url, "https://localhost:443"
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 %r{/organizations/\S*$}.match?(configuration[:chef_server_url])
+ 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
end
default :rest_timeout, 300
- default :yum_timeout, 900
- default :yum_lock_timeout, 30
+
+ # This solo setting is now almost entirely useless. It is set to true if chef-solo was
+ # invoked that way from the command-line (i.e. from Application::Solo as opposed to
+ # Application::Client). The more useful information is contained in the :solo_legacy_mode
+ # vs the :local_mode flags which will be set to true or false depending on how solo was
+ # invoked and actually change more of the behavior. There might be slight differences in
+ # the behavior of :local_mode due to the behavioral differences in Application::Solo vs.
+ # Application::Client and `chef-solo` vs `chef-client -z`, but checking this value and
+ # switching based on it is almost certainly doing the wrong thing and papering over
+ # bugs that should be fixed in one or the other class, and will be brittle and destined
+ # to break in the future (and not necessarily on a major version bump). Checking this value
+ # is also not sufficient to determine if we are not running against a server since this can
+ # be unset but :local_mode may be set. It would be accurate to check both :solo and :local_mode
+ # to determine if we're not running against a server, but the more semantically accurate test
+ # is going to be combining :solo_legacy_mode and :local_mode.
+ #
+ # TL;DR: `if Chef::Config[:solo]` is almost certainly buggy code, you should use:
+ # `if Chef::Config[:local_mode] || Chef::Config[:solo_legacy_mode]`
+ #
+ # @api private
default :solo, false
- # Are we running in old Chef Solo legacy mode?
+ # This is true for old chef-solo legacy mode without any chef-zero server (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
- # Possible values for :audit_mode
- # :enabled, :disabled, :audit_only,
- #
- # TODO: 11 Dec 2014: Currently audit-mode is an experimental feature
- # and is disabled by default. When users choose to enable audit-mode,
- # a warning is issued in application/client#reconfigure.
- # This can be removed when audit-mode is enabled by default.
- default :audit_mode, :disabled
# Chef only needs ohai to run the hostname plugin for the most basic
# functionality. If the rest of the ohai plugins are not needed (like in
# 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
#
@@ -442,12 +612,6 @@ module ChefConfig
default :named_run_list, nil
- # During initial development, users were required to set `use_policyfile true`
- # in `client.rb` to opt-in to policyfile use. Chef Client now examines
- # configuration, node json, and the stored node to determine if policyfile
- # usage is desired. This flag is still honored if set, but is unnecessary.
- default :use_policyfile, false
-
# Policyfiles can be used in a native mode (default) or compatibility mode.
# Native mode requires Chef Server 12.1 (it can be enabled via feature flag
# on some prior versions). In native mode, policies and associated
@@ -481,6 +645,16 @@ module ChefConfig
# be validated.
default :ssl_verify_mode, :verify_peer
+ # Needed to coerce string value to a symbol when loading settings from the
+ # credentials toml files which doesn't allow ruby symbol values
+ configurable(:ssl_verify_mode).writes_value do |value|
+ if value.is_a?(String) && value[0] == ":"
+ value[1..].to_sym
+ else
+ value.to_sym
+ end
+ end
+
# Whether or not to verify the SSL cert for HTTPS requests to the Chef
# server API. If set to `true`, the server's cert will be validated
# regardless of the :ssl_verify_mode setting. This is set to `true` when
@@ -491,7 +665,7 @@ module ChefConfig
# Path to the default CA bundle files.
default :ssl_ca_path, nil
default(:ssl_ca_file) do
- if ChefConfig.windows? && embedded_dir
+ if ChefUtils.windows? && embedded_dir
cacert_path = File.join(embedded_dir, "ssl/certs/cacert.pem")
cacert_path if File.exist?(cacert_path)
else
@@ -535,7 +709,7 @@ module ChefConfig
# Initialize openssl
def self.init_openssl
if fips
- self.enable_fips_mode
+ enable_fips_mode
end
end
@@ -543,7 +717,7 @@ module ChefConfig
# the 'mixlib-authorization' project for more detail). Currently, versions
# 1.0, 1.1, and 1.3 are available.
default :authentication_protocol_version do
- if fips
+ if fips || ssh_agent_signing
"1.3"
else
"1.1"
@@ -558,7 +732,21 @@ module ChefConfig
# `node_name` of the client.
#
# 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") }
+ default(:client_key) do
+ if chef_zero.enabled
+ nil
+ elsif target_mode?
+ PathHelper.cleanpath("#{etc_chef_dir}/#{target_mode.host}/client.pem")
+ else
+ PathHelper.cleanpath("#{etc_chef_dir}/client.pem")
+ end
+ end
+
+ # 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
@@ -566,22 +754,25 @@ module ChefConfig
# never be set to true or its possibly an easily exploitable security hole.
default :follow_client_key_symlink, false
+ # Enable ssh-agent signing mode. This requires {client_key} be set to a
+ # public key rather than the usual private key.
+ default :ssh_agent_signing, false
+
# This secret is used to decrypt encrypted data bag items.
default(:encrypted_data_bag_secret) do
- if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret"))
- platform_specific_path("/etc/chef/encrypted_data_bag_secret")
+ if target_mode? && File.exist?(PathHelper.cleanpath("#{etc_chef_dir}/#{target_mode.host}/encrypted_data_bag_secret"))
+ PathHelper.cleanpath("#{etc_chef_dir}/#{target_mode.host}/encrypted_data_bag_secret")
+ elsif File.exist?(PathHelper.cleanpath("#{etc_chef_dir}/encrypted_data_bag_secret"))
+ PathHelper.cleanpath("#{etc_chef_dir}/encrypted_data_bag_secret")
else
nil
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,
@@ -599,9 +790,18 @@ module ChefConfig
# The `validation_key` is never used if the `client_key` exists.
#
# If chef-zero is enabled, this defaults to nil (no authentication).
- default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") }
- default :validation_client_name, "chef-validator"
+ default(:validation_key) { chef_zero.enabled ? nil : PathHelper.cleanpath("#{etc_chef_dir}/validation.pem") }
+ default :validation_client_name do
+ # If the URL is set and looks like a normal Chef Server URL, extract the
+ # org name and use that as part of the default.
+ if chef_server_url.to_s =~ %r{/organizations/(.*)$}
+ "#{$1}-validator"
+ else
+ "#{ChefUtils::Dist::Infra::SHORT}-validator"
+ end
+ end
+ 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
@@ -613,11 +813,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, []
@@ -638,11 +837,11 @@ module ChefConfig
# the new (and preferred) configuration setting. If not set, knife will
# fall back to using cache_options[:path], which is deprecated but exists in
# many client configs generated by pre-Chef-11 bootstrappers.
- default(:syntax_check_cache_path) { cache_options[:path] }
+ default(:syntax_check_cache_path) { cache_options[:path] }.writes_value { |path| expand_relative_paths(path) }
# Deprecated:
# Move this to the default value of syntax_cache_path when this is removed.
- default(:cache_options) { { :path => PathHelper.join(config_dir, "syntaxcache") } }
+ default(:cache_options) { { path: PathHelper.join(config_dir, "syntaxcache") } }
# Whether errors should be raised for deprecation warnings. When set to
# `false` (the default setting), a warning is emitted but code using
@@ -659,39 +858,26 @@ module ChefConfig
ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
end
+ # Which deprecations warnings to silence. Can be set to `true` to silence
+ # all warnings, or an array of strings like either `"deprecation_type"` or
+ # `"filename.rb:lineno"`.
+ default :silence_deprecation_warnings, []
+
# Whether the resource count should be updated for log resource
# on running chef-client
- default :count_log_resource_updates, true
+ default :count_log_resource_updates, false
+
+ # The selected profile when using credentials.
+ default :profile, nil
+
+ default :chef_guid_path do
+ PathHelper.join(config_dir, "#{ChefUtils::Dist::Infra::SHORT}_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 :bootstrap_version, nil
- default :bootstrap_proxy, nil
- default :bootstrap_template, nil
- default :secret, nil
- default :secret_file, nil
- default :identity_file, nil
- default :host_key_verify, nil
- default :forward_agent, nil
- default :sort_status_reverse, nil
default :hints, {}
end
@@ -720,7 +906,7 @@ module ChefConfig
# Those lists of regular expressions define what chef considers a
# valid user and group name
- if ChefConfig.windows?
+ if ChefUtils.windows?
set_defaults_for_windows
else
set_defaults_for_nix
@@ -750,6 +936,7 @@ module ChefConfig
#
# NOTE: CHANGING THIS SETTING MAY CAUSE CORRUPTION, DATA LOSS AND
# INSTABILITY.
+ #
default :file_atomic_update, true
# There are 3 possible values for this configuration setting.
@@ -757,19 +944,28 @@ module ChefConfig
# false => file staging is done via tempfiles under ENV['TMP']
# :auto => file staging will try using destination directory if possible and
# will fall back to ENV['TMP'] if destination directory is not usable.
+ #
default :file_staging_uses_destdir, :auto
# Exit if another run is in progress and the chef-client is unable to
# get the lock before time expires. If nil, no timeout is enforced. (Exits
# immediately if 0.)
+ #
default :run_lock_timeout, nil
# Number of worker threads for syncing cookbooks in parallel. Increasing
# this number can result in gateway errors from the server (namely 503 and 504).
# If you are seeing this behavior while using the default setting, reducing
# the number of threads will help.
+ #
default :cookbook_sync_threads, 10
+ # True if all resources by default default to unified mode, with all resources
+ # applying in "compile" mode, with no "converge" mode. False is backwards compatible
+ # setting for Chef 11-15 behavior. This will break forward notifications.
+ #
+ default :resource_unified_mode_default, false
+
# At the beginning of the Chef Client run, the cookbook manifests are downloaded which
# contain URLs for every file in every relevant cookbook. Most of the files
# (recipes, resources, providers, libraries, etc) are immediately synchronized
@@ -787,24 +983,35 @@ module ChefConfig
# distribution.
#
# The disadvantages of lazily loading files are that users some time find it
- # confusing that their cookbooks are not fully synchronzied to the cache initially,
+ # confusing that their cookbooks are not fully synchronized to the cache initially,
# and more importantly the time-sensitive URLs which are in the manifest may time
# out on long Chef runs before the resource that uses the file is converged
# (leading to many confusing 403 errors on template/cookbook_file resources).
#
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
- # collect any data for save.
+ # A array of attributes you want sent over the wire when node
+ # data is saved. The default setting is nil, which collects all data.
+ # NOTE: Setting to [] will not collect ANY data to save.
+ default :allowed_automatic_attributes, nil
+ default :allowed_default_attributes, nil
+ default :allowed_normal_attributes, nil
+ default :allowed_override_attributes, nil
+
+ # An 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.
+ # NOTE: Setting to [] will still collect all data to save
+ default :blocked_automatic_attributes, nil
+ default :blocked_default_attributes, nil
+ default :blocked_normal_attributes, nil
+ default :blocked_override_attributes, nil
+
+ # deprecated config options that will be removed in Chef Infra Client 17
+ default :automatic_attribute_blacklist, nil
+ default :default_attribute_blacklist, nil
+ default :normal_attribute_blacklist, nil
+ default :override_attribute_blacklist, nil
default :automatic_attribute_whitelist, nil
default :default_attribute_whitelist, nil
default :normal_attribute_whitelist, nil
@@ -821,14 +1028,18 @@ module ChefConfig
default :watchdog_timeout, 2 * (60 * 60) # 2 hours
end
- # Add an empty and non-strict config_context for chefdk. This lets the user
- # have code like `chefdk.generator_cookbook "/path/to/cookbook"` in their
- # config.rb, and it will be ignored by tools like knife and ohai. ChefDK
- # itself can define the config options it accepts and enable strict mode,
+ # Add an empty and non-strict config_context for chefdk and chefcli.
+ # This lets the user have code like `chefdk.generator_cookbook "/path/to/cookbook"` or
+ # `chefcli[:generator_cookbook] = "/path/to/cookbook"` in their config.rb,
+ # and it will be ignored by tools like knife and ohai. ChefDK and ChefCLI
+ # themselves can define the config options it accepts and enable strict mode,
# and that will only apply when running `chef` commands.
config_context :chefdk do
end
+ config_context :chefcli 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
@@ -837,7 +1048,13 @@ module ChefConfig
# 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, nil
+ default(:server_url) do
+ if config_parent.solo_legacy_mode || 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
@@ -863,7 +1080,7 @@ module ChefConfig
# 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
+ default :organization, "#{ChefUtils::Dist::Infra::SHORT}_solo"
end
configurable(:http_proxy)
@@ -883,19 +1100,12 @@ module ChefConfig
# TODO add some post-file-parsing logic that automatically calls this so
# users don't have to
def self.export_proxies
- export_proxy("http", http_proxy, http_proxy_user, http_proxy_pass) if http_proxy
- export_proxy("https", https_proxy, https_proxy_user, https_proxy_pass) if https_proxy
- export_proxy("ftp", ftp_proxy, ftp_proxy_user, ftp_proxy_pass) if ftp_proxy
- export_no_proxy(no_proxy) if no_proxy
+ export_proxy("http", http_proxy, http_proxy_user, http_proxy_pass) if key?(:http_proxy) && http_proxy
+ export_proxy("https", https_proxy, https_proxy_user, https_proxy_pass) if key?(:https_proxy) && https_proxy
+ export_proxy("ftp", ftp_proxy, ftp_proxy_user, ftp_proxy_pass) if key?(:ftp_proxy) && ftp_proxy
+ export_no_proxy(no_proxy) if key?(:no_proxy) && 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
@@ -907,15 +1117,22 @@ module ChefConfig
# pass = password
# @api private
def self.export_proxy(scheme, path, user, pass)
+ # 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_class = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS
+ # The password part may have any valid USERINFO characters
+ password_class = user_class + "\\:"
+
path = "#{scheme}://#{path}" unless path.include?("://")
# URI.split returns the following parts:
# [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
uri = Addressable::URI.encode(path, Addressable::URI)
if user && !user.empty?
- userinfo = Addressable::URI.encode_component(user, USER)
+ userinfo = Addressable::URI.encode_component(user, user_class)
if pass
- userinfo << ":#{Addressable::URI.encode_component(pass, PASSWORD)}"
+ userinfo << ":#{Addressable::URI.encode_component(pass, password_class)}"
end
uri.userinfo = userinfo
end
@@ -932,7 +1149,7 @@ module ChefConfig
end
# Given a scheme, host, and port, return the correct proxy URI based on the
- # set environment variables, unless exluded by no_proxy, in which case nil
+ # set environment variables, unless excluded by no_proxy, in which case nil
# is returned
def self.proxy_uri(scheme, host, port)
proxy_env_var = ENV["#{scheme}_proxy"].to_s.strip
@@ -940,8 +1157,8 @@ module ChefConfig
# Check if the proxy string contains a scheme. If not, add the url's scheme to the
# proxy before parsing. The regex /^.*:\/\// matches, for example, http://. Reusing proxy
# here since we are really just trying to get the string built correctly.
- proxy = if !proxy_env_var.empty?
- if proxy_env_var =~ /^.*:\/\//
+ proxy = unless proxy_env_var.empty?
+ if %r{^.*://}.match?(proxy_env_var)
URI.parse(proxy_env_var)
else
URI.parse("#{scheme}://#{proxy_env_var}")
@@ -966,7 +1183,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.
#
@@ -990,15 +1207,15 @@ module ChefConfig
# Transform into the form en_ZZ.UTF-8
guessed_locale.gsub(/UTF-?8$/i, "UTF-8")
else
- ChefConfig.logger.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support."
+ ChefConfig.logger.warn "Please install an English UTF-8 locale for #{ChefUtils::Dist::Infra::PRODUCT} to use, falling back to C locale and disabling UTF-8 support."
"C"
end
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."
+ if ChefUtils.windows?
+ 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
@@ -1014,7 +1231,11 @@ 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, nil
+
+ # globally sets the default of the clear_sources property on the gem_package and chef_gem resources
+ default :clear_gem_sources, nil
# If installed via an omnibus installer, this gives the path to the
# "embedded" directory which contains all of the software packaged with
@@ -1039,9 +1260,9 @@ module ChefConfig
# @api private
def self.enable_fips_mode
OpenSSL.fips_mode = true
- require "digest"
- require "digest/sha1"
- require "digest/md5"
+ require "digest" unless defined?(Digest)
+ require "digest/sha1" unless defined?(Digest::SHA1)
+ require "digest/md5" unless defined?(Digest::MD5)
# Remove pre-existing constants if they do exist to reduce the
# amount of log spam and warnings.
Digest.send(:remove_const, "SHA1") if Digest.const_defined?("SHA1")
diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb
index db10a5f364..6d6609f574 100644
--- a/chef-config/lib/chef-config/exceptions.rb
+++ b/chef-config/lib/chef-config/exceptions.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +15,13 @@
# limitations under the License.
#
-require "chef-config/windows"
-require "chef-config/logger"
+require_relative "windows"
+require_relative "logger"
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
index 623ce87686..eb9e55afe6 100644
--- a/chef-config/lib/chef-config/fips.rb
+++ b/chef-config/lib/chef-config/fips.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,14 @@
# limitations under the License.
#
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+
module ChefConfig
def self.fips?
- if ChefConfig.windows?
+ if ChefUtils.windows?
begin
- require "win32/registry"
+ require "win32/registry" unless defined?(Win32::Registry)
rescue LoadError
return false
end
diff --git a/chef-config/lib/chef-config/logger.rb b/chef-config/lib/chef-config/logger.rb
index d239e85f43..6bcce2340f 100644
--- a/chef-config/lib/chef-config/logger.rb
+++ b/chef-config/lib/chef-config/logger.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,29 +21,23 @@ module ChefConfig
# logger for `ChefConfig.logger`
class NullLogger
- def <<(_msg)
- end
+ def <<(_msg); end
- def add(_severity, _message = nil, _progname = nil)
- end
+ def add(_severity, _message = nil, _progname = nil); end
- def debug(_progname = nil, &block)
- end
+ def trace(_progname = nil, &block); end
- def info(_progname = nil, &block)
- end
+ def debug(_progname = nil, &block); end
- def warn(_progname = nil, &block)
- end
+ def info(_progname = nil, &block); end
- def deprecation(_progname = nil, &block)
- end
+ def warn(_progname = nil, &block); end
- def error(_progname = nil, &block)
- end
+ def deprecation(_progname = nil, &block); end
- def fatal(_progname = nil, &block)
- end
+ def error(_progname = nil, &block); end
+
+ def fatal(_progname = nil, &block); end
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..a17d94b443
--- /dev/null
+++ b/chef-config/lib/chef-config/mixin/credentials.rb
@@ -0,0 +1,102 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+autoload :Tomlrb, "tomlrb"
+require_relative "../path_helper"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+module ChefConfig
+ module Mixin
+ # Helper methods for working with credentials files.
+ #
+ # @since 13.7
+ # @api internal
+ module Credentials
+ # Compute the active credentials profile name.
+ #
+ # The lookup order is argument (from --profile), environment variable
+ # ($CHEF_PROFILE), context file (~/.chef/context), and then "default" as
+ # a fallback.
+ #
+ # @since 14.4
+ # @param profile [String, nil] Optional override for the active profile,
+ # normally set via a command-line option.
+ # @return [String]
+ def credentials_profile(profile = nil)
+ context_file = PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "context").freeze
+ if !profile.nil?
+ profile
+ elsif ENV.include?("CHEF_PROFILE")
+ ENV["CHEF_PROFILE"]
+ elsif File.file?(context_file)
+ File.read(context_file).strip
+ else
+ "default"
+ end
+ end
+
+ # Compute the path to the credentials file.
+ #
+ # @since 14.4
+ # @return [String]
+ def credentials_file_path
+ PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "credentials").freeze
+ end
+
+ # Load and parse the credentials file.
+ #
+ # Returns `nil` if the credentials file is unavailable.
+ #
+ # @since 14.4
+ # @return [String, nil]
+ def parse_credentials_file
+ credentials_file = credentials_file_path
+ return nil unless File.file?(credentials_file)
+
+ begin
+ Tomlrb.load_file(credentials_file)
+ 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
+
+ # Load and process the active credentials.
+ #
+ # @see WorkstationConfigLoader#apply_credentials
+ # @param profile [String, nil] Optional override for the active profile,
+ # normally set via a command-line option.
+ # @return [void]
+ def load_credentials(profile = nil)
+ profile = credentials_profile(profile)
+ cred_config = parse_credentials_file
+ return if cred_config.nil? # No credentials, nothing to do here.
+
+ if cred_config[profile].nil?
+ # Unknown profile name. For "default" just silently ignore, otherwise
+ # raise an error.
+ return if profile == "default"
+
+ raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{credentials_file_path}."
+ end
+ apply_credentials(cred_config[profile], profile)
+ end
+ end
+ end
+end
diff --git a/chef-config/lib/chef-config/mixin/dot_d.rb b/chef-config/lib/chef-config/mixin/dot_d.rb
index 778c25d7f9..efe5c957f1 100644
--- a/chef-config/lib/chef-config/mixin/dot_d.rb
+++ b/chef-config/lib/chef-config/mixin/dot_d.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,22 +14,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef-config/path_helper"
+require_relative "../path_helper"
module ChefConfig
module Mixin
module DotD
+ # Find available configuration files in a `.d/` style include directory.
+ # Make sure we exclude anything that's not a file so we avoid directories ending in .rb (just in case)
+ #
+ # @api internal
+ # @param path [String] Base .d/ path to load from.
+ # @return [Array<String>]
+ def find_dot_d(path)
+ Dir["#{PathHelper.escape_glob_dir(path)}/*.rb"].select { |entry| File.file?(entry) }.sort
+ end
+
+ # Load configuration from a `.d/` style include directory.
+ #
+ # @api internal
+ # @param path [String] Base .d/ path to load from.
+ # @return [void]
def load_dot_d(path)
- dot_d_files =
- begin
- entries = Array.new
- entries << Dir.glob(File.join(
- ChefConfig::PathHelper.escape_glob_dir(path), "*.rb"))
- entries.flatten.select do |entry|
- File.file?(entry)
- end
- end
- dot_d_files.sort.map do |conf|
+ find_dot_d(path).each do |conf|
apply_config(IO.read(conf), conf)
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 6dd678840a..533db85860 100644
--- a/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
+++ b/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,17 +20,27 @@ module ChefConfig
module Mixin
module FuzzyHostnameMatcher
+ #
+ # Check to see if a hostname matches a match string. Used to see if hosts fall under our no_proxy config
+ #
+ # @param [String] hostname the hostname to check
+ # @param [String] matches the pattern to match
+ #
+ # @return [Boolean]
+ #
def fuzzy_hostname_match_any?(hostname, matches)
- return matches.to_s.split(/\s*,\s*/).compact.any? do |m|
- fuzzy_hostname_match?(hostname, m)
- end if (hostname != nil) && (matches != nil)
+ if hostname && matches
+ return matches.to_s.split(/\s*,\s*/).compact.any? do |m|
+ fuzzy_hostname_match?(hostname, m)
+ end
+ end
false
end
def fuzzy_hostname_match?(hostname, match)
# Do greedy matching by adding wildcard if it is not specified
- match = "*" + match if !match.start_with?("*")
+ match = "*" + match unless match.start_with?("*")
Fuzzyurl.matches?(Fuzzyurl.mask(hostname: match), hostname)
end
diff --git a/chef-config/lib/chef-config/mixin/train_transport.rb b/chef-config/lib/chef-config/mixin/train_transport.rb
new file mode 100644
index 0000000000..942f0ab3b7
--- /dev/null
+++ b/chef-config/lib/chef-config/mixin/train_transport.rb
@@ -0,0 +1,141 @@
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 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 "credentials"
+autoload :Train, "train"
+require_relative "../config"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+module ChefConfig
+ module Mixin
+ module TrainTransport
+ include ChefConfig::Mixin::Credentials
+
+ attr_accessor :logger
+
+ def initialize(logger)
+ @logger = logger
+ end
+
+ #
+ # Returns a RFC099 credentials profile as a hash
+ #
+ def load_credentials(profile)
+ # Tomlrb.load_file returns a hash with keys as strings
+ credentials = parse_credentials_file
+ if contains_split_fqdn?(credentials, profile)
+ logger.warn("Credentials file #{credentials_file_path} contains target '#{profile}' as a Hash, expected a string.")
+ logger.warn("Hostnames must be surrounded by single quotes, e.g. ['host.example.org']")
+ end
+
+ # host names must be specified in credentials file as ['foo.example.org'] with quotes
+ if !credentials.nil? && !credentials[profile].nil?
+ credentials[profile].transform_keys(&:to_sym) # return symbolized keys to match Train.options()
+ else
+ nil
+ end
+ end
+
+ # Toml creates hashes when a key is separated by periods, e.g.
+ # [host.example.org] => { host: { example: { org: {} } } }
+ #
+ # Returns true if the above example is true
+ #
+ # A hostname has to be specified as ['host.example.org']
+ # This will be a common mistake so we should catch it
+ #
+ def contains_split_fqdn?(hash, fqdn)
+ fqdn.split(".").reduce(hash) do |h, k|
+ v = h[k]
+ if Hash === v
+ v
+ else
+ break false
+ end
+ end
+ end
+
+ # ChefConfig::Mixin::Credentials.credentials_file_path is designed around knife,
+ # overriding it here.
+ #
+ # Credentials file preference:
+ #
+ # 1) target_mode.credentials_file
+ # 2) /etc/chef/TARGET_MODE_HOST/credentials
+ # 3) #credentials_file_path from parent ($HOME/.chef/credentials)
+ #
+ def credentials_file_path
+ tm_config = config.target_mode
+ profile = tm_config.host
+
+ credentials_file =
+ if tm_config.credentials_file && File.exist?(tm_config.credentials_file)
+ tm_config.credentials_file
+ elsif File.exist?(config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials"))
+ config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials")
+ else
+ super
+ end
+
+ raise ArgumentError, "No credentials file found for target '#{profile}'" unless credentials_file
+ raise ArgumentError, "Credentials file specified for target mode does not exist: '#{credentials_file}'" unless File.exist?(credentials_file)
+
+ logger.debug("Loading credentials file '#{credentials_file}' for target '#{profile}'")
+
+ credentials_file
+ end
+
+ def build_transport
+ return nil unless config.target_mode?
+
+ # TODO: Consider supporting parsing the protocol from a URI passed to `--target`
+ #
+ train_config = {}
+
+ # Load the target_mode config context from config, and place any valid settings into the train configuration
+ tm_config = config.target_mode
+ protocol = tm_config.protocol
+ train_config = tm_config.to_hash.select { |k| Train.options(protocol).key?(k) }
+ logger.trace("Using target mode options from #{ChefUtils::Dist::Infra::PRODUCT} config file: #{train_config.keys.join(", ")}") if train_config
+
+ # Load the credentials file, and place any valid settings into the train configuration
+ credentials = load_credentials(tm_config.host)
+ if credentials
+ valid_settings = credentials.select { |k| Train.options(protocol).key?(k) }
+ valid_settings[:enable_password] = credentials[:enable_password] if credentials.key?(:enable_password)
+ train_config.merge!(valid_settings)
+ logger.trace("Using target mode options from credentials file: #{valid_settings.keys.join(", ")}") if valid_settings
+ end
+
+ train_config[:logger] = logger
+
+ # Train handles connection retries for us
+ Train.create(protocol, train_config)
+ rescue SocketError => e # likely a dns failure, not caught by train
+ e.message.replace "Error connecting to #{train_config[:target]} - #{e.message}"
+ raise e
+ rescue Train::PluginLoadError
+ logger.error("Invalid target mode protocol: #{protocol}")
+ exit(1)
+ end
+
+ def config
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb
deleted file mode 100644
index de830c09d3..0000000000
--- a/chef-config/lib/chef-config/package_task.rb
+++ /dev/null
@@ -1,272 +0,0 @@
-#
-# Author:: Kartik Null Cating-Subramanian (<ksubramanian@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef, 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 "rake"
-require "rubygems"
-require "rubygems/package_task"
-
-module ChefConfig
- class PackageTask < Rake::TaskLib
-
- # Full path to root of top-level repository. All other files (like VERSION or
- # lib/<module_path>/version.rb are rooted at this path).
- attr_accessor :root_path
-
- # Name of the top-level module/library build built. This is used to define
- # 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
-
- # Paths to the roots of any components that also support ChefPackageTask.
- # If relative paths are provided, they are rooted against root_path.
- attr_accessor :component_paths
-
- # This is the module name as it appears on the path "lib/module/".
- # e.g. for module_name "ChefDK", you'd want module_path to be "chef-dk".
- # The default is module_name but lower-cased.
- attr_writer :module_path
-
- def module_path
- @module_path || module_name.downcase
- end
-
- # Directory used to store package files and output that is generated.
- # This has the same meaning (or lack thereof) as package_dir in
- # rake/packagetask.
- attr_accessor :package_dir
-
- # 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, 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, gem_name)
- @root_path = root_path
- @module_name = module_name
- @gem_name = gem_name
- @component_paths = []
- @module_path = nil
- @package_dir = "pkg"
- @git_remote = "origin"
- @generate_version_class = false
- end
-
- def component_full_paths
- component_paths.map { |path| File.expand_path(path, root_path) }
- end
-
- def version_rb_path
- File.expand_path("lib/#{module_path}/version.rb", root_path)
- end
-
- def chef_root_path
- module_name == "Chef" ? root_path : File.dirname(root_path)
- end
-
- def version_file_path
- 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
-
- def full_package_dir
- File.expand_path(package_dir, root_path)
- end
-
- def class_or_module
- generate_version_class ? "class" : "module"
- end
-
- def with_clean_env(&block)
- if defined?(Bundler)
- Bundler.with_clean_env(&block)
- else
- yield
- end
- end
-
- def define
- raise "Need to provide package root and module name" if root_path.nil? || module_name.nil?
-
- desc "Build Gems of component dependencies"
- task :package_components do
- component_full_paths.each do |component_path|
- Dir.chdir(component_path) do
- sh "rake package"
- end
- end
- end
-
- task :package => :package_components
-
- desc "Build and install component dependencies"
- task :install_components => :package_components do
- component_full_paths.each do |component_path|
- Dir.chdir(component_path) do
- sh "rake install"
- end
- end
- end
-
- task :install => :install_components
-
- desc "Clean up builds of component dependencies"
- task :clobber_component_packages do
- component_full_paths.each do |component_path|
- Dir.chdir(component_path) do
- sh "rake clobber_package"
- end
- end
- end
-
- task :clobber_package => :clobber_component_packages
-
- desc "Update the version number for component dependencies"
- task :update_components_versions do
- component_full_paths.each do |component_path|
- Dir.chdir(component_path) do
- sh "rake version"
- end
- end
- end
-
- namespace :version do
- 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
-
- 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
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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 is generated by running `rake version` in the top level of
-# this repo. Do not edit this manually. Edit the VERSION file and run the rake
-# task instead.
-#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-#{class_or_module} #{module_name}
- #{module_name.upcase}_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "#{version}"
-end
-
-#
-# NOTE: the Chef::Version class is defined in version_class.rb
-#
-# NOTE: DO NOT Use the Chef::Version class on #{module_name}::VERSIONs. The
-# Chef::Version class is for _cookbooks_ only, and cannot handle
-# pre-release versions like "10.14.0.rc.2". Please use Rubygem's
-# Gem::Version class instead.
-#
- VERSION_RB
- IO.write(version_rb_path, contents)
- end
-
- 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
-
- task :version => "version:update"
-
- gemspec_platform_to_install = ""
- Dir[File.expand_path("*.gemspec", root_path)].reverse_each do |gemspec_path|
- gemspec = eval(IO.read(gemspec_path))
- Gem::PackageTask.new(gemspec) do |task|
- task.package_dir = full_package_dir
- end
- gemspec_platform_to_install = "-#{gemspec.platform}" if gemspec.platform != Gem::Platform::RUBY && Gem::Platform.match(gemspec.platform)
- end
-
- desc "Build and install a #{module_path} gem"
- task :install => [:package] do
- with_clean_env do
- full_module_path = File.join(full_package_dir, module_path)
- sh %{gem install #{full_module_path}-#{version}#{gemspec_platform_to_install}.gem --no-rdoc --no-ri}
- end
- end
-
- task :uninstall do
- sh %{gem uninstall #{module_path} -x -v #{version} }
- end
-
- desc "Build it, tag it and ship it"
- task :ship => [:clobber_package, :gem] do
- sh("git tag #{version}")
- sh("git push #{git_remote} --tags")
- Dir[File.expand_path("*.gem", full_package_dir)].reverse_each do |built_gem|
- sh("gem push #{built_gem}")
- end
- end
- 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..8fe45febfc 100644
--- a/chef-config/lib/chef-config/path_helper.rb
+++ b/chef-config/lib/chef-config/path_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,24 @@
# limitations under the License.
#
-require "chef-config/windows"
-require "chef-config/logger"
-require "chef-config/exceptions"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require_relative "windows"
+require_relative "logger"
+require_relative "exceptions"
module ChefConfig
class PathHelper
# Maximum characters in a standard Windows path (260 including drive letter and NUL)
WIN_MAX_PATH = 259
- def self.dirname(path)
- if ChefConfig.windows?
+ def self.dirname(path, windows: ChefUtils.windows?)
+ if windows
# Find the first slash, not counting trailing slashes
end_slash = path.size
loop do
- slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1)
+ slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator(windows: windows))}]/, end_slash - 1)
if !slash
- return end_slash == path.size ? "." : path_separator
+ return end_slash == path.size ? "." : path_separator(windows: windows)
elsif slash == end_slash - 1
end_slash = slash
else
@@ -46,32 +47,28 @@ module ChefConfig
BACKSLASH = '\\'.freeze
- def self.path_separator
- if ChefConfig.windows?
- File::ALT_SEPARATOR || BACKSLASH
+ def self.path_separator(windows: ChefUtils.windows?)
+ if windows
+ BACKSLASH
else
File::SEPARATOR
end
end
- def self.join(*args)
- path_separator_regex = Regexp.escape(File::SEPARATOR)
- unless path_separator == File::SEPARATOR
- path_separator_regex << Regexp.escape(path_separator)
- end
-
- trailing_slashes = /[#{path_separator_regex}]+$/
- leading_slashes = /^[#{path_separator_regex}]+/
+ def self.join(*args, windows: ChefUtils.windows?)
+ path_separator_regex = Regexp.escape(windows ? "#{File::SEPARATOR}#{BACKSLASH}" : File::SEPARATOR)
+ trailing_slashes_regex = /[#{path_separator_regex}]+$/.freeze
+ leading_slashes_regex = /^[#{path_separator_regex}]+/.freeze
- args.flatten.inject() do |joined_path, component|
- joined_path = joined_path.sub(trailing_slashes, "")
- component = component.sub(leading_slashes, "")
- joined_path + "#{path_separator}#{component}"
+ args.flatten.inject do |joined_path, component|
+ joined_path = joined_path.sub(trailing_slashes_regex, "")
+ component = component.sub(leading_slashes_regex, "")
+ joined_path + "#{path_separator(windows: windows)}#{component}"
end
end
- def self.validate_path(path)
- if ChefConfig.windows?
+ def self.validate_path(path, windows: ChefUtils.windows?)
+ if windows
unless printable?(path)
msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings."
ChefConfig.logger.error(msg)
@@ -79,7 +76,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
@@ -90,7 +87,7 @@ module ChefConfig
def self.windows_max_length_exceeded?(path)
# Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
- unless path =~ /^\\\\?\\/
+ unless /^\\\\?\\/.match?(path)
if path.length > WIN_MAX_PATH
return true
end
@@ -102,7 +99,7 @@ module ChefConfig
def self.printable?(string)
# returns true if string is free of non-printable characters (escape sequences)
# this returns false for whitespace escape sequences as well, e.g. \n\t
- if string =~ /[^[:print:]]/
+ if /[^[:print:]]/.match?(string)
false
else
true
@@ -110,14 +107,14 @@ module ChefConfig
end
# Produces a comparable path.
- def self.canonical_path(path, add_prefix = true)
+ def self.canonical_path(path, add_prefix = true, windows: ChefUtils.windows?)
# First remove extra separators and resolve any relative paths
abs_path = File.absolute_path(path)
- if ChefConfig.windows?
+ if windows
# Add the \\?\ API prefix on Windows unless add_prefix is false
# Downcase on Windows where paths are still case-insensitive
- abs_path.gsub!(::File::SEPARATOR, path_separator)
+ abs_path.gsub!(::File::SEPARATOR, path_separator(windows: windows))
if add_prefix && abs_path !~ /^\\\\?\\/
abs_path.insert(0, "\\\\?\\")
end
@@ -128,36 +125,67 @@ module ChefConfig
abs_path
end
- # This is the INVERSE of Pathname#cleanpath, it converts forward
- # slashes to backwhacks for Windows. Since the Ruby API and the
- # Windows APIs all consume forward slashes, this helper function
- # should only be used for *DISPLAY* logic to send strings back
- # to the user with backwhacks. Internally, filename paths should
- # generally be stored with forward slashes for consistency. It is
- # not necessary or desired to blindly convert pathnames to have
- # backwhacks on Windows.
+ # The built in ruby Pathname#cleanpath method does not clean up forward slashes and
+ # backslashes. This is a wrapper around that which does. In general this is NOT
+ # recommended for internal use within ruby/chef since ruby does not care about forward slashes
+ # vs. backslashes, even on Windows. Where this generally matters is when being rendered
+ # to the user, or being rendered into things like the windows PATH or to commands that
+ # are being executed. In some cases it may be easier on windows to render paths to
+ # unix-style for being eventually eval'd by ruby in the future (templates being rendered
+ # with code to be consumed by ruby) where forcing unix-style forward slashes avoids the
+ # issue of needing to escape the backslashes in rendered strings. This has a boolean
+ # operator to force windows-style or non-windows style operation, where the default is
+ # determined by the underlying node['platform'] value.
+ #
+ # In general if you don't know if you need this routine, do not use it, best practice
+ # within chef/ruby itself is not to care. Only use it to force windows or unix style
+ # when it really matters.
+ #
+ # @param path [String] the path to clean
+ # @param windows [Boolean] optional flag to force to windows or unix-style
+ # @return [String] cleaned path
#
- # Generally, if the user isn't going to be seeing it, you should be
- # using Pathname#cleanpath intead of this function.
- def self.cleanpath(path)
+ def self.cleanpath(path, windows: ChefUtils.windows?)
path = Pathname.new(path).cleanpath.to_s
- # ensure all forward slashes are backslashes
- if ChefConfig.windows?
- path = path.gsub(File::SEPARATOR, path_separator)
+ if windows
+ # ensure all forward slashes are backslashes
+ path.gsub(File::SEPARATOR, path_separator(windows: windows))
+ else
+ # ensure all backslashes are forward slashes
+ path.gsub(BACKSLASH, File::SEPARATOR)
end
- path
end
- def self.paths_eql?(path1, path2)
- canonical_path(path1) == canonical_path(path2)
+ # This is not just escaping for something like use in Regexps, or in globs. For the former
+ # just use Regexp.escape. For the latter, use escape_glob_dir below.
+ #
+ # This is escaping where the path to be rendered is being put into a ruby file which will
+ # later be read back by ruby (or something similar) so we need quadruple backslashes.
+ #
+ # In order to print:
+ #
+ # file_cache_path "C:\\chef"
+ #
+ # We need to convert "C:\chef" to "C:\\\\chef" to interpolate into a string which is rendered
+ # into the output file with that line in it.
+ #
+ # @param path [String] the path to escape
+ # @return [String] the escaped path
+ #
+ def self.escapepath(path)
+ path.gsub(BACKSLASH, BACKSLASH * 4)
+ end
+
+ def self.paths_eql?(path1, path2, windows: ChefUtils.windows?)
+ canonical_path(path1, windows: windows) == canonical_path(path2, windows: windows)
end
- # Note: this method is deprecated. Please use escape_glob_dirs
+ # @deprecated this method is deprecated. Please use escape_glob_dirs
# Paths which may contain glob-reserved characters need
# to be escaped before globbing can be done.
# http://stackoverflow.com/questions/14127343
- def self.escape_glob(*parts)
- path = cleanpath(join(*parts))
+ def self.escape_glob(*parts, windows: ChefUtils.windows?)
+ path = cleanpath(join(*parts, windows: windows), windows: windows)
path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\" + x }
end
@@ -168,8 +196,20 @@ module ChefConfig
path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\" + x }
end
- def self.relative_path_from(from, to)
- Pathname.new(cleanpath(to)).relative_path_from(Pathname.new(cleanpath(from)))
+ def self.relative_path_from(from, to, windows: ChefUtils.windows?)
+ Pathname.new(cleanpath(to, windows: windows)).relative_path_from(Pathname.new(cleanpath(from, windows: windows)))
+ end
+
+ # Set the project-specific home directory environment variable.
+ #
+ # This can be used to allow per-tool home directory aliases like $KNIFE_HOME.
+ #
+ # @param [env_var] Key for an environment variable to use.
+ # @return [nil]
+ def self.per_tool_home_environment=(env_var)
+ @@per_tool_home_environment = env_var
+ # Reset this in case .home was already called.
+ @@home_dir = nil
end
# Retrieves the "home directory" of the current user while trying to ascertain the existence
@@ -185,9 +225,11 @@ module ChefConfig
# Home-path discovery is performed once. If a path is discovered, that value is memoized so
# that subsequent calls to home_dir don't bounce around.
#
- # See self.all_homes.
+ # @see all_homes
+ # @param args [Array<String>] Path components to look for under the home directory.
+ # @return [String]
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
@@ -201,9 +243,11 @@ module ChefConfig
#
# The return is a list of all the returned values from each block invocation or a list of paths
# if no block is provided.
- def self.all_homes(*args)
+ def self.all_homes(*args, windows: ChefUtils.windows?)
paths = []
- if ChefConfig.windows?
+ paths << ENV[@@per_tool_home_environment] if defined?(@@per_tool_home_environment) && @@per_tool_home_environment && ENV[@@per_tool_home_environment]
+ paths << ENV["CHEF_HOME"] if ENV["CHEF_HOME"]
+ if windows
# By default, Ruby uses the the following environment variables to determine Dir.home:
# HOME
# HOMEDRIVE HOMEPATH
@@ -232,10 +276,10 @@ module ChefConfig
# Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
# the particular brand of kool-aid you consume. This code assumes that \ and / are both
# path separators on any system being used.
- paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
+ paths = paths.map { |home_path| home_path.gsub(path_separator(windows: windows), ::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.
@@ -248,15 +292,15 @@ module ChefConfig
end
end
- # Determine if the given path is protected by OS X System Integrity Protection.
+ # Determine if the given path is protected by macOS System Integrity Protection.
def self.is_sip_path?(path, node)
- if node["platform"] == "mac_os_x" && Gem::Version.new(node["platform_version"]) >= Gem::Version.new("10.11")
- # todo: parse rootless.conf for this?
+ if ChefUtils.macos?
+ # @todo: parse rootless.conf for this?
sip_paths = [
"/System", "/bin", "/sbin", "/usr"
]
sip_paths.each do |sip_path|
- ChefConfig.logger.info("This is a SIP path, checking if it in exceptions list.")
+ ChefConfig.logger.info("#{sip_path} is a SIP path, checking if it is in the exceptions list.")
return true if path.start_with?(sip_path)
end
false
@@ -265,7 +309,7 @@ module ChefConfig
end
end
- # Determine if the given path is on the exception list for OS X System Integrity Protection.
+ # Determine if the given path is on the exception list for macOS System Integrity Protection.
def self.writable_sip_path?(path)
# todo: parse rootless.conf for this?
sip_exceptions = [
@@ -276,8 +320,31 @@ module ChefConfig
sip_exceptions.each do |exception_path|
return true if path.start_with?(exception_path)
end
- ChefConfig.logger.error("Cannot write to a SIP Path on OS X 10.11+")
+ ChefConfig.logger.error("Cannot write to a SIP path #{path} on macOS!")
false
end
+
+ # Splits a string into an array of tokens as commands and arguments
+ #
+ # str = 'command with "some arguments"'
+ # split_args(str) => ["command", "with", "\"some arguments\""]
+ #
+ def self.split_args(line)
+ cmd_args = []
+ field = ""
+ line.scan(/\s*(?>([^\s\\"]+|"([^"]*)"|'([^']*)')|(\S))(\s|\z)?/m) do |word, within_dq, within_sq, esc, sep|
+
+ # Append the string with Word & Escape Character
+ field << (word || esc.gsub(/\\(.)/, '\\1'))
+
+ # Re-build the field when any whitespace character or
+ # End of string is encountered
+ if sep
+ cmd_args << field
+ field = ""
+ end
+ end
+ cmd_args
+ end
end
end
diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb
index 77a000165a..ea7071ed03 100644
--- a/chef-config/lib/chef-config/version.rb
+++ b/chef-config/lib/chef-config/version.rb
@@ -1,4 +1,4 @@
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,22 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-# NOTE: This file is generated by running `rake version` in the top level of
-# this repo. Do not edit this manually. Edit the VERSION file and run the rake
-# task instead.
-#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
module ChefConfig
- CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "12.14.75"
+ CHEFCONFIG_ROOT = File.expand_path("..", __dir__)
+ VERSION = "17.0.32".freeze
end
-
-#
-# NOTE: the Chef::Version class is defined in version_class.rb
-#
-# NOTE: DO NOT Use the Chef::Version class on ChefConfig::VERSIONs. The
-# Chef::Version class is for _cookbooks_ only, and cannot handle
-# pre-release versions like "10.14.0.rc.2". Please use Rubygem's
-# Gem::Version class instead.
-#
diff --git a/chef-config/lib/chef-config/windows.rb b/chef-config/lib/chef-config/windows.rb
index 9c606110a4..ad3b825661 100644
--- a/chef-config/lib/chef-config/windows.rb
+++ b/chef-config/lib/chef-config/windows.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,10 @@
# limitations under the License.
#
-module ChefConfig
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+module ChefConfig
def self.windows?
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
- true
- else
- false
- end
+ ChefUtils.windows?
end
-
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..ea42211120 100644
--- a/chef-config/lib/chef-config/workstation_config_loader.rb
+++ b/chef-config/lib/chef-config/workstation_config_loader.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,30 +16,38 @@
# limitations under the License.
#
-require "chef-config/config"
-require "chef-config/exceptions"
-require "chef-config/logger"
-require "chef-config/path_helper"
-require "chef-config/windows"
-require "chef-config/mixin/dot_d"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require_relative "config"
+require_relative "exceptions"
+require_relative "logger"
+require_relative "path_helper"
+require_relative "windows"
+require_relative "mixin/dot_d"
+require_relative "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
@@ -51,7 +59,7 @@ module ChefConfig
@chef_config_dir = false
full_path = working_directory.split(File::SEPARATOR)
(full_path.length - 1).downto(0) do |i|
- candidate_directory = File.join(full_path[0..i] + [".chef"])
+ candidate_directory = File.join(full_path[0..i] + [ChefUtils::Dist::Infra::USER_CONF_DIR])
if File.exist?(candidate_directory) && File.directory?(candidate_directory)
@chef_config_dir = candidate_directory
break
@@ -62,9 +70,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
@@ -75,6 +84,8 @@ module ChefConfig
end
load_dot_d(Config[:config_d_dir]) if Config[:config_d_dir]
+
+ apply_defaults
end
# (Private API, public for test purposes)
@@ -118,7 +129,7 @@ module ChefConfig
candidate_configs << File.join(chef_config_dir, "knife.rb")
end
# Look for $HOME/.chef/knife.rb
- PathHelper.home(".chef") do |dot_chef_dir|
+ PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR) do |dot_chef_dir|
candidate_configs << File.join(dot_chef_dir, "config.rb")
candidate_configs << File.join(dot_chef_dir, "knife.rb")
end
@@ -129,13 +140,51 @@ module ChefConfig
end
def working_directory
- a = if ChefConfig.windows?
- env["CD"]
- else
- env["PWD"]
- end || Dir.pwd
+ if ChefUtils.windows?
+ env["CD"]
+ else
+ env["PWD"]
+ end || Dir.pwd
+ end
- a
+ def apply_credentials(creds, profile)
+ # Store the profile used in case other things want it.
+ Config.profile ||= profile
+ # Validate the credentials data.
+ 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
+
+ # Load credentials data into the Chef configuration.
+ creds.each do |key, value|
+ case key.to_s
+ when "client_name"
+ # Special case because it's weird to set your username via `node_name`.
+ Config.node_name = value
+ when "validation_key", "validator_key"
+ extract_key(value, :validation_key, :validation_key_contents)
+ when "client_key"
+ extract_key(value, :client_key, :client_key_contents)
+ when "knife"
+ Config.knife.merge!(value.transform_keys(&:to_sym))
+ else
+ Config[key.to_sym] = value
+ end
+ end
+ @credentials_found = true
+ end
+
+ def extract_key(key_value, config_path, config_contents)
+ if key_value.start_with?("-----BEGIN RSA PRIVATE KEY-----")
+ Config.send(config_contents, key_value)
+ else
+ abs_path = Pathname.new(key_value).expand_path(home_chef_dir)
+ Config.send(config_path, abs_path)
+ end
+ end
+
+ def home_chef_dir
+ @home_chef_dir ||= PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR)
end
def apply_config(config_content, config_file_path)
@@ -146,8 +195,8 @@ module ChefConfig
message = ""
message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n"
message << "#{e.class.name}: #{e.message}\n"
- if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/]
- line = file_line[/:([\d]+)$/, 1].to_i
+ if file_line = e.message[/#{Regexp.escape(config_file_path)}:\d+/]
+ line = file_line[/:(\d+)$/, 1].to_i
message << highlight_config_error(config_file_path, line)
end
raise ChefConfig::ConfigurationError, message
@@ -156,13 +205,63 @@ 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?
- line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/, 1]
+ 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
raise ChefConfig::ConfigurationError, message
end
+ # Apply default configuration values for workstation-style tools.
+ #
+ # Global defaults should go in {ChefConfig::Config} instead, this is only
+ # for things like `knife` and `chef`.
+ #
+ # @api private
+ # @since 14.3
+ # @return [void]
+ def apply_defaults
+ # If we don't have a better guess use the username.
+ Config[:node_name] ||= Etc.getlogin
+ # If we don't have a key (path or inline) check user.pem and $node_name.pem.
+ unless Config.key?(:client_key) || Config.key?(:client_key_contents)
+ key_path = find_default_key(["#{Config[:node_name]}.pem", "user.pem"])
+ Config[:client_key] = key_path if key_path
+ end
+ # Similarly look for a validation key file, though this should be less
+ # common these days.
+ unless Config.key?(:validation_key) || Config.key?(:validation_key_contents)
+ key_path = find_default_key(["#{Config[:validation_client_name]}.pem", "validator.pem", "validation.pem"])
+ Config[:validation_key] = key_path if key_path
+ end
+ end
+
+ # Look for a default key file.
+ #
+ # This searches for any of a list of possible default keys, checking both
+ # the local `.chef/` folder and the home directory `~/.chef/`. Returns `nil`
+ # if no matching file is found.
+ #
+ # @api private
+ # @since 14.3
+ # @param key_names [Array<String>] A list of possible filenames to check for.
+ # The first one found will be returned.
+ # @return [String, nil]
+ def find_default_key(key_names)
+ key_names.each do |filename|
+ path = Pathname.new(filename)
+ # If we have a config location (like ./.chef/), look there first.
+ if config_location
+ local_path = path.expand_path(File.dirname(config_location))
+ return local_path.to_s if local_path.exist?
+ end
+ # Then check ~/.chef.
+ home_path = path.expand_path(home_chef_dir)
+ return home_path.to_s if home_path.exist?
+ end
+ nil
+ end
+
def highlight_config_error(file, line)
config_file_lines = []
IO.readlines(file).each_with_index { |l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}" }
diff --git a/chef-config/spec/spec_helper.rb b/chef-config/spec/spec_helper.rb
index 107becb9eb..557f1f6432 100644
--- a/chef-config/spec/spec_helper.rb
+++ b/chef-config/spec/spec_helper.rb
@@ -1,4 +1,4 @@
-require "chef-config/windows"
+require "chef-utils"
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
@@ -32,8 +32,8 @@ RSpec.configure do |config|
config.filter_run :focus
config.run_all_when_everything_filtered = true
- config.filter_run_excluding :windows_only => true unless ChefConfig.windows?
- config.filter_run_excluding :unix_only => true if ChefConfig.windows?
+ config.filter_run_excluding windows_only: true unless ChefUtils.windows?
+ config.filter_run_excluding unix_only: true if ChefUtils.windows?
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index 0ddb56cf0d..658acca615 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,30 +68,147 @@ 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
+
+ describe "expand relative paths" do
+ let(:current_directory) { Dir.pwd }
+
+ context "when given cookbook_path" do
+ let(:extra_config_options) { [ "cookbook_path=cookbooks/" ] }
+
+ it "expanded cookbook_path" do
+ apply_config
+ expect(described_class[:cookbook_path]).to eq("#{current_directory}/cookbooks")
+ end
+ end
+
+ context "when passes multiple config options" do
+ let(:extra_config_options) { ["data_bag_path=data_bags/", "cookbook_path=cookbooks", "chef_repo_path=."] }
+
+ it "expanded paths" do
+ apply_config
+ expect(described_class[:data_bag_path]).to eq("#{current_directory}/data_bags")
+ expect(described_class[:cookbook_path]).to eq("#{current_directory}/cookbooks")
+ expect(described_class[:chef_repo_path]).to eq(current_directory)
+ end
+ end
+
+ context "when passes multiple cookbook_paths in config options" do
+ let(:extra_config_options) { ["cookbook_path=[first_cookbook, second_cookbooks]"] }
+
+ it "expanded paths" do
+ apply_config
+ expect(described_class[:cookbook_path]).to eq(["#{current_directory}/first_cookbook", "#{current_directory}/second_cookbooks"])
+ end
+ end
+ end
+ end
+
describe "when configuring formatters" do
- # if TTY and not(force-logger)
- # formatter = configured formatter or default formatter
- # formatter goes to STDOUT/ERR
- # if log file is writeable
- # log level is configured level or info
- # log location is file
- # else
- # log level is warn
- # log location is STDERR
- # end
- # elsif not(TTY) and force formatter
- # formatter = configured formatter or default formatter
- # if log_location specified
- # formatter goes to log_location
- # else
- # formatter goes to STDOUT/ERR
- # end
- # else
- # formatter = "null"
- # log_location = configured-value or defualt
- # log_level = info or defualt
- # end
- #
+ # if TTY and not(force-logger)
+ # formatter = configured formatter or default formatter
+ # formatter goes to STDOUT/ERR
+ # if log file is writeable
+ # log level is configured level or info
+ # log location is file
+ # else
+ # log level is warn
+ # log location is STDERR
+ # end
+ # elsif not(TTY) and force formatter
+ # formatter = configured formatter or default formatter
+ # if log_location specified
+ # formatter goes to log_location
+ # else
+ # formatter goes to STDOUT/ERR
+ # end
+ # else
+ # formatter = "null"
+ # log_location = configured-value or default
+ # log_level = info or default
+ # end
+ #
it "has an empty list of formatters by default" do
expect(ChefConfig::Config.formatters).to eq([])
end
@@ -105,29 +222,108 @@ RSpec.describe ChefConfig::Config do
ChefConfig::Config.add_formatter(:doc, "/var/log/formatter.log")
expect(ChefConfig::Config.formatters).to eq([[:doc, "/var/log/formatter.log"]])
end
-
end
- [ false, true ].each do |is_windows|
+ describe "#var_chef_path" do
+ let(:dirname) { ChefUtils::Dist::Infra::DIR_SUFFIX }
- context "On #{is_windows ? 'Windows' : 'Unix'}" do
- def to_platform(*args)
- ChefConfig::Config.platform_specific_path(*args)
+ context "on unix", :unix_only do
+ it "var_chef_dir is /var/chef" do
+ expect(ChefConfig::Config.var_chef_dir).to eql("/var/#{dirname}")
end
- before :each do
- allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ it "var_root_dir is /var" do
+ expect(ChefConfig::Config.var_root_dir).to eql("/var")
+ end
+
+ it "etc_chef_dir is /etc/chef" do
+ expect(ChefConfig::Config.etc_chef_dir).to eql("/etc/#{dirname}")
+ end
+ end
+
+ context "on windows", :windows_only do
+ it "var_chef_dir is C:\\chef" do
+ expect(ChefConfig::Config.var_chef_dir).to eql("C:\\#{dirname}")
+ end
+
+ it "var_root_dir is C:\\" do
+ expect(ChefConfig::Config.var_root_dir).to eql("C:\\")
+ end
+
+ it "etc_chef_dir is C:\\chef" do
+ expect(ChefConfig::Config.etc_chef_dir).to eql("C:\\#{dirname}")
+ end
+ end
+
+ context "when forced to unix" do
+ it "var_chef_dir is /var/chef" do
+ expect(ChefConfig::Config.var_chef_dir(windows: false)).to eql("/var/#{dirname}")
+ end
+
+ it "var_root_dir is /var" do
+ expect(ChefConfig::Config.var_root_dir(windows: false)).to eql("/var")
+ end
+
+ it "etc_chef_dir is /etc/chef" do
+ expect(ChefConfig::Config.etc_chef_dir(windows: false)).to eql("/etc/#{dirname}")
+ end
+ end
+
+ context "when forced to windows" do
+ it "var_chef_dir is C:\\chef" do
+ expect(ChefConfig::Config.var_chef_dir(windows: true)).to eql("C:\\#{dirname}")
+ end
+
+ it "var_root_dir is C:\\" do
+ expect(ChefConfig::Config.var_root_dir(windows: true)).to eql("C:\\")
+ end
+
+ it "etc_chef_dir is C:\\chef" do
+ expect(ChefConfig::Config.etc_chef_dir(windows: true)).to eql("C:\\#{dirname}")
end
+ end
+ end
+ [ false, true ].each do |is_windows|
+ context "On #{is_windows ? "Windows" : "Unix"}" do
+ before :each do
+ allow(ChefUtils).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
@@ -138,9 +334,10 @@ RSpec.describe ChefConfig::Config do
end
describe "default values" do
+ let(:system_drive) { ChefConfig::Config.env["SYSTEMDRIVE"] } if is_windows
let :primary_cache_path do
if is_windows
- "#{ChefConfig::Config.env['SYSTEMDRIVE']}\\chef"
+ "#{system_drive}\\chef"
else
"/var/chef"
end
@@ -165,6 +362,35 @@ RSpec.describe ChefConfig::Config do
allow(ChefConfig::Config).to receive(:path_accessible?).and_return(false)
end
+ describe "ChefConfig::Config[:client_key]" do
+ let(:path_to_client_key) { ChefConfig::Config.etc_chef_dir + ChefConfig::PathHelper.path_separator }
+
+ it "sets the default path to the client key" do
+ expect(ChefConfig::Config.client_key).to eq(path_to_client_key + "client.pem")
+ end
+
+ context "when target mode is enabled" do
+ let(:target_mode_host) { "fluffy.kittens.org" }
+
+ before do
+ ChefConfig::Config.target_mode.enabled = true
+ ChefConfig::Config.target_mode.host = target_mode_host
+ end
+
+ it "sets the default path to the client key with the target host name" do
+ expect(ChefConfig::Config.client_key).to eq(path_to_client_key + target_mode_host + ChefConfig::PathHelper.path_separator + "client.pem")
+ end
+ end
+
+ context "when local mode is enabled" do
+ before { ChefConfig::Config[:local_mode] = true }
+
+ it "returns nil" do
+ expect(ChefConfig::Config.client_key).to be_nil
+ end
+ end
+ end
+
describe "ChefConfig::Config[:fips]" do
let(:fips_enabled) { false }
@@ -260,37 +486,68 @@ RSpec.describe ChefConfig::Config do
end
describe "ChefConfig::Config[:cache_path]" do
+ let(:target_mode_host) { "fluffy.kittens.org" }
+ let(:target_mode_primary_cache_path) { ChefUtils.windows? ? "#{primary_cache_path}\\#{target_mode_host}" : "#{primary_cache_path}/#{target_mode_host}" }
+ let(:target_mode_secondary_cache_path) { ChefUtils.windows? ? "#{secondary_cache_path}\\#{target_mode_host}" : "#{secondary_cache_path}/#{target_mode_host}" }
+
+ before do
+ if is_windows
+ allow(File).to receive(:expand_path).and_return("#{system_drive}/Path/To/Executable")
+ end
+ end
+
context "when /var/chef exists and is accessible" do
+ before do
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(ChefConfig::Config.var_chef_dir).and_return(true)
+ end
+
it "defaults to /var/chef" do
- allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true)
expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
end
+
+ context "and target mode is enabled" do
+ it "cache path includes the target host name" do
+ ChefConfig::Config.target_mode.enabled = true
+ ChefConfig::Config.target_mode.host = target_mode_host
+ expect(ChefConfig::Config[:cache_path]).to eq(target_mode_primary_cache_path)
+ end
+ end
end
context "when /var/chef does not exist and /var is accessible" do
it "defaults to /var/chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
- allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(true)
+ allow(File).to receive(:exists?).with(ChefConfig::Config.var_chef_dir).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(ChefConfig::Config.var_root_dir).and_return(true)
expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
end
end
context "when /var/chef does not exist and /var is not accessible" do
it "defaults to $HOME/.chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
- allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(false)
+ allow(File).to receive(:exists?).with(ChefConfig::Config.var_chef_dir).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(ChefConfig::Config.var_root_dir).and_return(false)
expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
end
end
context "when /var/chef exists and is not accessible" do
- it "defaults to $HOME/.chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(true)
- allow(File).to receive(:readable?).with(to_platform("/var/chef")).and_return(true)
- allow(File).to receive(:writable?).with(to_platform("/var/chef")).and_return(false)
+ before do
+ allow(File).to receive(:exists?).with(ChefConfig::Config.var_chef_dir).and_return(true)
+ allow(File).to receive(:readable?).with(ChefConfig::Config.var_chef_dir).and_return(true)
+ allow(File).to receive(:writable?).with(ChefConfig::Config.var_chef_dir).and_return(false)
+ end
+ it "defaults to $HOME/.chef" do
expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
end
+
+ context "and target mode is enabled" do
+ it "cache path defaults to $HOME/.chef with the target host name" do
+ ChefConfig::Config.target_mode.enabled = true
+ ChefConfig::Config.target_mode.host = target_mode_host
+ expect(ChefConfig::Config[:cache_path]).to eq(target_mode_secondary_cache_path)
+ end
+ end
end
context "when chef is running in local mode" do
@@ -300,21 +557,21 @@ RSpec.describe ChefConfig::Config do
context "and config_dir is /a/b/c" do
before do
- ChefConfig::Config.config_dir to_platform("/a/b/c")
+ ChefConfig::Config.config_dir ChefConfig::PathHelper.cleanpath("/a/b/c")
end
it "cache_path is /a/b/c/local-mode-cache" do
- expect(ChefConfig::Config.cache_path).to eq(to_platform("/a/b/c/local-mode-cache"))
+ expect(ChefConfig::Config.cache_path).to eq(ChefConfig::PathHelper.cleanpath("/a/b/c/local-mode-cache"))
end
end
context "and config_dir is /a/b/c/" do
before do
- ChefConfig::Config.config_dir to_platform("/a/b/c/")
+ ChefConfig::Config.config_dir ChefConfig::PathHelper.cleanpath("/a/b/c/")
end
it "cache_path is /a/b/c/local-mode-cache" do
- expect(ChefConfig::Config.cache_path).to eq(to_platform("/a/b/c/local-mode-cache"))
+ expect(ChefConfig::Config.cache_path).to eq(ChefConfig::PathHelper.cleanpath("/a/b/c/local-mode-cache"))
end
end
end
@@ -447,7 +704,7 @@ RSpec.describe ChefConfig::Config do
# On Windows, we'll detect an omnibus build and set this to the
# cacert.pem included in the package, but it's nil if you're on Windows
# w/o omnibus (e.g., doing development on Windows, custom build, etc.)
- if !is_windows
+ unless is_windows
it "ChefConfig::Config[:ssl_ca_file] defaults to nil" do
expect(ChefConfig::Config[:ssl_ca_file]).to be_nil
end
@@ -480,15 +737,15 @@ RSpec.describe ChefConfig::Config do
end
it "expands the path when determining config_dir" do
- # config_dir goes through PathHelper.canonical_path, which
+ # config_dir goes through ChefConfig::PathHelper.canonical_path, which
# downcases on windows because the FS is case insensitive, so we
# have to downcase expected and actual to make the tests work.
- expect(ChefConfig::Config.config_dir.downcase).to eq(to_platform(Dir.pwd).downcase)
+ expect(ChefConfig::Config.config_dir.downcase).to eq(ChefConfig::PathHelper.cleanpath(Dir.pwd).downcase)
end
it "does not set derived paths at FS root" do
ChefConfig::Config.local_mode = true
- expect(ChefConfig::Config.cache_path.downcase).to eq(to_platform(File.join(Dir.pwd, "local-mode-cache")).downcase)
+ expect(ChefConfig::Config.cache_path.downcase).to eq(ChefConfig::PathHelper.cleanpath(File.join(Dir.pwd, "local-mode-cache")).downcase)
end
end
@@ -496,13 +753,13 @@ RSpec.describe ChefConfig::Config do
context "when the config file is /etc/chef/client.rb" do
before do
- config_location = to_platform("/etc/chef/client.rb").downcase
+ config_location = ChefConfig::PathHelper.cleanpath(ChefConfig::PathHelper.join(ChefConfig::Config.etc_chef_dir, "client.rb")).downcase
allow(File).to receive(:absolute_path).with(config_location).and_return(config_location)
ChefConfig::Config.config_file = config_location
end
it "config_dir is /etc/chef" do
- expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef").downcase)
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::Config.etc_chef_dir.downcase)
end
context "and chef is running in local mode" do
@@ -511,17 +768,17 @@ RSpec.describe ChefConfig::Config do
end
it "config_dir is /etc/chef" do
- expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef").downcase)
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::Config.etc_chef_dir.downcase)
end
end
context "when config_dir is set to /other/config/dir/" do
before do
- ChefConfig::Config.config_dir = to_platform("/other/config/dir/")
+ ChefConfig::Config.config_dir = ChefConfig::PathHelper.cleanpath("/other/config/dir/")
end
it "yields the explicit value" do
- expect(ChefConfig::Config.config_dir).to eq(to_platform("/other/config/dir/"))
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.cleanpath("/other/config/dir/"))
end
end
@@ -529,11 +786,11 @@ RSpec.describe ChefConfig::Config do
context "when the user's home dir is /home/charlie/" do
before do
- ChefConfig::Config.user_home = to_platform("/home/charlie")
+ ChefConfig::Config.user_home = "/home/charlie/"
end
it "config_dir is /home/charlie/.chef/" do
- expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ""))
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(ChefConfig::PathHelper.cleanpath("/home/charlie/"), ".chef", ""))
end
context "and chef is running in local mode" do
@@ -542,11 +799,34 @@ RSpec.describe ChefConfig::Config do
end
it "config_dir is /home/charlie/.chef/" do
- expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ""))
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(ChefConfig::PathHelper.cleanpath("/home/charlie/"), ".chef", ""))
end
end
end
+ if is_windows
+ context "when the user's home dir is windows specific" do
+ before do
+ ChefConfig::Config.user_home = ChefConfig::PathHelper.cleanpath("/home/charlie/")
+ end
+
+ it "config_dir is with backslashes" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(ChefConfig::PathHelper.cleanpath("/home/charlie/"), ".chef", ""))
+ end
+
+ context "and chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ it "config_dir is with backslashes" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(ChefConfig::PathHelper.cleanpath("/home/charlie/"), ".chef", ""))
+ end
+ end
+ end
+
+ end
+
end
if is_windows
@@ -583,7 +863,7 @@ RSpec.describe ChefConfig::Config do
describe "ChefConfig::Config[:user_home]" do
it "should set when HOME is provided" do
- expected = to_platform("/home/kitten")
+ expected = ChefConfig::PathHelper.cleanpath("/home/kitten")
allow(ChefConfig::PathHelper).to receive(:home).and_return(expected)
expect(ChefConfig::Config[:user_home]).to eq(expected)
end
@@ -595,7 +875,7 @@ RSpec.describe ChefConfig::Config do
end
describe "ChefConfig::Config[:encrypted_data_bag_secret]" do
- let(:db_secret_default_path) { to_platform("/etc/chef/encrypted_data_bag_secret") }
+ let(:db_secret_default_path) { ChefConfig::PathHelper.cleanpath("#{ChefConfig::Config.etc_chef_dir}/encrypted_data_bag_secret") }
before do
allow(File).to receive(:exist?).with(db_secret_default_path).and_return(secret_exists)
@@ -651,9 +931,9 @@ 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(:warn).with(/Please install an English UTF-8 locale for Chef Infra Client to use/)
+ 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
@@ -704,7 +984,7 @@ RSpec.describe ChefConfig::Config do
let(:locale_array) { ["af_ZA", "af_ZA.ISO8859-1", "af_ZA.ISO8859-15", "af_ZA.UTF-8"] }
it "should fall back to C locale" do
- expect(ChefConfig.logger).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.")
+ expect(ChefConfig.logger).to receive(:warn).with("Please install an English UTF-8 locale for Chef Infra Client to use, falling back to C locale and disabling UTF-8 support.")
expect(ChefConfig::Config.guess_internal_locale).to eq "C"
end
end
@@ -722,9 +1002,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
@@ -737,9 +1017,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
@@ -1042,4 +1326,65 @@ 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 legacy mode" do
+
+ before do
+ ChefConfig::Config[:solo_legacy_mode] = 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
+
+ context "for local mode" do
+
+ before do
+ ChefConfig::Config[:local_mode] = 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
+
+ describe "validation_client_name" do
+ context "with a normal server URL" do
+ before { ChefConfig::Config[:chef_server_url] = "https://chef.example/organizations/myorg" }
+
+ it "sets the validation client to myorg-validator" do
+ expect(ChefConfig::Config[:validation_client_name]).to eq "myorg-validator"
+ end
+ end
+
+ context "with an unusual server URL" do
+ before { ChefConfig::Config[:chef_server_url] = "https://chef.example/myorg" }
+
+ it "sets the validation client to chef-validator" do
+ expect(ChefConfig::Config[:validation_client_name]).to eq "chef-validator"
+ end
+ end
+ end
+
end
diff --git a/chef-config/spec/unit/fips_spec.rb b/chef-config/spec/unit/fips_spec.rb
index cf5af22ef1..4be6f64a2d 100644
--- a/chef-config/spec/unit/fips_spec.rb
+++ b/chef-config/spec/unit/fips_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,12 @@
require "chef-config/fips"
require "spec_helper"
+begin
+ require "win32/registry" unless defined?(Win32::Registry)
+rescue LoadError
+ # not on unix
+end
+
RSpec.describe "ChefConfig.fips?" do
let(:enabled) { "0" }
@@ -26,7 +32,7 @@ RSpec.describe "ChefConfig.fips?" do
let(:fips_path) { "/proc/sys/crypto/fips_enabled" }
before(:each) do
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).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
@@ -63,7 +69,7 @@ RSpec.describe "ChefConfig.fips?" do
let(:win_reg_entry) { { "Enabled" => enabled } }
before(:each) do
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(ChefUtils).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
diff --git a/chef-config/spec/unit/path_helper_spec.rb b/chef-config/spec/unit/path_helper_spec.rb
index 399b3dc965..5721e105ec 100644
--- a/chef-config/spec/unit/path_helper_spec.rb
+++ b/chef-config/spec/unit/path_helper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,9 +23,8 @@ RSpec.describe ChefConfig::PathHelper do
let(:path_helper) { described_class }
- shared_examples_for "common_functionality" do
- describe "join" do
-
+ context "common functionality" do
+ context "join" do
it "joins starting with '' resolve to absolute paths" do
expect(path_helper.join("", "a", "b")).to eq("#{path_helper.path_separator}a#{path_helper.path_separator}b")
end
@@ -33,10 +32,9 @@ RSpec.describe ChefConfig::PathHelper do
it "joins ending with '' add a / to the end" do
expect(path_helper.join("a", "b", "")).to eq("a#{path_helper.path_separator}b#{path_helper.path_separator}")
end
-
end
- describe "dirname" do
+ context "dirname" do
it "dirname('abc') is '.'" do
expect(path_helper.dirname("abc")).to eq(".")
end
@@ -55,42 +53,109 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- context "on windows" do
+ context "forcing windows/non-windows" do
+ context "forcing windows" do
+ it "path_separator is \\" do
+ expect(path_helper.path_separator(windows: true)).to eq('\\')
+ end
- before(:each) do
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ context "platform-specific #join behavior" do
+ it "joins components on Windows when some end with unix separators" do
+ expected = "C:\\foo\\bar\\baz"
+ expect(path_helper.join('C:\\foo/', "bar", "baz", windows: true)).to eq(expected)
+ end
+
+ it "joins components when some end with separators" do
+ expected = "C:\\foo\\bar\\baz"
+ expect(path_helper.join('C:\\foo\\', "bar", "baz", windows: true)).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = "C:\\foo\\bar\\baz"
+ expect(path_helper.join('C:\\foo\\', "bar/", "/baz", windows: true)).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = "C:\\foo\\bar\\baz"
+ expect(path_helper.join('C:\\foo', "bar", "baz", windows: true)).to eq(expected)
+ end
+ end
+
+ it "cleanpath changes slashes into backslashes and leaves backslashes alone" do
+ expect(path_helper.cleanpath('/a/b\\c/d/', windows: true)).to eq('\\a\\b\\c\\d')
+ end
+
+ it "cleanpath does not remove leading double backslash" do
+ expect(path_helper.cleanpath('\\\\a/b\\c/d/', windows: true)).to eq('\\\\a\\b\\c\\d')
+ end
end
- include_examples("common_functionality")
+ context "forcing unix" do
+ it "path_separator is /" do
+ expect(path_helper.path_separator(windows: false)).to eq("/")
+ end
+
+ it "cleanpath removes extra slashes alone" do
+ expect(path_helper.cleanpath("/a///b/c/d/", windows: false)).to eq("/a/b/c/d")
+ end
+
+ context "platform-specific #join behavior" do
+ it "joins components when some end with separators" do
+ expected = "/foo/bar/baz"
+ expect(path_helper.join("/foo/", "bar", "baz", windows: false)).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = "/foo/bar/baz"
+ expect(path_helper.join("/foo/", "bar/", "/baz", windows: false)).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = "/foo/bar/baz"
+ expect(path_helper.join("/foo", "bar", "baz", windows: false)).to eq(expected)
+ end
+ end
+
+ it "cleanpath changes backslashes into slashes and leaves slashes alone" do
+ expect(path_helper.cleanpath('/a/b\\c/d/', windows: false)).to eq("/a/b/c/d")
+ end
+
+ it "cleanpath does not remove leading double backslash" do
+ expect(path_helper.cleanpath('\\\\a/b\\c/d/', windows: false)).to eq("//a/b/c/d")
+ end
+ end
+ end
+
+ context "on windows", :windows_only do
+
+ before(:each) do
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ end
it "path_separator is \\" do
expect(path_helper.path_separator).to eq('\\')
end
- describe "platform-specific #join behavior" do
-
+ context "platform-specific #join behavior" do
it "joins components on Windows when some end with unix separators" do
- expect(path_helper.join('C:\\foo/', "bar", "baz")).to eq('C:\\foo\\bar\\baz')
+ expected = "C:\\foo\\bar\\baz"
+ expect(path_helper.join('C:\\foo/', "bar", "baz")).to eq(expected)
end
it "joins components when some end with separators" do
- expected = path_helper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}"
+ expected = "C:\\foo\\bar\\baz"
expect(path_helper.join('C:\\foo\\', "bar", "baz")).to eq(expected)
end
it "joins components when some end and start with separators" do
- expected = path_helper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}"
+ expected = "C:\\foo\\bar\\baz"
expect(path_helper.join('C:\\foo\\', "bar/", "/baz")).to eq(expected)
end
it "joins components that don't end in separators" do
- expected = path_helper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}"
+ expected = "C:\\foo\\bar\\baz"
expect(path_helper.join('C:\\foo', "bar", "baz")).to eq(expected)
end
-
end
it "cleanpath changes slashes into backslashes and leaves backslashes alone" do
@@ -100,17 +165,13 @@ RSpec.describe ChefConfig::PathHelper do
it "cleanpath does not remove leading double backslash" do
expect(path_helper.cleanpath('\\\\a/b\\c/d/')).to eq('\\\\a\\b\\c\\d')
end
-
end
- context "on unix" do
-
+ context "on unix", :unix_only do
before(:each) do
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
end
- include_examples("common_functionality")
-
it "path_separator is /" do
expect(path_helper.path_separator).to eq("/")
end
@@ -119,8 +180,7 @@ RSpec.describe ChefConfig::PathHelper do
expect(path_helper.cleanpath("/a///b/c/d/")).to eq("/a/b/c/d")
end
- describe "platform-specific #join behavior" do
-
+ context "platform-specific #join behavior" do
it "joins components when some end with separators" do
expected = path_helper.cleanpath("/foo/bar/baz")
expect(path_helper.join("/foo/", "bar", "baz")).to eq(expected)
@@ -135,16 +195,23 @@ RSpec.describe ChefConfig::PathHelper do
expected = path_helper.cleanpath("/foo/bar/baz")
expect(path_helper.join("/foo", "bar", "baz")).to eq(expected)
end
+ end
+ it "cleanpath changes backslashes into slashes and leaves slashes alone" do
+ expect(path_helper.cleanpath('/a/b\\c/d/', windows: false)).to eq("/a/b/c/d")
end
+ # NOTE: this seems a bit weird to me, but this is just the way Pathname#cleanpath works
+ it "cleanpath does not remove leading double backslash" do
+ expect(path_helper.cleanpath('\\\\a/b\\c/d/')).to eq("//a/b/c/d")
+ end
end
- describe "validate_path" do
+ context "validate_path" do
context "on windows" do
before(:each) do
# pass by default
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
allow(path_helper).to receive(:printable?).and_return(true)
allow(path_helper).to receive(:windows_max_length_exceeded?).and_return(false)
end
@@ -171,7 +238,7 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- describe "windows_max_length_exceeded?" do
+ context "windows_max_length_exceeded?" do
it "returns true if the path is too long (259 + NUL) for the API" do
expect(path_helper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 6)).to be_truthy
end
@@ -185,7 +252,7 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- describe "printable?" do
+ context "printable?" do
it "returns true if the string contains no non-printable characters" do
expect(path_helper.printable?("C:\\Program Files (x86)\\Microsoft Office\\Files.lst")).to be_truthy
end
@@ -208,7 +275,7 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- describe "canonical_path" do
+ context "canonical_path" do
context "on windows", :windows_only do
it "returns an absolute path with backslashes instead of slashes" do
expect(path_helper.canonical_path("\\\\?\\C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
@@ -230,43 +297,41 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- describe "paths_eql?" do
+ context "paths_eql?" do
it "returns true if the paths are the same" do
- allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/bandit/bandit")
- allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ allow(path_helper).to receive(:canonical_path).with("bandit", windows: ChefUtils.windows?).and_return("c:/bandit/bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit", windows: ChefUtils.windows?).and_return("c:/bandit/bandit")
expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_truthy
end
it "returns false if the paths are different" do
- allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/Bo/Bandit")
- allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ allow(path_helper).to receive(:canonical_path).with("bandit", windows: ChefUtils.windows?).and_return("c:/Bo/Bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit", windows: ChefUtils.windows?).and_return("c:/bandit/bandit")
expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_falsey
end
end
- describe "escape_glob" do
+ context "escape_glob" do
it "escapes characters reserved by glob" do
path = "C:\\this\\*path\\[needs]\\escaping?"
escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
- expect(path_helper.escape_glob(path)).to eq(escaped_path)
+ expect(path_helper.escape_glob(path, windows: true)).to eq(escaped_path)
end
context "when given more than one argument" do
it "joins, cleanpaths, and escapes characters reserved by glob" do
args = ["this/*path", "[needs]", "escaping?"]
- escaped_path = if ChefConfig.windows?
+ escaped_path = if ChefUtils.windows?
"this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
else
"this/\\*path/\\[needs\\]/escaping\\?"
end
- expect(path_helper).to receive(:join).with(*args).and_call_original
- expect(path_helper).to receive(:cleanpath).and_call_original
expect(path_helper.escape_glob(*args)).to eq(escaped_path)
end
end
end
- describe "escape_glob_dir" do
+ context "escape_glob_dir" do
it "escapes characters reserved by glob without using backslashes for path separators" do
path = "C:/this/*path/[needs]/escaping?"
escaped_path = "C:/this/\\*path/\\[needs\\]/escaping\\?"
@@ -283,21 +348,21 @@ RSpec.describe ChefConfig::PathHelper do
end
end
- describe "all_homes" do
+ context "all_homes" do
before do
stub_const("ENV", env)
- allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ allow(ChefUtils).to receive(:windows?).and_return(is_windows)
end
context "on windows" do
- let (:is_windows) { true }
+ let(:is_windows) { true }
end
context "on unix" do
- let (:is_windows) { false }
+ let(:is_windows) { false }
context "when HOME is not set" do
- let (:env) { {} }
+ let(:env) { {} }
it "returns an empty array" do
expect(path_helper.all_homes).to eq([])
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..70b42ad961 100644
--- a/chef-config/spec/unit/workstation_config_loader_spec.rb
+++ b/chef-config/spec/unit/workstation_config_loader_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,10 @@
#
require "spec_helper"
-require "tempfile"
+require "tempfile" unless defined?(Tempfile)
require "chef-config/exceptions"
-require "chef-config/windows"
+require "chef-utils"
require "chef-config/workstation_config_loader"
RSpec.describe ChefConfig::WorkstationConfigLoader do
@@ -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
@@ -51,7 +52,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
end
it "tests a path's existence" do
- expect(config_loader.path_exists?("/nope/nope/nope/nope/frab/jab/nab")).to be(false)
+ expect(config_loader.path_exists?("/nope/nope/nope/nope/slab/jab/nab")).to be(false)
expect(config_loader.path_exists?(__FILE__)).to be(true)
end
@@ -97,7 +98,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
let(:env_pwd) { "/path/to/cwd" }
before do
- if ChefConfig.windows?
+ if ChefUtils.windows?
env["CD"] = env_pwd
else
env["PWD"] = env_pwd
@@ -213,7 +214,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
describe "loading the config file" do
- context "when no explicit config is specifed and no implicit config is found" do
+ context "when no explicit config is specified and no implicit config is found" do
before do
allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
@@ -229,7 +230,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
context "when an explicit config is given but it doesn't exist" do
- let(:explicit_config_location) { "/nope/nope/nope/frab/jab/nab" }
+ let(:explicit_config_location) { "/nope/nope/nope/slab/jab/nab" }
it "raises a configuration error" do
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
@@ -270,6 +271,70 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
config_loader.load
expect(ChefConfig::Config.config_file).to eq(explicit_config_location)
end
+
+ it "loads a default value for node_name" do
+ allow(Etc).to receive(:getlogin).and_return("notauser")
+ config_loader.load
+ expect(ChefConfig::Config.node_name).to eq("notauser")
+ end
+
+ context "with a user.pem" do
+ before do
+ allow(Etc).to receive(:getlogin).and_return("notauser")
+ allow(FileTest).to receive(:exist?).and_call_original
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(false)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(true)
+ end
+
+ it "loads a default value for client_key" do
+ config_loader.load
+ expect(ChefConfig::Config.client_key).to eq(File.expand_path("../user.pem", explicit_config_location))
+ end
+ end
+
+ context "with a notauser.pem" do
+ before do
+ allow(Etc).to receive(:getlogin).and_return("notauser")
+ allow(FileTest).to receive(:exist?).and_call_original
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(true)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(false)
+ end
+
+ it "loads a default value for client_key" do
+ config_loader.load
+ expect(ChefConfig::Config.client_key).to eq(File.expand_path("../notauser.pem", explicit_config_location))
+ end
+ end
+
+ context "with a valclient.pem" do
+ before do
+ ChefConfig::Config.validation_client_name = "valclient"
+ allow(FileTest).to receive(:exist?).and_call_original
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(true)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(false)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
+ end
+
+ it "loads a default value for validation_key" do
+ config_loader.load
+ expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../valclient.pem", explicit_config_location))
+ end
+ end
+
+ context "with a validator.pem" do
+ before do
+ ChefConfig::Config.validation_client_name = "valclient"
+ allow(FileTest).to receive(:exist?).and_call_original
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(false)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(true)
+ allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
+ end
+
+ it "loads a default value for validation_key" do
+ config_loader.load
+ expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../validator.pem", explicit_config_location))
+ end
+ end
end
context "and has a syntax error" do
@@ -310,7 +375,8 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
before do
ChefConfig::Config[:config_d_dir] = tempdir
allow(config_loader).to receive(:path_exists?).with(
- an_instance_of(String)).and_return(false)
+ an_instance_of(String)
+ ).and_return(false)
end
after do
@@ -336,12 +402,12 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
end
context "has a non rb file" do
- let(:sytax_error_content) { "{{{{{:{{" }
+ let(:syntax_error_content) { "{{{{{:{{" }
let(:config_content) { "config_d_file_evaluated(true)" }
let!(:not_confd_file) do
Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".foorb"], tempdir).tap do |t|
- t.print(sytax_error_content)
+ t.print(syntax_error_content)
t.close
end
end
@@ -363,4 +429,205 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do
end
end
end
+
+ describe "when loading a credentials file" do
+ if ChefUtils.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"
+ invalid_config_option1234 = "foobar"
+ 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")
+ expect(ChefConfig::Config[:invalid_config_option1234]).to eq("foobar")
+ 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 ssl_verify_mode is a symbol string" do
+ let(:content) do
+ content = <<~EOH
+ [default]
+ ssl_verify_mode = ":verify_none"
+ EOH
+ content
+ end
+
+ it "raises a ConfigurationError" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
+ end
+ end
+
+ context "and ssl_verify_mode is a string" do
+ let(:content) do
+ content = <<~EOH
+ [default]
+ ssl_verify_mode = "verify_none"
+ EOH
+ content
+ end
+
+ it "raises a ConfigurationError" do
+ expect { config_loader.load_credentials }.not_to raise_error
+ expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
+ 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..fa95de76f5 100644
--- a/chef-universal-mingw32.gemspec
+++ b/chef-universal-mingw32.gemspec
@@ -1,24 +1,21 @@
-gemspec = eval(IO.read(File.expand_path("../chef.gemspec", __FILE__)))
+gemspec = eval(IO.read(File.expand_path("chef.gemspec", __dir__)))
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"
-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 "win32-process", "~> 0.9"
+gemspec.add_dependency "win32-service", ">= 2.1.5", "< 3.0"
gemspec.add_dependency "wmi-lite", "~> 1.0"
+gemspec.add_dependency "win32-taskscheduler", "~> 2.0"
+gemspec.add_dependency "iso8601", ">= 0.12.1", "< 0.14" # validate 0.14 when it comes out
+gemspec.add_dependency "win32-certstore", "~> 0.3"
gemspec.extensions << "ext/win32-eventlog/Rakefile"
-gemspec.files += %w{ext/win32-eventlog/Rakefile ext/win32-eventlog/chef-log.man}
-
-gemspec.executables += %w{ chef-service-manager chef-windows-service }
+gemspec.files += Dir.glob("{distro,ext}/**/*")
gemspec
diff --git a/chef-utils/.rspec b/chef-utils/.rspec
new file mode 100644
index 0000000000..da740c0724
--- /dev/null
+++ b/chef-utils/.rspec
@@ -0,0 +1,2 @@
+--color
+-fp
diff --git a/chef-utils/Gemfile b/chef-utils/Gemfile
new file mode 100644
index 0000000000..2555cf15d9
--- /dev/null
+++ b/chef-utils/Gemfile
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+source "https://rubygems.org"
+
+gemspec
+
+group(:development, :test) do
+ gem "fauxhai-ng"
+ gem "rake"
+ gem "rspec"
+end \ No newline at end of file
diff --git a/chef-utils/LICENSE b/chef-utils/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/chef-utils/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the 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.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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/chef-utils/README.md b/chef-utils/README.md
new file mode 100644
index 0000000000..d3088a7bc2
--- /dev/null
+++ b/chef-utils/README.md
@@ -0,0 +1,259 @@
+# Chef Utils gem
+
+**Umbrella Project**: [Chef Infra](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md)
+
+**Project State**: [Active](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#active)
+
+**Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
+
+**Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
+
+## Getting Started
+
+Chef Utils gem contains common code and mixins for the core Chef Infra Ruby gems. This is intended to be a "core" or "foundations" library
+for the chef ecosystem (and external related gems) which allows the use of core code and utility functions of the chef gem without requiring
+all the heaviness of the chef gem.
+
+### Platform Family Helpers
+
+The Platform Family helpers provide an alternative to comparing values from `node['platform_family']` or using the `platform_family?()` helper method. They allow you to write simpler logic that checks for specific platform family. Additionally we've provided several super families listed below, which bundle together common platform families into a single helper.
+
+* `aix?`
+* `amazon?`
+* `arch?` - includes arch, manjaro, and antergos platforms
+* `debian?` - includes debian, ubuntu, linuxmint, raspbian, and devuan platforms
+* `dragonflybsd?`
+* `fedora?` - includes arista_eos, fedora, and pidora platforms
+* `freebsd?`
+* `gentoo?`
+* `macos?`
+* `netbsd?`
+* `openbsd?`
+* `rhel?` - includes redhat, centos, scientific, oracle, and clearos platforms
+* `rhel6?` - includes redhat6, centos6, scientific6, oracle6, and clearos6 platforms
+* `rhel7?` - includes redhat7, centos7, scientific7, oracle7, and clearos7 platforms
+* `rhel8?` - includes redhat8, centos8, scientific8, oracle8, and clearos8 platforms
+* `smartos?`
+* `solaris2?`
+* `suse?` - includes suse and opensuseleap platforms
+* `windows?` - NOTE: in a class context when called without a node object (ChefUtils.windows?) this is not stubbable by ChefSpec, but when called with a node as the first argument or when called from the DSL it is stubabble by chefspec
+* `windows_ruby?` - this is always true if the ruby VM is running on a windows host and is not stubbed by ChefSpec
+
+Super Families:
+
+* `fedora_based?` - anything of fedora lineage (fedora, redhat, centos, amazon, pidora, etc)
+* `rpm_based?`- all `fedora_based` systems plus `suse` and any future linux distros based on RPM (excluding AIX)
+* `solaris_based?`- all solaris-derived systems (omnios, smartos, openindiana, etc)
+* `bsd_based?`- all bsd-derived systems (freebsd, netbsd, openbsd, dragonflybsd).
+
+### Platform Helpers
+
+The Platform helpers provide an alternative to comparing values from `node['platform']` or using the `platform?()` helper method. In general we'd highly suggest writing code using the Platform Family helpers, but these helpers can be used when it's necessary to target specific platforms.
+
+* `aix_platform?`
+* `amazon_platform?`
+* `arch_platform?`
+* `centos_platform?`
+* `clearos_platform?`
+* `debian_platform?`
+* `dragonfly_platform?`
+* `fedora_platform?`
+* `freebsd_platform?`
+* `gentoo_platform?`
+* `leap_platform?`
+* `linuxmint_platform?`
+* `macos_platform?`
+* `netbsd_platform?`
+* `omnios_platform?`
+* `openbsd_platform?`
+* `openindiana_platform?`
+* `opensuse_platform?`
+* `oracle_platform?`
+* `raspbian_platform?`
+* `redhat_platform?`
+* `scientific_platform?`
+* `slackware_platform?`
+* `smartos_platform?`
+* `solaris2_platform?`
+* `suse_platform?`
+* `ubuntu_platform?`
+* `windows_platform?`
+
+For compatibility with old chef-sugar code the following aliases work for backwards compatibility, but will be DEPRECATED in the future.
+
+* `centos?`
+* `clearos?`
+* `linuxmint?`
+* `omnios?`
+* `openindiana?`
+* `opensuse?`
+* `oracle?`
+* `raspbian?`
+* `redhat?`
+* `scientific?`
+* `ubuntu?`
+
+### OS Helpers
+
+The OS helpers provide an alternative to comparing data from `node['os']`.
+
+* `linux?` - Any Linux distribution
+* `darwin?` - Darwin or macOS platforms
+
+### Architecture Helpers
+
+Architecture Helpers allow you to determine the processor architecture of your node.
+
+* `_32_bit?`
+* `_64_bit?`
+* `arm?`
+* `armhf?`
+* `i386?`
+* `intel?`
+* `powerpc?`
+* `ppc64?`
+* `ppc64le?`
+* `s390?`
+* `s390x?`
+* `sparc?`
+
+### Cloud Helpers
+
+* `cloud?` - if the node is running in any cloud, including internal ones
+* `ec2?` - if the node is running in ec2
+* `gce?` - if the node is running in gce
+* `rackspace?` - if the node is running in rackspace
+* `eucalyptus?` - if the node is running under eucalyptus
+* `linode?` - if the node is running in linode
+* `openstack?` - if the node is running under openstack
+* `azure?` - if the node is running in azure
+* `digital_ocean?` - if the node is running in digital ocean
+* `softlayer?` - if the node is running in softlayer
+
+### Virtualization Helpers
+
+* `kvm?` - if the node is a kvm guest
+* `kvm_host?` - if the node is a kvm host
+* `lxc?` - if the node is an lxc guest
+* `lxc_host?` - if the node is an lxc host
+* `parallels?`- if the node is a parallels guest
+* `parallels_host?`- if the node is a parallels host
+* `vbox?` - if the node is a virtualbox guest
+* `vbox_host?` - if the node is a virtualbox host
+* `vmware?` - if the node is a vmware guest
+* `vmware_host?` - if the node is a vmware host
+* `openvz?` - if the node is an openvz guest
+* `openvz_host?` - if the node is an openvz host
+* `guest?` - if the node is detected as any kind of guest
+* `hypervisor?` - if the node is detected as being any kind of hypervisor
+* `physical?` - the node is not running as a guest (may be a hypervisor or may be bare-metal)
+* `vagrant?` - attempts to identify the node as a vagrant guest (this check may be error prone)
+
+### Train Helpers
+
+**EXPERIMENTAL**: APIs may have breaking changes any time without warning
+
+* `file_exist?`
+* `file_open`
+
+### Introspection Helpers
+
+* `docker?` - if the node is running inside of docker
+* `systemd?` - if the init system is systemd
+* `kitchen?` - if ENV['TEST_KITCHEN'] is set
+* `ci?` - if ENV['CI'] is set
+* `include_recipe?(recipe_name)` - if the `recipe_name` is in the run list, the expanded run list, or has been `include_recipe`'d.
+
+### Service Helpers
+
+* `debianrcd?` - if the `update-rc.d` binary is present
+* `invokercd?` - if the `invoke-rc.d` binary is present
+* `upstart?` - if the `initctl` binary is present
+* `insserv?` - if the `insserv` binary is present
+* `redhatrcd?` - if the `chkconfig` binary is present
+
+* `service_script_exist?(type, service)`
+
+### Which/Where Helpers
+
+* `which`
+* `where`
+
+### Path Sanity Helpers
+
+* `sanitized_path`
+
+## Documentation for Cookbook library authors
+
+To use the helpers in a class or module in a cookbook library file you can include the ChefUtils DSL:
+
+```ruby
+module MyHelper
+ include ChefUtils # or any individual module with DSL methods in it
+
+ def do_something
+ puts "RHEL" if rhel?
+ end
+
+ extend self
+end
+```
+
+Now you can include MyHelper in another class/module, or you can call MyHelper.do_something directly.
+
+## Documentation for Software Developers
+
+The design of the DSL helper libraries in this gem are designed around the Chef Infra Client use cases. Most of the helpers are
+accessible through the Chef DSL directly via the `ChefUtils` module. They are also available via class method calls on
+the ChefUtils module directly (e.g. `ChefUtils.debian?`). For that to be possible there is Chef Infra Client specific wiring in
+the `ChefUtils::Internal` class allowing the helpers to access the `Chef.run_context` global values. This allows them to be
+used from library helpers in cookbooks inside Chef Infra Client.
+
+For external use in other gems, this automatic wiring will not work correctly, and so it will not generally be possible to
+call helpers off of the `ChefUtils` class (some exceptions that do not require a node-like object or a train connection will
+may still work). For use in other gems you should create your own module and mixin the helper class. If you have a node
+method in your class/module then that method will be used.
+
+You can wire up a module which implements the Chef DSL with your own wiring using this template:
+
+```ruby
+module MyDSL
+ include ChefUtils # or any individual module with DSL methods in it
+
+ private
+
+ def __getnode
+ # return something Mash-like with node semantics with a node["platform"], etc.
+ end
+
+ def __transport_connection
+ # return a Train::Transport connection
+ end
+
+ extend self # if your wiring is to global state to make `MyDSL.helper?` work.
+end
+```
+
+Those methods are marked API private for the purposes of end-users, but are public APIs for the purposes of software development.
+
+## Getting Involved
+
+We'd love to have your help developing Chef Infra. See our [Contributing Document](../CONTRIBUTING.md) for more information on getting started.
+
+## License and Copyright
+
+Copyright 2008-2019, 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.
+```
diff --git a/chef-utils/Rakefile b/chef-utils/Rakefile
new file mode 100644
index 0000000000..237f47f72b
--- /dev/null
+++ b/chef-utils/Rakefile
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+require "bundler/gem_tasks"
+
+task default: :spec
+
+begin
+ require "rspec/core/rake_task"
+ desc "Run standard specs"
+ RSpec::Core::RakeTask.new(:spec) do |t|
+ t.verbose = false
+ 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-utils/chef-utils.gemspec b/chef-utils/chef-utils.gemspec
new file mode 100644
index 0000000000..836fef1ff9
--- /dev/null
+++ b/chef-utils/chef-utils.gemspec
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+lib = File.expand_path("lib", __dir__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require "chef-utils/version"
+
+Gem::Specification.new do |spec|
+ spec.name = "chef-utils"
+ spec.version = ChefUtils::VERSION
+ spec.authors = ["Chef Software, Inc"]
+ spec.email = ["oss@chef.io"]
+
+ spec.summary = %q{Basic utility functions for Core Chef Infra development}
+ spec.homepage = "https://github.com/chef/chef/tree/master/chef-utils"
+ spec.license = "Apache-2.0"
+
+ spec.required_ruby_version = ">= 2.6.0"
+
+ spec.metadata = {
+ "bug_tracker_uri" => "https://github.com/chef/chef/issues",
+ "changelog_uri" => "https://github.com/chef/chef/blob/master/CHANGELOG.md",
+ "documentation_uri" => "https://github.com/chef/chef/tree/master/chef-utils/README.md",
+ "homepage_uri" => "https://github.com/chef/chef/tree/master/chef-utils",
+ "source_code_uri" => "https://github.com/chef/chef/tree/master/chef-utils",
+ }
+
+ spec.require_paths = ["lib"]
+
+ #
+ # NOTE: DO NOT ADD RUNTIME DEPS TO OTHER CHEF ECOSYSTEM GEMS
+ # (e.g. chef, ohai, mixlib-anything, ffi-yajl, and IN PARTICULAR NOT chef-config)
+ #
+ # This is so that this set of common code can be reused in any other library without
+ # creating circular dependencies. If you find yourself wanting to do that you probably
+ # have a helper that should go into the library you want to declare a dependency on,
+ # or you need to create another gem that is not this one. You may also want to rub some
+ # dependency injection on your API to invert things so that you don't have to take
+ # a dependency on the thing you need (i.e. allow injecting a hash-like thing instead of taking
+ # a dep on mixlib-config and then require the consumer to wire up chef-config to your
+ # API). Same for mixlib-log and Chef::Log in general.
+ #
+ # ABSOLUTELY NO EXCEPTIONS
+ #
+
+ spec.files = %w{Rakefile LICENSE} + Dir.glob("*.gemspec") +
+ Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
+end
diff --git a/chef-utils/lib/chef-utils.rb b/chef-utils/lib/chef-utils.rb
new file mode 100644
index 0000000000..57a1e4017c
--- /dev/null
+++ b/chef-utils/lib/chef-utils.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "chef-utils/dsl/architecture"
+require_relative "chef-utils/dsl/cloud"
+require_relative "chef-utils/dsl/introspection"
+require_relative "chef-utils/dsl/os"
+require_relative "chef-utils/dsl/default_paths"
+require_relative "chef-utils/dsl/path_sanity"
+require_relative "chef-utils/dsl/platform"
+require_relative "chef-utils/dsl/platform_family"
+require_relative "chef-utils/dsl/platform_version"
+require_relative "chef-utils/dsl/service"
+require_relative "chef-utils/dsl/train_helpers"
+require_relative "chef-utils/dsl/virtualization"
+require_relative "chef-utils/dsl/which"
+require_relative "chef-utils/dsl/windows"
+require_relative "chef-utils/mash"
+
+# This is the Chef Infra Client DSL, not everything needs to go in here
+module ChefUtils
+ include ChefUtils::DSL::Architecture
+ include ChefUtils::DSL::Cloud
+ include ChefUtils::DSL::DefaultPaths
+ include ChefUtils::DSL::Introspection
+ include ChefUtils::DSL::OS
+ include ChefUtils::DSL::Platform
+ include ChefUtils::DSL::PlatformFamily
+ include ChefUtils::DSL::PlatformVersion
+ include ChefUtils::DSL::TrainHelpers
+ include ChefUtils::DSL::Virtualization
+ include ChefUtils::DSL::Which
+ include ChefUtils::DSL::Windows
+ # ChefUtils::DSL::Service is deliberately excluded
+
+ CANARY = 1 # used as a guard for requires
+ extend self
+end
diff --git a/chef-utils/lib/chef-utils/dist.rb b/chef-utils/lib/chef-utils/dist.rb
new file mode 100644
index 0000000000..c4fe72960d
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dist.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+module ChefUtils
+ # This class is not fully implemented, depending on it is not recommended!
+ module Dist
+ class Apply
+ # The chef-apply product name
+ PRODUCT = "Chef Infra Apply"
+
+ # The chef-apply binary
+ EXEC = "chef-apply"
+ end
+
+ class Automate
+ # name of the automate product
+ PRODUCT = "Chef Automate"
+ end
+
+ class Infra
+ # When referencing a product directly, like Chef (Now Chef Infra)
+ PRODUCT = "Chef Infra Client"
+
+ # A short designation for the product, used in Windows event logs
+ # and some nomenclature.
+ SHORT = "chef"
+
+ # The client's alias (chef-client)
+ CLIENT = "chef-client"
+
+ # The chef executable, as in `chef gem install` or `chef generate cookbook`
+ EXEC = "chef"
+
+ # The chef-shell executable
+ SHELL = "chef-shell"
+
+ # Configuration related constants
+ # The chef-shell configuration file
+ SHELL_CONF = "chef_shell.rb"
+
+ # The user's configuration directory
+ USER_CONF_DIR = ".chef"
+
+ # The suffix for Chef's /etc/chef, /var/chef and C:\\Chef directories
+ # "chef" => /etc/cinc, /var/cinc, C:\\cinc
+ DIR_SUFFIX = "chef"
+ end
+
+ class Org
+ # product Website address
+ WEBSITE = "https://chef.io"
+
+ # The downloads site
+ DOWNLOADS_URL = "downloads.chef.io"
+
+ # The legacy conf folder: C:/opscode/chef. Specifically the "opscode" part
+ # DIR_SUFFIX is appended to it in code where relevant
+ LEGACY_CONF_DIR = "opscode"
+
+ # Enable forcing Chef EULA
+ ENFORCE_LICENSE = true
+
+ # product patents page
+ PATENTS = "https://www.chef.io/patents"
+
+ # knife documentation page
+ KNIFE_DOCS = "https://docs.chef.io/workstation/knife/"
+ end
+
+ class Server
+ # The name of the server product
+ PRODUCT = "Chef Infra Server"
+
+ # The server's configuration directory
+ CONF_DIR = "/etc/chef-server"
+
+ # The servers's alias (chef-server)
+ SERVER = "chef-server"
+
+ # The server's configuration utility
+ SERVER_CTL = "chef-server-ctl"
+ end
+
+ class Solo
+ # Chef-Solo's product name
+ PRODUCT = "Chef Infra Solo"
+
+ # The chef-solo executable (legacy local mode)
+ EXEC = "chef-solo"
+ end
+
+ class Zero
+ # chef-zero executable
+ PRODUCT = "Chef Infra Zero"
+
+ # The chef-zero executable (local mode)
+ EXEC = "chef-zero"
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/architecture.rb b/chef-utils/lib/chef-utils/dsl/architecture.rb
new file mode 100644
index 0000000000..e1ac4a099a
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/architecture.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Architecture
+ include Internal
+
+ # Determine if the current architecture is 64-bit.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def _64_bit?(node = __getnode)
+ %w{amd64 x86_64 ppc64 ppc64le s390x ia64 sparc64 aarch64 arch64 arm64 sun4v sun4u}
+ .include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is 32-bit.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def _32_bit?(node = __getnode)
+ !_64_bit?(node)
+ end
+
+ # Determine if the current architecture is i386.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def i386?(node = __getnode)
+ _32_bit?(node) && intel?(node)
+ end
+
+ # Determine if the current architecture is Intel.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def intel?(node = __getnode)
+ %w{i86pc i386 x86_64 amd64 i686}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is SPARC.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def sparc?(node = __getnode)
+ %w{sun4u sun4v}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is PowerPC 64bit Big Endian.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def ppc64?(node = __getnode)
+ %w{ppc64}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is PowerPC 64bit Little Endian.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def ppc64le?(node = __getnode)
+ %w{ppc64le}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is PowerPC.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def powerpc?(node = __getnode)
+ %w{powerpc}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is arm
+ #
+ # @since 15.10
+ #
+ # @return [Boolean]
+ #
+ def arm?(node = __getnode)
+ %w{armv6l armv7l armhf aarch64 arm64 arch64}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is 32-bit ARM hard float.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def armhf?(node = __getnode)
+ %w{armv6l armv7l armhf}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is s390x.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def s390x?(node = __getnode)
+ %w{s390x}.include?(node["kernel"]["machine"])
+ end
+
+ # Determine if the current architecture is s390.
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def s390?(node = __getnode)
+ %w{s390}.include?(node["kernel"]["machine"])
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/cloud.rb b/chef-utils/lib/chef-utils/dsl/cloud.rb
new file mode 100644
index 0000000000..9d4de55656
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/cloud.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Cloud
+ include Internal
+
+ # Determine if the current node is "in the cloud".
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def cloud?(node = __getnode)
+ # cloud is always present, but nil if not on a cloud
+ !node["cloud"].nil?
+ end
+
+ # Return true if the current current node is in EC2.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def ec2?(node = __getnode)
+ node.key?("ec2")
+ end
+
+ # Return true if the current current node is in GCE.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def gce?(node = __getnode)
+ node.key?("gce")
+ end
+
+ # Return true if the current current node is in Rackspace.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def rackspace?(node = __getnode)
+ node.key?("rackspace")
+ end
+
+ # Return true if the current current node is in Eucalyptus.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def eucalyptus?(node = __getnode)
+ node.key?("eucalyptus")
+ end
+ # chef-sugar backcompat method
+ alias_method :euca?, :eucalyptus?
+
+ # Return true if the current current node is in Linode.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def linode?(node = __getnode)
+ node.key?("linode")
+ end
+
+ # Return true if the current current node is in OpenStack.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def openstack?(node = __getnode)
+ node.key?("openstack")
+ end
+
+ # Return true if the current current node is in Azure.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def azure?(node = __getnode)
+ node.key?("azure")
+ end
+
+ # Return true if the current current node is in DigitalOcean.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def digital_ocean?(node = __getnode)
+ node.key?("digital_ocean")
+ end
+ # chef-sugar backcompat method
+ alias_method :digitalocean?, :digital_ocean?
+
+ # Return true if the current current node is in SoftLayer.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def softlayer?(node = __getnode)
+ node.key?("softlayer")
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/default_paths.rb b/chef-utils/lib/chef-utils/dsl/default_paths.rb
new file mode 100644
index 0000000000..b2eb359273
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/default_paths.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+require_relative "platform_family"
+
+module ChefUtils
+ module DSL
+ module DefaultPaths
+ include Internal
+
+ # @since 15.5
+ def default_paths(env = nil)
+ env_path = env ? env["PATH"] : __env_path
+ env_path = "" if env_path.nil?
+ path_separator = ChefUtils.windows? ? ";" : ":"
+ # ensure the Ruby and Gem bindirs are included for omnibus 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
+ __default_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
+
+ def __default_paths
+ ChefUtils.windows? ? %w{} : %w{/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin}
+ end
+
+ def __ruby_bindir
+ RbConfig::CONFIG["bindir"]
+ end
+
+ def __gem_bindir
+ Gem.bindir
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/introspection.rb b/chef-utils/lib/chef-utils/dsl/introspection.rb
new file mode 100644
index 0000000000..4edfb8d139
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/introspection.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "train_helpers"
+
+module ChefUtils
+ module DSL
+ # This is for "introspection" helpers in the sense that we are inspecting the
+ # actual server or image under management to determine running state (duck-typing the system).
+ # The helpers here may use the node object state from ohai, but typically not the big 5: platform,
+ # platform_family, platform_version, arch, os. The helpers here should infer somewhat
+ # higher level facts about the system.
+ #
+ module Introspection
+ include TrainHelpers
+
+ # Determine if the node is a docker container.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 12.11
+ #
+ # @return [Boolean]
+ #
+ def docker?(node = __getnode)
+ # Using "File.exist?('/.dockerinit') || File.exist?('/.dockerenv')" makes Travis sad,
+ # and that makes us sad too.
+ !!(node && node.read("virtualization", "systems", "docker") == "guest")
+ end
+
+ # Determine if the node uses the systemd init system.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def systemd?(node = __getnode)
+ file_exist?("/proc/1/comm") && file_open("/proc/1/comm").gets.chomp == "systemd"
+ end
+
+ # Determine if the node is running in Test Kitchen.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def kitchen?(node = __getnode)
+ ENV.key?("TEST_KITCHEN")
+ end
+
+ # Determine if the node is running in a CI system that sets the CI env var.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def ci?(node = __getnode)
+ ENV.key?("CI")
+ end
+
+ # Determine if the a systemd service unit is present on the system.
+ #
+ # @param [String] svc_name
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def has_systemd_service_unit?(svc_name)
+ %w{ /etc /usr/lib /lib /run }.any? do |load_path|
+ file_exist?(
+ "#{load_path}/systemd/system/#{svc_name.gsub(/@.*$/, "@")}.service"
+ )
+ end
+ end
+
+ # Determine if the a systemd unit of any type is present on the system.
+ #
+ # @param [String] svc_name
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def has_systemd_unit?(svc_name)
+ # TODO: stop supporting non-service units with service resource
+ %w{ /etc /usr/lib /lib /run }.any? do |load_path|
+ file_exist?("#{load_path}/systemd/system/#{svc_name}")
+ end
+ end
+
+ # Determine if the current node includes the given recipe name.
+ #
+ # @param [String] recipe_name
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def includes_recipe?(recipe_name, node = __getnode)
+ node.recipe?(recipe_name)
+ end
+ # chef-sugar backcompat method
+ alias_method :include_recipe?, :includes_recipe?
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/os.rb b/chef-utils/lib/chef-utils/dsl/os.rb
new file mode 100644
index 0000000000..da6850a77a
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/os.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module OS
+ include Internal
+
+ #
+ # NOTE CAREFULLY: Most node['os'] values should not appear in this file at all.
+ #
+ # For cases where node['os'] == node['platform_family'] == node['platform'] then
+ # only the platform helper should be added.
+ #
+
+ # Determine if the current node is Linux.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def linux?(node = __getnode)
+ node["os"] == "linux"
+ end
+
+ # Determine if the current node is Darwin.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def darwin?(node = __getnode)
+ node["os"] == "darwin"
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/path_sanity.rb b/chef-utils/lib/chef-utils/dsl/path_sanity.rb
new file mode 100644
index 0000000000..7766ce3176
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/path_sanity.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "default_paths"
+
+module ChefUtils
+ module DSL
+ module PathSanity
+ include ChefUtils::DSL::DefaultPaths
+
+ def sanitized_path(env = nil)
+ default_paths(env)
+ end
+
+ private
+
+ def __sane_paths
+ __default_paths
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/platform.rb b/chef-utils/lib/chef-utils/dsl/platform.rb
new file mode 100644
index 0000000000..f44cb811bb
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/platform.rb
@@ -0,0 +1,372 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Platform
+ include Internal
+
+ # NOTE: if you are adding new platform helpers they should all have the `_platform?` suffix.
+ # DO NOT add new short aliases without the suffix (they will be deprecated in the future)
+ # aliases here are mostly for backwards compatibility with chef-sugar and new ones are DISCOURAGED.
+ # generally there should be one obviously correct way to do things.
+
+ # Determine if the current node is linux mint.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def linuxmint_platform?(node = __getnode)
+ node["platform"] == "linuxmint"
+ end
+ # chef-sugar backcompat method
+ alias_method :mint?, :linuxmint_platform?
+ # chef-sugar backcompat method
+ alias_method :linux_mint?, :linuxmint_platform?
+ # chef-sugar backcompat method
+ alias_method :linuxmint?, :linuxmint_platform?
+
+ # Determine if the current node is Ubuntu.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def ubuntu_platform?(node = __getnode)
+ node["platform"] == "ubuntu"
+ end
+ # chef-sugar backcompat method
+ alias_method :ubuntu?, :ubuntu_platform?
+
+ # Determine if the current node is Raspbian.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def raspbian_platform?(node = __getnode)
+ node["platform"] == "raspbian"
+ end
+ # chef-sugar backcompat method
+ alias_method :raspbian?, :raspbian_platform?
+
+ # Determine if the current node is Debian.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def debian_platform?(node = __getnode)
+ node["platform"] == "debian"
+ end
+
+ # Determine if the current node is Amazon Linux.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def amazon_platform?(node = __getnode)
+ node["platform"] == "amazon"
+ end
+
+ # Determine if the current node is Red Hat Enterprise Linux.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def redhat_platform?(node = __getnode)
+ node["platform"] == "redhat"
+ end
+ # chef-sugar backcompat method
+ alias_method :redhat_enterprise?, :redhat_platform?
+ # chef-sugar backcompat method
+ alias_method :redhat_enterprise_linux?, :redhat_platform?
+ # chef-sugar backcompat method
+ alias_method :redhat?, :redhat_platform?
+
+ # Determine if the current node is CentOS.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def centos_platform?(node = __getnode)
+ node["platform"] == "centos"
+ end
+ # chef-sugar backcompat method
+ alias_method :centos?, :centos_platform?
+
+ # Determine if the current node is Oracle Linux.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def oracle_platform?(node = __getnode)
+ node["platform"] == "oracle"
+ end
+ # chef-sugar backcompat method
+ alias_method :oracle_linux?, :oracle_platform?
+ # chef-sugar backcompat method
+ alias_method :oracle?, :oracle_platform?
+
+ # Determine if the current node is Scientific Linux.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def scientific_platform?(node = __getnode)
+ node["platform"] == "scientific"
+ end
+ # chef-sugar backcompat method
+ alias_method :scientific_linux?, :scientific_platform?
+ # chef-sugar backcompat method
+ alias_method :scientific?, :scientific_platform?
+
+ # Determine if the current node is ClearOS.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def clearos_platform?(node = __getnode)
+ node["platform"] == "clearos"
+ end
+ # chef-sugar backcompat method
+ alias_method :clearos?, :clearos_platform?
+
+ # Determine if the current node is Fedora.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def fedora_platform?(node = __getnode)
+ node["platform"] == "fedora"
+ end
+
+ # Determine if the current node is Arch Linux
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def arch_platform?(node = __getnode)
+ node["platform"] == "arch"
+ end
+
+ # Determine if the current node is Solaris2.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def solaris2_platform?(node = __getnode)
+ node["platform"] == "solaris2"
+ end
+
+ # Determine if the current node is SmartOS.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def smartos_platform?(node = __getnode)
+ node["platform"] == "smartos"
+ end
+
+ # Determine if the current node is OmniOS.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def omnios_platform?(node = __getnode)
+ node["platform"] == "omnios"
+ end
+ # chef-sugar backcompat method
+ alias_method :omnios?, :omnios_platform?
+
+ # Determine if the current node is OpenIndiana.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def openindiana_platform?(node = __getnode)
+ node["platform"] == "openindiana"
+ end
+ # chef-sugar backcompat method
+ alias_method :openindiana?, :openindiana_platform?
+
+ # Determine if the current node is AIX.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def aix_platform?(node = __getnode)
+ node["platform"] == "aix"
+ end
+
+ # Determine if the current node is FreeBSD.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def freebsd_platform?(node = __getnode)
+ node["platform"] == "freebsd"
+ end
+
+ # Determine if the current node is OpenBSD.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def openbsd_platform?(node = __getnode)
+ node["platform"] == "openbsd"
+ end
+
+ # Determine if the current node is NetBSD.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def netbsd_platform?(node = __getnode)
+ node["platform"] == "netbsd"
+ end
+
+ # Determine if the current node is DragonFly BSD.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def dragonfly_platform?(node = __getnode)
+ node["platform"] == "dragonfly"
+ end
+
+ # Determine if the current node is macOS.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def macos_platform?(node = __getnode)
+ node["platform"] == "mac_os_x"
+ end
+ # chef-sugar backcompat method
+ alias_method :mac_os_x_platform?, :macos_platform?
+
+ # Determine if the current node is Gentoo.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def gentoo_platform?(node = __getnode)
+ node["platform"] == "gentoo"
+ end
+
+ # Determine if the current node is Slackware.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def slackware_platform?(node = __getnode)
+ node["platform"] == "slackware"
+ end
+
+ # Determine if the current node is SuSE.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def suse_platform?(node = __getnode)
+ node["platform"] == "suse"
+ end
+
+ # Determine if the current node is openSUSE.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def opensuse_platform?(node = __getnode)
+ node["platform"] == "opensuse" || node["platform"] == "opensuseleap"
+ end
+ # chef-sugar backcompat method
+ alias_method :opensuse?, :opensuse_platform?
+ # chef-sugar backcompat method
+ alias_method :opensuseleap_platform?, :opensuse_platform?
+ # chef-sugar backcompat method
+ alias_method :leap_platform?, :opensuse_platform?
+ # NOTE: to anyone adding :tumbleweed_platform? - :[opensuse]leap_platform? should be false on tumbleweed, :opensuse[_platform]? should be true
+
+ # Determine if the current node is Windows.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def windows_platform?(node = __getnode)
+ node["platform"] == "windows"
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/platform_family.rb b/chef-utils/lib/chef-utils/dsl/platform_family.rb
new file mode 100644
index 0000000000..10d7b35901
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/platform_family.rb
@@ -0,0 +1,344 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module PlatformFamily
+ include Internal
+
+ # Determine if the current node is a member of the 'arch' family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def arch?(node = __getnode)
+ node["platform_family"] == "arch"
+ end
+ # chef-sugar backcompat method
+ alias_method :arch_linux?, :arch?
+
+ # Determine if the current node is a member of the 'aix' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def aix?(node = __getnode)
+ node["platform_family"] == "aix"
+ end
+
+ # Determine if the current node is a member of the 'debian' platform family (Debian, Ubuntu and derivatives).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def debian?(node = __getnode)
+ node["platform_family"] == "debian"
+ end
+
+ # Determine if the current node is a member of the 'fedora' platform family (Fedora and Arista).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def fedora?(node = __getnode)
+ node["platform_family"] == "fedora"
+ end
+
+ # Determine if the current node is a member of the 'mac_os_x' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def macos?(node = __getnode)
+ node["platform_family"] == "mac_os_x"
+ end
+ # chef-sugar backcompat method
+ alias_method :osx?, :macos?
+ # chef-sugar backcompat method
+ alias_method :mac?, :macos?
+ # chef-sugar backcompat method
+ alias_method :mac_os_x?, :macos?
+
+ # Determine if the current node is a member of the 'rhel' platform family (Red Hat, CentOS, Oracle or Scientific Linux, but NOT Amazon Linux or Fedora).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def rhel?(node = __getnode)
+ node["platform_family"] == "rhel"
+ end
+ # chef-sugar backcompat method
+ alias_method :el?, :rhel?
+
+ # Determine if the current node is a rhel6 compatible build (Red Hat, CentOS, Oracle or Scientific Linux).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def rhel6?(node = __getnode)
+ node["platform_family"] == "rhel" && node["platform_version"].to_f >= 6.0 && node["platform_version"].to_f < 7.0
+ end
+
+ # Determine if the current node is a rhel7 compatible build (Red Hat, CentOS, Oracle or Scientific Linux).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def rhel7?(node = __getnode)
+ node["platform_family"] == "rhel" && node["platform_version"].to_f >= 7.0 && node["platform_version"].to_f < 8.0
+ end
+
+ # Determine if the current node is a rhel8 compatible build (Red Hat, CentOS, Oracle or Scientific Linux).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def rhel8?(node = __getnode)
+ node["platform_family"] == "rhel" && node["platform_version"].to_f >= 8.0 && node["platform_version"].to_f < 9.0
+ end
+
+ # Determine if the current node is a member of the 'amazon' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def amazon?(node = __getnode)
+ node["platform_family"] == "amazon"
+ end
+ # chef-sugar backcompat method
+ alias_method :amazon_linux?, :amazon?
+
+ # Determine if the current node is a member of the 'solaris2' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def solaris2?(node = __getnode)
+ node["platform_family"] == "solaris2"
+ end
+ # chef-sugar backcompat method
+ alias_method :solaris?, :solaris2?
+
+ # Determine if the current node is a member of the 'smartos' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def smartos?(node = __getnode)
+ node["platform_family"] == "smartos"
+ end
+
+ # Determine if the current node is a member of the 'suse' platform family (openSUSE, SLES, and SLED).
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def suse?(node = __getnode)
+ node["platform_family"] == "suse"
+ end
+
+ # Determine if the current node is a member of the 'gentoo' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def gentoo?(node = __getnode)
+ node["platform_family"] == "gentoo"
+ end
+
+ # Determine if the current node is a member of the 'freebsd' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def freebsd?(node = __getnode)
+ node["platform_family"] == "freebsd"
+ end
+
+ # Determine if the current node is a member of the 'openbsd' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def openbsd?(node = __getnode)
+ node["platform_family"] == "openbsd"
+ end
+
+ # Determine if the current node is a member of the 'netbsd' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def netbsd?(node = __getnode)
+ node["platform_family"] == "netbsd"
+ end
+
+ # Determine if the current node is a member of the 'dragonflybsd' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def dragonflybsd?(node = __getnode)
+ node["platform_family"] == "dragonflybsd"
+ end
+
+ # Determine if the current node is a member of the 'windows' platform family.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def windows?(node = __getnode(true))
+ # This is all somewhat complicated. We prefer to get the node object so that chefspec can
+ # stub the node object. But we also have to deal with class-parsing time where there is
+ # no node object, so we have to fall back to RUBY_PLATFORM based detection. We cannot pull
+ # the node object out of the Chef.run_context.node global object here (which is what the
+ # false flag to __getnode is about) because some run-time code also cannot run under chefspec
+ # on non-windows where the node is stubbed to windows.
+ #
+ # As a result of this the `windows?` helper and the `ChefUtils.windows?` helper do not behave
+ # the same way in that the latter is not stubbable by chefspec.
+ #
+ node ? node["platform_family"] == "windows" : windows_ruby?
+ end
+
+ # Determine if the Ruby VM is currently running on a Windows node (ChefSpec can never stub
+ # this behavior, so this is useful for code which can never be parsed on a non-Windows box).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def windows_ruby?
+ !!(RUBY_PLATFORM =~ /mswin|mingw32|windows/)
+ end
+
+ #
+ # Platform-Family-like Helpers
+ #
+ # These are meta-helpers which address the issue that platform_family is single valued and cannot
+ # be an array while a tree-like Taxonomy is what is called for in some cases.
+ #
+
+ # If it uses RPM, it goes in here (rhel, fedora, amazon, suse platform_families). Deliberately does not
+ # include AIX because bff is AIX's primary package manager and adding it here would make this substantially
+ # less useful since in no way can AIX trace its lineage back to old redhat distros. This is most useful for
+ # "smells like redhat, including SuSE".
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def rpm_based?(node = __getnode)
+ fedora_derived?(node) || node["platform_family"] == "suse"
+ end
+
+ # RPM-based distros which are not SuSE and are very loosely similar to fedora, using yum or dnf. The historical
+ # lineage of the distro should have forked off from old redhat fedora distros at some point. Currently rhel,
+ # fedora and amazon. This is most useful for "smells like redhat, but isn't SuSE".
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def fedora_derived?(node = __getnode)
+ redhat_based?(node) || node["platform_family"] == "amazon"
+ end
+
+ # RedHat distros -- fedora and rhel platform_families, nothing else. This is most likely not as useful as the
+ # "fedora_derived?" helper.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def redhat_based?(node = __getnode)
+ %w{rhel fedora}.include?(node["platform_family"])
+ end
+
+ # All of the Solaris-lineage.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def solaris_based?(node = __getnode)
+ %w{solaris2 smartos omnios openindiana}.include?(node["platform"])
+ end
+
+ # All of the BSD-lineage.
+ #
+ # Note that macOS is not included since macOS deviates so significantly from BSD that including it would not be useful.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def bsd_based?(node = __getnode)
+ # we could use os, platform_family or platform here equally
+ %w{netbsd freebsd openbsd dragonflybsd}.include?(node["platform"])
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/platform_version.rb b/chef-utils/lib/chef-utils/dsl/platform_version.rb
new file mode 100644
index 0000000000..33a534c5fb
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/platform_version.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module PlatformVersion
+ include Internal
+
+ # Return the platform_version for the node. Acts like a String
+ # but also provides a mechanism for checking version constraints.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [ChefUtils::VersionString]
+ #
+ def platform_version(node = __getnode)
+ ChefUtils::VersionString.new(node["platform_version"])
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/service.rb b/chef-utils/lib/chef-utils/dsl/service.rb
new file mode 100644
index 0000000000..4dedc8b8d6
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/service.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+require_relative "train_helpers"
+
+module ChefUtils
+ module DSL
+ # NOTE: these are mixed into the service resource+providers specifically and deliberately not
+ # injected into the global namespace
+ module Service
+ include Internal
+ include TrainHelpers
+ include Introspection
+
+ # Returns if debian's old rc.d manager is installed (not necessarily the primary init system).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def debianrcd?
+ file_exist?("/usr/sbin/update-rc.d")
+ end
+
+ # Returns if debian's old invoke rc.d manager is installed (not necessarily the primary init system).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def invokercd?
+ file_exist?("/usr/sbin/invoke-rc.d")
+ end
+
+ # Returns if upstart is installed (not necessarily the primary init system).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def upstart?
+ file_exist?("/sbin/initctl")
+ end
+
+ # Returns if insserv is installed (not necessarily the primary init system).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def insserv?
+ file_exist?("/sbin/insserv")
+ end
+
+ # Returns if redhat's init system is installed (not necessarily the primary init system).
+ #
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def redhatrcd?
+ file_exist?("/sbin/chkconfig")
+ end
+
+ #
+ # Returns if a particular service exists for a particular service init system. Init systems may be :initd, :upstart, :etc_rcd, :xinetd, and :systemd. Example: service_script_exist?(:systemd, 'ntpd')
+ #
+ # @param [Symbol] type The type of init system. :initd, :upstart, :xinetd, :etc_rcd, or :systemd
+ # @param [String] script The name of the service
+ # @since 15.5
+ #
+ # @return [Boolean]
+ #
+ def service_script_exist?(type, script)
+ case type
+ when :initd
+ file_exist?("/etc/init.d/#{script}")
+ when :upstart
+ file_exist?("/etc/init/#{script}.conf")
+ when :xinetd
+ file_exist?("/etc/xinetd.d/#{script}")
+ when :etc_rcd
+ file_exist?("/etc/rc.d/#{script}")
+ when :systemd
+ file_exist?("/etc/init.d/#{script}") ||
+ has_systemd_service_unit?(script) ||
+ has_systemd_unit?(script)
+ else
+ raise ArgumentError, "type of service must be one of :initd, :upstart, :xinetd, :etc_rcd, or :systemd"
+ end
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/train_helpers.rb b/chef-utils/lib/chef-utils/dsl/train_helpers.rb
new file mode 100644
index 0000000000..8cc676d34d
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/train_helpers.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "stringio" unless defined?(StringIO)
+require_relative "../internal"
+
+module ChefUtils
+ module DSL
+ module TrainHelpers
+ include Internal
+
+ #
+ # FIXME: generally these helpers all use the pattern of checking for target_mode?
+ # and then if it is we use train. That approach should likely be flipped so that
+ # even when we're running without target mode we still use train in its local
+ # mode. A prerequisite for that will be better CI testing of train against
+ # chef-client though, and ensuring that the APIs are entirely compatible. This
+ # will be particularly problematic for shell_out APIs and eventual file-creating
+ # APIs which are unlikely to be as sophisticated as the exiting code in chef-client
+ # for locally shelling out and creating files, and just dropping inspec local mode
+ # into chef-client would break the world.
+ #
+
+ # Train wrapper around File.exist? to make it local mode aware.
+ #
+ # @param filename filename to check
+ # @return [Boolean] if it exists
+ #
+ def file_exist?(filename)
+ if __transport_connection
+ __transport_connection.file(filename).exist?
+ else
+ File.exist?(filename)
+ end
+ end
+
+ # XXX: modifications to the StringIO won't get written back
+ # FIXME: this is very experimental and may be a bad idea and may break at any time
+ # @api private
+ #
+ def file_open(*args, &block)
+ if __transport_connection
+ content = __transport_connection.file(args[0]).content
+ string_io = StringIO.new content
+ yield string_io if block_given?
+ string_io
+ else
+ File.open(*args, &block)
+ end
+ end
+
+ # Alias to easily convert IO.read / File.read to file_read
+ def file_read(path)
+ file_open(path).read
+ end
+
+ def file_directory?(path)
+ if __transport_connection
+ __transport_connection.file(filename).directory?
+ else
+ File.directory?(path)
+ end
+ end
+
+ # Alias to easily convert Dir.exist to dir_exist
+ def dir_exist?(path)
+ file_directory?(path)
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/virtualization.rb b/chef-utils/lib/chef-utils/dsl/virtualization.rb
new file mode 100644
index 0000000000..d2c1c5b531
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/virtualization.rb
@@ -0,0 +1,250 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Virtualization
+ include Internal
+
+ # Determine if the current node is a KVM guest.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def kvm?(node = __getnode)
+ node.dig("virtualization", "system") == "kvm" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is a KVM host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def kvm_host?(node = __getnode)
+ node.dig("virtualization", "system") == "kvm" && node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is running in a linux container.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def lxc?(node = __getnode)
+ node.dig("virtualization", "system") == "lxc" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is a linux container host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def lxc_host?(node = __getnode)
+ node.dig("virtualization", "system") == "lxc" && node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is running under Parallels Desktop.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ # true if the machine is currently running under Parallels Desktop, false
+ # otherwise
+ #
+ def parallels?(node = __getnode)
+ node.dig("virtualization", "system") == "parallels" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is a Parallels Desktop host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ # true if the machine is currently running under Parallels Desktop, false
+ # otherwise
+ #
+ def parallels_host?(node = __getnode)
+ node.dig("virtualization", "system") == "parallels" && node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is a VirtualBox guest.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def vbox?(node = __getnode)
+ node.dig("virtualization", "system") == "vbox" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is a VirtualBox host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def vbox_host?(node = __getnode)
+ node.dig("virtualization", "system") == "vbox" && node.dig("virtualization", "role") == "host"
+ end
+
+ # chef-sugar backcompat method
+ alias_method :virtualbox?, :vbox?
+
+ # Determine if the current node is a VMWare guest.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def vmware?(node = __getnode)
+ node.dig("virtualization", "system") == "vmware" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is VMware host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def vmware_host?(node = __getnode)
+ node.dig("virtualization", "system") == "vmware" && node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is an openvz guest.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def openvz?(node = __getnode)
+ node.dig("virtualization", "system") == "openvz" && node.dig("virtualization", "role") == "guest"
+ end
+
+ # Determine if the current node is an openvz host.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def openvz_host?(node = __getnode)
+ node.dig("virtualization", "system") == "openvz" && node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is running under any virtualization environment
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def guest?(node = __getnode)
+ node.dig("virtualization", "role") == "guest"
+ end
+
+ # chef-sugar backcompat method
+ alias_method :virtual?, :guest?
+
+ # Determine if the current node supports running guests under any virtualization environment
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def hypervisor?(node = __getnode)
+ node.dig("virtualization", "role") == "host"
+ end
+
+ # Determine if the current node is NOT running under any virtualization environment (bare-metal or hypervisor on metal)
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ #
+ def physical?(node = __getnode)
+ !virtual?(node)
+ end
+
+ # Determine if the current node is running as a vagrant guest.
+ #
+ # Note that this API is equivalent to just looking for the vagrant user or the
+ # vagrantup.com domain in the hostname, which is the best API we have.
+ #
+ # @param [Chef::Node] node
+ # @since 15.8
+ #
+ # @return [Boolean]
+ # true if the machine is currently running vagrant, false
+ # otherwise
+ #
+ def vagrant?(node = __getnode)
+ vagrant_key?(node) || vagrant_domain?(node) || vagrant_user?(node)
+ end
+
+ private
+
+ # Check if the +vagrant+ key exists on the +node+ object. This key is no
+ # longer populated by vagrant, but it is kept around for legacy purposes.
+ #
+ # @param (see vagrant?)
+ # @return (see vagrant?)
+ #
+ def vagrant_key?(node = __getnode)
+ node.key?("vagrant")
+ end
+
+ # Check if "vagrantup.com" is included in the node's domain.
+ #
+ # @param (see vagrant?)
+ # @return (see vagrant?)
+ #
+ def vagrant_domain?(node = __getnode)
+ node.key?("domain") && !node["domain"].nil? && node["domain"].include?("vagrantup.com")
+ end
+
+ # Check if the system contains a +vagrant+ user.
+ #
+ # @param (see vagrant?)
+ # @return (see vagrant?)
+ #
+ def vagrant_user?(node = __getnode)
+ !!(Etc.getpwnam("vagrant") rescue nil)
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/which.rb b/chef-utils/lib/chef-utils/dsl/which.rb
new file mode 100644
index 0000000000..49779f92a7
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/which.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Which
+ include Internal
+
+ # Lookup an executable through the systems search PATH. Allows specifying an array
+ # of executables to look for. The first executable that is found, along any path entry,
+ # will be the preferred one and returned first. The extra_path will override any default
+ # extra_paths which are added (allowing the user to pass an empty array to remove them).
+ #
+ # When passed a block the block will be called with the full pathname of any executables
+ # which are found, and the block should return truthy or falsey values to further filter
+ # the executable based on arbitrary criteria.
+ #
+ # This is syntactic sugar for `where(...).first`
+ #
+ # This helper can be used in target mode in chef or with train using the appropriate
+ # wiring externally.
+ #
+ # @example Find the most appropriate python executable, searching through the system PATH
+ # plus additionally the "/usr/libexec" directory, which has the dnf libraries
+ # installed and available.
+ #
+ # cmd = which("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
+ # shell_out("#{f} -c 'import dnf'").exitstatus == 0
+ # end
+ #
+ # @param [Array<String>] list of commands to search for
+ # @param [String,Array<String>] array of extra paths to search through
+ # @return [String] the first match
+ #
+ def which(*cmds, extra_path: nil, &block)
+ where(*cmds, extra_path: extra_path, &block).first || false
+ end
+
+ # Lookup all the instances of an an executable that can be found through the systems search PATH.
+ # Allows specifying an array of executables to look for. All the instances of the first executable
+ # that is found will be returned first. The extra_path will override any default extra_paths
+ # which are added (allowing the user to pass an empty array to remove them).
+ #
+ # When passed a block the block will be called with the full pathname of any executables
+ # which are found, and the block should return truthy or falsey values to further filter
+ # the executable based on arbitrary criteria.
+ #
+ # This helper can be used in target mode in chef or with train using the appropriate
+ # wiring externally.
+ #
+ # @example Find all the python executables, searching through the system PATH plus additionally
+ # the "/usr/libexec" directory, which have the dnf libraries installed and available.
+ #
+ # cmds = where("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
+ # shell_out("#{f} -c 'import dnf'").exitstatus == 0
+ # end
+ #
+ # @param [Array<String>] list of commands to search for
+ # @param [String,Array<String>] array of extra paths to search through
+ # @return [String] the first match
+ #
+ def where(*cmds, extra_path: nil, &block)
+ extra_path ||= __extra_path
+ paths = __env_path.split(File::PATH_SEPARATOR) + Array(extra_path)
+ paths.uniq!
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : []
+ exts.unshift("")
+ cmds.map do |cmd|
+ paths.map do |path|
+ exts.map do |ext|
+ filename = File.join(path, "#{cmd}#{ext}")
+ filename if __valid_executable?(filename, &block)
+ end.compact
+ end
+ end.flatten
+ end
+
+ private
+
+ # This is for injecting common extra_paths into the search PATH. The chef-client codebase overrides this into its
+ # own custom mixin to ensure that /usr/sbin, /sbin, etc are in the search PATH for chef-client.
+ #
+ # @api private
+ def __extra_path
+ nil
+ end
+
+ # Windows compatible and train/target-mode-enhanced helper to determine if an executable is valid.
+ #
+ # @api private
+ def __valid_executable?(filename, &block)
+ is_executable =
+ if __transport_connection
+ __transport_connection.file(filename).stat[:mode] & 1 && !__transport_connection.file(filename).directory?
+ else
+ File.executable?(filename) && !File.directory?(filename)
+ end
+ return false unless is_executable
+
+ block ? yield(filename) : true
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/dsl/windows.rb b/chef-utils/lib/chef-utils/dsl/windows.rb
new file mode 100644
index 0000000000..dc8cd4ebc0
--- /dev/null
+++ b/chef-utils/lib/chef-utils/dsl/windows.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "../internal"
+
+module ChefUtils
+ module DSL
+ module Windows
+ require_relative "../version_string"
+
+ include Internal
+
+ # Determine if the current node is Windows Server Core.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.7
+ #
+ # @return [Boolean]
+ #
+ def windows_server_core?(node = __getnode)
+ node["kernel"]["server_core"] == true
+ end
+
+ # Determine if the current node is Windows Workstation.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.7
+ #
+ # @return [Boolean]
+ #
+ def windows_workstation?(node = __getnode)
+ node["kernel"]["product_type"] == "Workstation"
+ end
+
+ # Determine if the current node is Windows Server.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.7
+ #
+ # @return [Boolean]
+ #
+ def windows_server?(node = __getnode)
+ node["kernel"]["product_type"] == "Server"
+ end
+
+ # Determine the current Windows NT version. The NT version often differs from the marketing version, but offers a good way to find desktop and server releases that are based on the same codebase. IE: NT 6.3 is Windows 8.1 and Windows 2012 R2.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [ChefUtils::VersionString]
+ #
+ def windows_nt_version(node = __getnode)
+ ChefUtils::VersionString.new(node["os_version"])
+ end
+
+ # Determine the installed version of PowerShell.
+ #
+ # @param [Chef::Node] node the node to check
+ # @since 15.8
+ #
+ # @return [ChefUtils::VersionString]
+ #
+ def powershell_version(node = __getnode)
+ ChefUtils::VersionString.new(node["languages"]["powershell"]["version"])
+ end
+
+ extend self
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/internal.rb b/chef-utils/lib/chef-utils/internal.rb
new file mode 100644
index 0000000000..e5a7e65c89
--- /dev/null
+++ b/chef-utils/lib/chef-utils/internal.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 ChefUtils
+ #
+ # This is glue code to make the helpers work when called as ChefUtils.helper? from inside of chef-client.
+ #
+ # This also is glue code to make the helpers work when mixed into classes that have node/run_context methods that
+ # provide those objects.
+ #
+ # It should not be assumed that any of this code runs from within chef-client and that the
+ # Chef class or run_context, etc exists.
+ #
+ # This gem may be used by gems like mixlib-shellout which can be consumed by external non-Chef utilities,
+ # so including brittle code here which depends on the existence of the chef-client will cause broken
+ # behavior downstream. You must practice defensive coding, and not make assumptions about running within chef-client.
+ #
+ # Other consumers may mix in the helper classes and then override the methods here and provide their own custom
+ # wiring and override what is provided here. They are marked as private because no downstream user should ever touch
+ # them -- they are intended to be subclassable and overridable by Chef developers in other projects. Chef Software
+ # reserves the right to change the implementation details of this class in minor revs which is what "api private" means,
+ # so external persons should subclass and override only when necessary (submit PRs and issues upstream if this is a problem).
+ #
+ module Internal
+ extend self
+
+ private
+
+ # This should be set to a Chef::Node instance or to some Hash/Mash-like configuration object with the same keys. It needs to
+ # expose keys like `:os`, `:platform`, `:platform_version` and `:platform_family` at least to be useful. It will automatically
+ # pick up a `node` method when mixed into an object that has that as a method (which is the encouraged "public" API to use
+ # for dependency injection rather than overriding the method in this case.
+ #
+ # @return [Hash] hash-like config object
+ #
+ # @api private
+ def __getnode(skip_global = false)
+ # Software developers should feel free to rely on the default wiring here to the node method by implementing the node method in their
+ # own class. For anything more complicated they should completely override the method (overriding the whole method is never wrong and
+ # is safer).
+ return node if respond_to?(:node) && node
+
+ return run_context&.node if respond_to?(:run_context) && run_context&.node
+
+ unless skip_global
+ return Chef.run_context&.node if defined?(Chef) && Chef.respond_to?(:run_context) && Chef.run_context&.node
+ end
+
+ nil
+ end
+
+ # Just a helper to pull the ENV["PATH"] in a train-independent way
+ #
+ # @api private
+ #
+ def __env_path
+ if __transport_connection
+ __transport_connection.run_command("echo $PATH").stdout || ""
+ else
+ ENV["PATH"] || ""
+ end
+ end
+
+ # This should be set to a Train::Plugins::Transport instance. You should wire this up to nil for not using a train transport connection.
+ #
+ # @return [Train::Plugins::Transport]
+ #
+ # @api private
+ #
+ def __transport_connection
+ # Software consumers MUST override this method with their own implementation. The default behavior here is subject to change.
+ return Chef.run_context.transport_connection if defined?(Chef) && Chef.respond_to?(:run_context) && Chef&.run_context&.transport_connection
+
+ nil
+ end
+
+ # This should be set to Chef::Config or to some Hash/Mash-like configuration object with the same keys. It must not be nil.
+ #
+ # @return [Hash] hash-like config object
+ #
+ # @api private
+ #
+ def __config
+ raise NotImplementedError
+ end
+
+ # This should be set to Chef::Log or something that duck-types like it. It must not be nil.
+ #
+ # @return [Chef::Log] logger-like logging object
+ #
+ # @api private
+ #
+ def __log
+ raise NotImplementedError
+ end
+
+ extend self
+ end
+end
diff --git a/chef-utils/lib/chef-utils/mash.rb b/chef-utils/lib/chef-utils/mash.rb
new file mode 100644
index 0000000000..484e172b33
--- /dev/null
+++ b/chef-utils/lib/chef-utils/mash.rb
@@ -0,0 +1,240 @@
+# frozen_string_literal: true
+# Copyright 2009-2016, Dan Kubb
+
+# 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.
+
+# ---
+# ---
+
+# Some portions of blank.rb and mash.rb are verbatim copies of software
+# licensed under the MIT license. That license is included below:
+
+# Copyright 2005-2016, David Heinemeier Hansson
+
+# 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.
+
+# This class has dubious semantics and we only have it so that people can write
+# params[:key] instead of params['key'].
+module ChefUtils
+ class Mash < Hash
+
+ # @param constructor<Object>
+ # The default value for the mash. Defaults to an empty hash.
+ #
+ # @details [Alternatives]
+ # If constructor is a Hash, a new mash will be created based on the keys of
+ # the hash and no default value will be set.
+ def initialize(constructor = {})
+ if constructor.is_a?(Hash)
+ super()
+ update(constructor)
+ else
+ super(constructor)
+ end
+ end
+
+ # @param orig<Object> Mash being copied
+ #
+ # @return [Object] A new copied Mash
+ def initialize_copy(orig)
+ super
+ # Handle nested values
+ each do |k, v|
+ if v.is_a?(Mash) || v.is_a?(Array)
+ self[k] = v.dup
+ end
+ end
+ self
+ end
+
+ # @param key<Object> The default value for the mash. Defaults to nil.
+ #
+ # @details [Alternatives]
+ # If key is a Symbol and it is a key in the mash, then the default value will
+ # be set to the value matching the key.
+ def default(key = nil)
+ if key.is_a?(Symbol) && include?(key = key.to_s)
+ self[key]
+ else
+ super
+ end
+ end
+
+ unless method_defined?(:regular_writer)
+ alias_method :regular_writer, :[]=
+ end
+
+ unless method_defined?(:regular_update)
+ alias_method :regular_update, :update
+ end
+
+ # @param key<Object> The key to set.
+ # @param value<Object>
+ # The value to set the key to.
+ #
+ # @see Mash#convert_key
+ # @see Mash#convert_value
+ def []=(key, value)
+ 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.
+ #
+ # @return [Mash] The updated mash.
+ def update(other_hash)
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
+ self
+ end
+
+ alias_method :merge!, :update
+
+ # @param key<Object> The key to check for. This will be run through convert_key.
+ #
+ # @return [Boolean] True if the key exists in the mash.
+ def key?(key)
+ super(convert_key(key))
+ end
+
+ # def include? def has_key? def member?
+ alias_method :include?, :key?
+ alias_method :has_key?, :key?
+ alias_method :member?, :key?
+
+ # @param key<Object> The key to fetch. This will be run through convert_key.
+ # @param *extras<Array> Default value.
+ #
+ # @return [Object] The value at key or the default value.
+ def fetch(key, *extras)
+ super(convert_key(key), *extras)
+ end
+
+ # @param *indices<Array>
+ # The keys to retrieve values for. These will be run through +convert_key+.
+ #
+ # @return [Array] The values at each of the provided keys
+ def values_at(*indices)
+ indices.collect { |key| self[convert_key(key)] }
+ end
+
+ # @param hash<Hash> The hash to merge with the mash.
+ #
+ # @return [Mash] A new mash with the hash values merged in.
+ def merge(hash)
+ dup.update(hash)
+ end
+
+ # @param key<Object>
+ # The key to delete from the mash.\
+ def delete(key)
+ super(convert_key(key))
+ end
+
+ # @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
+ #
+ # @return [Mash] A new mash without the selected keys.
+ #
+ # @example
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
+ # #=> { "two" => 2, "three" => 3 }
+ def except(*keys)
+ super(*keys.map { |k| convert_key(k) })
+ end
+
+ # Used to provide the same interface as Hash.
+ #
+ # @return [Mash] This mash unchanged.
+ def stringify_keys!; self end
+
+ # @return [Hash] The mash as a Hash with symbolized keys.
+ def symbolize_keys
+ h = Hash.new(default)
+ each { |key, val| h[key.to_sym] = val }
+ h
+ end
+
+ # @return [Hash] The mash as a Hash with string keys.
+ def to_hash
+ Hash.new(default).merge(self)
+ end
+
+ # @return [Mash] Convert a Hash into a Mash
+ # The input Hash's default value is maintained
+ def self.from_hash(hash)
+ mash = Mash.new(hash)
+ mash.default = hash.default
+ mash
+ end
+
+ protected
+
+ # @param key<Object> The key to convert.
+ #
+ # @param [Object]
+ # The converted key. If the key was a symbol, it will be converted to a
+ # string.
+ #
+ # @api private
+ def convert_key(key)
+ key.is_a?(Symbol) ? key.to_s : key
+ end
+
+ # @param value<Object> The value to convert.
+ #
+ # @return [Object]
+ # The converted value. A Hash or an Array of hashes, will be converted to
+ # their Mash equivalents.
+ #
+ # @api private
+ def convert_value(value)
+ if value.class == Hash
+ Mash.from_hash(value)
+ elsif value.is_a?(Array)
+ value.collect { |e| convert_value(e) }
+ else
+ value
+ end
+ end
+ end
+end
diff --git a/chef-utils/lib/chef-utils/version.rb b/chef-utils/lib/chef-utils/version.rb
new file mode 100644
index 0000000000..a99be2b8cf
--- /dev/null
+++ b/chef-utils/lib/chef-utils/version.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+# Copyright:: Copyright (c) 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 ChefUtils
+ CHEFUTILS_ROOT = File.expand_path("..", __dir__)
+ VERSION = "17.0.32"
+end
diff --git a/chef-utils/lib/chef-utils/version_string.rb b/chef-utils/lib/chef-utils/version_string.rb
new file mode 100644
index 0000000000..425fe5b050
--- /dev/null
+++ b/chef-utils/lib/chef-utils/version_string.rb
@@ -0,0 +1,160 @@
+# frozen_string_literal: true
+# 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.
+
+module ChefUtils
+ # 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)
+ val ||= ""
+ super(val)
+ begin
+ @parsed_version = ::Gem::Version.create(self)
+ rescue ArgumentError
+ @parsed_version = nil
+ end
+ 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
+ # ChefUtils::VersionString.new('1.0.0') =~ /^1/
+ # @example Match against a requirement
+ # ChefUtils::VersionString.new('1.0.0') =~ '~> 1.0'
+ def =~(other)
+ case other
+ when Regexp
+ super
+ else
+ begin
+ Gem::Requirement.create(other) =~ parsed_version
+ rescue ArgumentError
+ # one side of the comparison wasn't parsable
+ super
+ end
+ end
+ end
+
+ # Back-compat API for chef-sugar. The other APIs are preferable.
+ #
+ # @api private
+ def satisfies?(*constraints)
+ Gem::Requirement.new(*constraints).satisfied_by?(@parsed_version)
+ end
+ end
+end
diff --git a/chef-utils/spec/spec_helper.rb b/chef-utils/spec/spec_helper.rb
new file mode 100644
index 0000000000..da4fe0a03e
--- /dev/null
+++ b/chef-utils/spec/spec_helper.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+require "chef-utils"
+
+# FIXME: dynamically generate this for accuracy
+HELPER_MODULES = [
+ ChefUtils::DSL::Architecture,
+ ChefUtils::DSL::Cloud,
+ ChefUtils::DSL::Introspection,
+ ChefUtils::DSL::OS,
+ ChefUtils::DSL::DefaultPaths,
+ ChefUtils::DSL::Platform,
+ ChefUtils::DSL::PlatformFamily,
+ ChefUtils::DSL::Service,
+ ChefUtils::DSL::Virtualization,
+ ChefUtils::DSL::Which,
+ ChefUtils::DSL::Windows,
+].freeze
+
+ARCH_HELPERS = (ChefUtils::DSL::Architecture.methods - Module.methods).freeze
+CLOUD_HELPERS = (ChefUtils::DSL::Cloud.methods - Module.methods).freeze
+INTROSPECTION_HELPERS = (ChefUtils::DSL::Introspection.methods - Module.methods).freeze
+OS_HELPERS = (ChefUtils::DSL::OS.methods - Module.methods).freeze
+PLATFORM_FAMILY_HELPERS = (ChefUtils::DSL::PlatformFamily.methods - Module.methods).freeze
+PLATFORM_HELPERS = (ChefUtils::DSL::Platform.methods - Module.methods).freeze
+VIRTUALIZATION_HELPERS = (ChefUtils::DSL::Virtualization.methods - Module.methods).freeze
+WINDOWS_HELPERS = (ChefUtils::DSL::Windows.methods - Module.methods).freeze
+
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ config.filter_run_excluding windows_only: true unless ChefUtils.windows?
+ config.filter_run_excluding unix_only: true if ChefUtils.windows?
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
+ config.disable_monkey_patching!
+
+ # This setting enables warnings. It's recommended, but in some cases may
+ # be too noisy due to issues in dependencies.
+ config.warnings = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = "doc"
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ # config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+end
diff --git a/chef-utils/spec/unit/dsl/architecture_spec.rb b/chef-utils/spec/unit/dsl/architecture_spec.rb
new file mode 100644
index 0000000000..a5336d05ef
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/architecture_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 arch_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (ARCH_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Architecture do
+ let(:node) { { "kernel" => { "machine" => arch } } }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & ARCH_HELPERS).to be_empty
+ end
+ end
+
+ ARCH_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on x86_64" do
+ let(:arch) { "x86_64" }
+
+ arch_reports_true_for(:intel?, :_64_bit?)
+ end
+
+ context "on amd64" do
+ let(:arch) { "amd64" }
+
+ arch_reports_true_for(:intel?, :_64_bit?)
+ end
+ context "on ppc64" do
+ let(:arch) { "ppc64" }
+
+ arch_reports_true_for(:ppc64?, :_64_bit?)
+ end
+ context "on ppc64le" do
+ let(:arch) { "ppc64le" }
+
+ arch_reports_true_for(:ppc64le?, :_64_bit?)
+ end
+ context "on s390x" do
+ let(:arch) { "s390x" }
+
+ arch_reports_true_for(:s390x?, :_64_bit?)
+ end
+ context "on ia64" do
+ let(:arch) { "ia64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on sparc64" do
+ let(:arch) { "sparc64" }
+
+ arch_reports_true_for(:_64_bit?)
+ end
+ context "on aarch64" do
+ let(:arch) { "aarch64" }
+
+ arch_reports_true_for(:_64_bit?, :arm?)
+ end
+ context "on arch64" do
+ let(:arch) { "arch64" }
+
+ arch_reports_true_for(:_64_bit?, :arm?)
+ end
+ context "on arm64" do
+ let(:arch) { "arm64" }
+
+ arch_reports_true_for(:_64_bit?, :arm?)
+ end
+ context "on sun4v" do
+ let(:arch) { "sun4v" }
+
+ arch_reports_true_for(:sparc?, :_64_bit?)
+ end
+ context "on sun4u" do
+ let(:arch) { "sun4u" }
+
+ arch_reports_true_for(:sparc?, :_64_bit?)
+ end
+ context "on i86pc" do
+ let(:arch) { "i86pc" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on i386" do
+ let(:arch) { "i386" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on i686" do
+ let(:arch) { "i686" }
+
+ arch_reports_true_for(:i386?, :intel?, :_32_bit?)
+ end
+ context "on powerpc" do
+ let(:arch) { "powerpc" }
+
+ arch_reports_true_for(:powerpc?, :_32_bit?)
+ end
+ context "on armhf" do
+ let(:arch) { "armhf" }
+
+ arch_reports_true_for(:armhf?, :_32_bit?, :arm?)
+ end
+ context "on armv6l" do
+ let(:arch) { "armv6l" }
+
+ arch_reports_true_for(:armhf?, :_32_bit?, :arm?)
+ end
+ context "on armv7l" do
+ let(:arch) { "armv7l" }
+
+ arch_reports_true_for(:armhf?, :_32_bit?, :arm?)
+ end
+
+ context "on s390" do
+ let(:arch) { "s390" }
+
+ arch_reports_true_for(:s390?, :_32_bit?)
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/cloud_spec.rb b/chef-utils/spec/unit/dsl/cloud_spec.rb
new file mode 100644
index 0000000000..8a718dc37d
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/cloud_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "fauxhai"
+
+def cloud_reports_true_for(*args, node:)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (CLOUD_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Cloud do
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & CLOUD_HELPERS).to be_empty
+ end
+ end
+
+ CLOUD_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on ec2" do
+ cloud_reports_true_for(:cloud?, :ec2?, node: { "ec2" => {}, "cloud" => {} })
+ end
+
+ context "on gce" do
+ cloud_reports_true_for(:cloud?, :gce?, node: { "gce" => {}, "cloud" => {} })
+ end
+
+ context "on rackspace" do
+ cloud_reports_true_for(:cloud?, :rackspace?, node: { "rackspace" => {}, "cloud" => {} })
+ end
+
+ context "on eucalyptus" do
+ cloud_reports_true_for(:cloud?, :eucalyptus?, :euca?, node: { "eucalyptus" => {}, "cloud" => {} })
+ end
+
+ context "on linode" do
+ cloud_reports_true_for(:cloud?, :linode?, node: { "linode" => {}, "cloud" => {} })
+ end
+
+ context "on openstack" do
+ cloud_reports_true_for(:cloud?, :openstack?, node: { "openstack" => {}, "cloud" => {} })
+ end
+
+ context "on azure" do
+ cloud_reports_true_for(:cloud?, :azure?, node: { "azure" => {}, "cloud" => {} })
+ end
+
+ context "on digital_ocean" do
+ cloud_reports_true_for(:cloud?, :digital_ocean?, :digitalocean?, node: { "digital_ocean" => {}, "cloud" => {} })
+ end
+
+ context "on softlayer" do
+ cloud_reports_true_for(:cloud?, :softlayer?, node: { "softlayer" => {}, "cloud" => {} })
+ end
+
+ context "on virtualbox" do
+ it "does not return true for cloud?" do
+ expect(described_class.cloud?({ "virtualbox" => {}, "cloud" => nil })).to be false
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/dsl_spec.rb b/chef-utils/spec/unit/dsl/dsl_spec.rb
new file mode 100644
index 0000000000..fd8869222f
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/dsl_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils do
+ class ThingWithDSL
+ extend ChefUtils
+ end
+
+ (OS_HELPERS + ARCH_HELPERS + PLATFORM_HELPERS + PLATFORM_FAMILY_HELPERS + INTROSPECTION_HELPERS).each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ThingWithDSL).to respond_to(helper)
+ end
+ it "has the #{helper} class method in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/introspection_spec.rb b/chef-utils/spec/unit/dsl/introspection_spec.rb
new file mode 100644
index 0000000000..2a841dee68
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/introspection_spec.rb
@@ -0,0 +1,189 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils::DSL::Introspection do
+ class IntrospectionTestClass
+ include ChefUtils::DSL::Introspection
+ attr_accessor :node
+
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ let(:node) { double("node") }
+
+ let(:test_instance) { IntrospectionTestClass.new(node) }
+
+ context "#docker?" do
+ # FIXME: use a real VividMash for these tests instead of stubbing
+ it "is false by default" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return(nil)
+ expect(ChefUtils.docker?(node)).to be false
+ end
+ it "is true when ohai reports a docker guest" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return("guest")
+ expect(ChefUtils.docker?(node)).to be true
+ end
+ it "is false for any other value other than guest" do
+ expect(node).to receive(:read).with("virtualization", "systems", "docker").and_return("some nonsense")
+ expect(ChefUtils.docker?(node)).to be false
+ end
+ end
+
+ context "#systemd?" do
+ # FIXME: somehow test the train helpers
+ it "returns false if /proc/1/comm does not exist" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(false)
+ expect(ChefUtils.systemd?(node)).to be false
+ end
+
+ it "returns false if /proc/1/comm is not systemd" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(true)
+ expect(File).to receive(:open).with("/proc/1/comm").and_return(StringIO.new("upstart\n"))
+ expect(ChefUtils.systemd?(node)).to be false
+ end
+
+ it "returns true if /proc/1/comm is systemd" do
+ expect(File).to receive(:exist?).with("/proc/1/comm").and_return(true)
+ expect(File).to receive(:open).with("/proc/1/comm").and_return(StringIO.new("systemd\n"))
+ expect(ChefUtils.systemd?(node)).to be true
+ end
+ end
+
+ context "#kitchen?" do
+ before do
+ @saved = ENV["TEST_KITCHEN"]
+ end
+ after do
+ ENV["TEST_KITCHEN"] = @saved
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is not set" do
+ ENV.delete("TEST_KITCHEN")
+ expect(ChefUtils.kitchen?(node)).to be false
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is nil" do
+ ENV["TEST_KITCHEN"] = nil
+ expect(ChefUtils.kitchen?(node)).to be false
+ end
+
+ it "return true if ENV['TEST_KITCHEN'] is set" do
+ ENV["TEST_KITCHEN"] = "1"
+ expect(ChefUtils.kitchen?(node)).to be true
+ end
+ end
+
+ context "#ci?" do
+ before do
+ @saved = ENV["CI"]
+ end
+ after do
+ ENV["CI"] = @saved
+ end
+
+ it "return true if ENV['CI'] is not set" do
+ ENV.delete("CI")
+ expect(ChefUtils.ci?(node)).to be false
+ end
+
+ it "return true if ENV['CI'] is nil" do
+ ENV["CI"] = nil
+ expect(ChefUtils.ci?(node)).to be false
+ end
+
+ it "return true if ENV['CI'] is set" do
+ ENV["CI"] = "1"
+ expect(ChefUtils.ci?(node)).to be true
+ end
+ end
+
+ context "#has_systemd_service_unit?" do
+ # FIXME: test through train helpers
+
+ before do
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example.service").and_return(false)
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example@.service").and_return(false)
+ end
+ end
+
+ it "is false if no unit is present" do
+ expect(ChefUtils.has_systemd_service_unit?("example")).to be false
+ end
+
+ it "is false if no template is present" do
+ expect(ChefUtils.has_systemd_service_unit?("example@instance1")).to be false
+ end
+
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ it "finds a unit in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example.service").and_return(true)
+ expect(ChefUtils.has_systemd_service_unit?("example")).to be true
+ end
+
+ it "finds a template in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example@.service").and_return(true)
+ expect(ChefUtils.has_systemd_service_unit?("example@instance1")).to be true
+ end
+ end
+ end
+
+ context "#has_systemd_unit?" do
+ # FIXME: test through train helpers
+
+ before do
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ allow(File).to receive(:exist?).with("#{base}/systemd/system/example.mount").and_return(false)
+ end
+ end
+
+ it "is false if no unit is present" do
+ expect(ChefUtils.has_systemd_unit?("example.mount")).to be false
+ end
+
+ %w{ /etc /usr/lib /lib /run }.each do |base|
+ it "finds a unit in #{base}" do
+ expect(File).to receive(:exist?).with("#{base}/systemd/system/example.mount").and_return(true)
+ expect(ChefUtils.has_systemd_unit?("example.mount")).to be true
+ end
+ end
+ end
+
+ context "#include_recipe?" do
+ it "is true when the recipe has been seen by the node" do
+ expect(node).to receive(:recipe?).with("myrecipe").and_return(true)
+ expect(ChefUtils.include_recipe?("myrecipe", node)).to be true
+ end
+ it "is false when the recipe has not been seen by the node" do
+ expect(node).to receive(:recipe?).with("myrecipe").and_return(false)
+ expect(ChefUtils.include_recipe?("myrecipe", node)).to be false
+ end
+ it "the alias is true when the recipe has been seen by the node" do
+ expect(node).to receive(:recipe?).with("myrecipe").and_return(true)
+ expect(ChefUtils.includes_recipe?("myrecipe", node)).to be true
+ end
+ it "the alias is false when the recipe has not been seen by the node" do
+ expect(node).to receive(:recipe?).with("myrecipe").and_return(false)
+ expect(ChefUtils.includes_recipe?("myrecipe", node)).to be false
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/os_spec.rb b/chef-utils/spec/unit/dsl/os_spec.rb
new file mode 100644
index 0000000000..967517b957
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/os_spec.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "fauxhai"
+
+def os_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (OS_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::OS do
+ let(:node) { Fauxhai.mock(options).data }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & OS_HELPERS).to be_empty
+ end
+ end
+
+ OS_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on centos" do
+ let(:options) { { platform: "centos" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on clearos" do
+ let(:options) { { platform: "clearos" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ os_reports_true_for
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ os_reports_true_for
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ os_reports_true_for(:darwin?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ os_reports_true_for
+ end
+
+ context "on opensuse" do
+ let(:options) { { platform: "opensuse" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on oracle" do
+ let(:options) { { platform: "oracle" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on redhat" do
+ let(:options) { { platform: "redhat" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ os_reports_true_for
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ os_reports_true_for
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ os_reports_true_for(:linux?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ os_reports_true_for
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/path_sanity_spec.rb b/chef-utils/spec/unit/dsl/path_sanity_spec.rb
new file mode 100644
index 0000000000..50cadd7466
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/path_sanity_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils::DSL::DefaultPaths do
+ class DefaultPathsTestClass
+ include ChefUtils::DSL::DefaultPaths
+ end
+
+ before do
+ allow(Gem).to receive(:bindir).and_return("/opt/ruby/bin/bundle")
+ allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return("/opt/ruby/bin")
+ end
+
+ context "on unix" do
+ before do
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ end
+
+ let(:test_instance) { DefaultPathsTestClass.new }
+
+ it "works with no path" do
+ env = {}
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "works with nil path" do
+ env = { "PATH" => nil }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "works with empty path" do
+ env = { "PATH" => "" }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+ end
+
+ it "appends the default_paths to the end of the path, preserving any that already exist, in the same order" do
+ env = { "PATH" => "/bin:/opt/app/bin:/sbin" }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir}:#{RbConfig::CONFIG["bindir"]}:/bin:/opt/app/bin:/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin")
+ end
+ end
+
+ context "on windows" do
+ before do
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ end
+
+ let(:test_instance) { DefaultPathsTestClass.new }
+
+ it "works with no path" do
+ env = {}
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "works with nil path" do
+ env = { "PATH" => nil }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "works with empty path" do
+ env = { "PATH" => "" }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]}")
+ end
+
+ it "prepends to an existing path" do
+ env = { "PATH" => '%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\\' }
+ expect(test_instance.default_paths(env)).to eql("#{Gem.bindir};#{RbConfig::CONFIG["bindir"]};%SystemRoot%\\system32;%SystemRoot%;%SystemRoot%\\System32\\Wbem;%SYSTEMROOT%\\System32\\WindowsPowerShell\\v1.0\\")
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/platform_family_spec.rb b/chef-utils/spec/unit/dsl/platform_family_spec.rb
new file mode 100644
index 0000000000..fdf4584afd
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/platform_family_spec.rb
@@ -0,0 +1,223 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "fauxhai"
+
+def pf_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (PLATFORM_FAMILY_HELPERS - [ :windows_ruby? ] - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::PlatformFamily do
+ let(:node) { Fauxhai.mock(options).data }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & PLATFORM_FAMILY_HELPERS).to be_empty
+ end
+ end
+
+ ( PLATFORM_FAMILY_HELPERS - [ :windows_ruby? ]).each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ pf_reports_true_for(:debian?)
+ end
+
+ context "on aix" do
+ let(:options) { { platform: "aix" } }
+
+ pf_reports_true_for(:aix?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ pf_reports_true_for(:amazon?, :amazon_linux?, :rpm_based?, :fedora_derived?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ pf_reports_true_for(:arch?, :arch_linux?)
+ end
+
+ context "on centos6" do
+ let(:options) { { platform: "centos", version: "6.10" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
+ end
+
+ context "on centos7" do
+ let(:options) { { platform: "centos", version: "7.7.1908" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
+ end
+
+ context "on centos8" do
+ let(:options) { { platform: "centos", version: "8" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel8?)
+ end
+
+ context "on clearos7" do
+ let(:options) { { platform: "clearos", version: "7.4" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ pf_reports_true_for(:dragonflybsd?)
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ pf_reports_true_for(:fedora?, :rpm_based?, :fedora_derived?, :redhat_based?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ pf_reports_true_for(:freebsd?, :bsd_based?)
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ pf_reports_true_for(:gentoo?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ pf_reports_true_for(:mac_os_x?, :mac?, :osx?, :macos?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ pf_reports_true_for(:openbsd?, :bsd_based?)
+ end
+
+ context "on opensuse" do
+ let(:options) { { platform: "opensuse" } }
+
+ pf_reports_true_for(:suse?, :rpm_based?)
+ end
+
+ context "on oracle6" do
+ let(:options) { { platform: "oracle", version: "6.10" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
+ end
+
+ context "on oracle7" do
+ let(:options) { { platform: "oracle", version: "7.6" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
+ end
+
+ context "on redhat6" do
+ let(:options) { { platform: "redhat", version: "6.10" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
+ end
+
+ context "on redhat7" do
+ let(:options) { { platform: "redhat", version: "7.6" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
+ end
+
+ context "on redhat8" do
+ let(:options) { { platform: "redhat", version: "8" } }
+
+ pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel8?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ pf_reports_true_for(:smartos?, :solaris_based?)
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ pf_reports_true_for(:solaris?, :solaris2?, :solaris_based?)
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ pf_reports_true_for(:suse?, :rpm_based?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ pf_reports_true_for(:windows?)
+ end
+
+ context "node-independent windows APIs" do
+ if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+ it "reports true for :windows_ruby?" do
+ expect(described_class.windows_ruby?).to be true
+ end
+ else
+ it "reports false for :windows_ruby?" do
+ expect(described_class.windows_ruby?).to be false
+ end
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/platform_spec.rb b/chef-utils/spec/unit/dsl/platform_spec.rb
new file mode 100644
index 0000000000..216e15f112
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/platform_spec.rb
@@ -0,0 +1,238 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "fauxhai"
+
+def platform_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method} on the module given a node" do
+ expect(described_class.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with a node" do
+ expect(thing_with_a_node.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with a run_context" do
+ expect(thing_with_a_run_context.send(method, node)).to be true
+ end
+ it "reports true for #{method} when mixed into a class with the dsl" do
+ expect(thing_with_the_dsl.send(method, node)).to be true
+ end
+ it "reports true for #{method} on the main class give a node" do
+ expect(ChefUtils.send(method, node)).to be true
+ end
+ end
+ (PLATFORM_HELPERS - args).each do |method|
+ it "reports false for #{method} on the module given a node" do
+ expect(described_class.send(method, node)).to be false
+ end
+ it "reports false for #{method} when mixed into a class with a node" do
+ expect(thing_with_a_node.send(method, node)).to be false
+ end
+ it "reports false for #{method} when mixed into a class with the dsl" do
+ expect(thing_with_the_dsl.send(method, node)).to be false
+ end
+ it "reports false for #{method} on the main class give a node" do
+ expect(ChefUtils.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Platform do
+ let(:node) { Fauxhai.mock(options).data }
+
+ class ThingWithANode
+ include ChefUtils::DSL::Platform
+ attr_accessor :node
+
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ class ThingWithARunContext
+ include ChefUtils::DSL::Platform
+ class RunContext
+ attr_accessor :node
+ end
+ attr_accessor :run_context
+
+ def initialize(node)
+ @run_context = RunContext.new
+ run_context.node = node
+ end
+ end
+
+ class ThingWithTheDSL
+ include ChefUtils
+ attr_accessor :node
+
+ def initialize(node)
+ @node = node
+ end
+ end
+
+ let(:thing_with_a_node) { ThingWithANode.new(node) }
+ let(:thing_with_a_run_context) { ThingWithARunContext.new(node) }
+ let(:thing_with_the_dsl) { ThingWithTheDSL.new(node) }
+
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & PLATFORM_HELPERS).to be_empty
+ end
+ end
+
+ context "on ubuntu" do
+ let(:options) { { platform: "ubuntu" } }
+
+ platform_reports_true_for(:ubuntu?, :ubuntu_platform?)
+ end
+
+ context "on raspbian" do
+ let(:options) { { platform: "raspbian" } }
+
+ platform_reports_true_for(:raspbian?, :raspbian_platform?)
+ end
+
+ context "on linuxmint" do
+ let(:options) { { platform: "linuxmint" } }
+
+ platform_reports_true_for(:mint?, :linux_mint?, :linuxmint?, :linuxmint_platform?)
+ end
+
+ context "on debian" do
+ let(:options) { { platform: "debian" } }
+
+ platform_reports_true_for(:debian_platform?)
+ end
+
+ context "on aix" do
+ let(:options) { { platform: "aix" } }
+
+ platform_reports_true_for(:aix_platform?)
+ end
+
+ context "on amazon" do
+ let(:options) { { platform: "amazon" } }
+
+ platform_reports_true_for(:amazon_platform?)
+ end
+
+ context "on arch" do
+ let(:options) { { platform: "arch" } }
+
+ platform_reports_true_for(:arch_platform?)
+ end
+
+ context "on centos" do
+ let(:options) { { platform: "centos" } }
+
+ platform_reports_true_for(:centos?, :centos_platform?)
+ end
+
+ context "on clearos" do
+ let(:options) { { platform: "clearos" } }
+
+ platform_reports_true_for(:clearos?, :clearos_platform?)
+ end
+
+ context "on dragonfly4" do
+ let(:options) { { platform: "dragonfly4" } }
+
+ platform_reports_true_for(:dragonfly_platform?)
+ end
+
+ context "on fedora" do
+ let(:options) { { platform: "fedora" } }
+
+ platform_reports_true_for(:fedora_platform?)
+ end
+
+ context "on freebsd" do
+ let(:options) { { platform: "freebsd" } }
+
+ platform_reports_true_for(:freebsd_platform?)
+ end
+
+ context "on gentoo" do
+ let(:options) { { platform: "gentoo" } }
+
+ platform_reports_true_for(:gentoo_platform?)
+ end
+
+ context "on mac_os_x" do
+ let(:options) { { platform: "mac_os_x" } }
+
+ platform_reports_true_for(:mac_os_x_platform?, :macos_platform?)
+ end
+
+ context "on openbsd" do
+ let(:options) { { platform: "openbsd" } }
+
+ platform_reports_true_for(:openbsd_platform?)
+ end
+
+ context "on oracle" do
+ let(:options) { { platform: "oracle" } }
+
+ platform_reports_true_for(:oracle?, :oracle_linux?, :oracle_platform?)
+ end
+
+ context "on redhat" do
+ let(:options) { { platform: "redhat" } }
+
+ platform_reports_true_for(:redhat?, :redhat_enterprise_linux?, :redhat_enterprise?, :redhat_platform?)
+ end
+
+ context "on smartos" do
+ let(:options) { { platform: "smartos" } }
+
+ platform_reports_true_for(:smartos_platform?)
+ end
+
+ context "on solaris2" do
+ let(:options) { { platform: "solaris2" } }
+
+ platform_reports_true_for(:solaris2_platform?)
+ end
+
+ context "on suse" do
+ let(:options) { { platform: "suse" } }
+
+ platform_reports_true_for(:suse_platform?)
+ end
+
+ context "on windows" do
+ let(:options) { { platform: "windows" } }
+
+ platform_reports_true_for(:windows_platform?)
+ end
+
+ context "on opensuseleap" do
+ let(:node) { { "platform" => "opensuseleap", "platform_version" => "15.1", "platform_family" => "suse", "os" => "linux" } }
+
+ platform_reports_true_for(:opensuse_platform?, :opensuseleap_platform?, :opensuse?, :leap_platform?)
+ end
+
+ context "on opensuse" do
+ let(:node) { { "platform" => "opensuse", "platform_version" => "11.0", "platform_family" => "suse", "os" => "linux" } }
+
+ platform_reports_true_for(:opensuse_platform?, :opensuseleap_platform?, :opensuse?, :leap_platform?)
+ end
+
+end
diff --git a/chef-utils/spec/unit/dsl/service_spec.rb b/chef-utils/spec/unit/dsl/service_spec.rb
new file mode 100644
index 0000000000..bfb78441b2
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/service_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils::DSL::Service do
+ class ServiceTestClass
+ include ChefUtils::DSL::Service
+ end
+
+ let(:test_instance) { ServiceTestClass.new }
+
+ context "#debianrcd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/update-rc.d").and_return(true)
+ expect(test_instance.debianrcd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/update-rc.d").and_return(false)
+ expect(test_instance.debianrcd?).to be false
+ end
+ end
+
+ context "#invokercd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/invoke-rc.d").and_return(true)
+ expect(test_instance.invokercd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/usr/sbin/invoke-rc.d").and_return(false)
+ expect(test_instance.invokercd?).to be false
+ end
+ end
+
+ context "#upstart?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/initctl").and_return(true)
+ expect(test_instance.upstart?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/initctl").and_return(false)
+ expect(test_instance.upstart?).to be false
+ end
+ end
+
+ context "#insserv?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/insserv").and_return(true)
+ expect(test_instance.insserv?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/insserv").and_return(false)
+ expect(test_instance.insserv?).to be false
+ end
+ end
+
+ context "#redhatrcd?" do
+ it "is true if the binary is installed" do
+ expect(File).to receive(:exist?).with("/sbin/chkconfig").and_return(true)
+ expect(test_instance.redhatrcd?).to be true
+ end
+ it "is false if the binary is not installed" do
+ expect(File).to receive(:exist?).with("/sbin/chkconfig").and_return(false)
+ expect(test_instance.redhatrcd?).to be false
+ end
+ end
+
+ context "#service_script_exist?" do
+ it "is true if the type is :initd and /etc/init.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/init.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:initd, "example")).to be true
+ end
+ it "is false if the type is :initd and /etc/init.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/init.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:initd, "example")).to be false
+ end
+ it "is true if the type is :upstart and /etc/init script exists" do
+ expect(File).to receive(:exist?).with("/etc/init/example.conf").and_return(true)
+ expect(test_instance.service_script_exist?(:upstart, "example")).to be true
+ end
+ it "is false if the type is :upstart and /etc/init script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/init/example.conf").and_return(false)
+ expect(test_instance.service_script_exist?(:upstart, "example")).to be false
+ end
+ it "is true if the type is :xinetd and /etc/xinetd.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/xinetd.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:xinetd, "example")).to be true
+ end
+ it "is false if the type is :xinetd and /etc/xinetd.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/xinetd.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:xinetd, "example")).to be false
+ end
+ it "is true if the type is :etc_rcd and /etc/rc.d script exists" do
+ expect(File).to receive(:exist?).with("/etc/rc.d/example").and_return(true)
+ expect(test_instance.service_script_exist?(:etc_rcd, "example")).to be true
+ end
+ it "is false if the type is :etc_rcd and /etc/rc.d script does not exist" do
+ expect(File).to receive(:exist?).with("/etc/rc.d/example").and_return(false)
+ expect(test_instance.service_script_exist?(:etc_rcd, "example")).to be false
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/virtualization_spec.rb b/chef-utils/spec/unit/dsl/virtualization_spec.rb
new file mode 100644
index 0000000000..6ee7eb3d3b
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/virtualization_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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 "fauxhai"
+
+def virtualization_reports_true_for(*args, node:)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (VIRTUALIZATION_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Virtualization do
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & VIRTUALIZATION_HELPERS).to be_empty
+ end
+ end
+
+ VIRTUALIZATION_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "on kvm" do
+ virtualization_reports_true_for(:guest?, :virtual?, :kvm?, node: { "virtualization" => { "system" => "kvm", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :kvm_host?, node: { "virtualization" => { "system" => "kvm", "role" => "host" } })
+ end
+ context "on lxc" do
+ virtualization_reports_true_for(:guest?, :virtual?, :lxc?, node: { "virtualization" => { "system" => "lxc", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :lxc_host?, node: { "virtualization" => { "system" => "lxc", "role" => "host" } })
+ end
+ context "on parallels" do
+ virtualization_reports_true_for(:guest?, :virtual?, :parallels?, node: { "virtualization" => { "system" => "parallels", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :parallels_host?, node: { "virtualization" => { "system" => "parallels", "role" => "host" } })
+ end
+ context "on virtualbox" do
+ virtualization_reports_true_for(:guest?, :virtual?, :virtualbox?, :vbox?, node: { "virtualization" => { "system" => "vbox", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :vbox_host?, node: { "virtualization" => { "system" => "vbox", "role" => "host" } })
+ end
+ context "on vmware" do
+ virtualization_reports_true_for(:guest?, :virtual?, :vmware?, node: { "virtualization" => { "system" => "vmware", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :vmware_host?, node: { "virtualization" => { "system" => "vmware", "role" => "host" } })
+ end
+ context "on openvz" do
+ virtualization_reports_true_for(:guest?, :virtual?, :openvz?, node: { "virtualization" => { "system" => "openvz", "role" => "guest" } })
+ virtualization_reports_true_for(:hypervisor?, :physical?, :openvz_host?, node: { "virtualization" => { "system" => "openvz", "role" => "host" } })
+ end
+ context "on metal which is not a virt host" do
+ virtualization_reports_true_for(:physical?, node: {} )
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/which_spec.rb b/chef-utils/spec/unit/dsl/which_spec.rb
new file mode 100644
index 0000000000..5ea5f6ccea
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/which_spec.rb
@@ -0,0 +1,171 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils::DSL::Which do
+
+ class WhichTestClass
+ include ChefUtils::DSL::Which
+ end
+
+ let(:test) { WhichTestClass.new }
+
+ describe "#which" do
+ def self.test_which(description, *args, path: ["/dir1", "/dir2" ].join(File::PATH_SEPARATOR), finds: nil, others: [], directory: false, &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(ENV).to receive(:[]).with("PATH").and_return(path)
+ expect(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
+
+ # 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" ].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("doesn't 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 ["/dir2/foo1", "/dir1/foo2"].include?(f)
+
+ true
+ end
+ end
+
+ context "nil path" do
+ test_which("returns false when it does not find anything", "foo1", path: nil)
+ end
+ end
+
+ describe "#where" do
+ def self.test_where(description, *args, path: ["/dir1", "/dir2" ].join(File::PATH_SEPARATOR), finds: [], others: [], &block)
+ it description do
+ # stub the ENV['PATH']
+ expect(ENV).to receive(:[]).with("PATH").and_return(path)
+ expect(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
+
+ # 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" ].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 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
+
+ context "with a nil path" do
+ test_where("returns empty array when it doesn't find anything", "foo1", path: nil)
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/dsl/windows_spec.rb b/chef-utils/spec/unit/dsl/windows_spec.rb
new file mode 100644
index 0000000000..423cfe5187
--- /dev/null
+++ b/chef-utils/spec/unit/dsl/windows_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+#
+# Copyright:: Copyright (c) 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"
+
+WINDOWS_BOOL_HELPERS = %i{windows_server_core? windows_server? windows_workstation?}.freeze
+
+def windows_reports_true_for(*args)
+ args.each do |method|
+ it "reports true for #{method}" do
+ expect(described_class.send(method, node)).to be true
+ end
+ end
+ (WINDOWS_BOOL_HELPERS - args).each do |method|
+ it "reports false for #{method}" do
+ expect(described_class.send(method, node)).to be false
+ end
+ end
+end
+
+RSpec.describe ChefUtils::DSL::Windows do
+ ( HELPER_MODULES - [ described_class ] ).each do |klass|
+ it "does not have methods that collide with #{klass}" do
+ expect((klass.methods - Module.methods) & WINDOWS_HELPERS).to be_empty
+ end
+ end
+
+ WINDOWS_HELPERS.each do |helper|
+ it "has the #{helper} in the ChefUtils module" do
+ expect(ChefUtils).to respond_to(helper)
+ end
+ end
+
+ context "windows boolean helpers" do
+ context "on Windows Server Core" do
+ let(:node) { { "kernel" => { "server_core" => true } } }
+
+ windows_reports_true_for(:windows_server_core?)
+ end
+
+ context "on Windows Workstation" do
+ let(:node) { { "kernel" => { "product_type" => "Workstation" } } }
+
+ windows_reports_true_for(:windows_workstation?)
+ end
+
+ context "on Windows Server" do
+ let(:node) { { "kernel" => { "product_type" => "Server" } } }
+
+ windows_reports_true_for(:windows_server?)
+ end
+ end
+
+ context "#windows_nt_version on Windows Server 2012 R2" do
+ let(:node) { { "os_version" => "6.3.9600" } }
+ it "it returns a ChefUtils::VersionString object with 6.3.9600" do
+ expect(described_class.send(:windows_nt_version, node)).to eq "6.3.9600"
+ expect(described_class.send(:windows_nt_version, node)).to be_a_kind_of ChefUtils::VersionString
+ end
+ end
+
+ context "#powershell_version on Windows Server 2012 R2" do
+ let(:node) { { "languages" => { "powershell" => { "version" => "4.0" } } } }
+ it "it returns a ChefUtils::VersionString object with 4.0" do
+ expect(described_class.send(:powershell_version, node)).to eq "4.0"
+ expect(described_class.send(:powershell_version, node)).to be_a_kind_of ChefUtils::VersionString
+ end
+ end
+end
diff --git a/chef-utils/spec/unit/mash_spec.rb b/chef-utils/spec/unit/mash_spec.rb
new file mode 100644
index 0000000000..3f83823773
--- /dev/null
+++ b/chef-utils/spec/unit/mash_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+#
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 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"
+
+RSpec.describe ChefUtils::Mash do
+ it "should duplicate a simple key/value mash to a new mash" do
+ data = { x: "one", y: "two", z: "three" }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:x] = "four"
+ expect(@orig[:x]).to eq("one")
+ end
+
+ it "should duplicate a mash with an array to a new mash" do
+ data = { x: "one", y: "two", z: [1, 2, 3] }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:z] << 4
+ expect(@orig[:z]).to eq([1, 2, 3])
+ end
+
+ it "should duplicate a nested mash to a new mash" do
+ data = { x: "one", y: "two", z: ChefUtils::Mash.new({ a: [1, 2, 3] }) }
+ @orig = ChefUtils::Mash.new(data)
+ @copy = @orig.dup
+ expect(@copy.to_hash).to eq(ChefUtils::Mash.new(data).to_hash)
+ @copy[:z][:a] << 4
+ expect(@orig[:z][:a]).to eq([1, 2, 3])
+ end
+
+ # add more!
+end
diff --git a/chef.gemspec b/chef.gemspec
index 367761fbcc..edf70dabfc 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -5,57 +5,71 @@ Gem::Specification.new do |s|
s.name = "chef"
s.version = Chef::VERSION
s.platform = Gem::Platform::RUBY
- s.extra_rdoc_files = ["README.md", "CONTRIBUTING.md", "LICENSE" ]
+ s.extra_rdoc_files = ["README.md", "LICENSE" ]
s.summary = "A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure."
s.description = s.summary
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.2.0"
+ s.required_ruby_version = ">= 2.6.0"
s.add_dependency "chef-config", "= #{Chef::VERSION}"
+ s.add_dependency "chef-utils", "= #{Chef::VERSION}"
+ s.add_dependency "train-core", "~> 3.2", ">= 3.2.28" # 3.2.28 fixes sudo prompts. See https://github.com/chef/chef/pull/9635
+ s.add_dependency "train-winrm", ">= 0.2.5"
- s.add_dependency "mixlib-cli", "~> 1.7"
- s.add_dependency "mixlib-log", "~> 1.3"
- s.add_dependency "mixlib-authentication", "~> 1.4"
- s.add_dependency "mixlib-shellout", "~> 2.0"
- s.add_dependency "mixlib-archive", ">= 0.2.0"
- s.add_dependency "ohai", ">= 8.6.0.alpha.1", "< 9"
+ s.add_dependency "license-acceptance", ">= 1.0.5", "< 3"
+ s.add_dependency "mixlib-cli", ">= 2.1.1", "< 3.0"
+ s.add_dependency "mixlib-log", ">= 2.0.3", "< 4.0"
+ s.add_dependency "mixlib-authentication", ">= 2.1", "< 4"
+ s.add_dependency "mixlib-shellout", ">= 3.1.1", "< 4.0"
+ s.add_dependency "mixlib-archive", ">= 0.4", "< 2.0"
+ s.add_dependency "ohai", "~> 17.0"
+ s.add_dependency "inspec-core", "~> 4.23"
+ s.add_dependency "ffi", ">= 1.9.25", "< 1.14.0" # 1.14 breaks i386 windows. It should be fixed in 1.14.3
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-sftp", "~> 2.1", ">= 2.1.2"
- s.add_dependency "highline", "~> 1.6", ">= 1.6.9"
+ s.add_dependency "net-ssh", ">= 5.1", "< 7"
+ s.add_dependency "net-ssh-multi", "~> 1.2", ">= 1.2.1"
+ s.add_dependency "net-sftp", ">= 2.1.2", "< 4.0"
+ s.add_dependency "ed25519", "~> 1.2" # ed25519 ssh key support
+ s.add_dependency "bcrypt_pbkdf", "= 1.1.0.rc2" # ed25519 ssh key support
+ s.add_dependency "highline", ">= 1.6.9", "< 3"
+ s.add_dependency "tty-prompt", "~> 0.21" # knife ui.ask prompt
+ s.add_dependency "tty-screen", "~> 0.6" # knife list
+ s.add_dependency "tty-table", "~> 0.11" # knife render table output.
+ s.add_dependency "pastel" # knife ui.color
s.add_dependency "erubis", "~> 2.7"
- s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4"
-
- s.add_dependency "chef-zero", ">= 4.8"
+ s.add_dependency "diff-lcs", ">= 1.2.4", "< 1.4.0" # 1.4 breaks output
+ s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3"
+ s.add_dependency "chef-zero", ">= 14.0.11"
+ s.add_dependency "chef-vault"
s.add_dependency "plist", "~> 3.2"
s.add_dependency "iniparse", "~> 1.4"
s.add_dependency "addressable"
-
- # 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.5" }
- s.add_dependency "rspec_junit_formatter", "~> 0.2.0"
- s.add_dependency "serverspec", "~> 2.7"
- s.add_dependency "specinfra", "~> 2.10"
-
s.add_dependency "syslog-logger", "~> 1.6"
s.add_dependency "uuidtools", "~> 2.1.5"
s.add_dependency "proxifier", "~> 1.0"
- # v1.10 is needed as a runtime dep now for 'bundler/inline'
- # very deliberately avoiding putting a ceiling on this to avoid depsolver conflicts.
- s.add_dependency "bundler", ">= 1.10"
-
s.bindir = "bin"
- s.executables = %w{ chef-client chef-solo knife chef-shell chef-apply }
+ s.executables = %w{ knife }
+
+ s.require_paths = %w{ lib }
+ s.files = %w{Gemfile Rakefile LICENSE README.md} +
+ Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } +
+ Dir.glob("*.gemspec") +
+ Dir.glob("tasks/rspec.rb")
- s.require_paths = %w{ lib lib-backcompat }
- 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")
+ s.metadata = {
+ "bug_tracker_uri" => "https://github.com/chef/chef/issues",
+ "changelog_uri" => "https://github.com/chef/chef/blob/master/CHANGELOG.md",
+ "documentation_uri" => "https://docs.chef.io/",
+ "homepage_uri" => "https://www.chef.io",
+ "mailing_list_uri" => "https://discourse.chef.io/",
+ "source_code_uri" => "https://github.com/chef/chef/",
+ }
end
diff --git a/ci/bundle_install.sh b/ci/bundle_install.sh
deleted file mode 100755
index 6c6d76dc5d..0000000000
--- a/ci/bundle_install.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-set -evx
-
-gem environment
-bundler_version=$(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
-gem install bundler -v $bundler_version --user-install --conservative
-export BUNDLE_WITHOUT=default:omnibus_package:test:pry:integration:docgen:maintenance:changelog:travis:aix:bsd:linux:mac_os_x:solaris:windows
-bundle _${bundler_version}_ install
diff --git a/ci/dependency_update.sh b/ci/dependency_update.sh
deleted file mode 100755
index 9588652143..0000000000
--- a/ci/dependency_update.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-set -evx
-
-. ci/bundle_install.sh
-
-bundle exec rake dependencies
-
-git checkout .bundle/config
diff --git a/ci/verify-chef.bat b/ci/verify-chef.bat
deleted file mode 100755
index 7ba0817938..0000000000
--- a/ci/verify-chef.bat
+++ /dev/null
@@ -1,67 +0,0 @@
-
-@ECHO OFF
-
-REM ; %PROJECT_NAME% is set by Jenkins, this allows us to use the same script to verify
-REM ; Chef and Angry Chef
-cd C:\opscode\%PROJECT_NAME%\bin
-
-REM ; We don't want to add the embedded bin dir to the main PATH as this
-REM ; could mask issues in our binstub shebangs.
-SET EMBEDDED_BIN_DIR=C:\opscode\%PROJECT_NAME%\embedded\bin
-
-ECHO.
-
-REM ; Set the temporary directory to a custom location, and wipe it before
-REM ; and after the tests run.
-SET TEMP=%TEMP%\cheftest
-SET TMP=%TMP%\cheftest
-RMDIR /S /Q %TEMP%
-MKDIR %TEMP%
-
-FOR %%b IN (
- chef-client
- knife
- chef-solo
- ohai
-) DO (
-
-
- ECHO Checking for existence of binfile `%%b`...
-
- IF EXIST %%b (
- ECHO ...FOUND IT!
- ) ELSE (
- GOTO :error
- )
- ECHO.
-)
-
-call chef-client --version
-
-REM ; Exercise various packaged tools to validate binstub shebangs
-call %EMBEDDED_BIN_DIR%\ruby --version
-call %EMBEDDED_BIN_DIR%\gem --version
-call %EMBEDDED_BIN_DIR%\bundle --version
-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
-
-IF NOT EXIST "Gemfile.lock" (
- ECHO "Chef gem does not contain a Gemfile.lock! This is needed to run any tests."
- GOTO :error
-)
-
-IF "%PIPELINE_NAME%" == "chef-fips" (
- set CHEF_FIPS=1
-)
-
-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
diff --git a/ci/verify-chef.sh b/ci/verify-chef.sh
deleted file mode 100755
index 71d4afe0df..0000000000
--- a/ci/verify-chef.sh
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/bin/sh
-
-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
-mkdir -p $TMPDIR
-
-# $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
-export PATH
-
-BIN_DIR=/opt/$PROJECT_NAME/bin
-export BIN_DIR
-
-# We don't want to add the embedded bin dir to the main PATH as this
-# could mask issues in our binstub shebangs.
-EMBEDDED_BIN_DIR=/opt/$PROJECT_NAME/embedded/bin
-export EMBEDDED_BIN_DIR
-
-# If we are on Mac our symlinks are located under /usr/local/bin
-# otherwise they are under /usr/bin
-if [ -f /usr/bin/sw_vers ]; then
- USR_BIN_DIR="/usr/local/bin"
-else
- USR_BIN_DIR="/usr/bin"
-fi
-export USR_BIN_DIR
-
-# sanity check that we're getting the correct symlinks from the pre-install script
-# solaris doesn't have readlink or test -e. ls -n is different on BSD. proceed with caution.
-if [ ! -L $USR_BIN_DIR/chef-client ] || [ `ls -l $USR_BIN_DIR/chef-client | awk '{print$NF}'` != "$BIN_DIR/chef-client" ]; then
- echo "$USR_BIN_DIR/chef-client symlink to $BIN_DIR/chef-client was not correctly created by the pre-install script!"
- exit 1
-fi
-
-if [ ! -L $USR_BIN_DIR/knife ] || [ `ls -l $USR_BIN_DIR/knife | awk '{print$NF}'` != "$BIN_DIR/knife" ]; then
- echo "$USR_BIN_DIR/knife symlink to $BIN_DIR/knife was not correctly created by the pre-install script!"
- exit 1
-fi
-
-if [ ! -L $USR_BIN_DIR/chef-solo ] || [ `ls -l $USR_BIN_DIR/chef-solo | awk '{print$NF}'` != "$BIN_DIR/chef-solo" ]; then
- echo "$USR_BIN_DIR/chef-solo symlink to $BIN_DIR/chef-solo was not correctly created by the pre-install script!"
- exit 1
-fi
-
-if [ ! -L $USR_BIN_DIR/ohai ] || [ `ls -l $USR_BIN_DIR/ohai | awk '{print$NF}'` != "$BIN_DIR/ohai" ]; then
- echo "$USR_BIN_DIR/ohai symlink to $BIN_DIR/ohai was not correctly created by the pre-install script!"
- exit 1
-fi
-
-# Ensure the calling environment (disapproval look Bundler) does not
-# infect our Ruby environment created by the `chef-client` cli.
-for ruby_env_var in _ORIGINAL_GEM_PATH \
- BUNDLE_BIN_PATH \
- BUNDLE_GEMFILE \
- GEM_HOME \
- GEM_PATH \
- GEM_ROOT \
- RUBYLIB \
- RUBYOPT \
- RUBY_ENGINE \
- RUBY_ROOT \
- RUBY_VERSION
-
-do
- unset $ruby_env_var
-done
-
-chef-client --version
-
-# Exercise various packaged tools to validate binstub shebangs
-$EMBEDDED_BIN_DIR/ruby --version
-$EMBEDDED_BIN_DIR/gem --version
-$EMBEDDED_BIN_DIR/bundle --version
-$EMBEDDED_BIN_DIR/rspec --version
-
-# ffi-yajl must run in c-extension mode or we take perf hits, so we force it
-# before running rspec so that we don't wind up testing the ffi mode
-FORCE_FFI_YAJL=ext
-export FORCE_FFI_YAJL
-
-# 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
-
- # 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
-
- # 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
-
- 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
- PATH=/opt/$PROJECT_NAME/bin:/opt/$PROJECT_NAME/embedded/bin:$PATH
- export PATH
-
- # Test against the installed Chef gem
- cd /opt/$PROJECT_NAME
- CHEF_GEM=`bundle show chef`
- 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
-fi
diff --git a/ci/version_bump.sh b/ci/version_bump.sh
deleted file mode 100755
index dd53cebfd5..0000000000
--- a/ci/version_bump.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-set -evx
-
-. ci/bundle_install.sh
-
-bundle exec rake version:bump
-
-git checkout .bundle/config
diff --git a/ci/version_show.sh b/ci/version_show.sh
deleted file mode 100755
index 5348f6f090..0000000000
--- a/ci/version_show.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-cat VERSION
diff --git a/cspell.json b/cspell.json
new file mode 100644
index 0000000000..a55b2bfc40
--- /dev/null
+++ b/cspell.json
@@ -0,0 +1,1925 @@
+// cSpell Settings
+{
+ // Version of the setting file. Always 0.1
+ "version": "0.1",
+ // language - current active spelling language
+ "language": "en",
+ "dictionaryDefinitions": [
+ {
+ "name": "chef",
+ "file": "./chef_dictionary.txt",
+ "description": "Custom Chef Dictionary"
+ }
+ ],
+ "dictionaries": ["chef"],
+ // words - list of words to be always considered correct
+ "words": [
+ "abcz",
+ "Abdulin",
+ "ABORTIFHUNG",
+ "ACCOUNTDISABLE",
+ "activationkey",
+ "ADAP",
+ "addlock",
+ "addrs",
+ "ADMINI",
+ "adminonly",
+ "advapi",
+ "Afonov",
+ "agrs",
+ "airgapped",
+ "aixinit",
+ "aklyachkin",
+ "Akzhan",
+ "Alam",
+ "Albenzi",
+ "albertomurillo",
+ "Albertson",
+ "Algorta",
+ "Alloc",
+ "allowlist",
+ "allowlisted",
+ "allowlisting",
+ "allowlists",
+ "allowselfservice",
+ "allprofiles",
+ "alnum",
+ "alwayslastline",
+ "amazonec",
+ "amazonlinux",
+ "anewb",
+ "anonymized",
+ "Antergos",
+ "APISSL",
+ "applewood",
+ "Appscript",
+ "ARCHITEW",
+ "archq",
+ "argdup",
+ "arity",
+ "armhf",
+ "armv",
+ "ARPHELPLINK",
+ "ARPPRODUCTICON",
+ "arry",
+ "Arțăriși",
+ "artem",
+ "Ásgeirsson",
+ "Ashwini",
+ "ASSIGNPRIMARYTOKEN",
+ "astoltz",
+ "astring",
+ "attribs",
+ "attrname",
+ "AUTHN",
+ "AUTHROOT",
+ "AUTHZ",
+ "Autoconverted",
+ "AUTODATASEG",
+ "autodetect",
+ "autodetected",
+ "autogenerated",
+ "autohome",
+ "autoload",
+ "automake",
+ "autoneg",
+ "autorefresh",
+ "autoreplacing",
+ "Autorun",
+ "autoscaling",
+ "autostr",
+ "autotest",
+ "Autovivification",
+ "autovivifies",
+ "autovivify",
+ "autovivifying",
+ "Avishai",
+ "Awasthi",
+ "Axuba",
+ "backcompat",
+ "backends",
+ "backoff",
+ "BACKOFFICE",
+ "backport",
+ "backported",
+ "backporting",
+ "Backporting",
+ "backslashing",
+ "backtrace",
+ "backtraces",
+ "backupfile",
+ "BADKEY",
+ "badpasswordattempts",
+ "badssl",
+ "balasankarc",
+ "Balatero",
+ "barbaz",
+ "Basch",
+ "basearch",
+ "basenames",
+ "basepath",
+ "basestring",
+ "baseurls",
+ "bcast",
+ "Benesch",
+ "Bianco",
+ "bigip",
+ "binfile",
+ "binlink",
+ "binmode",
+ "binread",
+ "bitmask",
+ "bkup",
+ "blockdev",
+ "blocklist",
+ "blocklisted",
+ "blocklists",
+ "bmhatfield",
+ "bobberson",
+ "bobchaos",
+ "bobo",
+ "bobotclown",
+ "bootproto",
+ "bootstrapper",
+ "bootstrappers",
+ "borat",
+ "Brimager",
+ "brodock",
+ "bsearch",
+ "bsevn",
+ "bufptr",
+ "bufsize",
+ "bugfix",
+ "bugfixes",
+ "Bugfixes",
+ "bugfixing",
+ "bugok",
+ "Buildkite",
+ "BUNDER",
+ "busybox",
+ "bypassproxy",
+ "bytesize",
+ "cabfile",
+ "cacerts",
+ "Calastone",
+ "CALLGATE",
+ "calloc",
+ "canonicalize",
+ "CANTOPEN",
+ "CANTREAD",
+ "CANTWRITE",
+ "carg",
+ "caron",
+ "casecmp",
+ "Cavalca",
+ "Cavallo",
+ "ccdata",
+ "ccope",
+ "cdoherty",
+ "centos",
+ "certfile",
+ "Certstore",
+ "CFLAGS",
+ "CFPREFERENCES",
+ "cfprefsd",
+ "cgroup",
+ "chadmccune",
+ "Chaput",
+ "chardev",
+ "chatops",
+ "chdev",
+ "chdir",
+ "checkpointing",
+ "checksummed",
+ "checksumming",
+ "CHEFBIN",
+ "chefcli",
+ "chefclientbootstraptask",
+ "chefclientinstalldone",
+ "CHEFCONFIG",
+ "chefdir",
+ "chefdk",
+ "cheffish",
+ "cheffs",
+ "Chefignore",
+ "chefignored",
+ "chefignores",
+ "chefscriptresult",
+ "CHEFSERVERURL",
+ "cheftest",
+ "CHEFUTILS",
+ "chefzero",
+ "chgroup",
+ "chgrp",
+ "chgrpmem",
+ "chilcote",
+ "CHINESEBIG",
+ "chkconfig",
+ "CHKCONFIG",
+ "Chouhan",
+ "ckbk",
+ "cksum",
+ "cleanpath",
+ "cleanpaths",
+ "cleanroom",
+ "clearlinux",
+ "clearos",
+ "CLEARTEXT",
+ "CLIENTAUTHISSUER",
+ "clientname",
+ "CLOEXEC",
+ "Cloke",
+ "cmdlets",
+ "cmds",
+ "CMDS",
+ "Cmnd",
+ "cname",
+ "codepage",
+ "coderanger",
+ "CODESEG",
+ "colleciton",
+ "COLORINDEX",
+ "COLORREF",
+ "COLORSPACE",
+ "COMBOBOX",
+ "commandline",
+ "commmand",
+ "compat",
+ "COMPOSITECHECK",
+ "computerlevel",
+ "COMPUTERNAME",
+ "computersystem",
+ "comspec",
+ "COMSPEC",
+ "concat",
+ "Cond",
+ "confd",
+ "confdef",
+ "configfetcher",
+ "CONFIGLOCATION",
+ "CONFIGLOCATIONDIR",
+ "configurables",
+ "configurationdata",
+ "confnew",
+ "confold",
+ "constantize",
+ "CONSTS",
+ "containername",
+ "containerpath",
+ "contruct",
+ "convertto",
+ "cookbookname",
+ "cookiecurse",
+ "cookstyle",
+ "Cookstyle",
+ "coookbook",
+ "copypasta",
+ "coreutils",
+ "Cowie",
+ "cpio",
+ "cpjones",
+ "CPPFLAGS",
+ "Crae",
+ "CREAT",
+ "createhomedir",
+ "Createobject",
+ "creds",
+ "Cribbs",
+ "crlfile",
+ "crlnum",
+ "Crory",
+ "Crosta",
+ "CRYPTOPROTECT",
+ "CRYPTPROTECT",
+ "cssh",
+ "csshx",
+ "csum",
+ "ctfs",
+ "CTRY",
+ "CTYPE",
+ "curr",
+ "CVEs",
+ "Cxxx",
+ "dacl",
+ "Daemonization",
+ "daemonizing",
+ "Daemonizing",
+ "databag",
+ "databags",
+ "Datacenter",
+ "DATAFILE",
+ "DATALINK",
+ "datas",
+ "davide",
+ "Davin",
+ "dbag",
+ "DBATCH",
+ "DBCS",
+ "dbresson",
+ "DBUS",
+ "dctoken",
+ "DDTHH",
+ "debconf",
+ "debianrcd",
+ "decompile",
+ "Decompressor",
+ "decryptor",
+ "dedup",
+ "Deepali",
+ "DEFAULTCHAR",
+ "defaultguard",
+ "defn",
+ "defns",
+ "deinstall",
+ "delegators",
+ "deletable",
+ "deletin",
+ "delim",
+ "deliminates",
+ "delims",
+ "Deorukhkar",
+ "deps",
+ "depsolved",
+ "Depsolving",
+ "derekgroh",
+ "Désarmes",
+ "DESCR",
+ "deserialization",
+ "destdir",
+ "Devaux",
+ "devel",
+ "DEVICEEVENT",
+ "devicename",
+ "DEVIOCTL",
+ "devkit",
+ "devmode",
+ "devoptimist",
+ "devtools",
+ "devtoolset",
+ "devuan",
+ "Dfsn",
+ "DFSR",
+ "dgrade",
+ "dgreeninger",
+ "dhparam",
+ "Dialup",
+ "DIDNT",
+ "diffable",
+ "Diffie",
+ "digitalocean",
+ "dirglob",
+ "disablerepo",
+ "DISCARDABLE",
+ "DISCARDNS",
+ "dism",
+ "DISM",
+ "displayname",
+ "distro",
+ "distros",
+ "DMGs",
+ "dmidecode",
+ "dnephin",
+ "docgen",
+ "dockerd",
+ "dockerenv",
+ "dockerignore",
+ "dockerinit",
+ "dokken",
+ "Dokken",
+ "domainandname",
+ "domainname",
+ "domainuser",
+ "Doppel",
+ "DOSDUPHANDLE",
+ "dotdeb",
+ "dotdirs",
+ "dotfile",
+ "dotfiles",
+ "DOTMATCH",
+ "downcase",
+ "downcased",
+ "downcases",
+ "downto",
+ "DPAPI",
+ "dragonflybsd",
+ "dragonsmith",
+ "Dreamcat",
+ "drzewiec",
+ "dscacheutil",
+ "dscl",
+ "dscresource",
+ "dsimport",
+ "dslocal",
+ "Dubey",
+ "dup'd",
+ "DUPEUX",
+ "DWORDLONG",
+ "DYNALINK",
+ "DYNLINK",
+ "Eachern",
+ "EASTEUROPE",
+ "EBUSY",
+ "eckey",
+ "ecparam",
+ "edir",
+ "egid",
+ "elif",
+ "ELOOP",
+ "EMBEDDEDBIN",
+ "EMBEDDEDNT",
+ "EMBEDDEDSERVER",
+ "ENABLEDELAYEDEXPANSION",
+ "enablegroups",
+ "enablement",
+ "enablerepo",
+ "encap",
+ "encryptor",
+ "Encryptor",
+ "endlocal",
+ "ENOENT",
+ "entriesread",
+ "envdata",
+ "ENVVAR",
+ "epel",
+ "EPERM",
+ "EPIPE",
+ "EPOC",
+ "Erchef",
+ "erinn",
+ "Errno",
+ "erroraction",
+ "ERRORLEVEL",
+ "Erubis",
+ "Eruby",
+ "esac",
+ "escapepath",
+ "ESRCH",
+ "etag",
+ "etags",
+ "etcd",
+ "Etcd",
+ "ethtool",
+ "ETHTOOL",
+ "ETIMEDOUT",
+ "euca",
+ "Eugen",
+ "euid",
+ "eval",
+ "eventlog",
+ "EVENTNAME",
+ "EXCED",
+ "executability",
+ "executables",
+ "executionpolicy",
+ "execvp",
+ "exitcode",
+ "exitstatus",
+ "EXTGLOB",
+ "extname",
+ "extrastuff",
+ "exts",
+ "Exts",
+ "FACs",
+ "failburger",
+ "FAILCRITICALERRORS",
+ "failovermethod",
+ "fakeweb",
+ "falsey",
+ "FAPI",
+ "fastestmirror",
+ "fastrestart",
+ "faststart",
+ "faststop",
+ "FASTZIPAPPNAME",
+ "FASTZIPDIR",
+ "fatals",
+ "fauxhai",
+ "featurename",
+ "fflags",
+ "FFLAGS",
+ "Fichter",
+ "fieldname",
+ "Fileexists",
+ "filehandle",
+ "FILEMARK",
+ "fileno",
+ "filepath",
+ "filesets",
+ "filesize",
+ "filespecificity",
+ "filesystems",
+ "FILETIME",
+ "fileutils",
+ "Fileystem",
+ "filterm",
+ "finalizer",
+ "findfs",
+ "findstr",
+ "FIPS",
+ "Fixnums",
+ "fizzbuzz",
+ "flushcache",
+ "fmri",
+ "fname",
+ "fonttbl",
+ "foobarbam",
+ "foorb",
+ "FORCEMINIMIZE",
+ "FORCEOFFFEEDBACK",
+ "FORCEONFEEDBACK",
+ "forwardable",
+ "forwardslashes",
+ "fqdns",
+ "FSCTL",
+ "fsync",
+ "FUGLY",
+ "fullpath",
+ "fullpolicy",
+ "FULLSCREEN",
+ "fuzzifier",
+ "fuzzify",
+ "fuzzyurl",
+ "Fuzzyurl",
+ "fzipi",
+ "gaffneyc",
+ "gecos",
+ "gemdirs",
+ "gencaches",
+ "getaddrinfo",
+ "getbinaryfile",
+ "getc",
+ "getchef",
+ "GETFD",
+ "getgrgid",
+ "getgrnam",
+ "gethostbyname",
+ "gethostname",
+ "getlogin",
+ "getmodhandle",
+ "getnode",
+ "getobj",
+ "getopts",
+ "getppid",
+ "getprint",
+ "getprocaddr",
+ "getpwnam",
+ "getpwuid",
+ "getremotelogin",
+ "getspnam",
+ "gettext",
+ "gettimezone",
+ "gettype",
+ "gids",
+ "Gilles",
+ "gitkeep",
+ "glibc",
+ "globalstate",
+ "globbing",
+ "globsafe",
+ "gmake",
+ "gmetric",
+ "gname",
+ "googlechrome",
+ "GOTOU",
+ "gotoyuzo",
+ "gpasswd",
+ "gpgautoimportkeys",
+ "Graty",
+ "Groh",
+ "groupmembership",
+ "groupname",
+ "gsingla",
+ "gzipped",
+ "habicat",
+ "HACCEL",
+ "hacky",
+ "halp",
+ "HANGEUL",
+ "Hankins",
+ "Hansson",
+ "hardstatus",
+ "HARDWAREPROFILECHANGE",
+ "Haselwanter",
+ "hashify",
+ "hawtness",
+ "HBITMAP",
+ "HBRUSH",
+ "HCOLORSPACE",
+ "HCONV",
+ "HCONVLIST",
+ "HCURSOR",
+ "HDDEDATA",
+ "HDESK",
+ "hdidutil",
+ "hdiutil",
+ "HDROP",
+ "HDWP",
+ "HENHMETAFILE",
+ "hexdigest",
+ "hexencode",
+ "heyjodom",
+ "HFILE",
+ "HFONT",
+ "HGDIOBJ",
+ "HGLOBAL",
+ "HHOOK",
+ "HIBYTE",
+ "HICON",
+ "Hinderliter",
+ "HINSTANCE",
+ "hintname",
+ "hivename",
+ "HIWORD",
+ "HKCC",
+ "HKCR",
+ "HKCU",
+ "HKEY",
+ "HKLM",
+ "HLOCAL",
+ "hmac",
+ "HMDQ",
+ "HMENU",
+ "HMETAFILE",
+ "HMOD",
+ "HMODULE",
+ "HMONITOR",
+ "HOMEDRIVE",
+ "homepath",
+ "HOMEPATH",
+ "HOMESHARE",
+ "Hongli",
+ "hostnamectl",
+ "hostnames",
+ "Hostnames",
+ "hostport",
+ "hostspec",
+ "hostspecific",
+ "hoststr",
+ "hotfix",
+ "HPALETTE",
+ "HPEN",
+ "HRESULT",
+ "HRGN",
+ "HRSRC",
+ "Hsiao",
+ "htop",
+ "httpd",
+ "Huon",
+ "hwaddr",
+ "hwaddress",
+ "HWINSTA",
+ "HWND",
+ "HWNDS",
+ "hyperv",
+ "ichannel",
+ "iconlocation",
+ "idempotence",
+ "idempotency",
+ "idempotently",
+ "idmv",
+ "IEND",
+ "iface",
+ "ifdef",
+ "IGNORECASE",
+ "IHDR",
+ "illumos",
+ "imag",
+ "imageinfo",
+ "Immutablize",
+ "includedir",
+ "includepkgs",
+ "includer",
+ "indentable",
+ "Indentable",
+ "indentt",
+ "inet",
+ "infile",
+ "INFLOOP",
+ "ingobecker",
+ "iniparse",
+ "inno",
+ "inout",
+ "inpipe",
+ "INPORT",
+ "inputformat",
+ "insserv",
+ "installable",
+ "INSTALLDIR",
+ "INSTALLLOCATION",
+ "installondemand",
+ "installonlypkgs",
+ "installp",
+ "installshield",
+ "instanceid",
+ "integerish",
+ "integerize",
+ "INTERDOMAIN",
+ "interfacetype",
+ "interpolatedexitcode",
+ "Interupts",
+ "Intility",
+ "ints",
+ "invokercd",
+ "invokereturnasis",
+ "Ionuț",
+ "IOPL",
+ "ipaddr",
+ "IPADDR",
+ "ipaddress",
+ "Ippolito",
+ "ipscopes",
+ "Ipscopes",
+ "ipsec",
+ "iptables",
+ "Ireton",
+ "isalnum",
+ "isalpha",
+ "isatty",
+ "isdigit",
+ "isuftin",
+ "Itanium",
+ "iterencode",
+ "ivar",
+ "ivars",
+ "Jagdale",
+ "Jagtap",
+ "jailmode",
+ "jakauppila",
+ "janky",
+ "jasonwbarnett",
+ "javatooling",
+ "jdoe",
+ "Jeppe",
+ "jeremyhage",
+ "jeroenj",
+ "Jesai",
+ "jimwise",
+ "JOHAB",
+ "josephmilla",
+ "joyent",
+ "jsvana",
+ "Juanje",
+ "jugatsu",
+ "julienhuon",
+ "jxvf",
+ "katello",
+ "Kauppila",
+ "kaustubh",
+ "Kaustubh",
+ "Keane",
+ "keepalive",
+ "keepalives",
+ "keeppackages",
+ "kenmacleod",
+ "kerberos",
+ "keyfile",
+ "keygen",
+ "keyid",
+ "keyivgen",
+ "KEYNAME",
+ "keyscan",
+ "keyserver",
+ "keystore",
+ "keytab",
+ "keyurl",
+ "kitforbes",
+ "klass",
+ "kool",
+ "Korn",
+ "Kouznetsov",
+ "KSEC",
+ "Kubb",
+ "Kundan",
+ "kwarg",
+ "Laco",
+ "Landauer",
+ "Langenbach",
+ "LANGID",
+ "lastcmdlet",
+ "LASTDAY",
+ "LASTEXITCODE",
+ "launchctl",
+ "launchctrl",
+ "Lavey",
+ "lchmod",
+ "lchown",
+ "LCID",
+ "LCTYPE",
+ "LDAP",
+ "ldif",
+ "LEADBYTE",
+ "Leff",
+ "Leinartas",
+ "Lellan",
+ "lgrmi",
+ "lgrpi",
+ "LGRPID",
+ "LHND",
+ "libarchive",
+ "LIBC",
+ "libexec",
+ "libffi",
+ "libiconv",
+ "liblzma",
+ "libmysqlclient",
+ "LIBPATH",
+ "libtool",
+ "libvirt",
+ "libxml",
+ "Libxml",
+ "libxslt",
+ "libyaml",
+ "lifecycle",
+ "lifecycles",
+ "linuxmint",
+ "LISTBOX",
+ "listprop",
+ "ljust",
+ "lltstype",
+ "LMEM",
+ "LMSHARE",
+ "LMSTR",
+ "LOBYTE",
+ "localdomain",
+ "LOCALGROUP",
+ "localgroupname",
+ "localip",
+ "localmachine",
+ "localname",
+ "localport",
+ "localsystem",
+ "localtime",
+ "LOCKCOUNT",
+ "logfile",
+ "logfiles",
+ "loginwindow",
+ "LOGLOCATION",
+ "logopts",
+ "logrotate",
+ "logstring",
+ "LONGLONG",
+ "loopback",
+ "lowercased",
+ "LOWORD",
+ "lpar",
+ "LPARAM",
+ "LPBOOL",
+ "LPBY",
+ "LPBYTE",
+ "LPCOLORREF",
+ "LPCSTR",
+ "LPCTSTR",
+ "LPCVOID",
+ "LPCWSTR",
+ "lpdw",
+ "LPDWORD",
+ "LPHANDLE",
+ "LPINT",
+ "LPLONG",
+ "lplp",
+ "LPOSVERSIONINFO",
+ "LPOVERLAPPED",
+ "LPSECURITY",
+ "LPSTR",
+ "lpsz",
+ "LPTR",
+ "LPTSTR",
+ "LPVOID",
+ "LPWIN",
+ "LPWORD",
+ "LPWSTR",
+ "LRESULT",
+ "lsfs",
+ "lslpp",
+ "lssrc",
+ "lstat",
+ "lsuser",
+ "ltrace",
+ "lucene",
+ "luid",
+ "LUIDS",
+ "lytao",
+ "lzma",
+ "MACCP",
+ "machinename",
+ "macports",
+ "macterm",
+ "Madsen",
+ "magick",
+ "mailservers",
+ "mailslot",
+ "MAILSLOT",
+ "mailto",
+ "mainloop",
+ "Mainmodule",
+ "makecache",
+ "makedepend",
+ "MAKELONG",
+ "MAKEWORD",
+ "manpages",
+ "Manyanza",
+ "margs",
+ "markerid",
+ "martinisoft",
+ "Masayoshi",
+ "mashify",
+ "Mathieson",
+ "Mathieu",
+ "MAXARGS",
+ "maxbadpasswords",
+ "MAXDWORD",
+ "mbrtowc",
+ "mbtowc",
+ "MDICHILD",
+ "MEDIUMBUSINESS",
+ "megamorf",
+ "memcpy",
+ "memlock",
+ "memoizes",
+ "memoizing",
+ "merlinjim",
+ "MESSAGEDEST",
+ "MESSAGENAME",
+ "metafile",
+ "METAFILE",
+ "metalink",
+ "metasearch",
+ "Miah",
+ "michaellihs",
+ "Microarchitecture",
+ "midddleware",
+ "middlewares",
+ "Midgley",
+ "miimon",
+ "MINALLOCSIZE",
+ "mingw",
+ "minitest",
+ "minmax",
+ "mirrorexpire",
+ "mirrorlist",
+ "mirrorlists",
+ "MITM",
+ "mixins",
+ "Mixins",
+ "mixlib",
+ "mkdir",
+ "mkgroup",
+ "mkmf",
+ "mktemp",
+ "MKTEMP",
+ "mktmpdir",
+ "mname",
+ "mntfs",
+ "mobileconfig",
+ "modname",
+ "MODULETYPE",
+ "Mollison",
+ "Monkeypatches",
+ "monologger",
+ "MONTHLYDATE",
+ "MONTHLYDOW",
+ "motd",
+ "mountpoint",
+ "mounttab",
+ "mpkg",
+ "MPSSVC",
+ "MSDNQTR",
+ "Msftedit",
+ "msgarbossa",
+ "MSGBOX",
+ "msgmax",
+ "MSIERRORCODE",
+ "msiexec",
+ "MSIFASTINSTALL",
+ "MSIHANDLE",
+ "MSVCRT",
+ "mswin",
+ "MSXML",
+ "msys",
+ "MSYSTEM",
+ "Mtyj",
+ "Multicast",
+ "MULTICAST",
+ "multiline",
+ "multipackage",
+ "multiplatform",
+ "multiresource",
+ "Multiresource",
+ "multitenant",
+ "multithreaded",
+ "Multiuser",
+ "multivalue",
+ "munge",
+ "Murawski",
+ "Mutators",
+ "mutexes",
+ "MUXWAITERS",
+ "Mware",
+ "myapp",
+ "mycert",
+ "mycook",
+ "Mycook",
+ "mycorp",
+ "mydirectory",
+ "myecrequest",
+ "myfail",
+ "myfile",
+ "mygem",
+ "myname",
+ "myorg",
+ "myprop",
+ "myrecipe",
+ "myrsarequest",
+ "myserver",
+ "myshare",
+ "mysignedcert",
+ "mything",
+ "mytoken",
+ "myuser",
+ "myworkgroup",
+ "Mzyk",
+ "nagios",
+ "nameservers",
+ "namespacing",
+ "NCRC",
+ "Nehate",
+ "Nejsum",
+ "NERR",
+ "netaddr",
+ "netapi",
+ "NETBINDADD",
+ "NETBINDCHANGE",
+ "NETBINDDISABLE",
+ "NETBINDENABLE",
+ "NETBINDREMOVE",
+ "NETCARD",
+ "netdom",
+ "NETLOGON",
+ "netmsg",
+ "NETNAME",
+ "NETPATH",
+ "netpbm",
+ "nevra",
+ "nevras",
+ "newcron",
+ "newfile",
+ "newguard",
+ "newpkg",
+ "newval",
+ "nillable",
+ "Nimisha",
+ "nlist",
+ "nmcspadden",
+ "NOACCESS",
+ "NOACTION",
+ "NOALIGNMENTFAULTEXCEPT",
+ "noarch",
+ "noauth",
+ "noauto",
+ "nobrowse",
+ "NOCHANGES",
+ "NOCOMPACT",
+ "noconfirm",
+ "Nodearray",
+ "nodename",
+ "NODISCARD",
+ "nodoc",
+ "nodocument",
+ "noexec",
+ "nofile",
+ "NOFOLLOW",
+ "noforce",
+ "NOGPFAULTERRORBOX",
+ "nogpgcheck",
+ "nokeys",
+ "NOLICPROMPT",
+ "nologo",
+ "NOLOGON",
+ "noninteractive",
+ "NONPAGED",
+ "NONROOT",
+ "NONZEROLHND",
+ "NONZEROLPTR",
+ "NOOPENFILEERRORBOX",
+ "nopasswd",
+ "noprofile",
+ "noprogressbar",
+ "norestart",
+ "NORESTART",
+ "nospace",
+ "nospinner",
+ "notapplicable",
+ "notauser",
+ "notconfigured",
+ "notdoingit",
+ "notfound",
+ "NOTIMEOUTIFNOTHUNG",
+ "notlike",
+ "NOTUSED",
+ "noverify",
+ "NOWAIT",
+ "NPAS",
+ "nproc",
+ "nread",
+ "NTSTATUS",
+ "nupkg",
+ "nyan",
+ "objfs",
+ "objs",
+ "OEMCP",
+ "ohai",
+ "Ohai",
+ "Ojeda",
+ "oldguard",
+ "OLDLOGLOCATION",
+ "oldpackage",
+ "oldval",
+ "omry",
+ "onboot",
+ "Onddo",
+ "oneshot",
+ "onidle",
+ "onlogon",
+ "onparent",
+ "ONPARENT",
+ "onstart",
+ "OPLOCK",
+ "OPTARG",
+ "OPTIND",
+ "optstr",
+ "oracledb",
+ "orgname",
+ "orgs",
+ "OSVERSIONINFOEX",
+ "otherfiles",
+ "outform",
+ "outpipe",
+ "outputpath",
+ "Overridable",
+ "Overriders",
+ "ovpn",
+ "OXID",
+ "packageinfo",
+ "pacman",
+ "Pagefile",
+ "pagefiles",
+ "pagefileset",
+ "paludis",
+ "Parallelizer",
+ "PARAMCHANGE",
+ "parms",
+ "passstr",
+ "passw",
+ "passwordage",
+ "passwordless",
+ "PASSWORDNAME",
+ "PATCHLEVEL",
+ "pathed",
+ "PATHEXT",
+ "PATHFINDING",
+ "pathnames",
+ "pbkdf",
+ "PBOOL",
+ "PBOOLEAN",
+ "PBYTE",
+ "pcch",
+ "PCHAR",
+ "PCRYPTPROTECT",
+ "PCSTR",
+ "PCTSTR",
+ "PCWSTR",
+ "PDATA",
+ "PDWORD",
+ "PDWORDLONG",
+ "Pelberg",
+ "Penniman",
+ "performant",
+ "PFILETIME",
+ "PFLOAT",
+ "PGENERICMAPPING",
+ "phabricator",
+ "PHALF",
+ "PHANDLE",
+ "Philis",
+ "PHKEY",
+ "PIDFILE",
+ "pidfiles",
+ "pidora",
+ "pixdrift",
+ "Piyush",
+ "PKCS",
+ "pkgin",
+ "pkginfo",
+ "pkginfos",
+ "pkgng",
+ "pkgrm",
+ "pkgs",
+ "pkgutil",
+ "pkgutils",
+ "PKINIT",
+ "pkitool",
+ "PLCID",
+ "plist",
+ "plistbuddy",
+ "plists",
+ "PLONG",
+ "PLONGLONG",
+ "PLSA",
+ "PLUID",
+ "plutil",
+ "PNAME",
+ "Policybuilder",
+ "policyfile",
+ "Policyfile",
+ "Policyfiles",
+ "POLICYNAME",
+ "popen",
+ "portageq",
+ "PORTVERSION",
+ "POSI",
+ "POSIX",
+ "POST'ing",
+ "POWEREVENT",
+ "powerpc",
+ "ppid",
+ "ppmd",
+ "PQRJ",
+ "Prabhu",
+ "Prajakta",
+ "prco",
+ "Precompiled",
+ "PRECOMPOSED",
+ "precreated",
+ "prefixlen",
+ "prefmaxlen",
+ "prefname",
+ "preinstall",
+ "PREPARSE",
+ "prepend",
+ "prepended",
+ "prepender",
+ "Prepending",
+ "preprod",
+ "prereqs",
+ "preseed",
+ "preseeding",
+ "PRESHUTDOWN",
+ "primarygroupid",
+ "PRINC",
+ "PRINTPROCESSOR",
+ "PRINTQ",
+ "privatekey",
+ "PROCNUM",
+ "procs",
+ "progname",
+ "projectdir",
+ "PROJECTLOCATION",
+ "PROJECTLOCATIONBIN",
+ "PROMPTSTRUCT",
+ "PROTO",
+ "PROTSEQ",
+ "PROTSEQS",
+ "proxified",
+ "proxifier",
+ "Proxifier",
+ "psapi",
+ "PSAPI",
+ "pschaumburg",
+ "Pscx",
+ "Psec",
+ "PSECURITY",
+ "PSHORT",
+ "PSIZE",
+ "PSMODULES",
+ "psobject",
+ "psrepository",
+ "PSSIZE",
+ "PSTR",
+ "pswd",
+ "PTBYTE",
+ "PTCHAR",
+ "PTSTR",
+ "pubkey",
+ "pubkeys",
+ "pubout",
+ "PUCHAR",
+ "PUHALF",
+ "PUINT",
+ "PULONG",
+ "PULONGLONG",
+ "Purohit",
+ "PUSHORT",
+ "putc",
+ "PUTing",
+ "PVOID",
+ "PWCHAR",
+ "pwdp",
+ "pwent",
+ "PWIN",
+ "PWORD",
+ "PWSTR",
+ "PYTHONUNBUFFERED",
+ "pzstd",
+ "qstr",
+ "Quer",
+ "queryformat",
+ "questionmark",
+ "quickconfig",
+ "qword",
+ "rassoc",
+ "rbag",
+ "rbconfig",
+ "rbenv",
+ "RBENV",
+ "rcscript",
+ "rcvar",
+ "rdoc",
+ "RDWR",
+ "readline",
+ "readlines",
+ "readlink",
+ "realloc",
+ "realname",
+ "realpath",
+ "rebooter",
+ "Rebooter",
+ "rebootnow",
+ "rebuilddb",
+ "reconfig",
+ "Recurse",
+ "Recurses",
+ "recursing",
+ "redhat",
+ "redhatrcd",
+ "REDIR",
+ "redirections",
+ "REDIRECTOR",
+ "Redistributable",
+ "redownloading",
+ "reenable",
+ "Reenable",
+ "reenabled",
+ "reenabling",
+ "refcount",
+ "regen",
+ "regexes",
+ "REGSAM",
+ "reimplement",
+ "reimplementing",
+ "reindexing",
+ "REINITIALIZATION",
+ "RELOC",
+ "remoteaccess",
+ "remoteip",
+ "remoteport",
+ "remotesigned",
+ "removelock",
+ "REMOVEME",
+ "removerepo",
+ "Renaud",
+ "renewall",
+ "REPARSE",
+ "reparses",
+ "reparsing",
+ "REPLACESTR",
+ "repocontrols",
+ "repodata",
+ "repoid",
+ "repolist",
+ "repomd",
+ "repos",
+ "repositoryid",
+ "requiretty",
+ "reregister",
+ "reregistered",
+ "resolv",
+ "resourcelist",
+ "restarter",
+ "restorecon",
+ "resumehandle",
+ "retriable",
+ "rhash",
+ "rhosts",
+ "rhscl",
+ "rindex",
+ "rjust",
+ "rmatch",
+ "rmdir",
+ "rmgroup",
+ "RMODE",
+ "rmtree",
+ "rname",
+ "robuye",
+ "roundrobin",
+ "rpartition",
+ "rpmdb",
+ "rpmdep",
+ "rpmds",
+ "rpms",
+ "rpmvercmp",
+ "rquery",
+ "rsakey",
+ "RSAT",
+ "rsyslog",
+ "rtprio",
+ "rubyinstaller",
+ "runas",
+ "RUNFULLSCREEN",
+ "runid",
+ "runlock",
+ "runpid",
+ "runrun",
+ "runtimes",
+ "Runtimes",
+ "RXACT",
+ "rxhash",
+ "rxvlan",
+ "sacl",
+ "safematix",
+ "samus",
+ "savetime",
+ "sawanoboly",
+ "SAWANOBORI",
+ "sbin",
+ "Sbzl",
+ "schtasks",
+ "SCODE",
+ "scopeid",
+ "screenrc",
+ "Scriptable",
+ "SCROLLBAR",
+ "SCROLLBARS",
+ "secoption",
+ "secopts",
+ "secp",
+ "securerandom",
+ "SECURITYPOLICY",
+ "secvalue",
+ "SEGDPL",
+ "SEGLIM",
+ "selinuxenabled",
+ "SEPCHARS",
+ "SERENUM",
+ "serializable",
+ "Sertelon",
+ "servermanagercmd",
+ "servername",
+ "SERVERR",
+ "SERVICENAME",
+ "SESS",
+ "SESSIONCHANGE",
+ "SESSIONID",
+ "SETCOUNT",
+ "setenv",
+ "SETFD",
+ "setlocal",
+ "SETMARK",
+ "setobj",
+ "setprop",
+ "setremotelogin",
+ "setsid",
+ "settimezone",
+ "SETTINGCHANGE",
+ "setuid",
+ "SETX",
+ "SHARENAME",
+ "SHAs",
+ "shellcmd",
+ "shellescape",
+ "shelljoin",
+ "shellout",
+ "shellsplit",
+ "Shellwords",
+ "Sheng",
+ "SHIFTJIS",
+ "shmem",
+ "shoekstra",
+ "Shopify",
+ "shortcode",
+ "shortname",
+ "Shouldnotexist",
+ "SHOWDEFAULT",
+ "showhold",
+ "SHOWMAXIMIZED",
+ "SHOWMINIMIZED",
+ "SHOWMINNOACTIVE",
+ "SHOWNA",
+ "SHOWNOACTIVATE",
+ "SHOWNORMAL",
+ "showpkg",
+ "SHOWWIN",
+ "sideload",
+ "sidorenko",
+ "Sierles",
+ "sigar",
+ "signedheaderauth",
+ "signoff",
+ "SIGQUIT",
+ "SIGTERM",
+ "SIGUSR",
+ "simplejson",
+ "singal",
+ "singleline",
+ "Singleuser",
+ "SINGLEUSERTS",
+ "sitearchdir",
+ "SITENAME",
+ "skipkeys",
+ "skippyj",
+ "skus",
+ "Sliim",
+ "SMALLBUSINESS",
+ "SMARTCARD",
+ "SMARTCARDROOT",
+ "smbshare",
+ "Smokish",
+ "SMTO",
+ "SOLOEXEC",
+ "solv",
+ "somedir",
+ "somefile",
+ "someotherfile",
+ "someuser",
+ "sourceline",
+ "sparc",
+ "spawninstance",
+ "specdoc",
+ "srand",
+ "SRCCHARSET",
+ "SRCCODEPAGE",
+ "SRCFONTSIG",
+ "SRCLOCALE",
+ "srcs",
+ "sshd",
+ "sshock",
+ "SSIZE",
+ "sslcacert",
+ "sslclientcert",
+ "sslclientkey",
+ "sslverify",
+ "STACKSEG",
+ "stacktraces",
+ "Stanislav",
+ "STARTF",
+ "startname",
+ "startsrc",
+ "STARTSSH",
+ "STARTUPINFO",
+ "stdcall",
+ "stdlib",
+ "stepable",
+ "Stepable",
+ "stopsrc",
+ "strace",
+ "Stratton",
+ "strftime",
+ "stringio",
+ "strptime",
+ "struct",
+ "Struct",
+ "stubabble",
+ "stubbable",
+ "subclassable",
+ "subclassing",
+ "subcollections",
+ "subcommand",
+ "subcommands",
+ "subcontext",
+ "subdir",
+ "subdirs",
+ "subfeatures",
+ "SUBFOLDERS",
+ "SUBKEY",
+ "subkeys",
+ "subklass",
+ "sublicensable",
+ "submodule",
+ "submodules",
+ "subprocesses",
+ "subrecords",
+ "subresource",
+ "subresources",
+ "subsession",
+ "SUBSTED",
+ "subsytem",
+ "subtrees",
+ "SUPPRESSMSGBOXES",
+ "Sutay",
+ "svcadm",
+ "svccfg",
+ "svcs",
+ "SWAPERROR",
+ "swapfiles",
+ "swapoff",
+ "swapon",
+ "swappable",
+ "swappiness",
+ "symlink",
+ "Symlink",
+ "symlinked",
+ "symlinking",
+ "symlinks",
+ "sync",
+ "syntaxcache",
+ "sysadminctl",
+ "sysconf",
+ "Sysconf",
+ "sysconfig",
+ "sysctl",
+ "sysctld",
+ "sysctls",
+ "Sysexits",
+ "Sysinternals",
+ "syslog",
+ "sysread",
+ "systctl",
+ "systemdrive",
+ "SYSTEMDRIVE",
+ "systemrestart",
+ "SYSTEMROOT",
+ "systemsetup",
+ "systemshare",
+ "SYSTEMSTART",
+ "Systemtime",
+ "syswrite",
+ "Taddeo",
+ "Tahoma",
+ "TAKAHASHI",
+ "TARGETDIR",
+ "taskscheduler",
+ "TBYTE",
+ "TCBS",
+ "TCHAR",
+ "tcpdump",
+ "TCPIP",
+ "tdev",
+ "tecracer",
+ "tempcron",
+ "tempdir",
+ "tempfile",
+ "tempfiles",
+ "templating",
+ "termsig",
+ "TERMSRV",
+ "testswapfile",
+ "testswapfiledir",
+ "texteditor",
+ "TFTP",
+ "theinen",
+ "Thom",
+ "THRDS",
+ "THREADID",
+ "thumbsup",
+ "tilesize",
+ "Timberman",
+ "TIMECHANGE",
+ "timedatectl",
+ "timh",
+ "timtest",
+ "tmap",
+ "tmpdir",
+ "tmpfs",
+ "tmpname",
+ "tmux",
+ "Tollef",
+ "Tolstov",
+ "toolchain",
+ "Toomas",
+ "toplevel",
+ "totalentries",
+ "traceback",
+ "traceroute",
+ "trainwreck",
+ "triaging",
+ "TRIGGEREVENT",
+ "TRUNC",
+ "TRUSTEDDEVICES",
+ "TRUSTEDPEOPLE",
+ "TRUSTEDPUBLISHER",
+ "TSTDUP",
+ "TSTOVFL",
+ "TTCS",
+ "tuid",
+ "tvfs",
+ "typecode",
+ "tzdata",
+ "tzutil",
+ "ucanhaz",
+ "uchar",
+ "udiff",
+ "UHALF",
+ "uids",
+ "uint",
+ "UINT",
+ "ulong",
+ "ULONGLONG",
+ "UNAVAIL",
+ "uncask",
+ "uncategorized",
+ "uncommented",
+ "uncompress",
+ "Uncompressing",
+ "Undock",
+ "unencrypted",
+ "unescaping",
+ "UNEXP",
+ "UNEXPORTED",
+ "unforked",
+ "unformatter",
+ "Unformatter",
+ "unhold",
+ "unicast",
+ "Unicast",
+ "unignored",
+ "uninst",
+ "unintuitive",
+ "unixy",
+ "Unjoin",
+ "unmanaged",
+ "unmerge",
+ "unmergeables",
+ "unmount",
+ "unmounting",
+ "Unparsable",
+ "Unprocessable",
+ "unredacted",
+ "unregister",
+ "unregistering",
+ "Unregisters",
+ "unscoped",
+ "unscrubbed",
+ "unsecure",
+ "Unsets",
+ "unshift",
+ "UNSPEC",
+ "untag",
+ "untaint",
+ "untampered",
+ "untap",
+ "untar",
+ "untracked",
+ "untrusted",
+ "Untrusted",
+ "unversioned",
+ "UNWRITABLE",
+ "upcase",
+ "uploader",
+ "uploaders",
+ "upsert",
+ "upto",
+ "urllib",
+ "urlopen",
+ "usag",
+ "usags",
+ "useb",
+ "usebackq",
+ "usecount",
+ "USECOUNTCHARS",
+ "USEFILLATTRIBUTE",
+ "USEGLYPHCHARS",
+ "usename",
+ "USEPOSITION",
+ "useradd",
+ "userdefaults",
+ "userdel",
+ "USERDOMAIN",
+ "USEREXIT",
+ "userinfo",
+ "usermod",
+ "USERPROFILE",
+ "USESHOWWINDOW",
+ "USESIZE",
+ "USESTDHANDLES",
+ "ushort",
+ "usri",
+ "ustring",
+ "utime",
+ "uuidgen",
+ "UUIDs",
+ "Vaidas",
+ "valclient",
+ "Validatorless",
+ "varargs",
+ "Vargo",
+ "variablevalue",
+ "Vasiliy",
+ "vendored",
+ "versio",
+ "versioncompare",
+ "versionlock",
+ "VERYSILENT",
+ "vfstab",
+ "viewkind",
+ "vincentaubert",
+ "VIOKBD",
+ "Virender",
+ "virt",
+ "Vivek",
+ "vkhatri",
+ "VMBUS",
+ "voidcmd",
+ "vxfs",
+ "waitfor",
+ "waitpid",
+ "waitsem",
+ "wakeup",
+ "Walck",
+ "waym",
+ "Wbem",
+ "WBITS",
+ "WCHAR",
+ "wchars",
+ "webhook",
+ "WEBHOSTING",
+ "webrick",
+ "webserver",
+ "webui",
+ "WECs",
+ "westus",
+ "whatavailable",
+ "whateverd",
+ "whatif",
+ "whatinstalled",
+ "whatprovides",
+ "whereis",
+ "WINAPI",
+ "winbase",
+ "windef",
+ "windir",
+ "windmc",
+ "WINDOWSTATION",
+ "windres",
+ "winerror",
+ "winevt",
+ "Winmgmt",
+ "winmgmts",
+ "winnt",
+ "WINNT",
+ "winprog",
+ "WINVER",
+ "WIXUI",
+ "WKSTA",
+ "WMIGUID",
+ "woot",
+ "workdir",
+ "WPARAM",
+ "wrlinux",
+ "WSCHILD",
+ "wstring",
+ "wtime",
+ "xabcz",
+ "Xabier",
+ "XATTR",
+ "xchar",
+ "xdigit",
+ "XEEDS",
+ "XFORM",
+ "XMLHTTP",
+ "xproto",
+ "Xtdate",
+ "yieldparam",
+ "zanecodes",
+ "Zanetti",
+ "Zapp",
+ "zeproc",
+ "ZEROEXEC",
+ "ZEROINIT",
+ "Zimmek",
+ "zmscwx",
+ "zolo",
+ "zombiejs",
+ "Zuazo",
+ "zypp"
+ ],
+ // flagWords - list of words to be always considered incorrect
+ // This is useful for offensive words and common spelling errors.
+ // For example "hte" should be "the"
+ "flagWords": [
+ "hte"
+ ],
+ "ignorePaths": [
+ "CHANGELOG.md",
+ "CLA_ARCHIVE.md",
+ "**/*.gemspec",
+ "**/Gemfile.lock",
+ "**/Gemfile",
+ ".expeditor/**/*",
+ "**/*.yml",
+ "**/*.toml",
+ "**/Berksfile",
+ "spec/data/**/*",
+ "lib/chef/provider/package/yum/simplejson/**/*",
+ "lib/chef/provider/package/yum/simplejson/*",
+ "omnibus/resources/chef/**/*",
+ "kitchen-tests/cookbooks/end_to_end/files/*",
+ "spec/**",
+ "docs_site",
+ "distro/ruby_bin_folder/**/*"
+ ],
+ "ignoreRegExpList": [
+ // Ignore "'s" at the end of a word. If "Chef" is an accepted word, so is "Chef's".
+ "/'s\\b/",
+ // Ignore "'d" at the end of a word. If "dup" is an accepted word, so is "dup'd".
+ "/'d\\b/"
+ ]
+}
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.psm1 b/distro/powershell/chef/chef.psm1
deleted file mode 100644
index 6646226795..0000000000
--- a/distro/powershell/chef/chef.psm1
+++ /dev/null
@@ -1,327 +0,0 @@
-
-function Load-Win32Bindings {
- Add-Type -TypeDefinition @"
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace Chef
-{
-
-[StructLayout(LayoutKind.Sequential)]
-public struct PROCESS_INFORMATION
-{
- public IntPtr hProcess;
- public IntPtr hThread;
- public uint dwProcessId;
- public uint dwThreadId;
-}
-
-[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
-public struct STARTUPINFO
-{
- public uint cb;
- public string lpReserved;
- public string lpDesktop;
- public string lpTitle;
- public uint dwX;
- public uint dwY;
- public uint dwXSize;
- public uint dwYSize;
- public uint dwXCountChars;
- public uint dwYCountChars;
- public uint dwFillAttribute;
- public STARTF dwFlags;
- public ShowWindow wShowWindow;
- public short cbReserved2;
- public IntPtr lpReserved2;
- public IntPtr hStdInput;
- public IntPtr hStdOutput;
- public IntPtr hStdError;
-}
-
-[StructLayout(LayoutKind.Sequential)]
-public struct SECURITY_ATTRIBUTES
-{
- public int length;
- public IntPtr lpSecurityDescriptor;
- public bool bInheritHandle;
-}
-
-[Flags]
-public enum CreationFlags : int
-{
- NONE = 0,
- DEBUG_PROCESS = 0x00000001,
- DEBUG_ONLY_THIS_PROCESS = 0x00000002,
- CREATE_SUSPENDED = 0x00000004,
- DETACHED_PROCESS = 0x00000008,
- CREATE_NEW_CONSOLE = 0x00000010,
- CREATE_NEW_PROCESS_GROUP = 0x00000200,
- CREATE_UNICODE_ENVIRONMENT = 0x00000400,
- CREATE_SEPARATE_WOW_VDM = 0x00000800,
- CREATE_SHARED_WOW_VDM = 0x00001000,
- CREATE_PROTECTED_PROCESS = 0x00040000,
- EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
- CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
- CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
- CREATE_DEFAULT_ERROR_MODE = 0x04000000,
- CREATE_NO_WINDOW = 0x08000000,
-}
-
-[Flags]
-public enum STARTF : uint
-{
- STARTF_USESHOWWINDOW = 0x00000001,
- STARTF_USESIZE = 0x00000002,
- STARTF_USEPOSITION = 0x00000004,
- STARTF_USECOUNTCHARS = 0x00000008,
- STARTF_USEFILLATTRIBUTE = 0x00000010,
- STARTF_RUNFULLSCREEN = 0x00000020, // ignored for non-x86 platforms
- STARTF_FORCEONFEEDBACK = 0x00000040,
- STARTF_FORCEOFFFEEDBACK = 0x00000080,
- STARTF_USESTDHANDLES = 0x00000100,
-}
-
-public enum ShowWindow : short
-{
- SW_HIDE = 0,
- SW_SHOWNORMAL = 1,
- SW_NORMAL = 1,
- SW_SHOWMINIMIZED = 2,
- SW_SHOWMAXIMIZED = 3,
- SW_MAXIMIZE = 3,
- SW_SHOWNOACTIVATE = 4,
- SW_SHOW = 5,
- SW_MINIMIZE = 6,
- SW_SHOWMINNOACTIVE = 7,
- SW_SHOWNA = 8,
- SW_RESTORE = 9,
- SW_SHOWDEFAULT = 10,
- SW_FORCEMINIMIZE = 11,
- SW_MAX = 11
-}
-
-public enum StandardHandle : int
-{
- Input = -10,
- Output = -11,
- Error = -12
-}
-
-public static class Kernel32
-{
- [DllImport("kernel32.dll", SetLastError=true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CreateProcess(
- string lpApplicationName,
- string lpCommandLine,
- ref SECURITY_ATTRIBUTES lpProcessAttributes,
- ref SECURITY_ATTRIBUTES lpThreadAttributes,
- [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
- CreationFlags dwCreationFlags,
- IntPtr lpEnvironment,
- string lpCurrentDirectory,
- ref STARTUPINFO lpStartupInfo,
- out PROCESS_INFORMATION lpProcessInformation);
-
- [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", SetLastError=true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CloseHandle(
- IntPtr hObject);
-
- [DllImport("kernel32", SetLastError=true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool GetExitCodeProcess(
- IntPtr hProcess,
- out int lpExitCode);
-}
-}
-"@
-}
-
-function Run-ExecutableAndWait($AppPath, $ArgumentString) {
- # Use the Win32 API to create a new process and wait for it to terminate.
- $null = Load-Win32Bindings
-
- $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
- $tSec = New-Object Chef.SECURITY_ATTRIBUTES
- $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)
- 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) {
- $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- throw "Could not wait for process to terminate. Error code $reason."
- } else {
- throw "WaitForSingleObject failed with return code $waitReason - it's impossible!"
- }
- }
- $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]::CloseHandle($pi.hProcess)
- if (-Not $success) {
- $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- throw "Unable to release process handle. Error code $reason."
- }
- $success = [Chef.Kernel32]::CloseHandle($pi.hThread)
- if (-Not $success) {
- $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
- throw "Unable to release thread handle. Error code $reason."
- }
-}
-
-function Get-ScriptDirectory {
- if (!$PSScriptRoot) {
- $Invocation = (Get-Variable MyInvocation -Scope 1).Value
- $PSScriptRoot = Split-Path $Invocation.MyCommand.Path
- }
- $PSScriptRoot
-}
-
-function Run-RubyCommand($command, $argList) {
- # This method exists to take the given list of arguments and get it past ruby's command-line
- # interpreter unscathed and untampered. See https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L1582
- # for a list of transformations that ruby attempts to perform with your command-line arguments
- # before passing it onto a script. The most important task is to defeat the globbing
- # and wild-card expansion that ruby performs. Note that ruby does not use MSVCRT's argc/argv
- # and deliberately reparses the raw command-line instead.
- #
- # To stop ruby from interpreting command-line arguments as globs, they need to be enclosed in '
- # Ruby doesn't allow any escape characters inside '. This unfortunately prevents us from sending
- # any strings which themselves contain '. Ruby does allow multi-fragment arguments though.
- # "foo bar"'baz qux'123"foo" is interpreted as 1 argument because there are no un-escaped
- # whitespace there. The argument would be interpreted as the string "foo barbaz qux123foo".
- # This lets us escape ' characters by exiting the ' quoted string, injecting a "'" fragment and
- # then resuming the ' quoted string again.
- #
- # In the process of defeating ruby, one must also defeat the helpfulness of powershell.
- # When arguments come into this method, the standard PS rules for interpreting cmdlet arguments
- # apply. When using & (call operator) and providing an array of arguments, powershell (verified
- # on PS 4.0 on Windows Server 2012R2) will not evaluate them but (contrary to documentation),
- # it will still marginally interpret them. The behaviour of PS 5.0 seems to be different but
- # ignore that for now. If any of the provided arguments has a space in it, powershell checks
- # the first and last character to ensure that they are " characters (and that's all it checks).
- # If they are not, it will blindly surround that argument with " characters. It won't do this
- # operation if no space is present, even if other special characters are present. If it notices
- # leading and trailing " characters, it won't actually check to see if there are other "
- # characters in the string. Since PS 5.0 changes this behavior, we could consider using the --%
- # "stop screwing up my arguments" operator, which is available since PS 3.0. When encountered
- # --% indicates that the rest of line is to be sent literally... except if the parser encounters
- # %FOO% cmd style environment variables. Because reasons. And there is no way to escape the
- # % character in *any* waym shape or form.
- # https://connect.microsoft.com/PowerShell/feedback/details/376207/executing-commands-which-require-quotes-and-variables-is-practically-impossible
- #
- # In case you think that you're either reading this incorrectly or that I'm full of shit, here
- # are some examples. These use EchoArgs.exe from the PowerShell Community Extensions package.
- # I have not included the argument parsing output from EchoArgs.exe to prevent confusing you with
- # more details about MSVCRT's parsing algorithm.
- #
- # $x = "foo '' bar `"baz`""
- # & EchoArgs @($x, $x)
- # Command line:
- # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" "foo '' bar "baz"" "foo '' bar "baz""
- #
- # $x = "abc'123'nospace`"lulz`"!!!"
- # & EchoArgs @($x, $x)
- # Command line:
- # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lulz"!!! abc'123'nospace"lulz"!!!
- #
- # $x = "`"`"Look ma! Tonnes of spaces! 'foo' 'bar'`"`""
- # & EchoArgs @($x, $x)
- # Command line:
- # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" ""Look ma! Tonnes of spaces! 'foo' 'bar'"" ""Look ma! Tonnes of spaces! 'foo' 'bar'""
- #
- # Given all this, we can now device a strategy to work around all these immensely helpful, well
- # documented and useful tools by looking at each incoming argument, escaping any ' characters
- # with a '"'"' sequence, surrounding each argument with ' & joining them with a space separating
- # them.
- # There is another bug (https://bugs.ruby-lang.org/issues/11142) that causes ruby to mangle any
- # "" two-character double quote sequence but since we always emit our strings inside ' except for
- # ' characters, this should be ok. Just remember that an argument '' should get translated to
- # ''"'"''"'"'' on the command line. If those intervening empty ''s are not present, the presence
- # of "" will cause ruby to mangle that argument.
- $transformedList = $argList | foreach { "'" + ( $_ -replace "'","'`"'`"'" ) + "'" }
- $fortifiedArgString = $transformedList -join ' '
-
- # Use the correct embedded ruby path. We'll be deployed at a path that looks like
- # [C:\opscode or some other prefix]\chef\modules\chef
- $ruby = Join-Path (Get-ScriptDirectory) "..\..\embedded\bin\ruby.exe"
- $commandPath = Join-Path (Get-ScriptDirectory) "..\..\bin\$command"
-
- Run-ExecutableAndWait $ruby """$ruby"" '$commandPath' $fortifiedArgString"
-}
-
-
-function chef-apply {
- Run-RubyCommand 'chef-apply' $args
-}
-
-function chef-client {
- Run-RubyCommand 'chef-client' $args
-}
-
-function chef-service-manager {
- Run-RubyCommand 'chef-service-manager' $args
-}
-
-function chef-shell {
- Run-RubyCommand 'chef-shell' $args
-}
-
-function chef-solo {
- Run-RubyCommand 'chef-solo' $args
-}
-
-function chef-windows-service {
- Run-RubyCommand 'chef-windows-service' $args
-}
-
-function knife {
- Run-RubyCommand 'knife' $args
-}
-
-Export-ModuleMember -function chef-apply
-Export-ModuleMember -function chef-client
-Export-ModuleMember -function chef-service-manager
-Export-ModuleMember -function chef-shell
-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.
-# Export-ModuleMember -function Run-RubyCommand
-# Remove-Module chef
-# Import-Module chef
-# "puts ARGV" | Out-File C:\opscode\chef\bin\puts_args
-# Run-RubyCommand puts_args 'Here' "are" some '"very interesting"' 'arguments[to]' "`"try out`""
diff --git a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll
new file mode 100644
index 0000000000..022591ffd1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll
new file mode 100644
index 0000000000..2316fffdb5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/Ijwhost.dll b/distro/ruby_bin_folder/AMD64/Ijwhost.dll
new file mode 100644
index 0000000000..e2c6c9fc90
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/Ijwhost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/Newtonsoft.Json.dll b/distro/ruby_bin_folder/AMD64/Newtonsoft.Json.dll
new file mode 100644
index 0000000000..1971a35679
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/Newtonsoft.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/concrt140.dll b/distro/ruby_bin_folder/AMD64/concrt140.dll
new file mode 100644
index 0000000000..87dd4f08fe
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/concrt140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/host/fxr/5.0.0/hostfxr.dll b/distro/ruby_bin_folder/AMD64/host/fxr/5.0.0/hostfxr.dll
new file mode 100644
index 0000000000..7595b4cd83
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/host/fxr/5.0.0/hostfxr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/msvcp140.dll b/distro/ruby_bin_folder/AMD64/msvcp140.dll
new file mode 100644
index 0000000000..0e0639bc22
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/msvcp140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/msvcp140_1.dll b/distro/ruby_bin_folder/AMD64/msvcp140_1.dll
new file mode 100644
index 0000000000..53845b5f1f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/msvcp140_1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/msvcp140_2.dll b/distro/ruby_bin_folder/AMD64/msvcp140_2.dll
new file mode 100644
index 0000000000..4365b9f06d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/msvcp140_2.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/msvcp140_atomic_wait.dll b/distro/ruby_bin_folder/AMD64/msvcp140_atomic_wait.dll
new file mode 100644
index 0000000000..1cfb379c67
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/msvcp140_atomic_wait.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/msvcp140_codecvt_ids.dll b/distro/ruby_bin_folder/AMD64/msvcp140_codecvt_ids.dll
new file mode 100644
index 0000000000..42cc3406a5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/msvcp140_codecvt_ids.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
new file mode 100644
index 0000000000..cb9a5c276b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
new file mode 100644
index 0000000000..3f472f2c81
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
new file mode 100644
index 0000000000..03a0860086
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json
new file mode 100644
index 0000000000..9d0500cd16
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json
@@ -0,0 +1,9 @@
+{
+ "runtimeOptions": {
+ "tfm": "net5.0",
+ "framework": {
+ "name": "Microsoft.NETCore.App",
+ "version": "5.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll
new file mode 100644
index 0000000000..e2c6c9fc90
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll
new file mode 100644
index 0000000000..2a70d37424
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll
new file mode 100644
index 0000000000..f1d216624a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll
new file mode 100644
index 0000000000..fe584ad61c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll
new file mode 100644
index 0000000000..8b6f598975
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll
new file mode 100644
index 0000000000..bfdf0b004b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.amd64.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.amd64.dll
new file mode 100644
index 0000000000..e376a20352
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.amd64.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll
new file mode 100644
index 0000000000..2e5c7ea7d5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll
new file mode 100644
index 0000000000..c70b1d3205
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll
new file mode 100644
index 0000000000..6d14e763cd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json
new file mode 100644
index 0000000000..a415e4237a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json
@@ -0,0 +1,2576 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v5.0/win10-x64",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v5.0": {},
+ ".NETCoreApp,Version=v5.0/win10-x64": {
+ "Chef.Powershell.Core/1.0.0": {
+ "dependencies": {
+ "Microsoft.PowerShell.Commands.Diagnostics": "7.1.0",
+ "Microsoft.PowerShell.Commands.Management": "7.1.0",
+ "Microsoft.PowerShell.Commands.Utility": "7.1.0",
+ "Microsoft.PowerShell.ConsoleHost": "7.1.0",
+ "Microsoft.WSMan.Management": "7.1.0",
+ "Microsoft.WSMan.Runtime": "7.1.0",
+ "Newtonsoft.Json": "12.0.3",
+ "System.Management.Automation": "7.1.0",
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x64": "5.0.0"
+ },
+ "runtime": {
+ "Chef.Powershell.Core.dll": {}
+ }
+ },
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/5.0.0": {
+ "runtime": {
+ "Microsoft.CSharp.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.VisualBasic.Core.dll": {
+ "assemblyVersion": "10.0.6.0",
+ "fileVersion": "11.0.20.51904"
+ },
+ "Microsoft.VisualBasic.dll": {
+ "assemblyVersion": "10.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.Win32.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.Win32.Registry.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.AppContext.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Buffers.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Concurrent.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Immutable.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.NonGeneric.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Specialized.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.Annotations.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.DataAnnotations.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.EventBasedAsync.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.TypeConverter.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Configuration.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Console.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Core.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.Common.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.DataSetExtensions.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Contracts.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Debug.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.DiagnosticSource.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.FileVersionInfo.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Process.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.StackTrace.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.TextWriterTraceListener.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Tools.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.TraceSource.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Tracing.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Drawing.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Drawing.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Dynamic.Runtime.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Formats.Asn1.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.Calendars.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.Brotli.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.FileSystem.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.ZipFile.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.DriveInfo.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.Watcher.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.IsolatedStorage.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.MemoryMappedFiles.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Pipes.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Pipes.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.UnmanagedMemoryStream.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Expressions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Parallel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Queryable.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Memory.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Http.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Http.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.HttpListener.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Mail.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.NameResolution.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.NetworkInformation.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Ping.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Requests.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Security.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.ServicePoint.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Sockets.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebClient.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebHeaderCollection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebProxy.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebSockets.Client.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebSockets.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Numerics.Vectors.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Numerics.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ObjectModel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.DataContractSerialization.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Uri.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Xml.Linq.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Xml.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.DispatchProxy.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.ILGeneration.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.Lightweight.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Metadata.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.TypeExtensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.Reader.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.ResourceManager.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.Writer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.CompilerServices.Unsafe.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.CompilerServices.VisualC.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Handles.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.InteropServices.RuntimeInformation.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.InteropServices.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Intrinsics.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Loader.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Numerics.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Formatters.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Xml.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Claims.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Algorithms.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Cng.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Csp.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Encoding.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.OpenSsl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.X509Certificates.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Principal.Windows.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Principal.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.SecureString.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ServiceModel.Web.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ServiceProcess.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.CodePages.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encodings.Web.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.RegularExpressions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Channels.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Overlapped.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Dataflow.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Parallel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Thread.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.ThreadPool.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Timer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Transactions.Local.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Transactions.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ValueTuple.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Web.HttpUtility.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Web.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Windows.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.Linq.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.ReaderWriter.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.Serialization.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XPath.XDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XPath.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XmlDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XmlSerializer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "WindowsBase.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscorlib.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "netstandard.dll": {
+ "assemblyVersion": "2.1.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.CoreLib.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ },
+ "native": {
+ "Microsoft.DiaSymReader.Native.amd64.dll": {
+ "fileVersion": "14.12.25830.2"
+ },
+ "api-ms-win-core-console-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-console-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-datetime-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-debug-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-errorhandling-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l2-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-handle-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-heap-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-interlocked-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-libraryloader-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-localization-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-memory-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-namedpipe-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processenvironment-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processthreads-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processthreads-l1-1-1.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-profile-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-rtlsupport-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-string-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-synch-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-synch-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-sysinfo-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-timezone-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-util-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-conio-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-convert-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-environment-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-filesystem-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-heap-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-locale-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-math-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-multibyte-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-private-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-process-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-runtime-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-stdio-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-string-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-time-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-utility-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "clrcompression.dll": {
+ "fileVersion": "42.42.42.42424"
+ },
+ "clretwrc.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "clrjit.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "coreclr.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "createdump.exe": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "dbgshim.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "hostfxr.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "hostpolicy.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordaccore.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordaccore_amd64_amd64_5.0.20.51904.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordbi.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscorrc.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "ucrtbase.dll": {
+ "fileVersion": "10.0.19041.1"
+ }
+ }
+ },
+ "Markdig.Signed/0.21.1": {
+ "runtime": {
+ "lib/netcoreapp3.1/Markdig.Signed.dll": {
+ "assemblyVersion": "0.21.1.0",
+ "fileVersion": "0.21.1.0"
+ }
+ }
+ },
+ "Microsoft.ApplicationInsights/2.15.0": {
+ "dependencies": {
+ "System.Diagnostics.DiagnosticSource": "4.6.0",
+ "System.Memory": "4.5.4"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.ApplicationInsights.dll": {
+ "assemblyVersion": "2.15.0.44797",
+ "fileVersion": "2.15.0.44797"
+ }
+ }
+ },
+ "Microsoft.CodeAnalysis.Analyzers/3.0.0": {},
+ "Microsoft.CodeAnalysis.Common/3.7.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.0.0",
+ "System.Collections.Immutable": "1.5.0",
+ "System.Memory": "4.5.4",
+ "System.Reflection.Metadata": "1.6.0",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0",
+ "System.Text.Encoding.CodePages": "5.0.0",
+ "System.Threading.Tasks.Extensions": "4.5.3"
+ },
+ "runtime": {
+ "lib/netcoreapp3.1/Microsoft.CodeAnalysis.dll": {
+ "assemblyVersion": "3.7.0.0",
+ "fileVersion": "3.700.20.37502"
+ }
+ },
+ "resources": {
+ "lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "cs"
+ },
+ "lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "de"
+ },
+ "lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "es"
+ },
+ "lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "fr"
+ },
+ "lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "it"
+ },
+ "lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ja"
+ },
+ "lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ko"
+ },
+ "lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "pl"
+ },
+ "lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "pt-BR"
+ },
+ "lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ru"
+ },
+ "lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "tr"
+ },
+ "lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "zh-Hans"
+ },
+ "lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "zh-Hant"
+ }
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp/3.7.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Common": "3.7.0"
+ },
+ "runtime": {
+ "lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.dll": {
+ "assemblyVersion": "3.7.0.0",
+ "fileVersion": "3.700.20.37502"
+ }
+ },
+ "resources": {
+ "lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "cs"
+ },
+ "lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "de"
+ },
+ "lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "es"
+ },
+ "lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "fr"
+ },
+ "lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "it"
+ },
+ "lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ja"
+ },
+ "lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ko"
+ },
+ "lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "pl"
+ },
+ "lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "pt-BR"
+ },
+ "lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ru"
+ },
+ "lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "tr"
+ },
+ "lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "zh-Hans"
+ },
+ "lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "zh-Hant"
+ }
+ }
+ },
+ "Microsoft.CSharp/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Dynamic.Runtime": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.Linq.Expressions": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Extensions": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/Microsoft.CSharp.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "Microsoft.Management.Infrastructure/2.0.0": {
+ "dependencies": {
+ "Microsoft.Management.Infrastructure.Runtime.Unix": "2.0.0",
+ "Microsoft.Management.Infrastructure.Runtime.Win": "2.0.0"
+ }
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Unix/2.0.0": {},
+ "Microsoft.Management.Infrastructure.Runtime.Win/2.0.0": {
+ "runtime": {
+ "runtimes/win10-x64/lib/netstandard1.6/Microsoft.Management.Infrastructure.Native.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x64/lib/netstandard1.6/Microsoft.Management.Infrastructure.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "10.0.18362.1"
+ }
+ },
+ "native": {
+ "runtimes/win10-x64/native/Microsoft.Management.Infrastructure.Native.Unmanaged.dll": {
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x64/native/mi.dll": {
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x64/native/miutils.dll": {
+ "fileVersion": "10.0.18362.1"
+ }
+ }
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {},
+ "Microsoft.NETCore.Targets/1.1.0": {},
+ "Microsoft.PowerShell.Commands.Diagnostics/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Diagnostics.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Commands.Management/7.1.0": {
+ "dependencies": {
+ "Microsoft.PowerShell.Security": "7.1.0",
+ "System.ServiceProcess.ServiceController": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Management.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Commands.Utility/7.1.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.CSharp": "3.7.0",
+ "Microsoft.PowerShell.MarkdownRender": "7.1.0",
+ "NJsonSchema": "10.2.2",
+ "System.Drawing.Common": "5.0.0",
+ "System.Management.Automation": "7.1.0",
+ "System.Threading.AccessControl": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Utility.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.ConsoleHost/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.ConsoleHost.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.CoreCLR.Eventing/7.1.0": {
+ "dependencies": {
+ "System.Diagnostics.EventLog": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.CoreCLR.Eventing.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.MarkdownRender/7.1.0": {
+ "dependencies": {
+ "Markdig.Signed": "0.21.1",
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.MarkdownRender.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Native/7.1.0": {
+ "native": {
+ "runtimes/win-x64/native/PowerShell.Core.Instrumentation.dll": {
+ "fileVersion": "10.0.10011.16384"
+ },
+ "runtimes/win-x64/native/pwrshplugin.dll": {
+ "fileVersion": "10.0.10011.16384"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Security/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Security.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.Win32.Registry/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Win32.Registry.AccessControl/5.0.0": {
+ "dependencies": {
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.Security.AccessControl": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Win32.SystemEvents/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/Microsoft.Win32.SystemEvents.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.WSMan.Management/7.1.0": {
+ "dependencies": {
+ "Microsoft.WSMan.Runtime": "7.1.0",
+ "System.Management.Automation": "7.1.0",
+ "System.ServiceProcess.ServiceController": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.WSMan.Management.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.WSMan.Runtime/7.1.0": {
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.WSMan.Runtime.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Namotion.Reflection/1.0.14": {
+ "dependencies": {
+ "Microsoft.CSharp": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Namotion.Reflection.dll": {
+ "assemblyVersion": "1.0.14.0",
+ "fileVersion": "1.0.14.0"
+ }
+ }
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "runtime": {
+ "lib/netstandard2.0/Newtonsoft.Json.dll": {
+ "assemblyVersion": "12.0.0.0",
+ "fileVersion": "12.0.3.23909"
+ }
+ }
+ },
+ "NJsonSchema/10.2.2": {
+ "dependencies": {
+ "Namotion.Reflection": "1.0.14",
+ "Newtonsoft.Json": "12.0.3"
+ },
+ "runtime": {
+ "lib/netstandard2.0/NJsonSchema.dll": {
+ "assemblyVersion": "10.2.2.0",
+ "fileVersion": "10.2.2.0"
+ }
+ }
+ },
+ "runtime.any.System.Collections/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Collections.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Globalization/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Globalization.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.IO/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.5/System.IO.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.5/System.Reflection.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection.Extensions/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Extensions.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection.Primitives/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Primitives.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Resources.ResourceManager.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.5/System.Runtime.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Runtime.Handles.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.6/System.Runtime.InteropServices.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Text.Encoding/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Text.Encoding.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Threading.Tasks/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Threading.Tasks.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard1.3/System.Diagnostics.Debug.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard1.5/System.Runtime.Extensions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win7.System.Private.Uri/4.3.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard1.0/System.Private.Uri.dll": {
+ "assemblyVersion": "4.0.3.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.CodeDom/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.CodeDom.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Collections/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Collections": "4.3.0"
+ }
+ },
+ "System.Collections.Immutable/1.5.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Collections.Immutable.dll": {
+ "assemblyVersion": "1.2.3.0",
+ "fileVersion": "4.6.26515.6"
+ }
+ }
+ },
+ "System.Configuration.ConfigurationManager/5.0.0": {
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "5.0.0",
+ "System.Security.Permissions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/System.Configuration.ConfigurationManager.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Diagnostics.Debug/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.win.System.Diagnostics.Debug": "4.3.0"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource/4.6.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {
+ "assemblyVersion": "4.0.4.0",
+ "fileVersion": "4.700.19.46214"
+ }
+ }
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Diagnostics.EventLog.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.DirectoryServices/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.IO.FileSystem.AccessControl": "5.0.0",
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Permissions": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.DirectoryServices.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Drawing.Common/5.0.0": {
+ "dependencies": {
+ "Microsoft.Win32.SystemEvents": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Drawing.Common.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Dynamic.Runtime/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.Linq.Expressions": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Dynamic.Runtime.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Formats.Asn1/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Formats.Asn1.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Globalization/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Globalization": "4.3.0"
+ }
+ },
+ "System.IO/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.any.System.IO": "4.3.0"
+ }
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Linq/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.6/System.Linq.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Linq.Expressions/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Emit.Lightweight": "4.3.0",
+ "System.Reflection.Extensions": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.6/System.Linq.Expressions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Management/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.CodeDom": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Management.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Management.Automation/7.1.0": {
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.15.0",
+ "Microsoft.Management.Infrastructure": "2.0.0",
+ "Microsoft.PowerShell.CoreCLR.Eventing": "7.1.0",
+ "Microsoft.PowerShell.Native": "7.1.0",
+ "Microsoft.Win32.Registry.AccessControl": "5.0.0",
+ "Newtonsoft.Json": "12.0.3",
+ "System.Configuration.ConfigurationManager": "5.0.0",
+ "System.DirectoryServices": "5.0.0",
+ "System.IO.FileSystem.AccessControl": "5.0.0",
+ "System.Management": "5.0.0",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0",
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Cryptography.Pkcs": "5.0.0",
+ "System.Security.Permissions": "5.0.0",
+ "System.Text.Encoding.CodePages": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/System.Management.Automation.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "System.Memory/4.5.4": {},
+ "System.ObjectModel/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.ObjectModel.dll": {
+ "assemblyVersion": "4.0.13.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Private.Uri/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "runtime.win7.System.Private.Uri": "4.3.0"
+ }
+ },
+ "System.Reflection/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.IO": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection": "4.3.0"
+ }
+ },
+ "System.Reflection.Emit/4.3.0": {
+ "dependencies": {
+ "System.IO": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Emit.ILGeneration/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Emit.Lightweight/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Extensions/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection.Extensions": "4.3.0"
+ }
+ },
+ "System.Reflection.Metadata/1.6.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Reflection.Metadata.dll": {
+ "assemblyVersion": "1.4.3.0",
+ "fileVersion": "4.6.26515.6"
+ }
+ }
+ },
+ "System.Reflection.Primitives/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection.Primitives": "4.3.0"
+ }
+ },
+ "System.Reflection.TypeExtensions/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.5/System.Reflection.TypeExtensions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Resources.ResourceManager/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Globalization": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Resources.ResourceManager": "4.3.0"
+ }
+ },
+ "System.Runtime/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "runtime.any.System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.CompilerServices.Unsafe/5.0.0": {
+ "runtime": {
+ "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Runtime.Extensions/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.win.System.Runtime.Extensions": "4.3.0"
+ }
+ },
+ "System.Runtime.Handles/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Runtime.Handles": "4.3.0"
+ }
+ },
+ "System.Runtime.InteropServices/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "runtime.any.System.Runtime.InteropServices": "4.3.0"
+ }
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.Cng/5.0.0": {
+ "dependencies": {
+ "System.Formats.Asn1": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Security.Cryptography.Cng.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.Pkcs/5.0.0": {
+ "dependencies": {
+ "System.Formats.Asn1": "5.0.0",
+ "System.Security.Cryptography.Cng": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Security.Cryptography.Pkcs.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.ProtectedData/5.0.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Permissions/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Windows.Extensions": "5.0.0"
+ },
+ "runtime": {
+ "lib/net5.0/System.Security.Permissions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Principal.Windows/5.0.0": {
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.1/System.Security.Principal.Windows.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.ServiceProcess.ServiceController/5.0.0": {
+ "dependencies": {
+ "System.Diagnostics.EventLog": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.ServiceProcess.ServiceController.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Text.Encoding/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Text.Encoding": "4.3.0"
+ }
+ },
+ "System.Text.Encoding.CodePages/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Threading/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Threading.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Threading.AccessControl/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.Threading.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Threading.Tasks/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "System.Threading.Tasks.Extensions/4.5.3": {},
+ "System.Windows.Extensions/5.0.0": {
+ "dependencies": {
+ "System.Drawing.Common": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Windows.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Chef.Powershell.Core/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/5.0.0": {
+ "type": "runtimepack",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "Markdig.Signed/0.21.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Lg0FPQdKUXai4/XZpQGlQ/HldpHsGnsU6Jd+udJgHj/R7i3ngM2TtR+SZqdHRtgItIHe3dFh2DS/M5qXGSPRiQ==",
+ "path": "markdig.signed/0.21.1",
+ "hashPath": "markdig.signed.0.21.1.nupkg.sha512"
+ },
+ "Microsoft.ApplicationInsights/2.15.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-tG9WZoFc0BTbkj+UjH4IUp8qkT9NA5st8zvPzlHbU8rJDgPFqZDJ3SSAQajl42jet1ghhJ5ZixsjjqVvwi4kaQ==",
+ "path": "microsoft.applicationinsights/2.15.0",
+ "hashPath": "microsoft.applicationinsights.2.15.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.Analyzers/3.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ojG5pGAhTPmjxRGTNvuszO3H8XPZqksDwr9xLd4Ae/JBjZZdl6GuoLk7uLMf+o7yl5wO0TAqoWcEKkEWqrZE5g==",
+ "path": "microsoft.codeanalysis.analyzers/3.0.0",
+ "hashPath": "microsoft.codeanalysis.analyzers.3.0.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.Common/3.7.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SFEdnbw8204hTlde3JePYSIpNX58h/MMXa7LctUsUDigWMR8Ar9gE8LnsLqAIFM0O33JEuQbJ0G4Sat+cPGldw==",
+ "path": "microsoft.codeanalysis.common/3.7.0",
+ "hashPath": "microsoft.codeanalysis.common.3.7.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.CSharp/3.7.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-sKi5PIVy9nVDerkbplY6OQhJBNzEO4XJsMGrnmb6KFEa6K1ulGCHIv6NtDjdUQ/dGrouU3OExc3yzww0COD76w==",
+ "path": "microsoft.codeanalysis.csharp/3.7.0",
+ "hashPath": "microsoft.codeanalysis.csharp.3.7.0.nupkg.sha512"
+ },
+ "Microsoft.CSharp/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-P+MBhIM0YX+JqROuf7i306ZLJEjQYA9uUyRDE+OqwUI5sh41e2ZbPQV3LfAPh+29cmceE1pUffXsGfR4eMY3KA==",
+ "path": "microsoft.csharp/4.3.0",
+ "hashPath": "microsoft.csharp.4.3.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-IaKZRNBBv3sdrmBWd+aqwHq8cVHk/3WgWFAN/dt40MRY9rbtHiDfTTmaEN0tGTmQqGCGDo/ncntA8MvFMvcsRw==",
+ "path": "microsoft.management.infrastructure/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Unix/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-p0lslMX5bdWLxO2P7ao+rjAMOB0LEwPYpzvdCQ2OEYgX2NxFpQ8ILvqPGnYlTAb53rT8gu5DyIol1HboHFYfxQ==",
+ "path": "microsoft.management.infrastructure.runtime.unix/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.runtime.unix.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Win/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vjBWQeDOjgernkrOdbEgn7M70SF7hof7ORdKPSlL06Uc15+oYdth5dZju9KsgUoti/cwnkZTiwtDx/lRtay0sA==",
+ "path": "microsoft.management.infrastructure.runtime.win/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.runtime.win.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==",
+ "path": "microsoft.netcore.platforms/5.0.0",
+ "hashPath": "microsoft.netcore.platforms.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Targets/1.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==",
+ "path": "microsoft.netcore.targets/1.1.0",
+ "hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Diagnostics/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Hipqx38QOv1xWXbV4yGWHOmFIV8zp0O15z2H+Vx0Z0cOnzu4eb+MUblb4Dc1FA3S7ZE56qplC3XBK3hs0EsvYA==",
+ "path": "microsoft.powershell.commands.diagnostics/7.1.0",
+ "hashPath": "microsoft.powershell.commands.diagnostics.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Management/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3S5CU0Zq8ecdywaI3NoKyzjyaLaS7OKAi6z2AfiyKDG5IZfoLTBgIzNQElNFag8UALTNRlKGD8cumVtM3myAQA==",
+ "path": "microsoft.powershell.commands.management/7.1.0",
+ "hashPath": "microsoft.powershell.commands.management.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Utility/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-6BXJ7NRCs8xCHqspv1VoijXKLfLrM6O8rJwyTk0mPc5BZvpC2NSQq1NAJHAlk60ENFTu0vtxK2IzWkbFvJ/BUQ==",
+ "path": "microsoft.powershell.commands.utility/7.1.0",
+ "hashPath": "microsoft.powershell.commands.utility.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.ConsoleHost/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vfs4i5cViK5YsiIveuFv7OTrN2m5vPfOXj3XBkrQHtlMb2u0mkIsqVMTDHRMAyopcpp0ZJ1H+4ZsZ9z1bcpagQ==",
+ "path": "microsoft.powershell.consolehost/7.1.0",
+ "hashPath": "microsoft.powershell.consolehost.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.CoreCLR.Eventing/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-TCTaoyysnTHUErm0w8W50CVB5Mzwj1PQyb740K1DLvbw8Ba607Q1HCSzBHClEDlG+Cphuv9ie2pbvEWcUKslVg==",
+ "path": "microsoft.powershell.coreclr.eventing/7.1.0",
+ "hashPath": "microsoft.powershell.coreclr.eventing.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.MarkdownRender/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FD8ARPyKg22yRhU3gUk6NKZtmtSk5uekqufjJCiTu8A5lvWF9A0mCr/OUYE7+HQLnJoBu8BR19vwnED0Jpd1WQ==",
+ "path": "microsoft.powershell.markdownrender/7.1.0",
+ "hashPath": "microsoft.powershell.markdownrender.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Native/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-PJ/ei1HnYC+CMVDdUMT96PgWFONwVv/ZaJ5j//qLdk073alzdzOPWZiGsxYimJaRLP0TW4uomgIsR9q6jrf48A==",
+ "path": "microsoft.powershell.native/7.1.0",
+ "hashPath": "microsoft.powershell.native.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Security/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-I4ab6z1Vlu25c33jc0gG0Val6YpSgyOyxNsNOENcBGue9TDJQ+d6ppw1IKcCILefpVAo/UWiE4xrWxO04KUc3g==",
+ "path": "microsoft.powershell.security/7.1.0",
+ "hashPath": "microsoft.powershell.security.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.Registry/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+ "path": "microsoft.win32.registry/5.0.0",
+ "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.Registry.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rwF501ZS/xKGWz5H3RLBvwta6E5kcMLB0UYGTgrZ8nL5bvrbGmtEcEObgMC/qRFhA3og/0Zh+eacrcA+0FBXJA==",
+ "path": "microsoft.win32.registry.accesscontrol/5.0.0",
+ "hashPath": "microsoft.win32.registry.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.SystemEvents/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==",
+ "path": "microsoft.win32.systemevents/5.0.0",
+ "hashPath": "microsoft.win32.systemevents.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.WSMan.Management/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-noQxoEaJW5W4vbRfwIB1l+ecERA+K4ZygZBv1YNu0IwdDos0mIiPY+YpFKz2uHyeNSk1Z3j1fsXV0b/r+eRT1Q==",
+ "path": "microsoft.wsman.management/7.1.0",
+ "hashPath": "microsoft.wsman.management.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.WSMan.Runtime/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-DVtFU3QQ12w5ej86Gw/vNtJjCXPd7r9fLJs9tzYqptEO6WtJX+vH65qPacP0DN/LEuc+93+iRVfNjjf/EMuNBQ==",
+ "path": "microsoft.wsman.runtime/7.1.0",
+ "hashPath": "microsoft.wsman.runtime.7.1.0.nupkg.sha512"
+ },
+ "Namotion.Reflection/1.0.14": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-wuJGiFvGfehH2w7jAhMbCJt0/rvUuHyqSZn0sMhNTviDfBZRyX8LFlR/ndQcofkGWulPDfH5nKYTeGXE8xBHPA==",
+ "path": "namotion.reflection/1.0.14",
+ "hashPath": "namotion.reflection.1.0.14.nupkg.sha512"
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==",
+ "path": "newtonsoft.json/12.0.3",
+ "hashPath": "newtonsoft.json.12.0.3.nupkg.sha512"
+ },
+ "NJsonSchema/10.2.2": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-s6oNUrjw5Ix5WVkYdU0vgyzoutZdi7p+uQqTGYa3QbLtjDYrkD4ahfGnfyCpHNoJUXun9pHKGy6AD0LJNcSgjQ==",
+ "path": "njsonschema/10.2.2",
+ "hashPath": "njsonschema.10.2.2.nupkg.sha512"
+ },
+ "runtime.any.System.Collections/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==",
+ "path": "runtime.any.system.collections/4.3.0",
+ "hashPath": "runtime.any.system.collections.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Globalization/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==",
+ "path": "runtime.any.system.globalization/4.3.0",
+ "hashPath": "runtime.any.system.globalization.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.IO/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==",
+ "path": "runtime.any.system.io/4.3.0",
+ "hashPath": "runtime.any.system.io.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==",
+ "path": "runtime.any.system.reflection/4.3.0",
+ "hashPath": "runtime.any.system.reflection.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==",
+ "path": "runtime.any.system.reflection.extensions/4.3.0",
+ "hashPath": "runtime.any.system.reflection.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==",
+ "path": "runtime.any.system.reflection.primitives/4.3.0",
+ "hashPath": "runtime.any.system.reflection.primitives.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==",
+ "path": "runtime.any.system.resources.resourcemanager/4.3.0",
+ "hashPath": "runtime.any.system.resources.resourcemanager.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==",
+ "path": "runtime.any.system.runtime/4.3.0",
+ "hashPath": "runtime.any.system.runtime.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==",
+ "path": "runtime.any.system.runtime.handles/4.3.0",
+ "hashPath": "runtime.any.system.runtime.handles.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==",
+ "path": "runtime.any.system.runtime.interopservices/4.3.0",
+ "hashPath": "runtime.any.system.runtime.interopservices.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Text.Encoding/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==",
+ "path": "runtime.any.system.text.encoding/4.3.0",
+ "hashPath": "runtime.any.system.text.encoding.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Threading.Tasks/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==",
+ "path": "runtime.any.system.threading.tasks/4.3.0",
+ "hashPath": "runtime.any.system.threading.tasks.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==",
+ "path": "runtime.win.system.diagnostics.debug/4.3.0",
+ "hashPath": "runtime.win.system.diagnostics.debug.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==",
+ "path": "runtime.win.system.runtime.extensions/4.3.0",
+ "hashPath": "runtime.win.system.runtime.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.win7.System.Private.Uri/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Q+IBgaPYicSQs2tBlmXqbS25c/JLIthWrgrpMwxKSOobW/OqIMVFruUGfuaz4QABVzV8iKdCAbN7APY7Tclbnw==",
+ "path": "runtime.win7.system.private.uri/4.3.0",
+ "hashPath": "runtime.win7.system.private.uri.4.3.0.nupkg.sha512"
+ },
+ "System.CodeDom/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==",
+ "path": "system.codedom/5.0.0",
+ "hashPath": "system.codedom.5.0.0.nupkg.sha512"
+ },
+ "System.Collections/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
+ "path": "system.collections/4.3.0",
+ "hashPath": "system.collections.4.3.0.nupkg.sha512"
+ },
+ "System.Collections.Immutable/1.5.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ==",
+ "path": "system.collections.immutable/1.5.0",
+ "hashPath": "system.collections.immutable.1.5.0.nupkg.sha512"
+ },
+ "System.Configuration.ConfigurationManager/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==",
+ "path": "system.configuration.configurationmanager/5.0.0",
+ "hashPath": "system.configuration.configurationmanager.5.0.0.nupkg.sha512"
+ },
+ "System.Diagnostics.Debug/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
+ "path": "system.diagnostics.debug/4.3.0",
+ "hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512"
+ },
+ "System.Diagnostics.DiagnosticSource/4.6.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-mbBgoR0rRfl2uimsZ2avZY8g7Xnh1Mza0rJZLPcxqiMWlkGukjmRkuMJ/er+AhQuiRIh80CR/Hpeztr80seV5g==",
+ "path": "system.diagnostics.diagnosticsource/4.6.0",
+ "hashPath": "system.diagnostics.diagnosticsource.4.6.0.nupkg.sha512"
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FHkCwUfsTs+/5tsK+c0egLfacUgbhvcwi3wUFWSEEArSXao343mYqcpOVVFMlcCkdNtjU4YwAWaKYwal6f02og==",
+ "path": "system.diagnostics.eventlog/5.0.0",
+ "hashPath": "system.diagnostics.eventlog.5.0.0.nupkg.sha512"
+ },
+ "System.DirectoryServices/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lAS54Y3KO1XV68akGa0/GJeddkkuuiv2CtcSkMiTmLHQ6o6kFbKpw4DmJZADF7a6KjPwYxmZnH4D3eGicrJdcg==",
+ "path": "system.directoryservices/5.0.0",
+ "hashPath": "system.directoryservices.5.0.0.nupkg.sha512"
+ },
+ "System.Drawing.Common/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==",
+ "path": "system.drawing.common/5.0.0",
+ "hashPath": "system.drawing.common.5.0.0.nupkg.sha512"
+ },
+ "System.Dynamic.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==",
+ "path": "system.dynamic.runtime/4.3.0",
+ "hashPath": "system.dynamic.runtime.4.3.0.nupkg.sha512"
+ },
+ "System.Formats.Asn1/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w==",
+ "path": "system.formats.asn1/5.0.0",
+ "hashPath": "system.formats.asn1.5.0.0.nupkg.sha512"
+ },
+ "System.Globalization/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
+ "path": "system.globalization/4.3.0",
+ "hashPath": "system.globalization.4.3.0.nupkg.sha512"
+ },
+ "System.IO/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
+ "path": "system.io/4.3.0",
+ "hashPath": "system.io.4.3.0.nupkg.sha512"
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==",
+ "path": "system.io.filesystem.accesscontrol/5.0.0",
+ "hashPath": "system.io.filesystem.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Linq/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
+ "path": "system.linq/4.3.0",
+ "hashPath": "system.linq.4.3.0.nupkg.sha512"
+ },
+ "System.Linq.Expressions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==",
+ "path": "system.linq.expressions/4.3.0",
+ "hashPath": "system.linq.expressions.4.3.0.nupkg.sha512"
+ },
+ "System.Management/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
+ "path": "system.management/5.0.0",
+ "hashPath": "system.management.5.0.0.nupkg.sha512"
+ },
+ "System.Management.Automation/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-WjmMs8kNETGWUmbE2A87cTcAGRz0WJ24TOZm5xkhop/93fIINK8TEcghEr2I0mSgXb7t0pWkPdKdEA5vizFQPA==",
+ "path": "system.management.automation/7.1.0",
+ "hashPath": "system.management.automation.7.1.0.nupkg.sha512"
+ },
+ "System.Memory/4.5.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==",
+ "path": "system.memory/4.5.4",
+ "hashPath": "system.memory.4.5.4.nupkg.sha512"
+ },
+ "System.ObjectModel/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==",
+ "path": "system.objectmodel/4.3.0",
+ "hashPath": "system.objectmodel.4.3.0.nupkg.sha512"
+ },
+ "System.Private.Uri/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==",
+ "path": "system.private.uri/4.3.0",
+ "hashPath": "system.private.uri.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
+ "path": "system.reflection/4.3.0",
+ "hashPath": "system.reflection.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==",
+ "path": "system.reflection.emit/4.3.0",
+ "hashPath": "system.reflection.emit.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit.ILGeneration/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
+ "path": "system.reflection.emit.ilgeneration/4.3.0",
+ "hashPath": "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit.Lightweight/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
+ "path": "system.reflection.emit.lightweight/4.3.0",
+ "hashPath": "system.reflection.emit.lightweight.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
+ "path": "system.reflection.extensions/4.3.0",
+ "hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Metadata/1.6.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
+ "path": "system.reflection.metadata/1.6.0",
+ "hashPath": "system.reflection.metadata.1.6.0.nupkg.sha512"
+ },
+ "System.Reflection.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
+ "path": "system.reflection.primitives/4.3.0",
+ "hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.TypeExtensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
+ "path": "system.reflection.typeextensions/4.3.0",
+ "hashPath": "system.reflection.typeextensions.4.3.0.nupkg.sha512"
+ },
+ "System.Resources.ResourceManager/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
+ "path": "system.resources.resourcemanager/4.3.0",
+ "hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
+ "path": "system.runtime/4.3.0",
+ "hashPath": "system.runtime.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.CompilerServices.Unsafe/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==",
+ "path": "system.runtime.compilerservices.unsafe/5.0.0",
+ "hashPath": "system.runtime.compilerservices.unsafe.5.0.0.nupkg.sha512"
+ },
+ "System.Runtime.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
+ "path": "system.runtime.extensions/4.3.0",
+ "hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.Handles/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
+ "path": "system.runtime.handles/4.3.0",
+ "hashPath": "system.runtime.handles.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.InteropServices/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
+ "path": "system.runtime.interopservices/4.3.0",
+ "hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512"
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+ "path": "system.security.accesscontrol/5.0.0",
+ "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.Cng/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
+ "path": "system.security.cryptography.cng/5.0.0",
+ "hashPath": "system.security.cryptography.cng.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.Pkcs/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-9TPLGjBCGKmNvG8pjwPeuYy0SMVmGZRwlTZvyPHDbYv/DRkoeumJdfumaaDNQzVGMEmbWtg07zUpSW9q70IlDQ==",
+ "path": "system.security.cryptography.pkcs/5.0.0",
+ "hashPath": "system.security.cryptography.pkcs.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.ProtectedData/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==",
+ "path": "system.security.cryptography.protecteddata/5.0.0",
+ "hashPath": "system.security.cryptography.protecteddata.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Permissions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==",
+ "path": "system.security.permissions/5.0.0",
+ "hashPath": "system.security.permissions.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Principal.Windows/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
+ "path": "system.security.principal.windows/5.0.0",
+ "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
+ },
+ "System.ServiceProcess.ServiceController/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-p2yX167GW1pr2DCR6cW+cBKrvhli4thckXk108faFaTPHnoudb0AYPcIPq3nmrwn7IQj9FEmjpyJlXzcOmIjjw==",
+ "path": "system.serviceprocess.servicecontroller/5.0.0",
+ "hashPath": "system.serviceprocess.servicecontroller.5.0.0.nupkg.sha512"
+ },
+ "System.Text.Encoding/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
+ "path": "system.text.encoding/4.3.0",
+ "hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
+ },
+ "System.Text.Encoding.CodePages/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==",
+ "path": "system.text.encoding.codepages/5.0.0",
+ "hashPath": "system.text.encoding.codepages.5.0.0.nupkg.sha512"
+ },
+ "System.Threading/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
+ "path": "system.threading/4.3.0",
+ "hashPath": "system.threading.4.3.0.nupkg.sha512"
+ },
+ "System.Threading.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-WJ9w9m4iHJVq0VoH7hZvYAccbRq95itYRhAAXd6M4kVCzLmT6NqTwmSXKwp3oQilWHhYTXgqaIXxBfg8YaqtmA==",
+ "path": "system.threading.accesscontrol/5.0.0",
+ "hashPath": "system.threading.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Threading.Tasks/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
+ "path": "system.threading.tasks/4.3.0",
+ "hashPath": "system.threading.tasks.4.3.0.nupkg.sha512"
+ },
+ "System.Threading.Tasks.Extensions/4.5.3": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+MvhNtcvIbqmhANyKu91jQnvIRVSTiaOiFNfKWwXGHG48YAb4I/TyH8spsySiPYla7gKal5ZnF3teJqZAximyQ==",
+ "path": "system.threading.tasks.extensions/4.5.3",
+ "hashPath": "system.threading.tasks.extensions.4.5.3.nupkg.sha512"
+ },
+ "System.Windows.Extensions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==",
+ "path": "system.windows.extensions/5.0.0",
+ "hashPath": "system.windows.extensions.5.0.0.nupkg.sha512"
+ }
+ },
+ "runtimes": {
+ "win10-x64": [
+ "win10",
+ "win81-x64",
+ "win81",
+ "win8-x64",
+ "win8",
+ "win7-x64",
+ "win7",
+ "win-x64",
+ "win",
+ "any",
+ "base"
+ ],
+ "win10-x64-aot": [
+ "win10-aot",
+ "win10-x64",
+ "win10",
+ "win81-x64-aot",
+ "win81-aot",
+ "win81-x64",
+ "win81",
+ "win8-x64-aot",
+ "win8-aot",
+ "win8-x64",
+ "win8",
+ "win7-x64-aot",
+ "win7-aot",
+ "win7-x64",
+ "win7",
+ "win-x64-aot",
+ "win-aot",
+ "win-x64",
+ "win",
+ "aot",
+ "any",
+ "base"
+ ]
+ }
+} \ No newline at end of file
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll
new file mode 100644
index 0000000000..9ada03f00f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll
new file mode 100644
index 0000000000..7d8d5afcd3
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll
new file mode 100644
index 0000000000..b63f889771
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll
new file mode 100644
index 0000000000..96fa29deea
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll
new file mode 100644
index 0000000000..1dc32290ac
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll
new file mode 100644
index 0000000000..6af4347c1d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll
new file mode 100644
index 0000000000..fd06cf702f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll
new file mode 100644
index 0000000000..bf973055a5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll
new file mode 100644
index 0000000000..52a95e4526
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll
new file mode 100644
index 0000000000..d33a2ffd25
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll
new file mode 100644
index 0000000000..5644be0dfd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll
new file mode 100644
index 0000000000..98e3049e20
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll
new file mode 100644
index 0000000000..934802f5c2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll
new file mode 100644
index 0000000000..990f4b057b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll
new file mode 100644
index 0000000000..b5aa0c4cd3
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll
new file mode 100644
index 0000000000..36ecf91055
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll
new file mode 100644
index 0000000000..8cbfb13909
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll
new file mode 100644
index 0000000000..b501fb6a29
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll
new file mode 100644
index 0000000000..61939347a6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll
new file mode 100644
index 0000000000..f81811110f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll
new file mode 100644
index 0000000000..9c954491b6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll
new file mode 100644
index 0000000000..873495d3bd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll
new file mode 100644
index 0000000000..8e151864d0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll
new file mode 100644
index 0000000000..3681784cd9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll
new file mode 100644
index 0000000000..55193843db
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll
new file mode 100644
index 0000000000..5c6823f5c2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll
new file mode 100644
index 0000000000..d2d620e0cd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll
new file mode 100644
index 0000000000..710e80bef0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll
new file mode 100644
index 0000000000..acf9bcc273
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll
new file mode 100644
index 0000000000..e3e976dfa8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll
new file mode 100644
index 0000000000..9e3165a748
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll
new file mode 100644
index 0000000000..666295ff6b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll
new file mode 100644
index 0000000000..d2e8f92286
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll
new file mode 100644
index 0000000000..1644e5d6d6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll
new file mode 100644
index 0000000000..d0287ad5b7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll
new file mode 100644
index 0000000000..ea4ce56f07
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll
new file mode 100644
index 0000000000..032abb8207
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll
new file mode 100644
index 0000000000..07d640324b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll
new file mode 100644
index 0000000000..03158d5e3d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll
new file mode 100644
index 0000000000..a89797b701
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll
new file mode 100644
index 0000000000..e1229986d2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll
new file mode 100644
index 0000000000..044d000167
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll
new file mode 100644
index 0000000000..666679b5f8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll
new file mode 100644
index 0000000000..bb76446a5e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll
new file mode 100644
index 0000000000..7f834d498d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll
new file mode 100644
index 0000000000..32299e23a3
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll
new file mode 100644
index 0000000000..1d96415e59
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll
new file mode 100644
index 0000000000..e797b5fd8e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll
new file mode 100644
index 0000000000..092ad2ccf2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll
new file mode 100644
index 0000000000..de6cf08e2b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll
new file mode 100644
index 0000000000..da993d1a04
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll
new file mode 100644
index 0000000000..e43577d592
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll
new file mode 100644
index 0000000000..87fe0ae8bb
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll
new file mode 100644
index 0000000000..35286a516c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll
new file mode 100644
index 0000000000..a21e8cc76b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll
new file mode 100644
index 0000000000..275fa6d994
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll
new file mode 100644
index 0000000000..650e9bb3e6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll
new file mode 100644
index 0000000000..6f2e9eefcd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll
new file mode 100644
index 0000000000..634eb7b297
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll
new file mode 100644
index 0000000000..517afce5c2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll
new file mode 100644
index 0000000000..fc19d7815a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll
new file mode 100644
index 0000000000..843262414d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll
new file mode 100644
index 0000000000..72ef4b77af
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll
new file mode 100644
index 0000000000..dc745f66f6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll
new file mode 100644
index 0000000000..327874faaa
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll
new file mode 100644
index 0000000000..f54b160fd1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll
new file mode 100644
index 0000000000..3b56126a89
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll
new file mode 100644
index 0000000000..15e01a461f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll
new file mode 100644
index 0000000000..caf3778f8b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll
new file mode 100644
index 0000000000..c121ec29b7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll
new file mode 100644
index 0000000000..52baaf84af
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll
new file mode 100644
index 0000000000..94913a4021
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll
new file mode 100644
index 0000000000..02388213c6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll
new file mode 100644
index 0000000000..d212f76efe
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll
new file mode 100644
index 0000000000..01351ea53f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll
new file mode 100644
index 0000000000..3bcbdf86d2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll
new file mode 100644
index 0000000000..2f9ba0dd89
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll
new file mode 100644
index 0000000000..8a21ca83ad
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll
new file mode 100644
index 0000000000..18d0639ae9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll
new file mode 100644
index 0000000000..aa3f0fccb5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll
new file mode 100644
index 0000000000..a3a1f45197
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll
new file mode 100644
index 0000000000..77d2cdda43
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll
new file mode 100644
index 0000000000..7da87e3740
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll
new file mode 100644
index 0000000000..e4393e6752
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll
new file mode 100644
index 0000000000..636963cac0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll
new file mode 100644
index 0000000000..9509f21237
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll
new file mode 100644
index 0000000000..29188f8f9a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll
new file mode 100644
index 0000000000..326355dabf
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll
new file mode 100644
index 0000000000..328bc5f46e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll
new file mode 100644
index 0000000000..d8e5c3a5d6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll
new file mode 100644
index 0000000000..37c48e1e8c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll
new file mode 100644
index 0000000000..a2871b03c4
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll
new file mode 100644
index 0000000000..fd0a20b76a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll
new file mode 100644
index 0000000000..0ac5ac2d0d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll
new file mode 100644
index 0000000000..04a01f819b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll
new file mode 100644
index 0000000000..6d21c31724
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll
new file mode 100644
index 0000000000..0938326a2a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll
new file mode 100644
index 0000000000..911e8133df
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll
new file mode 100644
index 0000000000..5856ef1860
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll
new file mode 100644
index 0000000000..f4a8e21c93
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll
new file mode 100644
index 0000000000..64e386ab2e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll
new file mode 100644
index 0000000000..acb7768759
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll
new file mode 100644
index 0000000000..82232ac154
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll
new file mode 100644
index 0000000000..04beba2523
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll
new file mode 100644
index 0000000000..e94210ef1b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll
new file mode 100644
index 0000000000..d9c687bcc9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll
new file mode 100644
index 0000000000..1f9e6df91a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll
new file mode 100644
index 0000000000..82bc088c3c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll
new file mode 100644
index 0000000000..a12ad00d9b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll
new file mode 100644
index 0000000000..d3ee172b1d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll
new file mode 100644
index 0000000000..11cf232098
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll
new file mode 100644
index 0000000000..547b51da9b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll
new file mode 100644
index 0000000000..97649b05d8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll
new file mode 100644
index 0000000000..aa05364448
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll
new file mode 100644
index 0000000000..ab69e12842
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll
new file mode 100644
index 0000000000..a9cf60b6db
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll
new file mode 100644
index 0000000000..73950b85bd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll
new file mode 100644
index 0000000000..2f8858f663
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll
new file mode 100644
index 0000000000..c6d291ba30
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll
new file mode 100644
index 0000000000..c40c10f8a6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll
new file mode 100644
index 0000000000..6795902430
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll
new file mode 100644
index 0000000000..bc71ada005
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll
new file mode 100644
index 0000000000..2bd8e0d741
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll
new file mode 100644
index 0000000000..d727c910ca
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll
new file mode 100644
index 0000000000..a03b1a6f1d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll
new file mode 100644
index 0000000000..837a99c035
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll
new file mode 100644
index 0000000000..37df042a74
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll
new file mode 100644
index 0000000000..e715c212cd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll
new file mode 100644
index 0000000000..25380f2ed1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll
new file mode 100644
index 0000000000..0ea2110aae
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll
new file mode 100644
index 0000000000..ed7c938cac
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll
new file mode 100644
index 0000000000..84a3e36faf
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll
new file mode 100644
index 0000000000..319c0d5944
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll
new file mode 100644
index 0000000000..290b36ab27
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll
new file mode 100644
index 0000000000..12bee9bb6e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll
new file mode 100644
index 0000000000..0a7b19a8d7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll
new file mode 100644
index 0000000000..c206ec806e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll
new file mode 100644
index 0000000000..65323a1662
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll
new file mode 100644
index 0000000000..295838d658
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll
new file mode 100644
index 0000000000..0d0c3ae91d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll
new file mode 100644
index 0000000000..d3ab8e17c9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll
new file mode 100644
index 0000000000..1e5067d955
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll
new file mode 100644
index 0000000000..252b80c246
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll
new file mode 100644
index 0000000000..23f957e836
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll
new file mode 100644
index 0000000000..99215bb1df
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll
new file mode 100644
index 0000000000..b91dc9c114
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll
new file mode 100644
index 0000000000..b380d0856e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll
new file mode 100644
index 0000000000..a6353d473c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll
new file mode 100644
index 0000000000..44ef9ee6ee
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll
new file mode 100644
index 0000000000..92c09d098c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll
new file mode 100644
index 0000000000..e958ac5c9c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll
new file mode 100644
index 0000000000..0fcea369c1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll
new file mode 100644
index 0000000000..9074d56d58
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll
new file mode 100644
index 0000000000..a8a70761fb
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll
new file mode 100644
index 0000000000..9fbf6011d0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll
new file mode 100644
index 0000000000..373a39f93b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll
new file mode 100644
index 0000000000..9b7a4c0eac
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll
new file mode 100644
index 0000000000..b200a7b437
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll
new file mode 100644
index 0000000000..c7b337a6c2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll
new file mode 100644
index 0000000000..7faf7c1d55
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll
new file mode 100644
index 0000000000..49d43249f0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll
new file mode 100644
index 0000000000..1959e050e9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll
new file mode 100644
index 0000000000..9c19e78ac5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll
new file mode 100644
index 0000000000..54983b20df
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll
new file mode 100644
index 0000000000..91e7719fb7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll
new file mode 100644
index 0000000000..45651b24a8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll
new file mode 100644
index 0000000000..bc84d5a36c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll
new file mode 100644
index 0000000000..f4e3352ab3
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll
new file mode 100644
index 0000000000..697af93c2c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll
new file mode 100644
index 0000000000..d1ba57bf67
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll
new file mode 100644
index 0000000000..134e38ecdb
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll
new file mode 100644
index 0000000000..b207cd0417
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll
new file mode 100644
index 0000000000..3cc5173a0a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll
new file mode 100644
index 0000000000..c21dd6c06b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll
new file mode 100644
index 0000000000..ced26d15f1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll
new file mode 100644
index 0000000000..a9c50e5a10
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll
new file mode 100644
index 0000000000..ab82e8395a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll
new file mode 100644
index 0000000000..b11be78695
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll
new file mode 100644
index 0000000000..bcd2122844
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll
new file mode 100644
index 0000000000..83a00174e7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll
new file mode 100644
index 0000000000..5497f03eff
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll
new file mode 100644
index 0000000000..f643007fe0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll
new file mode 100644
index 0000000000..f371876149
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll
new file mode 100644
index 0000000000..03e3fb685d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll
new file mode 100644
index 0000000000..2d0f5d884c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll
new file mode 100644
index 0000000000..80e5f69e35
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll
new file mode 100644
index 0000000000..52a37de8d6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.dll
new file mode 100644
index 0000000000..6f8837770d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/System.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll
new file mode 100644
index 0000000000..84d7856f8b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll
new file mode 100644
index 0000000000..779c378e98
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll
new file mode 100644
index 0000000000..dd5a2cf1ac
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll
new file mode 100644
index 0000000000..d3876f9c68
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll
new file mode 100644
index 0000000000..9a311a06ee
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll
new file mode 100644
index 0000000000..9c1cdb24e2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll
new file mode 100644
index 0000000000..97189f2494
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll
new file mode 100644
index 0000000000..f85edc04fc
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll
new file mode 100644
index 0000000000..d4c53a90a8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll
new file mode 100644
index 0000000000..128fa35e4d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll
new file mode 100644
index 0000000000..bd1a637721
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll
new file mode 100644
index 0000000000..4e5a595be2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll
new file mode 100644
index 0000000000..16b450ac85
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll
new file mode 100644
index 0000000000..0b162ee0f1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll
new file mode 100644
index 0000000000..c790187d65
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll
new file mode 100644
index 0000000000..0d8aa9b209
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll
new file mode 100644
index 0000000000..efaf87cee0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll
new file mode 100644
index 0000000000..a95cd5b172
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll
new file mode 100644
index 0000000000..4a9af92c83
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll
new file mode 100644
index 0000000000..fb6733b074
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll
new file mode 100644
index 0000000000..4f7d5e27c4
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll
new file mode 100644
index 0000000000..11d95be913
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll
new file mode 100644
index 0000000000..6a55ee74f3
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll
new file mode 100644
index 0000000000..9fed7ef08c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll
new file mode 100644
index 0000000000..1c9ac4ca96
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll
new file mode 100644
index 0000000000..3516fec483
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll
new file mode 100644
index 0000000000..c3ab5763f4
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll
new file mode 100644
index 0000000000..d503f25196
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll
new file mode 100644
index 0000000000..d3fbc883e2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll
new file mode 100644
index 0000000000..d67a4264b0
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll
new file mode 100644
index 0000000000..51e6ccd72e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll
new file mode 100644
index 0000000000..a7b06ae008
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll
new file mode 100644
index 0000000000..46f9aac028
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll
new file mode 100644
index 0000000000..b90fdd77cf
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll
new file mode 100644
index 0000000000..d40d70010e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll
new file mode 100644
index 0000000000..d9ac672f8f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll
new file mode 100644
index 0000000000..39141736ce
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll
new file mode 100644
index 0000000000..83488ea089
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll
new file mode 100644
index 0000000000..d18b79b28e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll
new file mode 100644
index 0000000000..639610b3c9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll
new file mode 100644
index 0000000000..ba595515cd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll
new file mode 100644
index 0000000000..2b4e367d00
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll
new file mode 100644
index 0000000000..c900f4a816
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll
new file mode 100644
index 0000000000..a3c8dfb053
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll
new file mode 100644
index 0000000000..c1494a2c55
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll
new file mode 100644
index 0000000000..7f113519cd
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/createdump.exe b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/createdump.exe
new file mode 100644
index 0000000000..c66264ad34
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/createdump.exe
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..4b72058de6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..43acdf3d4c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll
new file mode 100644
index 0000000000..3569641c9c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..55d6b391fa
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..698f08e5e2
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..e770ccc2e8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..62e2e9a98b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..f605e75a4d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..a5aec6aa4e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll
new file mode 100644
index 0000000000..7595b4cd83
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll
new file mode 100644
index 0000000000..87ce55dbe9
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..5aedc4b83b
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..0ecca4c37d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2437ed9332
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..0b985d837c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..c908377e1f
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..bdb9eb5248
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mi.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mi.dll
new file mode 100644
index 0000000000..f169cbe4b8
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mi.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/miutils.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/miutils.dll
new file mode 100644
index 0000000000..f9c9d3f2e7
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/miutils.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll
new file mode 100644
index 0000000000..65e8b8da0e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_amd64_amd64_5.0.20.51904.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_amd64_amd64_5.0.20.51904.dll
new file mode 100644
index 0000000000..257792282e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_amd64_amd64_5.0.20.51904.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll
new file mode 100644
index 0000000000..cae3ce0bb5
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll
new file mode 100644
index 0000000000..482261960c
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll
new file mode 100644
index 0000000000..f75271c522
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll
new file mode 100644
index 0000000000..4166bf4d61
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..8f2b775300
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..b287bd94de
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..4c64afcfda
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..10a885e034
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll
new file mode 100644
index 0000000000..f7368e471a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2b8db7b639
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..cb414f7bb6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..87c1ca2c76
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..5b1ff67570
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll
new file mode 100644
index 0000000000..5109f1d7f1
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..8b19e6492a
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..32204d4633
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2acfeed16e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..57283cf4f6
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/vccorlib140.dll b/distro/ruby_bin_folder/AMD64/vccorlib140.dll
new file mode 100644
index 0000000000..bd5e97837d
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/vccorlib140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/vcruntime140.dll b/distro/ruby_bin_folder/AMD64/vcruntime140.dll
new file mode 100644
index 0000000000..e6f0a40170
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/vcruntime140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/vcruntime140_1.dll b/distro/ruby_bin_folder/AMD64/vcruntime140_1.dll
new file mode 100644
index 0000000000..a46461a37e
--- /dev/null
+++ b/distro/ruby_bin_folder/AMD64/vcruntime140_1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Chef.PowerShell.dll b/distro/ruby_bin_folder/x86/Chef.PowerShell.dll
new file mode 100644
index 0000000000..8a528ad226
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/Chef.PowerShell.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll b/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll
new file mode 100644
index 0000000000..203d243b37
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Ijwhost.dll b/distro/ruby_bin_folder/x86/Ijwhost.dll
new file mode 100644
index 0000000000..333cb4dcd8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/Ijwhost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Newtonsoft.Json.dll b/distro/ruby_bin_folder/x86/Newtonsoft.Json.dll
new file mode 100644
index 0000000000..1971a35679
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/Newtonsoft.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/concrt140.dll b/distro/ruby_bin_folder/x86/concrt140.dll
new file mode 100644
index 0000000000..1d75093197
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/concrt140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/host/fxr/5.0.0/hostfxr.dll b/distro/ruby_bin_folder/x86/host/fxr/5.0.0/hostfxr.dll
new file mode 100644
index 0000000000..9074a6c2a1
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/host/fxr/5.0.0/hostfxr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/msvcp140.dll b/distro/ruby_bin_folder/x86/msvcp140.dll
new file mode 100644
index 0000000000..76529d554a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/msvcp140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/msvcp140_1.dll b/distro/ruby_bin_folder/x86/msvcp140_1.dll
new file mode 100644
index 0000000000..4dfe3ee783
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/msvcp140_1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/msvcp140_2.dll b/distro/ruby_bin_folder/x86/msvcp140_2.dll
new file mode 100644
index 0000000000..8f251b32cc
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/msvcp140_2.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/msvcp140_atomic_wait.dll b/distro/ruby_bin_folder/x86/msvcp140_atomic_wait.dll
new file mode 100644
index 0000000000..4c758375f7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/msvcp140_atomic_wait.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/msvcp140_codecvt_ids.dll b/distro/ruby_bin_folder/x86/msvcp140_codecvt_ids.dll
new file mode 100644
index 0000000000..efa73e134b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/msvcp140_codecvt_ids.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/API-MS-Win-core-xstate-l2-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/API-MS-Win-core-xstate-l2-1-0.dll
new file mode 100644
index 0000000000..7c9413ee34
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/API-MS-Win-core-xstate-l2-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
new file mode 100644
index 0000000000..879df9ab6e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
new file mode 100644
index 0000000000..ff50126c80
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
new file mode 100644
index 0000000000..5c979f1750
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json
new file mode 100644
index 0000000000..9d0500cd16
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Wrapper.Core.runtimeconfig.json
@@ -0,0 +1,9 @@
+{
+ "runtimeOptions": {
+ "tfm": "net5.0",
+ "framework": {
+ "name": "Microsoft.NETCore.App",
+ "version": "5.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll
new file mode 100644
index 0000000000..333cb4dcd8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Ijwhost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll
new file mode 100644
index 0000000000..2a70d37424
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Markdig.Signed.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll
new file mode 100644
index 0000000000..f1d216624a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.ApplicationInsights.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll
new file mode 100644
index 0000000000..a00c560168
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CSharp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll
new file mode 100644
index 0000000000..8b6f598975
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.CSharp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll
new file mode 100644
index 0000000000..bfdf0b004b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.CodeAnalysis.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.x86.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.x86.dll
new file mode 100644
index 0000000000..5ebef7fe29
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.DiaSymReader.Native.x86.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll
new file mode 100644
index 0000000000..434e1f4130
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.Unmanaged.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll
new file mode 100644
index 0000000000..e1819d5fe4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.Native.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll
new file mode 100644
index 0000000000..13d9f3b430
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Management.Infrastructure.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json
new file mode 100644
index 0000000000..08ea0aaff0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.NETCore.App.deps.json
@@ -0,0 +1,2579 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v5.0/win10-x86",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v5.0": {},
+ ".NETCoreApp,Version=v5.0/win10-x86": {
+ "Chef.Powershell.Core/1.0.0": {
+ "dependencies": {
+ "Microsoft.PowerShell.Commands.Diagnostics": "7.1.0",
+ "Microsoft.PowerShell.Commands.Management": "7.1.0",
+ "Microsoft.PowerShell.Commands.Utility": "7.1.0",
+ "Microsoft.PowerShell.ConsoleHost": "7.1.0",
+ "Microsoft.WSMan.Management": "7.1.0",
+ "Microsoft.WSMan.Runtime": "7.1.0",
+ "Newtonsoft.Json": "12.0.3",
+ "System.Management.Automation": "7.1.0",
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x86": "5.0.0"
+ },
+ "runtime": {
+ "Chef.Powershell.Core.dll": {}
+ }
+ },
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x86/5.0.0": {
+ "runtime": {
+ "Microsoft.CSharp.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.VisualBasic.Core.dll": {
+ "assemblyVersion": "10.0.6.0",
+ "fileVersion": "11.0.20.51904"
+ },
+ "Microsoft.VisualBasic.dll": {
+ "assemblyVersion": "10.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.Win32.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "Microsoft.Win32.Registry.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.AppContext.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Buffers.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Concurrent.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Immutable.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.NonGeneric.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.Specialized.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Collections.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.Annotations.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.DataAnnotations.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.EventBasedAsync.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.TypeConverter.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ComponentModel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Configuration.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Console.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Core.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.Common.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.DataSetExtensions.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Data.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Contracts.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Debug.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.DiagnosticSource.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.FileVersionInfo.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Process.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.StackTrace.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.TextWriterTraceListener.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Tools.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.TraceSource.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Diagnostics.Tracing.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Drawing.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Drawing.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Dynamic.Runtime.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Formats.Asn1.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.Calendars.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Globalization.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.Brotli.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.FileSystem.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.ZipFile.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Compression.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.DriveInfo.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.Watcher.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.FileSystem.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.IsolatedStorage.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.MemoryMappedFiles.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Pipes.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.Pipes.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.UnmanagedMemoryStream.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.IO.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Expressions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Parallel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.Queryable.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Linq.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Memory.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Http.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Http.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.HttpListener.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Mail.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.NameResolution.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.NetworkInformation.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Ping.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Requests.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Security.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.ServicePoint.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.Sockets.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebClient.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebHeaderCollection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebProxy.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebSockets.Client.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.WebSockets.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Net.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Numerics.Vectors.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Numerics.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ObjectModel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.DataContractSerialization.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Uri.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Xml.Linq.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.Xml.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.DispatchProxy.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.ILGeneration.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.Lightweight.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Emit.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Metadata.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.TypeExtensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Reflection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.Reader.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.ResourceManager.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Resources.Writer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.CompilerServices.Unsafe.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.CompilerServices.VisualC.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Handles.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.InteropServices.RuntimeInformation.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.InteropServices.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Intrinsics.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Loader.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Numerics.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Formatters.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.Xml.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.Serialization.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Runtime.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Claims.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Algorithms.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Cng.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Csp.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Encoding.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.OpenSsl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Cryptography.X509Certificates.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Principal.Windows.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.Principal.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.SecureString.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Security.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ServiceModel.Web.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ServiceProcess.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.CodePages.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encoding.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Encodings.Web.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Text.RegularExpressions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Channels.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Overlapped.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Dataflow.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.Parallel.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Tasks.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Thread.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.ThreadPool.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.Timer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Threading.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Transactions.Local.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Transactions.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.ValueTuple.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Web.HttpUtility.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Web.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Windows.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.Linq.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.ReaderWriter.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.Serialization.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XPath.XDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XPath.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XmlDocument.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.XmlSerializer.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Xml.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "WindowsBase.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscorlib.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "netstandard.dll": {
+ "assemblyVersion": "2.1.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "System.Private.CoreLib.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ },
+ "native": {
+ "API-MS-Win-core-xstate-l2-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "Microsoft.DiaSymReader.Native.x86.dll": {
+ "fileVersion": "14.12.25830.2"
+ },
+ "api-ms-win-core-console-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-console-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-datetime-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-debug-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-errorhandling-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-file-l2-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-handle-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-heap-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-interlocked-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-libraryloader-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-localization-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-memory-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-namedpipe-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processenvironment-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processthreads-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-processthreads-l1-1-1.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-profile-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-rtlsupport-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-string-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-synch-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-synch-l1-2-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-sysinfo-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-timezone-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-core-util-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-conio-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-convert-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-environment-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-filesystem-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-heap-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-locale-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-math-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-multibyte-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-private-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-process-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-runtime-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-stdio-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-string-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-time-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "api-ms-win-crt-utility-l1-1-0.dll": {
+ "fileVersion": "10.0.19041.1"
+ },
+ "clrcompression.dll": {
+ "fileVersion": "42.42.42.42424"
+ },
+ "clretwrc.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "clrjit.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "coreclr.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "createdump.exe": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "dbgshim.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "hostfxr.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "hostpolicy.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordaccore.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordaccore_x86_x86_5.0.20.51904.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscordbi.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "mscorrc.dll": {
+ "fileVersion": "5.0.20.51904"
+ },
+ "ucrtbase.dll": {
+ "fileVersion": "10.0.19041.1"
+ }
+ }
+ },
+ "Markdig.Signed/0.21.1": {
+ "runtime": {
+ "lib/netcoreapp3.1/Markdig.Signed.dll": {
+ "assemblyVersion": "0.21.1.0",
+ "fileVersion": "0.21.1.0"
+ }
+ }
+ },
+ "Microsoft.ApplicationInsights/2.15.0": {
+ "dependencies": {
+ "System.Diagnostics.DiagnosticSource": "4.6.0",
+ "System.Memory": "4.5.4"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.ApplicationInsights.dll": {
+ "assemblyVersion": "2.15.0.44797",
+ "fileVersion": "2.15.0.44797"
+ }
+ }
+ },
+ "Microsoft.CodeAnalysis.Analyzers/3.0.0": {},
+ "Microsoft.CodeAnalysis.Common/3.7.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.0.0",
+ "System.Collections.Immutable": "1.5.0",
+ "System.Memory": "4.5.4",
+ "System.Reflection.Metadata": "1.6.0",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0",
+ "System.Text.Encoding.CodePages": "5.0.0",
+ "System.Threading.Tasks.Extensions": "4.5.3"
+ },
+ "runtime": {
+ "lib/netcoreapp3.1/Microsoft.CodeAnalysis.dll": {
+ "assemblyVersion": "3.7.0.0",
+ "fileVersion": "3.700.20.37502"
+ }
+ },
+ "resources": {
+ "lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "cs"
+ },
+ "lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "de"
+ },
+ "lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "es"
+ },
+ "lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "fr"
+ },
+ "lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "it"
+ },
+ "lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ja"
+ },
+ "lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ko"
+ },
+ "lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "pl"
+ },
+ "lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "pt-BR"
+ },
+ "lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "ru"
+ },
+ "lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "tr"
+ },
+ "lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "zh-Hans"
+ },
+ "lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
+ "locale": "zh-Hant"
+ }
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp/3.7.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Common": "3.7.0"
+ },
+ "runtime": {
+ "lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.dll": {
+ "assemblyVersion": "3.7.0.0",
+ "fileVersion": "3.700.20.37502"
+ }
+ },
+ "resources": {
+ "lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "cs"
+ },
+ "lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "de"
+ },
+ "lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "es"
+ },
+ "lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "fr"
+ },
+ "lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "it"
+ },
+ "lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ja"
+ },
+ "lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ko"
+ },
+ "lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "pl"
+ },
+ "lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "pt-BR"
+ },
+ "lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "ru"
+ },
+ "lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "tr"
+ },
+ "lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "zh-Hans"
+ },
+ "lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
+ "locale": "zh-Hant"
+ }
+ }
+ },
+ "Microsoft.CSharp/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Dynamic.Runtime": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.Linq.Expressions": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Extensions": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/Microsoft.CSharp.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "Microsoft.Management.Infrastructure/2.0.0": {
+ "dependencies": {
+ "Microsoft.Management.Infrastructure.Runtime.Unix": "2.0.0",
+ "Microsoft.Management.Infrastructure.Runtime.Win": "2.0.0"
+ }
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Unix/2.0.0": {},
+ "Microsoft.Management.Infrastructure.Runtime.Win/2.0.0": {
+ "runtime": {
+ "runtimes/win10-x86/lib/netstandard1.6/Microsoft.Management.Infrastructure.Native.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x86/lib/netstandard1.6/Microsoft.Management.Infrastructure.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "10.0.18362.1"
+ }
+ },
+ "native": {
+ "runtimes/win10-x86/native/Microsoft.Management.Infrastructure.Native.Unmanaged.dll": {
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x86/native/mi.dll": {
+ "fileVersion": "10.0.18362.1"
+ },
+ "runtimes/win10-x86/native/miutils.dll": {
+ "fileVersion": "10.0.18362.1"
+ }
+ }
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {},
+ "Microsoft.NETCore.Targets/1.1.0": {},
+ "Microsoft.PowerShell.Commands.Diagnostics/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Diagnostics.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Commands.Management/7.1.0": {
+ "dependencies": {
+ "Microsoft.PowerShell.Security": "7.1.0",
+ "System.ServiceProcess.ServiceController": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Management.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Commands.Utility/7.1.0": {
+ "dependencies": {
+ "Microsoft.CodeAnalysis.CSharp": "3.7.0",
+ "Microsoft.PowerShell.MarkdownRender": "7.1.0",
+ "NJsonSchema": "10.2.2",
+ "System.Drawing.Common": "5.0.0",
+ "System.Management.Automation": "7.1.0",
+ "System.Threading.AccessControl": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Commands.Utility.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.ConsoleHost/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.ConsoleHost.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.CoreCLR.Eventing/7.1.0": {
+ "dependencies": {
+ "System.Diagnostics.EventLog": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.CoreCLR.Eventing.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.MarkdownRender/7.1.0": {
+ "dependencies": {
+ "Markdig.Signed": "0.21.1",
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.MarkdownRender.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Native/7.1.0": {
+ "native": {
+ "runtimes/win-x86/native/PowerShell.Core.Instrumentation.dll": {
+ "fileVersion": "10.0.10011.16384"
+ },
+ "runtimes/win-x86/native/pwrshplugin.dll": {
+ "fileVersion": "10.0.10011.16384"
+ }
+ }
+ },
+ "Microsoft.PowerShell.Security/7.1.0": {
+ "dependencies": {
+ "System.Management.Automation": "7.1.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.PowerShell.Security.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.Win32.Registry/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Win32.Registry.AccessControl/5.0.0": {
+ "dependencies": {
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.Security.AccessControl": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Win32.SystemEvents/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/Microsoft.Win32.SystemEvents.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.WSMan.Management/7.1.0": {
+ "dependencies": {
+ "Microsoft.WSMan.Runtime": "7.1.0",
+ "System.Management.Automation": "7.1.0",
+ "System.ServiceProcess.ServiceController": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.WSMan.Management.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Microsoft.WSMan.Runtime/7.1.0": {
+ "runtime": {
+ "runtimes/win/lib/net5.0/Microsoft.WSMan.Runtime.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "Namotion.Reflection/1.0.14": {
+ "dependencies": {
+ "Microsoft.CSharp": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Namotion.Reflection.dll": {
+ "assemblyVersion": "1.0.14.0",
+ "fileVersion": "1.0.14.0"
+ }
+ }
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "runtime": {
+ "lib/netstandard2.0/Newtonsoft.Json.dll": {
+ "assemblyVersion": "12.0.0.0",
+ "fileVersion": "12.0.3.23909"
+ }
+ }
+ },
+ "NJsonSchema/10.2.2": {
+ "dependencies": {
+ "Namotion.Reflection": "1.0.14",
+ "Newtonsoft.Json": "12.0.3"
+ },
+ "runtime": {
+ "lib/netstandard2.0/NJsonSchema.dll": {
+ "assemblyVersion": "10.2.2.0",
+ "fileVersion": "10.2.2.0"
+ }
+ }
+ },
+ "runtime.any.System.Collections/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Collections.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Globalization/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Globalization.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.IO/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.5/System.IO.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.5/System.Reflection.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection.Extensions/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Extensions.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Reflection.Primitives/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Primitives.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Resources.ResourceManager.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.5/System.Runtime.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Runtime.Handles.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.6/System.Runtime.InteropServices.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Text.Encoding/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Text.Encoding.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.any.System.Threading.Tasks/4.3.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Threading.Tasks.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard1.3/System.Diagnostics.Debug.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard1.5/System.Runtime.Extensions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "runtime.win7.System.Private.Uri/4.3.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard1.0/System.Private.Uri.dll": {
+ "assemblyVersion": "4.0.3.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.CodeDom/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.CodeDom.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Collections/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Collections": "4.3.0"
+ }
+ },
+ "System.Collections.Immutable/1.5.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Collections.Immutable.dll": {
+ "assemblyVersion": "1.2.3.0",
+ "fileVersion": "4.6.26515.6"
+ }
+ }
+ },
+ "System.Configuration.ConfigurationManager/5.0.0": {
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "5.0.0",
+ "System.Security.Permissions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/System.Configuration.ConfigurationManager.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Diagnostics.Debug/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.win.System.Diagnostics.Debug": "4.3.0"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource/4.6.0": {
+ "runtime": {
+ "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {
+ "assemblyVersion": "4.0.4.0",
+ "fileVersion": "4.700.19.46214"
+ }
+ }
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Diagnostics.EventLog.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.DirectoryServices/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.IO.FileSystem.AccessControl": "5.0.0",
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Permissions": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.DirectoryServices.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Drawing.Common/5.0.0": {
+ "dependencies": {
+ "Microsoft.Win32.SystemEvents": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Drawing.Common.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Dynamic.Runtime/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.Linq.Expressions": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Dynamic.Runtime.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Formats.Asn1/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Formats.Asn1.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Globalization/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Globalization": "4.3.0"
+ }
+ },
+ "System.IO/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.any.System.IO": "4.3.0"
+ }
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Linq/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.6/System.Linq.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Linq.Expressions/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.Linq": "4.3.0",
+ "System.ObjectModel": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Emit.Lightweight": "4.3.0",
+ "System.Reflection.Extensions": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.6/System.Linq.Expressions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Management/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.CodeDom": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Management.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Management.Automation/7.1.0": {
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.15.0",
+ "Microsoft.Management.Infrastructure": "2.0.0",
+ "Microsoft.PowerShell.CoreCLR.Eventing": "7.1.0",
+ "Microsoft.PowerShell.Native": "7.1.0",
+ "Microsoft.Win32.Registry.AccessControl": "5.0.0",
+ "Newtonsoft.Json": "12.0.3",
+ "System.Configuration.ConfigurationManager": "5.0.0",
+ "System.DirectoryServices": "5.0.0",
+ "System.IO.FileSystem.AccessControl": "5.0.0",
+ "System.Management": "5.0.0",
+ "System.Runtime.CompilerServices.Unsafe": "5.0.0",
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Cryptography.Pkcs": "5.0.0",
+ "System.Security.Permissions": "5.0.0",
+ "System.Text.Encoding.CodePages": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/net5.0/System.Management.Automation.dll": {
+ "assemblyVersion": "7.1.0.0",
+ "fileVersion": "7.1.0.0"
+ }
+ }
+ },
+ "System.Memory/4.5.4": {},
+ "System.ObjectModel/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Threading": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.ObjectModel.dll": {
+ "assemblyVersion": "4.0.13.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Private.Uri/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "runtime.win7.System.Private.Uri": "4.3.0"
+ }
+ },
+ "System.Reflection/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.IO": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection": "4.3.0"
+ }
+ },
+ "System.Reflection.Emit/4.3.0": {
+ "dependencies": {
+ "System.IO": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Emit.ILGeneration/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Emit.Lightweight/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Emit.ILGeneration": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {
+ "assemblyVersion": "4.0.2.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Reflection.Extensions/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection.Extensions": "4.3.0"
+ }
+ },
+ "System.Reflection.Metadata/1.6.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.Reflection.Metadata.dll": {
+ "assemblyVersion": "1.4.3.0",
+ "fileVersion": "4.6.26515.6"
+ }
+ }
+ },
+ "System.Reflection.Primitives/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Reflection.Primitives": "4.3.0"
+ }
+ },
+ "System.Reflection.TypeExtensions/4.3.0": {
+ "dependencies": {
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.5/System.Reflection.TypeExtensions.dll": {
+ "assemblyVersion": "4.1.1.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Resources.ResourceManager/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Globalization": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Resources.ResourceManager": "4.3.0"
+ }
+ },
+ "System.Runtime/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "runtime.any.System.Runtime": "4.3.0"
+ }
+ },
+ "System.Runtime.CompilerServices.Unsafe/5.0.0": {
+ "runtime": {
+ "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Runtime.Extensions/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.win.System.Runtime.Extensions": "4.3.0"
+ }
+ },
+ "System.Runtime.Handles/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Runtime.Handles": "4.3.0"
+ }
+ },
+ "System.Runtime.InteropServices/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "runtime.any.System.Runtime.InteropServices": "4.3.0"
+ }
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.Cng/5.0.0": {
+ "dependencies": {
+ "System.Formats.Asn1": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Security.Cryptography.Cng.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.Pkcs/5.0.0": {
+ "dependencies": {
+ "System.Formats.Asn1": "5.0.0",
+ "System.Security.Cryptography.Cng": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Security.Cryptography.Pkcs.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Cryptography.ProtectedData/5.0.0": {
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Permissions/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Windows.Extensions": "5.0.0"
+ },
+ "runtime": {
+ "lib/net5.0/System.Security.Permissions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Security.Principal.Windows/5.0.0": {
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.1/System.Security.Principal.Windows.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.ServiceProcess.ServiceController/5.0.0": {
+ "dependencies": {
+ "System.Diagnostics.EventLog": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.ServiceProcess.ServiceController.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Text.Encoding/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Text.Encoding": "4.3.0"
+ }
+ },
+ "System.Text.Encoding.CodePages/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Threading/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/System.Threading.dll": {
+ "assemblyVersion": "4.0.12.0",
+ "fileVersion": "4.6.24705.1"
+ }
+ }
+ },
+ "System.Threading.AccessControl/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netstandard2.0/System.Threading.AccessControl.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Threading.Tasks/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.1.0",
+ "System.Runtime": "4.3.0",
+ "runtime.any.System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "System.Threading.Tasks.Extensions/4.5.3": {},
+ "System.Windows.Extensions/5.0.0": {
+ "dependencies": {
+ "System.Drawing.Common": "5.0.0"
+ },
+ "runtime": {
+ "runtimes/win/lib/netcoreapp3.0/System.Windows.Extensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Chef.Powershell.Core/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "runtimepack.Microsoft.NETCore.App.Runtime.win-x86/5.0.0": {
+ "type": "runtimepack",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "Markdig.Signed/0.21.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Lg0FPQdKUXai4/XZpQGlQ/HldpHsGnsU6Jd+udJgHj/R7i3ngM2TtR+SZqdHRtgItIHe3dFh2DS/M5qXGSPRiQ==",
+ "path": "markdig.signed/0.21.1",
+ "hashPath": "markdig.signed.0.21.1.nupkg.sha512"
+ },
+ "Microsoft.ApplicationInsights/2.15.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-tG9WZoFc0BTbkj+UjH4IUp8qkT9NA5st8zvPzlHbU8rJDgPFqZDJ3SSAQajl42jet1ghhJ5ZixsjjqVvwi4kaQ==",
+ "path": "microsoft.applicationinsights/2.15.0",
+ "hashPath": "microsoft.applicationinsights.2.15.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.Analyzers/3.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ojG5pGAhTPmjxRGTNvuszO3H8XPZqksDwr9xLd4Ae/JBjZZdl6GuoLk7uLMf+o7yl5wO0TAqoWcEKkEWqrZE5g==",
+ "path": "microsoft.codeanalysis.analyzers/3.0.0",
+ "hashPath": "microsoft.codeanalysis.analyzers.3.0.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.Common/3.7.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SFEdnbw8204hTlde3JePYSIpNX58h/MMXa7LctUsUDigWMR8Ar9gE8LnsLqAIFM0O33JEuQbJ0G4Sat+cPGldw==",
+ "path": "microsoft.codeanalysis.common/3.7.0",
+ "hashPath": "microsoft.codeanalysis.common.3.7.0.nupkg.sha512"
+ },
+ "Microsoft.CodeAnalysis.CSharp/3.7.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-sKi5PIVy9nVDerkbplY6OQhJBNzEO4XJsMGrnmb6KFEa6K1ulGCHIv6NtDjdUQ/dGrouU3OExc3yzww0COD76w==",
+ "path": "microsoft.codeanalysis.csharp/3.7.0",
+ "hashPath": "microsoft.codeanalysis.csharp.3.7.0.nupkg.sha512"
+ },
+ "Microsoft.CSharp/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-P+MBhIM0YX+JqROuf7i306ZLJEjQYA9uUyRDE+OqwUI5sh41e2ZbPQV3LfAPh+29cmceE1pUffXsGfR4eMY3KA==",
+ "path": "microsoft.csharp/4.3.0",
+ "hashPath": "microsoft.csharp.4.3.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-IaKZRNBBv3sdrmBWd+aqwHq8cVHk/3WgWFAN/dt40MRY9rbtHiDfTTmaEN0tGTmQqGCGDo/ncntA8MvFMvcsRw==",
+ "path": "microsoft.management.infrastructure/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Unix/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-p0lslMX5bdWLxO2P7ao+rjAMOB0LEwPYpzvdCQ2OEYgX2NxFpQ8ILvqPGnYlTAb53rT8gu5DyIol1HboHFYfxQ==",
+ "path": "microsoft.management.infrastructure.runtime.unix/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.runtime.unix.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.Management.Infrastructure.Runtime.Win/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vjBWQeDOjgernkrOdbEgn7M70SF7hof7ORdKPSlL06Uc15+oYdth5dZju9KsgUoti/cwnkZTiwtDx/lRtay0sA==",
+ "path": "microsoft.management.infrastructure.runtime.win/2.0.0",
+ "hashPath": "microsoft.management.infrastructure.runtime.win.2.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==",
+ "path": "microsoft.netcore.platforms/5.0.0",
+ "hashPath": "microsoft.netcore.platforms.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Targets/1.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==",
+ "path": "microsoft.netcore.targets/1.1.0",
+ "hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Diagnostics/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Hipqx38QOv1xWXbV4yGWHOmFIV8zp0O15z2H+Vx0Z0cOnzu4eb+MUblb4Dc1FA3S7ZE56qplC3XBK3hs0EsvYA==",
+ "path": "microsoft.powershell.commands.diagnostics/7.1.0",
+ "hashPath": "microsoft.powershell.commands.diagnostics.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Management/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3S5CU0Zq8ecdywaI3NoKyzjyaLaS7OKAi6z2AfiyKDG5IZfoLTBgIzNQElNFag8UALTNRlKGD8cumVtM3myAQA==",
+ "path": "microsoft.powershell.commands.management/7.1.0",
+ "hashPath": "microsoft.powershell.commands.management.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Commands.Utility/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-6BXJ7NRCs8xCHqspv1VoijXKLfLrM6O8rJwyTk0mPc5BZvpC2NSQq1NAJHAlk60ENFTu0vtxK2IzWkbFvJ/BUQ==",
+ "path": "microsoft.powershell.commands.utility/7.1.0",
+ "hashPath": "microsoft.powershell.commands.utility.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.ConsoleHost/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vfs4i5cViK5YsiIveuFv7OTrN2m5vPfOXj3XBkrQHtlMb2u0mkIsqVMTDHRMAyopcpp0ZJ1H+4ZsZ9z1bcpagQ==",
+ "path": "microsoft.powershell.consolehost/7.1.0",
+ "hashPath": "microsoft.powershell.consolehost.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.CoreCLR.Eventing/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-TCTaoyysnTHUErm0w8W50CVB5Mzwj1PQyb740K1DLvbw8Ba607Q1HCSzBHClEDlG+Cphuv9ie2pbvEWcUKslVg==",
+ "path": "microsoft.powershell.coreclr.eventing/7.1.0",
+ "hashPath": "microsoft.powershell.coreclr.eventing.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.MarkdownRender/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FD8ARPyKg22yRhU3gUk6NKZtmtSk5uekqufjJCiTu8A5lvWF9A0mCr/OUYE7+HQLnJoBu8BR19vwnED0Jpd1WQ==",
+ "path": "microsoft.powershell.markdownrender/7.1.0",
+ "hashPath": "microsoft.powershell.markdownrender.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Native/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-PJ/ei1HnYC+CMVDdUMT96PgWFONwVv/ZaJ5j//qLdk073alzdzOPWZiGsxYimJaRLP0TW4uomgIsR9q6jrf48A==",
+ "path": "microsoft.powershell.native/7.1.0",
+ "hashPath": "microsoft.powershell.native.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.PowerShell.Security/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-I4ab6z1Vlu25c33jc0gG0Val6YpSgyOyxNsNOENcBGue9TDJQ+d6ppw1IKcCILefpVAo/UWiE4xrWxO04KUc3g==",
+ "path": "microsoft.powershell.security/7.1.0",
+ "hashPath": "microsoft.powershell.security.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.Registry/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+ "path": "microsoft.win32.registry/5.0.0",
+ "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.Registry.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rwF501ZS/xKGWz5H3RLBvwta6E5kcMLB0UYGTgrZ8nL5bvrbGmtEcEObgMC/qRFhA3og/0Zh+eacrcA+0FBXJA==",
+ "path": "microsoft.win32.registry.accesscontrol/5.0.0",
+ "hashPath": "microsoft.win32.registry.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Win32.SystemEvents/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==",
+ "path": "microsoft.win32.systemevents/5.0.0",
+ "hashPath": "microsoft.win32.systemevents.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.WSMan.Management/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-noQxoEaJW5W4vbRfwIB1l+ecERA+K4ZygZBv1YNu0IwdDos0mIiPY+YpFKz2uHyeNSk1Z3j1fsXV0b/r+eRT1Q==",
+ "path": "microsoft.wsman.management/7.1.0",
+ "hashPath": "microsoft.wsman.management.7.1.0.nupkg.sha512"
+ },
+ "Microsoft.WSMan.Runtime/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-DVtFU3QQ12w5ej86Gw/vNtJjCXPd7r9fLJs9tzYqptEO6WtJX+vH65qPacP0DN/LEuc+93+iRVfNjjf/EMuNBQ==",
+ "path": "microsoft.wsman.runtime/7.1.0",
+ "hashPath": "microsoft.wsman.runtime.7.1.0.nupkg.sha512"
+ },
+ "Namotion.Reflection/1.0.14": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-wuJGiFvGfehH2w7jAhMbCJt0/rvUuHyqSZn0sMhNTviDfBZRyX8LFlR/ndQcofkGWulPDfH5nKYTeGXE8xBHPA==",
+ "path": "namotion.reflection/1.0.14",
+ "hashPath": "namotion.reflection.1.0.14.nupkg.sha512"
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==",
+ "path": "newtonsoft.json/12.0.3",
+ "hashPath": "newtonsoft.json.12.0.3.nupkg.sha512"
+ },
+ "NJsonSchema/10.2.2": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-s6oNUrjw5Ix5WVkYdU0vgyzoutZdi7p+uQqTGYa3QbLtjDYrkD4ahfGnfyCpHNoJUXun9pHKGy6AD0LJNcSgjQ==",
+ "path": "njsonschema/10.2.2",
+ "hashPath": "njsonschema.10.2.2.nupkg.sha512"
+ },
+ "runtime.any.System.Collections/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==",
+ "path": "runtime.any.system.collections/4.3.0",
+ "hashPath": "runtime.any.system.collections.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Globalization/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==",
+ "path": "runtime.any.system.globalization/4.3.0",
+ "hashPath": "runtime.any.system.globalization.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.IO/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==",
+ "path": "runtime.any.system.io/4.3.0",
+ "hashPath": "runtime.any.system.io.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==",
+ "path": "runtime.any.system.reflection/4.3.0",
+ "hashPath": "runtime.any.system.reflection.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==",
+ "path": "runtime.any.system.reflection.extensions/4.3.0",
+ "hashPath": "runtime.any.system.reflection.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==",
+ "path": "runtime.any.system.reflection.primitives/4.3.0",
+ "hashPath": "runtime.any.system.reflection.primitives.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==",
+ "path": "runtime.any.system.resources.resourcemanager/4.3.0",
+ "hashPath": "runtime.any.system.resources.resourcemanager.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==",
+ "path": "runtime.any.system.runtime/4.3.0",
+ "hashPath": "runtime.any.system.runtime.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==",
+ "path": "runtime.any.system.runtime.handles/4.3.0",
+ "hashPath": "runtime.any.system.runtime.handles.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==",
+ "path": "runtime.any.system.runtime.interopservices/4.3.0",
+ "hashPath": "runtime.any.system.runtime.interopservices.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Text.Encoding/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==",
+ "path": "runtime.any.system.text.encoding/4.3.0",
+ "hashPath": "runtime.any.system.text.encoding.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Threading.Tasks/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==",
+ "path": "runtime.any.system.threading.tasks/4.3.0",
+ "hashPath": "runtime.any.system.threading.tasks.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==",
+ "path": "runtime.win.system.diagnostics.debug/4.3.0",
+ "hashPath": "runtime.win.system.diagnostics.debug.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==",
+ "path": "runtime.win.system.runtime.extensions/4.3.0",
+ "hashPath": "runtime.win.system.runtime.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.win7.System.Private.Uri/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Q+IBgaPYicSQs2tBlmXqbS25c/JLIthWrgrpMwxKSOobW/OqIMVFruUGfuaz4QABVzV8iKdCAbN7APY7Tclbnw==",
+ "path": "runtime.win7.system.private.uri/4.3.0",
+ "hashPath": "runtime.win7.system.private.uri.4.3.0.nupkg.sha512"
+ },
+ "System.CodeDom/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==",
+ "path": "system.codedom/5.0.0",
+ "hashPath": "system.codedom.5.0.0.nupkg.sha512"
+ },
+ "System.Collections/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
+ "path": "system.collections/4.3.0",
+ "hashPath": "system.collections.4.3.0.nupkg.sha512"
+ },
+ "System.Collections.Immutable/1.5.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ==",
+ "path": "system.collections.immutable/1.5.0",
+ "hashPath": "system.collections.immutable.1.5.0.nupkg.sha512"
+ },
+ "System.Configuration.ConfigurationManager/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==",
+ "path": "system.configuration.configurationmanager/5.0.0",
+ "hashPath": "system.configuration.configurationmanager.5.0.0.nupkg.sha512"
+ },
+ "System.Diagnostics.Debug/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
+ "path": "system.diagnostics.debug/4.3.0",
+ "hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512"
+ },
+ "System.Diagnostics.DiagnosticSource/4.6.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-mbBgoR0rRfl2uimsZ2avZY8g7Xnh1Mza0rJZLPcxqiMWlkGukjmRkuMJ/er+AhQuiRIh80CR/Hpeztr80seV5g==",
+ "path": "system.diagnostics.diagnosticsource/4.6.0",
+ "hashPath": "system.diagnostics.diagnosticsource.4.6.0.nupkg.sha512"
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FHkCwUfsTs+/5tsK+c0egLfacUgbhvcwi3wUFWSEEArSXao343mYqcpOVVFMlcCkdNtjU4YwAWaKYwal6f02og==",
+ "path": "system.diagnostics.eventlog/5.0.0",
+ "hashPath": "system.diagnostics.eventlog.5.0.0.nupkg.sha512"
+ },
+ "System.DirectoryServices/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lAS54Y3KO1XV68akGa0/GJeddkkuuiv2CtcSkMiTmLHQ6o6kFbKpw4DmJZADF7a6KjPwYxmZnH4D3eGicrJdcg==",
+ "path": "system.directoryservices/5.0.0",
+ "hashPath": "system.directoryservices.5.0.0.nupkg.sha512"
+ },
+ "System.Drawing.Common/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==",
+ "path": "system.drawing.common/5.0.0",
+ "hashPath": "system.drawing.common.5.0.0.nupkg.sha512"
+ },
+ "System.Dynamic.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==",
+ "path": "system.dynamic.runtime/4.3.0",
+ "hashPath": "system.dynamic.runtime.4.3.0.nupkg.sha512"
+ },
+ "System.Formats.Asn1/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w==",
+ "path": "system.formats.asn1/5.0.0",
+ "hashPath": "system.formats.asn1.5.0.0.nupkg.sha512"
+ },
+ "System.Globalization/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
+ "path": "system.globalization/4.3.0",
+ "hashPath": "system.globalization.4.3.0.nupkg.sha512"
+ },
+ "System.IO/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
+ "path": "system.io/4.3.0",
+ "hashPath": "system.io.4.3.0.nupkg.sha512"
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==",
+ "path": "system.io.filesystem.accesscontrol/5.0.0",
+ "hashPath": "system.io.filesystem.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Linq/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
+ "path": "system.linq/4.3.0",
+ "hashPath": "system.linq.4.3.0.nupkg.sha512"
+ },
+ "System.Linq.Expressions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==",
+ "path": "system.linq.expressions/4.3.0",
+ "hashPath": "system.linq.expressions.4.3.0.nupkg.sha512"
+ },
+ "System.Management/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
+ "path": "system.management/5.0.0",
+ "hashPath": "system.management.5.0.0.nupkg.sha512"
+ },
+ "System.Management.Automation/7.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-WjmMs8kNETGWUmbE2A87cTcAGRz0WJ24TOZm5xkhop/93fIINK8TEcghEr2I0mSgXb7t0pWkPdKdEA5vizFQPA==",
+ "path": "system.management.automation/7.1.0",
+ "hashPath": "system.management.automation.7.1.0.nupkg.sha512"
+ },
+ "System.Memory/4.5.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==",
+ "path": "system.memory/4.5.4",
+ "hashPath": "system.memory.4.5.4.nupkg.sha512"
+ },
+ "System.ObjectModel/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==",
+ "path": "system.objectmodel/4.3.0",
+ "hashPath": "system.objectmodel.4.3.0.nupkg.sha512"
+ },
+ "System.Private.Uri/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==",
+ "path": "system.private.uri/4.3.0",
+ "hashPath": "system.private.uri.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
+ "path": "system.reflection/4.3.0",
+ "hashPath": "system.reflection.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==",
+ "path": "system.reflection.emit/4.3.0",
+ "hashPath": "system.reflection.emit.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit.ILGeneration/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
+ "path": "system.reflection.emit.ilgeneration/4.3.0",
+ "hashPath": "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Emit.Lightweight/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
+ "path": "system.reflection.emit.lightweight/4.3.0",
+ "hashPath": "system.reflection.emit.lightweight.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
+ "path": "system.reflection.extensions/4.3.0",
+ "hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.Metadata/1.6.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
+ "path": "system.reflection.metadata/1.6.0",
+ "hashPath": "system.reflection.metadata.1.6.0.nupkg.sha512"
+ },
+ "System.Reflection.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
+ "path": "system.reflection.primitives/4.3.0",
+ "hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512"
+ },
+ "System.Reflection.TypeExtensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
+ "path": "system.reflection.typeextensions/4.3.0",
+ "hashPath": "system.reflection.typeextensions.4.3.0.nupkg.sha512"
+ },
+ "System.Resources.ResourceManager/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
+ "path": "system.resources.resourcemanager/4.3.0",
+ "hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
+ "path": "system.runtime/4.3.0",
+ "hashPath": "system.runtime.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.CompilerServices.Unsafe/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==",
+ "path": "system.runtime.compilerservices.unsafe/5.0.0",
+ "hashPath": "system.runtime.compilerservices.unsafe.5.0.0.nupkg.sha512"
+ },
+ "System.Runtime.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
+ "path": "system.runtime.extensions/4.3.0",
+ "hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.Handles/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
+ "path": "system.runtime.handles/4.3.0",
+ "hashPath": "system.runtime.handles.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.InteropServices/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
+ "path": "system.runtime.interopservices/4.3.0",
+ "hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512"
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+ "path": "system.security.accesscontrol/5.0.0",
+ "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.Cng/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
+ "path": "system.security.cryptography.cng/5.0.0",
+ "hashPath": "system.security.cryptography.cng.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.Pkcs/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-9TPLGjBCGKmNvG8pjwPeuYy0SMVmGZRwlTZvyPHDbYv/DRkoeumJdfumaaDNQzVGMEmbWtg07zUpSW9q70IlDQ==",
+ "path": "system.security.cryptography.pkcs/5.0.0",
+ "hashPath": "system.security.cryptography.pkcs.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Cryptography.ProtectedData/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==",
+ "path": "system.security.cryptography.protecteddata/5.0.0",
+ "hashPath": "system.security.cryptography.protecteddata.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Permissions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==",
+ "path": "system.security.permissions/5.0.0",
+ "hashPath": "system.security.permissions.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Principal.Windows/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
+ "path": "system.security.principal.windows/5.0.0",
+ "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
+ },
+ "System.ServiceProcess.ServiceController/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-p2yX167GW1pr2DCR6cW+cBKrvhli4thckXk108faFaTPHnoudb0AYPcIPq3nmrwn7IQj9FEmjpyJlXzcOmIjjw==",
+ "path": "system.serviceprocess.servicecontroller/5.0.0",
+ "hashPath": "system.serviceprocess.servicecontroller.5.0.0.nupkg.sha512"
+ },
+ "System.Text.Encoding/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
+ "path": "system.text.encoding/4.3.0",
+ "hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
+ },
+ "System.Text.Encoding.CodePages/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==",
+ "path": "system.text.encoding.codepages/5.0.0",
+ "hashPath": "system.text.encoding.codepages.5.0.0.nupkg.sha512"
+ },
+ "System.Threading/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
+ "path": "system.threading/4.3.0",
+ "hashPath": "system.threading.4.3.0.nupkg.sha512"
+ },
+ "System.Threading.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-WJ9w9m4iHJVq0VoH7hZvYAccbRq95itYRhAAXd6M4kVCzLmT6NqTwmSXKwp3oQilWHhYTXgqaIXxBfg8YaqtmA==",
+ "path": "system.threading.accesscontrol/5.0.0",
+ "hashPath": "system.threading.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Threading.Tasks/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
+ "path": "system.threading.tasks/4.3.0",
+ "hashPath": "system.threading.tasks.4.3.0.nupkg.sha512"
+ },
+ "System.Threading.Tasks.Extensions/4.5.3": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+MvhNtcvIbqmhANyKu91jQnvIRVSTiaOiFNfKWwXGHG48YAb4I/TyH8spsySiPYla7gKal5ZnF3teJqZAximyQ==",
+ "path": "system.threading.tasks.extensions/4.5.3",
+ "hashPath": "system.threading.tasks.extensions.4.5.3.nupkg.sha512"
+ },
+ "System.Windows.Extensions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==",
+ "path": "system.windows.extensions/5.0.0",
+ "hashPath": "system.windows.extensions.5.0.0.nupkg.sha512"
+ }
+ },
+ "runtimes": {
+ "win10-x86": [
+ "win10",
+ "win81-x86",
+ "win81",
+ "win8-x86",
+ "win8",
+ "win7-x86",
+ "win7",
+ "win-x86",
+ "win",
+ "any",
+ "base"
+ ],
+ "win10-x86-aot": [
+ "win10-aot",
+ "win10-x86",
+ "win10",
+ "win81-x86-aot",
+ "win81-aot",
+ "win81-x86",
+ "win81",
+ "win8-x86-aot",
+ "win8-aot",
+ "win8-x86",
+ "win8",
+ "win7-x86-aot",
+ "win7-aot",
+ "win7-x86",
+ "win7",
+ "win-x86-aot",
+ "win-aot",
+ "win-x86",
+ "win",
+ "aot",
+ "any",
+ "base"
+ ]
+ }
+} \ No newline at end of file
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll
new file mode 100644
index 0000000000..9ada03f00f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Diagnostics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll
new file mode 100644
index 0000000000..7d8d5afcd3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll
new file mode 100644
index 0000000000..b63f889771
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Commands.Utility.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll
new file mode 100644
index 0000000000..96fa29deea
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.ConsoleHost.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll
new file mode 100644
index 0000000000..1dc32290ac
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.CoreCLR.Eventing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll
new file mode 100644
index 0000000000..6af4347c1d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.MarkdownRender.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll
new file mode 100644
index 0000000000..fd06cf702f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.PowerShell.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll
new file mode 100644
index 0000000000..ef7985f068
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll
new file mode 100644
index 0000000000..f4169868c0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.VisualBasic.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll
new file mode 100644
index 0000000000..d33a2ffd25
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll
new file mode 100644
index 0000000000..5644be0dfd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.WSMan.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll
new file mode 100644
index 0000000000..8b3954033a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll
new file mode 100644
index 0000000000..934802f5c2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll
new file mode 100644
index 0000000000..4dbe1ef978
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.Registry.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll
new file mode 100644
index 0000000000..b5aa0c4cd3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Microsoft.Win32.SystemEvents.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll
new file mode 100644
index 0000000000..36ecf91055
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/NJsonSchema.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll
new file mode 100644
index 0000000000..8cbfb13909
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Namotion.Reflection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll
new file mode 100644
index 0000000000..b501fb6a29
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Newtonsoft.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll
new file mode 100644
index 0000000000..f592b9a511
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/PowerShell.Core.Instrumentation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll
new file mode 100644
index 0000000000..b1a20b58c8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.AppContext.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll
new file mode 100644
index 0000000000..b881d20368
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Buffers.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll
new file mode 100644
index 0000000000..873495d3bd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.CodeDom.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll
new file mode 100644
index 0000000000..a56d0897b4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Concurrent.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll
new file mode 100644
index 0000000000..9a6816af59
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Immutable.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll
new file mode 100644
index 0000000000..371f7e96cb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.NonGeneric.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll
new file mode 100644
index 0000000000..4007eecec0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.Specialized.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll
new file mode 100644
index 0000000000..1d38f28b98
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Collections.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll
new file mode 100644
index 0000000000..9371560992
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Annotations.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll
new file mode 100644
index 0000000000..848ff822f6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.DataAnnotations.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll
new file mode 100644
index 0000000000..beb02a9fb6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.EventBasedAsync.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll
new file mode 100644
index 0000000000..25950c2e76
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll
new file mode 100644
index 0000000000..3f7c1962f9
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.TypeConverter.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll
new file mode 100644
index 0000000000..bcfd36f18e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ComponentModel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll
new file mode 100644
index 0000000000..1644e5d6d6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.ConfigurationManager.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll
new file mode 100644
index 0000000000..336518895c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Configuration.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll
new file mode 100644
index 0000000000..caf6502e59
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Console.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll
new file mode 100644
index 0000000000..733ec59349
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll
new file mode 100644
index 0000000000..37a2d2cb3b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.Common.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll
new file mode 100644
index 0000000000..c1e4676375
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.DataSetExtensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll
new file mode 100644
index 0000000000..96d612a6e5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Data.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll
new file mode 100644
index 0000000000..aee8520460
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Contracts.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll
new file mode 100644
index 0000000000..b682fa66e0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Debug.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll
new file mode 100644
index 0000000000..cef5d89e43
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.DiagnosticSource.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll
new file mode 100644
index 0000000000..bb76446a5e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.EventLog.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll
new file mode 100644
index 0000000000..466d54c708
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.FileVersionInfo.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll
new file mode 100644
index 0000000000..3eff2da950
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Process.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll
new file mode 100644
index 0000000000..533ccde2bd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.StackTrace.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll
new file mode 100644
index 0000000000..b991388669
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TextWriterTraceListener.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll
new file mode 100644
index 0000000000..437a872743
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tools.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll
new file mode 100644
index 0000000000..3edfc46ee2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.TraceSource.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll
new file mode 100644
index 0000000000..05ec366f23
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Diagnostics.Tracing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll
new file mode 100644
index 0000000000..e43577d592
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.DirectoryServices.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll
new file mode 100644
index 0000000000..87fe0ae8bb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Common.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll
new file mode 100644
index 0000000000..536cff8bc8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll
new file mode 100644
index 0000000000..80ff7228c1
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Drawing.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll
new file mode 100644
index 0000000000..a99a2e9644
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Dynamic.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll
new file mode 100644
index 0000000000..f194c14ec7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Formats.Asn1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll
new file mode 100644
index 0000000000..06078378ef
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Calendars.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll
new file mode 100644
index 0000000000..bdd86cbb93
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll
new file mode 100644
index 0000000000..fb7a3d39c4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Globalization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll
new file mode 100644
index 0000000000..1993116705
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.Brotli.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll
new file mode 100644
index 0000000000..f5531bb397
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.FileSystem.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll
new file mode 100644
index 0000000000..bec719f635
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.ZipFile.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll
new file mode 100644
index 0000000000..f71aef8973
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Compression.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll
new file mode 100644
index 0000000000..23f159eebb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll
new file mode 100644
index 0000000000..7d29ca9574
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.DriveInfo.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll
new file mode 100644
index 0000000000..bc8964043d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll
new file mode 100644
index 0000000000..17b00763f1
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.Watcher.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll
new file mode 100644
index 0000000000..5f8557523f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.FileSystem.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll
new file mode 100644
index 0000000000..78cbb117ec
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.IsolatedStorage.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll
new file mode 100644
index 0000000000..ca78c89331
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.MemoryMappedFiles.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll
new file mode 100644
index 0000000000..927ee75b92
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll
new file mode 100644
index 0000000000..eb52591f57
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.Pipes.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll
new file mode 100644
index 0000000000..a5dfd8faa7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.UnmanagedMemoryStream.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll
new file mode 100644
index 0000000000..5299140cee
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.IO.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll
new file mode 100644
index 0000000000..0a04617ef0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Expressions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll
new file mode 100644
index 0000000000..0226ed41fb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Parallel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll
new file mode 100644
index 0000000000..1693c4fca5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.Queryable.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll
new file mode 100644
index 0000000000..5d0beaec5b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll
new file mode 100644
index 0000000000..aa3f0fccb5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.Automation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll
new file mode 100644
index 0000000000..a3a1f45197
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Management.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll
new file mode 100644
index 0000000000..47ed8149bc
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Memory.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll
new file mode 100644
index 0000000000..1f979b0376
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll
new file mode 100644
index 0000000000..2f028f3609
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Http.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll
new file mode 100644
index 0000000000..c754ea8d90
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.HttpListener.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll
new file mode 100644
index 0000000000..6d0c9971b4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Mail.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll
new file mode 100644
index 0000000000..43c577bbed
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NameResolution.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll
new file mode 100644
index 0000000000..7838d83930
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.NetworkInformation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll
new file mode 100644
index 0000000000..f1ec22bbcd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Ping.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll
new file mode 100644
index 0000000000..be0bcda333
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll
new file mode 100644
index 0000000000..03f5ad8e42
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Requests.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll
new file mode 100644
index 0000000000..24aabd0bc7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll
new file mode 100644
index 0000000000..b34797f854
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.ServicePoint.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll
new file mode 100644
index 0000000000..ac67d5483f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.Sockets.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll
new file mode 100644
index 0000000000..0754ba93e2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebClient.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll
new file mode 100644
index 0000000000..05473da99f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebHeaderCollection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll
new file mode 100644
index 0000000000..31d311d8e0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebProxy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll
new file mode 100644
index 0000000000..dba7236e6b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.Client.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll
new file mode 100644
index 0000000000..b3745f3113
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.WebSockets.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll
new file mode 100644
index 0000000000..425660b794
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Net.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll
new file mode 100644
index 0000000000..22dcbb47fd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.Vectors.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll
new file mode 100644
index 0000000000..52e5f9ec9c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Numerics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll
new file mode 100644
index 0000000000..89643fc28c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ObjectModel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll
new file mode 100644
index 0000000000..9bdc5cdbe6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.CoreLib.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll
new file mode 100644
index 0000000000..30a3cbb29a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.DataContractSerialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll
new file mode 100644
index 0000000000..9c008e9963
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Uri.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll
new file mode 100644
index 0000000000..b560c635d2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll
new file mode 100644
index 0000000000..2a1dd23e85
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Private.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll
new file mode 100644
index 0000000000..8ae5a39086
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.DispatchProxy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll
new file mode 100644
index 0000000000..6f2e202809
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.ILGeneration.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll
new file mode 100644
index 0000000000..a974e21555
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.Lightweight.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll
new file mode 100644
index 0000000000..b06bfefa06
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Emit.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll
new file mode 100644
index 0000000000..c8c12d3437
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll
new file mode 100644
index 0000000000..cc1425439f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Metadata.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll
new file mode 100644
index 0000000000..1eb8399956
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll
new file mode 100644
index 0000000000..7b192b6be0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.TypeExtensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll
new file mode 100644
index 0000000000..4724275c88
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Reflection.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll
new file mode 100644
index 0000000000..9a9a3701bf
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Reader.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll
new file mode 100644
index 0000000000..18a0279be7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.ResourceManager.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll
new file mode 100644
index 0000000000..7312dfe471
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Resources.Writer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll
new file mode 100644
index 0000000000..91ce3a40f2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.Unsafe.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll
new file mode 100644
index 0000000000..bab432741f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.CompilerServices.VisualC.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll
new file mode 100644
index 0000000000..252595deb0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll
new file mode 100644
index 0000000000..af7abdd805
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Handles.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll
new file mode 100644
index 0000000000..55522e818e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.RuntimeInformation.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll
new file mode 100644
index 0000000000..2026dfa90e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.InteropServices.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll
new file mode 100644
index 0000000000..d352c5f6d3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Intrinsics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll
new file mode 100644
index 0000000000..cd16e3f1bc
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Loader.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll
new file mode 100644
index 0000000000..8a88bc72ae
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Numerics.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll
new file mode 100644
index 0000000000..c7913c1c97
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Formatters.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll
new file mode 100644
index 0000000000..57d39b6f8c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll
new file mode 100644
index 0000000000..2840880115
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll
new file mode 100644
index 0000000000..39d120a5fb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll
new file mode 100644
index 0000000000..85be088af7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.Serialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll
new file mode 100644
index 0000000000..96d92a7e51
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Runtime.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll
new file mode 100644
index 0000000000..5a58cc15ab
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll
new file mode 100644
index 0000000000..3c913a3694
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Claims.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll
new file mode 100644
index 0000000000..da6d3e1ada
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Algorithms.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll
new file mode 100644
index 0000000000..7f53360c71
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Cng.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll
new file mode 100644
index 0000000000..7f2c2e1f04
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Csp.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll
new file mode 100644
index 0000000000..473e5c5d32
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Encoding.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll
new file mode 100644
index 0000000000..097e377e33
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.OpenSsl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll
new file mode 100644
index 0000000000..252b80c246
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Pkcs.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll
new file mode 100644
index 0000000000..3c7a2e881a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.Primitives.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll
new file mode 100644
index 0000000000..99215bb1df
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.ProtectedData.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll
new file mode 100644
index 0000000000..9c73368356
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Cryptography.X509Certificates.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll
new file mode 100644
index 0000000000..b380d0856e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Permissions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll
new file mode 100644
index 0000000000..6333081398
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.Windows.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll
new file mode 100644
index 0000000000..5696a1fa42
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.Principal.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll
new file mode 100644
index 0000000000..5a41c8dcd4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.SecureString.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll
new file mode 100644
index 0000000000..87fbd3dced
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Security.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll
new file mode 100644
index 0000000000..f903215cb9
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceModel.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll
new file mode 100644
index 0000000000..9074d56d58
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.ServiceController.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll
new file mode 100644
index 0000000000..3812b90902
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ServiceProcess.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll
new file mode 100644
index 0000000000..f05265e306
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.CodePages.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll
new file mode 100644
index 0000000000..76abcaa617
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll
new file mode 100644
index 0000000000..86835aaaa8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encoding.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll
new file mode 100644
index 0000000000..efaa0493ba
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Encodings.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll
new file mode 100644
index 0000000000..a24fa22007
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.Json.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll
new file mode 100644
index 0000000000..92f48821d3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Text.RegularExpressions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll
new file mode 100644
index 0000000000..49d43249f0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.AccessControl.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll
new file mode 100644
index 0000000000..a6654dccee
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Channels.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll
new file mode 100644
index 0000000000..c7d1fcabee
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Overlapped.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll
new file mode 100644
index 0000000000..717e007115
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Dataflow.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll
new file mode 100644
index 0000000000..cb1414526e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll
new file mode 100644
index 0000000000..377d88013a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.Parallel.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll
new file mode 100644
index 0000000000..d7c4a35567
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Tasks.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll
new file mode 100644
index 0000000000..318d2ab8fb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Thread.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll
new file mode 100644
index 0000000000..da11a9984e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.ThreadPool.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll
new file mode 100644
index 0000000000..e67577e288
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.Timer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll
new file mode 100644
index 0000000000..cdf5081b53
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Threading.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll
new file mode 100644
index 0000000000..baea5d453b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.Local.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll
new file mode 100644
index 0000000000..701406c8d7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Transactions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll
new file mode 100644
index 0000000000..28240172ac
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.ValueTuple.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll
new file mode 100644
index 0000000000..cc2f0e1f7a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.HttpUtility.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll
new file mode 100644
index 0000000000..681076e2ed
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Web.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll
new file mode 100644
index 0000000000..ab82e8395a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.Extensions.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll
new file mode 100644
index 0000000000..0d75104c0f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Windows.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll
new file mode 100644
index 0000000000..57b6ed9cfb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Linq.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll
new file mode 100644
index 0000000000..2d6130a1f4
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.ReaderWriter.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll
new file mode 100644
index 0000000000..13c8818672
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.Serialization.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll
new file mode 100644
index 0000000000..55b27bb917
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll
new file mode 100644
index 0000000000..5e148afeaf
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.XDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll
new file mode 100644
index 0000000000..a0d0aa8032
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XPath.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll
new file mode 100644
index 0000000000..e6a15313ee
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlDocument.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll
new file mode 100644
index 0000000000..94cd19d1e7
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.XmlSerializer.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll
new file mode 100644
index 0000000000..8e7b9a6df3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.Xml.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.dll
new file mode 100644
index 0000000000..cca1ec8420
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/System.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll
new file mode 100644
index 0000000000..0a3f0a784a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/WindowsBase.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll
new file mode 100644
index 0000000000..e825ac47fb
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll
new file mode 100644
index 0000000000..acc6988e6b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-console-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll
new file mode 100644
index 0000000000..ca6284ed4b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-datetime-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll
new file mode 100644
index 0000000000..2a3eef142e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-debug-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll
new file mode 100644
index 0000000000..691f086cf0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-errorhandling-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll
new file mode 100644
index 0000000000..8870d27b04
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll
new file mode 100644
index 0000000000..bf151884b0
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll
new file mode 100644
index 0000000000..627d9a2e29
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-file-l2-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll
new file mode 100644
index 0000000000..5f73df1b6c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-handle-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll
new file mode 100644
index 0000000000..d347d553e6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-heap-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll
new file mode 100644
index 0000000000..8a5f4a284d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-interlocked-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll
new file mode 100644
index 0000000000..b8106634fd
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-libraryloader-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll
new file mode 100644
index 0000000000..4a1cdaa438
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-localization-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll
new file mode 100644
index 0000000000..30767ccf60
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-memory-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll
new file mode 100644
index 0000000000..93622f475b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-namedpipe-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll
new file mode 100644
index 0000000000..ff47ce3988
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processenvironment-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll
new file mode 100644
index 0000000000..4190813a31
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll
new file mode 100644
index 0000000000..c29fe9deaa
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-processthreads-l1-1-1.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll
new file mode 100644
index 0000000000..a045613703
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-profile-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll
new file mode 100644
index 0000000000..8f5b40d7b3
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-rtlsupport-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll
new file mode 100644
index 0000000000..dd307ec66a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-string-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll
new file mode 100644
index 0000000000..cd9b68e6e2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll
new file mode 100644
index 0000000000..d1b372c487
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-synch-l1-2-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll
new file mode 100644
index 0000000000..2ca7c75533
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-sysinfo-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll
new file mode 100644
index 0000000000..629b637338
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-timezone-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll
new file mode 100644
index 0000000000..d5e82ecb23
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-core-util-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll
new file mode 100644
index 0000000000..7cfcdc8c0c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-conio-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll
new file mode 100644
index 0000000000..bda798b407
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-convert-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll
new file mode 100644
index 0000000000..8b4aba3947
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-environment-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll
new file mode 100644
index 0000000000..628bdb55ae
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-filesystem-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll
new file mode 100644
index 0000000000..7de5fff618
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-heap-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll
new file mode 100644
index 0000000000..4c8770d0a6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-locale-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll
new file mode 100644
index 0000000000..0e80796d12
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-math-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll
new file mode 100644
index 0000000000..b549546cc5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-multibyte-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll
new file mode 100644
index 0000000000..cae53b9940
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-private-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll
new file mode 100644
index 0000000000..b9055e120d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-process-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll
new file mode 100644
index 0000000000..e2e29a026f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-runtime-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll
new file mode 100644
index 0000000000..3e35079771
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-stdio-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll
new file mode 100644
index 0000000000..e1dbc43573
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-string-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll
new file mode 100644
index 0000000000..3157c9e0e5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-time-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll
new file mode 100644
index 0000000000..e7e806212d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/api-ms-win-crt-utility-l1-1-0.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll
new file mode 100644
index 0000000000..3db37f23a6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrcompression.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll
new file mode 100644
index 0000000000..380f9daa52
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clretwrc.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll
new file mode 100644
index 0000000000..8f6d97266f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/clrjit.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll
new file mode 100644
index 0000000000..e4fc1030a5
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/coreclr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/createdump.exe b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/createdump.exe
new file mode 100644
index 0000000000..62fd3acb3e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/createdump.exe
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..4b72058de6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..43acdf3d4c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/cs/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll
new file mode 100644
index 0000000000..ff7bda2d5f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/dbgshim.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..55d6b391fa
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..698f08e5e2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/de/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..e770ccc2e8
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..62e2e9a98b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/es/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..f605e75a4d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..a5aec6aa4e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/fr/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll
new file mode 100644
index 0000000000..9074a6c2a1
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostfxr.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll
new file mode 100644
index 0000000000..ad628e6986
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/hostpolicy.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..5aedc4b83b
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..0ecca4c37d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/it/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2437ed9332
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..0b985d837c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ja/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..c908377e1f
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..bdb9eb5248
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ko/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mi.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mi.dll
new file mode 100644
index 0000000000..cfa384604d
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mi.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/miutils.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/miutils.dll
new file mode 100644
index 0000000000..6a6fdd1bb2
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/miutils.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll
new file mode 100644
index 0000000000..be9218e86c
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_x86_x86_5.0.20.51904.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_x86_x86_5.0.20.51904.dll
new file mode 100644
index 0000000000..0fdf537711
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordaccore_x86_x86_5.0.20.51904.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll
new file mode 100644
index 0000000000..3f17afda6e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscordbi.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll
new file mode 100644
index 0000000000..607a8cb582
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorlib.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll
new file mode 100644
index 0000000000..d1e73f7cef
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/mscorrc.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll
new file mode 100644
index 0000000000..cbecb91ced
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/netstandard.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..8f2b775300
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..b287bd94de
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pl/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..4c64afcfda
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..10a885e034
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pt-BR/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll
new file mode 100644
index 0000000000..93ef54cd98
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/pwrshplugin.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2b8db7b639
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..cb414f7bb6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ru/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..87c1ca2c76
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..5b1ff67570
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/tr/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll
new file mode 100644
index 0000000000..0f10704112
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/ucrtbase.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..8b19e6492a
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..32204d4633
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll
new file mode 100644
index 0000000000..2acfeed16e
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll
new file mode 100644
index 0000000000..57283cf4f6
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/vccorlib140.dll b/distro/ruby_bin_folder/x86/vccorlib140.dll
new file mode 100644
index 0000000000..f9933d56ac
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/vccorlib140.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/vcruntime140.dll b/distro/ruby_bin_folder/x86/vcruntime140.dll
new file mode 100644
index 0000000000..dc526fc062
--- /dev/null
+++ b/distro/ruby_bin_folder/x86/vcruntime140.dll
Binary files differ
diff --git a/distro/templates/powershell/chef/chef.psm1.erb b/distro/templates/powershell/chef/chef.psm1.erb
new file mode 100644
index 0000000000..35344d907d
--- /dev/null
+++ b/distro/templates/powershell/chef/chef.psm1.erb
@@ -0,0 +1,459 @@
+
+function Load-Win32Bindings {
+ Add-Type -TypeDefinition @"
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Chef
+{
+
+[StructLayout(LayoutKind.Sequential)]
+public struct PROCESS_INFORMATION
+{
+ public IntPtr hProcess;
+ public IntPtr hThread;
+ public uint dwProcessId;
+ public uint dwThreadId;
+}
+
+[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+public struct STARTUPINFO
+{
+ public uint cb;
+ public string lpReserved;
+ public string lpDesktop;
+ public string lpTitle;
+ public uint dwX;
+ public uint dwY;
+ public uint dwXSize;
+ public uint dwYSize;
+ public uint dwXCountChars;
+ public uint dwYCountChars;
+ public uint dwFillAttribute;
+ public STARTF dwFlags;
+ public ShowWindow wShowWindow;
+ public short cbReserved2;
+ public IntPtr lpReserved2;
+ public IntPtr hStdInput;
+ public IntPtr hStdOutput;
+ public IntPtr hStdError;
+}
+
+[StructLayout(LayoutKind.Sequential)]
+public struct SECURITY_ATTRIBUTES
+{
+ public int length;
+ public IntPtr lpSecurityDescriptor;
+ public bool bInheritHandle;
+}
+
+[Flags]
+public enum CreationFlags : int
+{
+ NONE = 0,
+ DEBUG_PROCESS = 0x00000001,
+ DEBUG_ONLY_THIS_PROCESS = 0x00000002,
+ CREATE_SUSPENDED = 0x00000004,
+ DETACHED_PROCESS = 0x00000008,
+ CREATE_NEW_CONSOLE = 0x00000010,
+ CREATE_NEW_PROCESS_GROUP = 0x00000200,
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400,
+ CREATE_SEPARATE_WOW_VDM = 0x00000800,
+ CREATE_SHARED_WOW_VDM = 0x00001000,
+ CREATE_PROTECTED_PROCESS = 0x00040000,
+ EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
+ CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
+ CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
+ CREATE_DEFAULT_ERROR_MODE = 0x04000000,
+ CREATE_NO_WINDOW = 0x08000000,
+}
+
+[Flags]
+public enum STARTF : uint
+{
+ STARTF_USESHOWWINDOW = 0x00000001,
+ STARTF_USESIZE = 0x00000002,
+ STARTF_USEPOSITION = 0x00000004,
+ STARTF_USECOUNTCHARS = 0x00000008,
+ STARTF_USEFILLATTRIBUTE = 0x00000010,
+ STARTF_RUNFULLSCREEN = 0x00000020, // ignored for non-x86 platforms
+ STARTF_FORCEONFEEDBACK = 0x00000040,
+ STARTF_FORCEOFFFEEDBACK = 0x00000080,
+ STARTF_USESTDHANDLES = 0x00000100,
+}
+
+public enum ShowWindow : short
+{
+ SW_HIDE = 0,
+ SW_SHOWNORMAL = 1,
+ SW_NORMAL = 1,
+ SW_SHOWMINIMIZED = 2,
+ SW_SHOWMAXIMIZED = 3,
+ SW_MAXIMIZE = 3,
+ SW_SHOWNOACTIVATE = 4,
+ SW_SHOW = 5,
+ SW_MINIMIZE = 6,
+ SW_SHOWMINNOACTIVE = 7,
+ SW_SHOWNA = 8,
+ SW_RESTORE = 9,
+ SW_SHOWDEFAULT = 10,
+ SW_FORCEMINIMIZE = 11,
+ SW_MAX = 11
+}
+
+public enum StandardHandle : int
+{
+ Input = -10,
+ Output = -11,
+ 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)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CreateProcess(
+ string lpApplicationName,
+ string lpCommandLine,
+ ref SECURITY_ATTRIBUTES lpProcessAttributes,
+ ref SECURITY_ATTRIBUTES lpThreadAttributes,
+ [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
+ CreationFlags dwCreationFlags,
+ IntPtr lpEnvironment,
+ string lpCurrentDirectory,
+ ref STARTUPINFO lpStartupInfo,
+ out PROCESS_INFORMATION lpProcessInformation);
+
+ [DllImport("kernel32.dll", SetLastError=true)]
+ public static extern IntPtr GetStdHandle(
+ StandardHandle nStdHandle);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool SetHandleInformation(
+ IntPtr hObject,
+ int dwMask,
+ uint dwFlags);
+
+ [DllImport("kernel32", SetLastError=true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CloseHandle(
+ IntPtr hObject);
+
+ [DllImport("kernel32", SetLastError=true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ 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;
+}
+}
+"@
+}
+
+function Run-ExecutableAndWait($AppPath, $ArgumentString) {
+ # Use the Win32 API to create a new process and wait for it to terminate.
+ $null = Load-Win32Bindings
+
+ $si = New-Object Chef.STARTUPINFO
+ $pi = New-Object Chef.PROCESS_INFORMATION
+
+ $pSec = New-Object Chef.SECURITY_ATTRIBUTES
+ $pSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($pSec)
+ $pSec.bInheritHandle = $true
+ $tSec = New-Object Chef.SECURITY_ATTRIBUTES
+ $tSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($tSec)
+ $tSec.bInheritHandle = $true
+
+ # 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."
+ }
+
+ $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 "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 {
+ # 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
+ }
+ }
+
+ # Cleanup handles
+ $success = [Chef.Kernel32]::CloseHandle($pi.hProcess)
+ if (-Not $success) {
+ $reason = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ throw "Unable to release process handle. Error code $reason."
+ }
+ $success = [Chef.Kernel32]::CloseHandle($pi.hThread)
+ if (-Not $success) {
+ $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 {
+ if (!$PSScriptRoot) {
+ $Invocation = (Get-Variable MyInvocation -Scope 1).Value
+ $PSScriptRoot = Split-Path $Invocation.MyCommand.Path
+ }
+ $PSScriptRoot
+}
+
+function Run-RubyCommand($command, $argList) {
+ # This method exists to take the given list of arguments and get it past ruby's command-line
+ # interpreter unscathed and untampered. See https://github.com/ruby/ruby/blob/trunk/win32/win32.c#L1582
+ # for a list of transformations that ruby attempts to perform with your command-line arguments
+ # before passing it onto a script. The most important task is to defeat the globbing
+ # and wild-card expansion that ruby performs. Note that ruby does not use MSVCRT's argc/argv
+ # and deliberately reparses the raw command-line instead.
+ #
+ # To stop ruby from interpreting command-line arguments as globs, they need to be enclosed in '
+ # Ruby doesn't allow any escape characters inside '. This unfortunately prevents us from sending
+ # any strings which themselves contain '. Ruby does allow multi-fragment arguments though.
+ # "foo bar"'baz qux'123"foo" is interpreted as 1 argument because there are no un-escaped
+ # whitespace there. The argument would be interpreted as the string "foo barbaz qux123foo".
+ # This lets us escape ' characters by exiting the ' quoted string, injecting a "'" fragment and
+ # then resuming the ' quoted string again.
+ #
+ # In the process of defeating ruby, one must also defeat the helpfulness of powershell.
+ # When arguments come into this method, the standard PS rules for interpreting cmdlet arguments
+ # apply. When using & (call operator) and providing an array of arguments, powershell (verified
+ # on PS 4.0 on Windows Server 2012R2) will not evaluate them but (contrary to documentation),
+ # it will still marginally interpret them. The behavior of PS 5.0 seems to be different but
+ # ignore that for now. If any of the provided arguments has a space in it, powershell checks
+ # the first and last character to ensure that they are " characters (and that's all it checks).
+ # If they are not, it will blindly surround that argument with " characters. It won't do this
+ # operation if no space is present, even if other special characters are present. If it notices
+ # leading and trailing " characters, it won't actually check to see if there are other "
+ # characters in the string. Since PS 5.0 changes this behavior, we could consider using the --%
+ # "stop screwing up my arguments" operator, which is available since PS 3.0. When encountered
+ # --% indicates that the rest of line is to be sent literally... except if the parser encounters
+ # %FOO% cmd style environment variables. Because reasons. And there is no way to escape the
+ # % character in *any* waym shape or form.
+ # https://connect.microsoft.com/PowerShell/feedback/details/376207/executing-commands-which-require-quotes-and-variables-is-practically-impossible
+ #
+ # In case you think that you're either reading this incorrectly or that I'm full of shit, here
+ # are some examples. These use EchoArgs.exe from the PowerShell Community Extensions package.
+ # I have not included the argument parsing output from EchoArgs.exe to prevent confusing you with
+ # more details about MSVCRT's parsing algorithm.
+ #
+ # $x = "foo '' bar `"baz`""
+ # & EchoArgs @($x, $x)
+ # Command line:
+ # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" "foo '' bar "baz"" "foo '' bar "baz""
+ #
+ # $x = "abc'123'nospace`"lol`"!!!"
+ # & EchoArgs @($x, $x)
+ # Command line:
+ # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lol"!!! abc'123'nospace"lol"!!!
+ #
+ # $x = "`"`"Look ma! Tonnes of spaces! 'foo' 'bar'`"`""
+ # & EchoArgs @($x, $x)
+ # Command line:
+ # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" ""Look ma! Tonnes of spaces! 'foo' 'bar'"" ""Look ma! Tonnes of spaces! 'foo' 'bar'""
+ #
+ # Given all this, we can now device a strategy to work around all these immensely helpful, well
+ # documented and useful tools by looking at each incoming argument, escaping any ' characters
+ # with a '"'"' sequence, surrounding each argument with ' & joining them with a space separating
+ # them.
+ # There is another bug (https://bugs.ruby-lang.org/issues/11142) that causes ruby to mangle any
+ # "" two-character double quote sequence but since we always emit our strings inside ' except for
+ # ' characters, this should be ok. Just remember that an argument '' should get translated to
+ # ''"'"''"'"'' on the command line. If those intervening empty ''s are not present, the presence
+ # of "" will cause ruby to mangle that argument.
+ $transformedList = $argList | foreach { "'" + ( $_ -replace "'","'`"'`"'" ) + "'" }
+ $fortifiedArgString = $transformedList -join ' '
+
+ # Use the correct embedded ruby path. We'll be deployed at a path that looks like
+ # [C:\opscode or some other prefix]\chef\modules\chef
+ $ruby = Join-Path (Get-ScriptDirectory) "..\..\embedded\bin\ruby.exe"
+ $commandPath = Join-Path (Get-ScriptDirectory) "..\..\bin\$command"
+
+ Run-ExecutableAndWait $ruby """$ruby"" '$commandPath' $fortifiedArgString"
+}
+
+
+function <%= ChefUtils::Dist::Apply::EXEC %> {
+ Run-RubyCommand '<%= ChefUtils::Dist::Apply::EXEC %>' $args
+}
+
+function <%= ChefUtils::Dist::Infra::CLIENT %> {
+ Run-RubyCommand '<%= ChefUtils::Dist::Infra::CLIENT %>' $args
+}
+
+function <%= ChefUtils::Dist::Infra::EXEC %>-service-manager {
+ Run-RubyCommand '<%= ChefUtils::Dist::Infra::EXEC %>-service-manager' $args
+}
+
+function <%= ChefUtils::Dist::Infra::SHELL %> {
+ Run-RubyCommand '<%= ChefUtils::Dist::Infra::SHELL %>' $args
+}
+
+function <%= ChefUtils::Dist::Solo::EXEC %> {
+ Run-RubyCommand '<%= ChefUtils::Dist::Solo::EXEC %>' $args
+}
+
+function <%= ChefUtils::Dist::Infra::EXEC %>-windows-service {
+ Run-RubyCommand '<%= ChefUtils::Dist::Infra::EXEC %>-windows-service' $args
+}
+
+function knife {
+ Run-RubyCommand 'knife' $args
+}
+
+Export-ModuleMember -function <%= ChefUtils::Dist::Apply::EXEC %>
+Export-ModuleMember -function <%= ChefUtils::Dist::Infra::CLIENT %>
+Export-ModuleMember -function <%= ChefUtils::Dist::Infra::EXEC %>-service-manager
+Export-ModuleMember -function <%= ChefUtils::Dist::Infra::SHELL %>
+Export-ModuleMember -function <%= ChefUtils::Dist::Solo::EXEC %>
+Export-ModuleMember -function <%= ChefUtils::Dist::Infra::EXEC %>-windows-service
+Export-ModuleMember -function knife
+
+# 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 -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/dobi.yaml b/dobi.yaml
new file mode 100644
index 0000000000..b038f8c411
--- /dev/null
+++ b/dobi.yaml
@@ -0,0 +1,21 @@
+# Chef Expeditor uses this file to build the chef/chef Docker image using Dobi (dnephin/dobi)
+#
+# If you wish to build the chef-infra-client Docker image, you do not need to use this file.
+# You can build the Docker image for yourself by simply running:
+#
+# docker build .
+#
+# For more information on how this file is used, please check out the Chef Expeditor docs:
+# https://expeditor.chef.io/docs/pipelines/docker/
+
+image=chef:
+ image: '{env.IMAGE_REGISTRY}/chef'
+ context: .
+ tags:
+ - '{env.EXPEDITOR_VERSION}'
+ args:
+ VERSION: '{env.EXPEDITOR_VERSION}'
+ CHANNEL: unstable
+ annotations:
+ tags:
+ - expeditor:final-channel-tags={{major}},{{major}}.{{minor}} \ No newline at end of file
diff --git a/docs/dev/README.md b/docs/dev/README.md
new file mode 100644
index 0000000000..20a756b1f4
--- /dev/null
+++ b/docs/dev/README.md
@@ -0,0 +1,31 @@
+# Chef Infra Development Documentation
+
+This directory contains a collection of useful how-to guides for both new and seasoned Chef contributors. There are many guides explaining how to build, test, and contribute to Chef Infra, as well as documents describing how the many subsystems function.
+
+A good first start is our [How Chef Infra Is Built](./design_documents/how_chef_is_tested_and_built.md) and [Chef Infra Release and Support Schedule](./policy/release_and_support_schedule.md)
+
+## How-To Guides
+
+- [Building and Installing Locally](./how_to/building_and_installing.md)
+- [Branching and Backporting Changes](./how_to/branching_and_backporting.md)
+- [Updating Dependencies](./how_to/updating_dependencies.md)
+- [Bumping Major and Minor Versions](./how_to/bumping_minor_or_major_versions.md)
+
+## Design Documents
+
+- [Client Release Cadence](./design_documents/client_release_cadence.md)
+- [Data Collection](./design_documents/data_collector.md)
+- [Action Collection](./design_documents/action_collection.md)
+- [Deprecations Within Resources](./design_documents/deprecations_in_resources.md)
+- [Resource Guard Interpreters](./design_documents/resource_guard_interpreters.md)
+- [Resource Load and Converge Methods](./design_documents/resource_load_and_converge_methods.md)
+- [Resource Property Validation Messaging](./design_documents/resource_property_validation_messaging.md)
+- [Self Documenting Resources](./design_documents/self_documenting_resources.md)
+- [Resource Before Notifications](./design_documents/resource_before_notifications.md)
+- [Resource File Content Verification](./design_documents/resource_file_content_verification.md)
+- [Ohai Cookbook Segment](./design_documents/ohai_cookbook_segment.md)
+- [Cookbook Root Aliases](./design_documents/cookbook_root_aliases.md)
+- [Event Handler Recipe DSL](./design_documents/event_handler_recipe_dsl.md)
+- [Gem Installation via Metadata](./design_documents/gem_installation_via_metadata.md)
+- [Client Exit Codes](./design_documents/client_exit_codes.md)
+- [Server Enforced Recipes](./design_documents/server_enforced_recipes.md)
diff --git a/docs/dev/design_documents/action_collection.md b/docs/dev/design_documents/action_collection.md
new file mode 100644
index 0000000000..df7dd46a84
--- /dev/null
+++ b/docs/dev/design_documents/action_collection.md
@@ -0,0 +1,106 @@
+---
+title: Action Collection
+---
+
+# Action Collection Design
+
+* Extract common code from the Resource Reporter and Data Collector.
+* Expose a general purpose API for querying a record of all actions taken during the Chef run.
+* Enable utilities like the 'zap' cookbook to be written to interact properly with Custom Resources.
+
+The Action Collection tracks all actions taken by all Chef resources. The resources can be in recipe code, as sub-resources of custom resources or
+they may be built "by hand". Since the Action Collection hooks the events which are fired from the `run_action` method on Chef::Resource it does
+not matter how the resources were built (as long as they were correctly passed the Chef `run_context`).
+
+This is complementary, but superior, to the resource collection which has an incomplete picture of what might happen or has happened in the run since there are
+many common ways of invoking resource actions which are not captured by how the resource collection is built. Replaying the sequence of actions in
+the Action Collection would be closer to replaying the chef-client converge than trying to re-converge the resource collection (although both of
+those models are still flawed in the presence of any imperative code that controls the shape of those objects).
+
+This design extracts common duplicated code from the Data Collection and old Resource Reporter, and is designed to be used by other consumers which
+need to ask questions like "in this run, what file resources had actions fired on them?", which can then be used to answer questions like
+"which files is Chef managing in this directory?".
+
+# Usage
+
+## Action Collection Event Hook Registration
+
+Consumers may register an event handler which hooks the `action_collection_registration` hook. This event is fired directly before recipes are
+compiled and converged (after library loading, attributes, etc). This is just before the earliest point in time that a resource should fire an
+action so represents the latest point that a consumer should make a decision about if it needs the Action Collection to be enabled or not.
+
+Consumers can hook this method. They will be passed the Action Collection instance, which can be saved by the caller to be queried later. They
+should then register themselves with the Action Collection (since without registering any interest, the Action Collection will disable itself).
+
+```ruby
+ def action_collection_registration(action_collection)
+ @action_collection = action_collection
+ action_collection.register(self)
+ end
+```
+
+## Library Registration
+
+Any cookbook library code may also register itself with the Action Collection. The Action Collection will be registered with the `run_context` after
+it is created, so registration may be accomplished easily:
+
+```ruby
+ Chef.run_context.action_collection.register(self)
+```
+
+## Action Collection Requires Registration
+
+If one of the prior methods is not used to register for the Action Collection, then the Action Collection will disable itself and will not compile
+the Action Collection in order to not waste the memory overhead of tracking the actions during the run. The Data Collector takes advantage of this
+since if the run start message from the Data Collector is refused by the server, then the Data Collector disables itself, and then does not register
+with the Action Collection, which would disable the Action Collection. This makes use of the delayed hooking through the `action_collection_registration`
+so that the Data Collector never registers itself after it is disabled.
+
+## Searching
+
+There is a function `filtered_collection` which returns "slices" off of the `ActionCollection` object. The `max_nesting` argument can be used to prune
+how deep into sub-resources the returned view goes (`max_nesting: 0` will return only resources in recipe context, with any hand created resources, but
+no subresources). There are also 5 different states of the action: `up_to_date`, `skipped`, `updated`, `failed`, `unprocessed` which can be filtered
+on. All of these are true by default, so they must be disabled to remove them from the filtered collection.
+
+The `ActionCollection` object itself implements enumerable and returns `ActionRecord` objects (see the `ActionCollection` code for the fields exposed on
+`ActionRecord`s).
+
+This would return all file resources in any state in the recipe context:
+
+```
+Chef.run_context.action_collection.filtered_collection(max_nesting: 0).select { |rec| rec.new_resource.is_a?(Chef::Resource::File) }
+```
+
+NOTE:
+As the Action Collection API was initially designed around the Resource Reporter and Data Collector use cases, the searching API is currently rudimentary
+and could easily lift some of the searching features on the name of the resource from the resource collection, and could use a more fluent API
+for composing searches.
+
+# Implementation Details
+
+## Resource Event Lifecycle Hooks
+
+Resources actions fire off several events in sequence:
+
+1. `resource_action_start` - this is always fired first
+2. `resource_current_state_loaded` - this is normally always second, but may be skipped in the case of a resource which throws an exception during
+`load_current_resource` (which means that the `current_resource` off the `ActionRecord` may be nil).
+3. `resource_up_to_date` / `resource_skipped` / `resource_updated` / `resource_failed` - one of these is always called which corresponds to the state of the action.
+4. `resource_completed` - this is always fired last
+
+For skipped resources, the conditional will be saved in the `ActionRecord`. For failed resources the exception is saved in the `ActionRecord`.
+
+## Unprocessed Resources
+
+The unprocessed resource concept is to report on resources which are left in the resource collection after a failure. A successful Chef run should
+never leave any unprocessed resources (`action :nothing` resources are still inspected by the resource collection and are processed). There must be
+an exception thrown during the execution of the resource collection, and the unprocessed resources were never visited by the runner that executes
+the resource collection.
+
+This list will be necessarily incomplete of any unprocessed sub-resources in custom resources, since the run was aborted before those resources
+executed actions and built their own sub-resource collections.
+
+This was a design requirement of the Data Collector.
+
+To implement this in a more sane manner the runner that evaluates the resource collection now tracks the resources that it visits.
diff --git a/docs/dev/design_documents/bootstrap_with_train.md b/docs/dev/design_documents/bootstrap_with_train.md
new file mode 100644
index 0000000000..3a83568d21
--- /dev/null
+++ b/docs/dev/design_documents/bootstrap_with_train.md
@@ -0,0 +1,150 @@
+# Bootstrap with Train
+
+Update `knife bootstrap` to use `train` as its backend via `chef-core`, and integrate Windows bootstrap support.
+
+## Motivation
+
+ As a Chef User,
+ I want to be able to bootstrap a system without logging secure data on that system
+ so that chef-client's keys are not exposed to anyone who can read the logs.
+
+ As a Chef User who administers Windows nodes,
+ I want to be able to bootstrap a system using the core Chef package
+ so that I don't have extra things to download first.
+
+ As a Chef Developer who works on bootstrap,
+ I want to be able to maintain one copy of the bootstrap logic
+ so that I don't have to spend time keeping a second copy in sync.
+
+## Summary
+
+The Windows bootstrap process has lived outside of core Chef for a long time.
+Switching to Train as the supporting back-end gives us the opportunity to merge
+the `knife-windows` bootstrap behavior into core knife. This will reduce the maintenance burden
+of maintaining what is a mostly-complete copy of bootstrap behaviors in `knife-windows`.
+
+This also addresses [CVE-2015-8559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-8559), in which
+the bootstrap mechanism runs the full bootstrap script as an inline argument to bash/cmd.exe, resulting
+in sensitive data potentially getting logged on the remote system. Train provides a back-end that knows how to
+do file management and command execution over supported protocols. This allows us to upload the
+bootstrap script and execute it in a remote shell without exposing the contents in a way that could result
+in capturing them in system logs.
+
+## Anatomy of a Bootstrap
+
+Bootstrap follows this general flow:
+
+* validate CLI options are proper
+* register the new client if not using org validation key to create it
+* determine connection properties based on protocol
+* connect to the remote host
+* generate and upload the bootstrap script
+* remotely run the bootstrap script
+
+This change focuses on configuring the connection
+and executing the bootstrap script. The underlying bootstrap behavior itself
+remains largely unchanged.
+
+## Implementation
+
+### Remove Unsupported Behaviors
+
+We will also remove the following obsolete or unsupported behaviors:
+
+* `--prerelease` flag - Chef hasn't been pre-released in quite some time.
+* `--install-as-service` - For many years we have suggested users not run chef-client as a service due to memory leaks in long running Ruby processes.
+* `--kerberos-keytab-file` - this is not implemented in the WinRM gem we use, and so was
+passed through to no effect.
+* remove explicit support for versions of Chef older than 12.8. Versions older than the supported
+ Chef client distributions will continue to be use at your own risk.
+* Remove support for Windows 2003 in the Windows bootstrap template as Chef does not support EOL Windows 2003 installs.
+
+### CLI Flag Changes
+
+As part of this change, CLI options from `knife bootstrap windows winrm` and `knife bootstrap`
+need to be merged. The majority will be untouched, but we'll also take this opportunity
+to make flag names more accurately describe what they're doing, and updating several options that are
+protocol-specific to be prefixed with the protocol (e.g. `--ssl-peer-fingerprint` to `--winrm-ssl-peer-fingerprint`)
+When a direct mapping exists, the original names will continue to work with backward
+compatibility and a deprecation warning if they have changed.
+
+#### New CLI Flags
+
+| Flag | Description |
+|-----:|:------------|
+| --max-wait SECONDS | Maximum time to wait for initial connection to be established. |
+| --winrm-basic-auth-only | Perform only Basic Authentication to the target WinRM node. |
+| --connection-protocol PROTOCOL|Connection protocol to use. Valid values are 'winrm' and 'ssh'. Default is 'ssh'. |
+| --connection-user | user to authenticate as, regardless of protocol |
+| --connection-password| Password to authenticate as, regardless of protocol |
+| --connection-port | port to connect to, regardless of protocol |
+
+`--connection-user`, `--connection-port`, and `--connection-password` replace their protocol-specific counterparts, since
+these are applicable to all supported transports. Their original knife config keys (`ssh\_user`, `ssh\_password`, etc.) remain
+available for use.
+
+Note that auth-related configuration may see further changes as work proceeds on credential set support for train.
+
+### Changed CLI Flags
+
+| Flag | New Option | Notes |
+|-----:|:-----------|:------|
+| --[no-]host-key-verify |--[no-]ssh-verify-host-key| |
+| --forward-agent | --ssh-forward-agent| |
+| --session-timeout MINUTES | --session-timeout SECONDS|New for ssh, existing for winrm. The unit has changed from MINUTES to SECONDS for consistency with other timeouts.|
+| --ssh-password | --connection-password | |
+| --ssh-port | --connection-port | `knife[:ssh_port]` config setting remains available.
+| --ssh-user | --connection-user | `knife[:ssh_user]` config setting remains available.
+| --ssl-peer-fingerprint | --winrm-ssl-peer-fingerprint | |
+| --winrm-authentication-protocol=PROTO | --winrm-auth-method=AUTH-METHOD | Valid values: plaintext, kerberos, ssl, _negotiate_|
+| --winrm-password| --connection-password | |
+| --winrm-port| --connection-port | `knife[:winrm_port]` config setting remains available.|
+| --winrm-ssl-verify-mode MODE | --winrm-no-verify-cert | [1] Mode is not accepted. When flag is present, SSL cert will not be verified. Same as original mode of 'verify_none'. |
+| --winrm-transport TRANSPORT | --winrm-ssl | [1] Use this flag if the target host is accepts WinRM connections over SSL.
+| --winrm-user | --connection-user | `knife[:winrm_user]` config setting remains available.|
+
+1. These flags do not have an automatic mapping of old flag -> new flag. The
+ new flag must be used.
+
+### Removed Flags
+
+| Flag | Notes |
+|-----:|:------|
+|--kerberos-keytab-file| This option existed but was not implemented.|
+|--winrm-codepage| This was used under `knife-windows` because bootstrapping was performed over a `cmd` shell. It is now invoked from `powershell`, so this option is no longer required.|
+|--winrm-shell| This option was ignored for bootstrap.|
+|--prerelease|Prerelease Chef hasn't existed for some time.|
+|--install-as-service|Installing Chef client as a service is not supported|
+
+### Conversion to ChefCore and Train
+
+CLI and knife options will be mapped to their train counterparts, and passed through to `TargetHost` to establish a connection.
+The TargetHost instance will be used for all upload and execution operations.
+
+Tests must ensure that options resolve correctly from the CLI, knife configuration, and defaults; and that they map to the corresponding
+`train` options.
+
+#### Validation
+
+Existing windows bootstrap validation checks should be preserved, unless they are superseded by related
+validations for ssh bootstrap.
+
+#### Context
+
+`WindowsBootstrapContext` will be moved into knife, with updates for namespacing as needed.
+
+#### Template
+
+`knife-windows/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb` will be moved into
+knife's bootstrap templates.
+
+### Future Improvements
+
+Because there are only two supported protocols for the near-term future,
+it does not add much benefit to split out the bootstrap CLI behavior based on
+protocol, so both are handled within the bootstrap command directly.
+
+If we want to support additional protocols, it will become unwieldy to continue with protocol `if`
+checks, and would be advisable to separate out protocol-specific behaviors
+into classes determined at runtime based on protocol.
+
diff --git a/docs/dev/design_documents/client_exit_codes.md b/docs/dev/design_documents/client_exit_codes.md
new file mode 100644
index 0000000000..63bb28a1bf
--- /dev/null
+++ b/docs/dev/design_documents/client_exit_codes.md
@@ -0,0 +1,62 @@
+# Chef-Client Exit Codes
+
+Chef-client exit codes signal outside tools to the result of the Chef-Client run.
+
+## Motivation
+
+ As a Chef user,
+ I want to be able to determine when a chef-client run is rebooting the node,
+ so that Test-Kitchen/Vagrant/any outside tool can wait for the node to reboot, and continue converging.
+
+## Specification
+
+* Chef applications (e.g. chef-client) that interpret recipes should use the specified exit codes
+* Chef tools (e.g. knife) should behave appropriately for the exit code, or pass it to the user
+
+### Exit codes Reserved by Operating System
+
+* Windows- [Link](https://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx)
+* Linux - [Sysexits](http://www.freebsd.org/cgi/man.cgi?query=sysexits&apropos=0&sektion=0&manpath=FreeBSD+4.3-RELEASE&format=html), [Bash Scripting](http://tldp.org/LDP/abs/html/exitcodes.html)
+
+### Remaining Available Exit Codes
+
+All exit codes defined should be usable on all supported Chef Platforms. Also, the exit codes used should be identical across all platforms. That limits the total range from 1-255. Exit codes not explicitly used by Linux/Windows are listed below. There are 59 exit codes that are available on both platforms.
+ * Any numbers below that have a strike-through are used below in the **Exit Codes in Use** section
+ * Exit Codes Available for Chef use :
+ * ~~35,37,40,41,42,43~~,44,45,46,47,48,49,79,81,90,91,92,93,94,95,96,97
+ * 98,99,115,116,168,169,172,175,176,177,178,179,181,184,185,204,211
+ * ~~213~~,219,227,228,235,236,237,238,239,241,242,243,244,245
+
+### Precedence
+
+* Reboot exit codes should take precedence over Chef Execution State
+* Precedence within a table should be evaluated from the top down.
+
+## Exit Codes in Use
+
+### Reboot Requirement
+
+Exit Code | Reason | Details
+------------- | ------------- |-----
+35 | Reboot Scheduled | Reboot has been scheduled in the run state
+37 | Reboot Needed | Reboot needs to be completed
+41 | Reboot Failed | Initiated Reboot failed - due to permissions or any other reason
+
+### Chef Run State
+
+Exit Code | Reason | Details
+------------- | ------------- |-----
+-1 | Failed execution* | Generic error during Chef execution. On Linux this will show up as 255, on Windows as -1
+0 | Successful run | Any successful execution of a Chef utility should return this exit code
+1 | Failed execution | Generic error during Chef execution.
+2 | SIGINT received | Received an interrupt signal
+3 | SIGTERM received | Received an terminate signal
+42 | Audit Mode Failure | Audit mode failed, but chef converged successfully.
+43 | Invalid config | Exit due to invalid configuration
+213 | Client upgrade | Chef has exited during a client upgrade
+
+* \*Next release should deprecate any use of this exit code.
+
+## Extend
+
+If there is a need for additional exit codes, please open a Design Proposal PR to discuss the change, and then a PR to update this document.
diff --git a/docs/dev/design_documents/client_release_cadence.md b/docs/dev/design_documents/client_release_cadence.md
new file mode 100644
index 0000000000..21fa5546d9
--- /dev/null
+++ b/docs/dev/design_documents/client_release_cadence.md
@@ -0,0 +1,15 @@
+# Chef Infra Client Release Cadence
+
+Chef Infra Client follows [Semantic Versioning](https://semver.org/) for releases. Major versions (eg. 16.x -> 17.x) will include backwards-incompatible changes. Minor versions (eg 16.7 -> 16.8) will include new features and bug fixes, but will be backwards-compatible to the best of our ability. Patch releases will contain bug and security fixes only.
+
+Chef Infra Client feature releases are promoted to the stable channel once per month. It is expected that this occur during the second week of the month unless circumstances intervene. Additional patch releases for a given feature release may be promoted if critical issues are found.
+
+Chef Workstation is released once per month in order to pull in the latest Chef Infra Client. It is expected that this occur during the fourth week of the month unless circumstances intervene.
+
+The Chef release in April of each year is a major version release, which will contain backwards-incompatible changes. A reminder notice will be sent via Discourse and Slack in March that will summarize the changes slated for the release.
+
+## Rationale
+
+Monthly releases help ensure we get new features and minor bug fixes out to Chef Infra users in a timely fashion while not overloading the maintainer teams. Similarly, offsetting the Chef Infra Client and Chef Workstation releases allows Workstation to ship with current Chef Infra Client releases.
+
+Major releases in April avoids releasing during winter holidays, summer vacations, ChefConf, and Chef Summits.
diff --git a/docs/dev/design_documents/cookbook_root_aliases.md b/docs/dev/design_documents/cookbook_root_aliases.md
new file mode 100644
index 0000000000..c7be543188
--- /dev/null
+++ b/docs/dev/design_documents/cookbook_root_aliases.md
@@ -0,0 +1,53 @@
+# Root Aliases in Cookbooks
+
+There are several common cases when writing Chef cookbooks that result in a
+folder containing a single file, usually called `default.rb`. Root aliases
+allow for using a single file instead of a folder.
+
+## Motivation
+
+ As a cookbook author,
+ I want to less complex directory layouts,
+ so that learning and maintenance is easier.
+
+## Specification
+
+There are two common cases where a single-file-in-folder comes up:
+
+1. `attributes/default.rb`
+2. `recipes/default.rb`
+
+With `attributes`, this single-file-in-folder case is common to the point of almost
+complete irrelevance of other layouts, given that all attribute files are always
+loaded. `recipes` are not exclusively a single-file-in-folder case, but it is common
+enough to warrant a special case.
+
+With this in mind, aliases are available for each:
+
+1. `attributes.rb`
+2. `recipe.rb`
+
+It is an error for a cookbook to contain both an alias and its target, or two
+aliases for the same target.
+
+No aliases are provided for other types as they are generally a more advanced
+use case where the worry about learning curve is reduced.
+
+Aliases are equivalent to their target file for purposes of loading either via
+standard cookbook loading, or methods like `include_recipe`.
+
+## Rationale
+
+This meshes well with RFC017 towards a goal of reducing the file layout
+complexity of simple cookbooks. There can be compatibility issues with tools
+that parse the cookbook manifest data and presume that all files from a given
+segment reside under the previously required folder. The author knows
+of no such tools, and given that the manifest format is mostly an internal
+representation, this is not considered a blocker. Overall, the goal of these RFCs
+is to remove the frequent use of single-child folders.
+
+The choice of which aliases to provide and what to name them is mostly driven
+by the common cases, but is not exhaustive. `attributes.rb` and `recipe.rb` are
+chosen to match their usage grammatically. An additional alias of `recipes.rb`
+could be provided to match the folder name, but this is left for a future
+improvement based on usage feedback. \ No newline at end of file
diff --git a/docs/dev/design_documents/data_collector.md b/docs/dev/design_documents/data_collector.md
new file mode 100644
index 0000000000..67782ed9ba
--- /dev/null
+++ b/docs/dev/design_documents/data_collector.md
@@ -0,0 +1,568 @@
+---
+title: Data Collector
+---
+
+# Data Collector Design
+
+## Motivation
+
+ As a Chef user who uses both Chef Client Mode and Chef Solo Mode (including the mode commonly known as "Chef Client Local Mode"),
+ I want to be able to collect data about my entire fleet regardless of their client operation type,
+ so that I may better understand the impacts of my changes and may better detect failures.
+
+### Definitions
+
+To eliminate ambiguity and confusion, the following terms are used throughout this RFC:
+
+ * **Chef**: the tool used to automate your system.
+ * **Chef Client Mode**: Chef configured in "client mode" where a Chef Server is used to provide Chef its resources and artifacts
+ * **Chef Solo Mode**: Chef configured in a mode that utilizes a local Chef Zero server. Formerly known as "Chef Client Local Mode" (run as `chef-client --local-mode`)
+ * **Chef Solo Legacy Mode**: Chef in the pre 12.10 Solo operational mode (run as `chef-solo`) or Chef run as `chef-solo --legacy-mode`
+
+### Specification
+
+Similar to how data is collected and reported for Chef Reporting, we expect to implement a new EventDispatch class/instance that collects data about the Chef run and reports it accordingly. Unlike Chef Reporting, the server that receives this data is **not** running on the Chef Server, allowing users to utilize this function whether they use Chef Server or not. No new data collection methods are expected to be implemented as a result of this change; this change serves to implement a generic way to report the collected data in a "webhook-like" fashion to a non-Chef-Server receiver.
+
+The implementation must work with Chef running in any mode:
+
+ * Chef Client Mode
+ * Chef Solo Mode
+ * Chef Solo Legacy Mode
+
+#### Protocol and Authentication
+
+All payloads will be sent to the Data Collector server via HTTP POST to the URL specified in the `data_collector_server_url` configuration parameter. Users should be encouraged to use a TLS-protected endpoint.
+
+Optionally, payloads may also be written out to multiple HTTP endpoints or JSON files on the local filesystem (of the node running `chef-client`) by specifying the `data_collector_output_locations` configuration parameter.
+
+For the initial implementation, transmissions to the Data Collector server can optionally be authenticated with the use of a pre-shared token, which will be sent in a HTTP header. Given that the receiver is not the Chef Server, existing methods of using a Chef `client` key to authenticate the request are unavailable.
+
+#### Configuration
+
+The configuration required for this new functionality can be placed in the `client.rb` or any other `Chef::Config`-supported location (such as a client.d or solo.d directory).
+
+##### Parameters
+
+ * **data\_collector\_server\_url**: required*. The full URL to the data collector server API. All messages will be POST'd to this URL. The Data Collector class will be registered and enabled if this config parameter is specified. * If the `data_collector_output_locations` configuration parameter is specified, this setting may be omitted.
+ * **data\_collector\_token**: optional. A pre-shared token that, if present, will be passed as an HTTP header named `x-data-collector-token` to the Data Collector server. The server can choose to accept or reject the data posted based on the token or lack thereof.
+ * **data\_collector\_mode**: The Chef mode in which the Data Collector will be enabled. For example, you may wish to only enable the Data Collector when running in Chef Solo Mode. Must be one of: `:solo`, `:client`, or `:both`. The `:solo` value is used for Chef operating in Chef Solo Mode or Chef Solo Legacy Mode. Default: `:both`.
+ * **data\_collector\_raise\_on\_failure**: If true, the Chef run will fatally exit if it is unable to successfully POST to the Data Collector server. Default: `false`.
+ * **data\_collector\_output\_locations**: optional. An array of URLs and/or file paths to which data collection payloads will also be written. This may be used without specifying the `data_collector_server_url` configuration parameter.
+
+### Schemas
+
+For the initial implementation, three JSON schemas will be utilized.
+
+##### Action Schema
+
+The Action Schema is used to notify when a Chef object changes. In our case, the primary use will be to update the Data Collector server with the current node object.
+
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Data Collector - action schema",
+ "properties": {
+ "entity_name": {
+ "description": "The name of the entity",
+ "type": "string"
+ },
+ "entity_type": {
+ "description": "The type of the entity",
+ "type": "string",
+ "enum": [
+ "bag",
+ "client",
+ "cookbook",
+ "environment",
+ "group",
+ "item",
+ "node",
+ "organization",
+ "permission",
+ "role",
+ "user",
+ "version"]
+ },
+ "entity_uuid": {
+ "description": "Unique ID identifying this object, which should persist across runs and invocations",
+ "type": "string",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+ },
+ "id": {
+ "description": "Globally Unique ID for this message",
+ "type": "string",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+ },
+ "message_version": {
+ "description": "Message Version",
+ "type": "string",
+ "enum": [
+ "1.1.0"
+ ]
+ },
+ "message_type": {
+ "description": "Message Type",
+ "type": "string",
+ "enum": ["action"]
+ },
+ "organization_name": {
+ "description": "It is the name of the org on which the run took place",
+ "type": ["string", "null"]
+ },
+ "recorded_at": {
+ "description": "It is the ISO timestamp when the action happened",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-5][0-9]:[0-9]{2}Z$",
+ "type": "string"
+ },
+ "remote_hostname": {
+ "description": "The remote hostname which initiated the action",
+ "type": "string"
+ },
+ "requestor_name": {
+ "description": "The name of the client or user that initiated the action",
+ "type": "string"
+ },
+ "requestor_type": {
+ "description": "Was the requestor a client or user?",
+ "type": "string",
+ "enum": ["client", "user"]
+ },
+ "run_id": {
+ "description": "The run ID of the run in which this node object was updated",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
+ "type": "string"
+ },
+ "service_hostname": {
+ "description": "The FQDN of the Chef server, if appropriate",
+ "type": "string"
+ },
+ "source": {
+ "description": "The tool / client mode that initiated the action. Note that 'chef_solo' includes Chef Solo Mode and Chef Solo Legacy Mode.",
+ "type": "string",
+ "enum": ["chef_solo", "chef_client"]
+ },
+ "task": {
+ "description": "What action was performed?",
+ "type": "string",
+ "enum": ["associate", "create", "delete", "dissociate", "invite", "reject", "update"]
+ },
+ "user_agent": {
+ "description": "The User-Agent of the requestor",
+ "type": "string"
+ },
+ "data": {
+ "description": "The payload containing the entire request data",
+ "type": "object"
+ }
+ },
+ "required": [
+ "entity_name",
+ "entity_type",
+ "entity_uuid",
+ "id",
+ "message_type",
+ "message_version",
+ "organization_name",
+ "recorded_at",
+ "remote_hostname",
+ "requestor_name",
+ "requestor_type",
+ "run_id",
+ "service_hostname",
+ "source",
+ "task",
+ "user_agent"
+ ],
+ "title": "ActionSchema",
+ "type": "object"
+}
+```
+
+The `data` field will contain the value of the object on which an action took place.
+
+##### Run Start Schema
+
+The Run Start Schema will be used by Chef to notify the data collection server at the start of the Chef run.
+
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Data Collector - Runs run_start schema",
+ "properties": {
+ "chef_server_fqdn": {
+ "description": "It is the FQDN of the chef_server against which current reporting instance runs",
+ "type": "string"
+ },
+ "entity_uuid": {
+ "description": "Unique ID identifying this node, which should persist across Chef runs",
+ "type": "string",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+ },
+ "id": {
+ "description": "It is the internal message id for the run",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
+ "type": "string"
+ },
+ "message_version": {
+ "description": "Message Version",
+ "type": "string",
+ "enum": [
+ "1.0.0"
+ ]
+ },
+ "message_type": {
+ "description": "It defines the type of message being sent",
+ "type": "string",
+ "enum": ["run_start"]
+ },
+ "node_name": {
+ "description": "It is the name of the node on which the run took place",
+ "type": "string"
+ },
+ "organization_name": {
+ "description": "It is the name of the org on which the run took place",
+ "type": "string"
+ },
+ "run_id": {
+ "description": "It is the runid for the run",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
+ "type": "string"
+ },
+ "source": {
+ "description": "The tool / client mode that initiated the action. Note that 'chef_solo' includes Chef Solo Mode and Chef Solo Legacy Mode.",
+ "type": "string",
+ "enum": ["chef_solo", "chef_client"]
+ },
+ "start_time": {
+ "description": "It is the ISO timestamp of when the run started",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
+ "type": "string"
+ }
+ },
+ "required": [
+ "chef_server_fqdn",
+ "entity_uuid",
+ "id",
+ "message_version",
+ "message_type",
+ "node_name",
+ "organization_name",
+ "run_id",
+ "source",
+ "start_time"
+ ],
+ "title": "RunStartSchema",
+ "type": "object"
+}
+```
+
+##### Run End Schema
+
+The Run End Schema will be used by Chef Client to notify the data collection server at the completion of the Chef Client's converge phase, and to report data on the Chef Client run, including resources changed and any errors encountered.
+
+```json
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Data Collector - Runs run_converge schema",
+ "properties": {
+ "chef_server_fqdn": {
+ "description": "It is the FQDN of the chef_server against which current reporting instance runs",
+ "type": "string"
+ },
+ "end_time": {
+ "description": "It is the ISO timestamp of when the run ended",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
+ "type": "string"
+ },
+ "entity_uuid": {
+ "description": "Unique ID identifying this node, which should persist across Chef Client/Solo runs",
+ "type": "string",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+ },
+ "error": {
+ "description": "It has the details of the error in the run if any",
+ "type": "object"
+ },
+ "expanded_run_list": {
+ "description": "The expanded run list object from the node",
+ "type": "object"
+ },
+ "id": {
+ "description": "It is the internal message id for the run",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
+ "type": "string"
+ },
+ "message_type": {
+ "description": "It defines the type of message being sent",
+ "type": "string",
+ "enum": ["run_converge"]
+ },
+ "message_version": {
+ "description": "Message Version",
+ "type": "string",
+ "enum": [
+ "1.1.0"
+ ]
+ },
+ "node": {
+ "description": "The node object after the converge completed",
+ "type": "object"
+ },
+ "node_name": {
+ "description": "Node Name",
+ "type": "string",
+ "format": "node-name"
+ },
+ "organization_name": {
+ "description": "Organization Name",
+ "type": "string"
+ },
+ "resources": {
+ "description": "This is the list of all resources for the run",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "after": {
+ "description": "Final State of the resource",
+ "type": "object"
+ },
+ "before": {
+ "description": "Initial State of the resource",
+ "type": "object"
+ },
+ "cookbook_name": {
+ "description": "Name of the cookbook that initiated the change",
+ "type": "string"
+ },
+ "cookbook_version": {
+ "description": "Version of the cookbook that initiated the change",
+ "type": "string",
+ "pattern": "^[0-9]*\\.[0-9]*(\\.[0-9]*)?$"
+ },
+ "delta": {
+ "description": "Difference between initial and final value of resource",
+ "type": "string"
+ },
+ "duration": {
+ "description": "Duration of the run consumed by processing of this resource, in milliseconds",
+ "type": "string"
+ },
+ "id": {
+ "description": "Resource ID",
+ "type": "string"
+ },
+ "ignore_failure": {
+ "description": "the ignore_failure setting on a resource, indicating if a failure on this resource should be ignored",
+ "type": "boolean"
+ },
+ "name": {
+ "description": "Resource Name",
+ "type": "string"
+ },
+ "result": {
+ "description": "The action taken on the resource",
+ "type": "string"
+ },
+ "status": {
+ "description": "Status indicating how Chef processed the resource",
+ "type": "string",
+ "enum": [
+ "failed",
+ "skipped",
+ "unprocessed",
+ "up-to-date",
+ "updated"
+ ]
+ },
+ "type": {
+ "description": "Resource Type",
+ "type": "string"
+ }
+ },
+ "required": [
+ "after",
+ "before",
+ "delta",
+ "duration",
+ "id",
+ "ignore_failure",
+ "name",
+ "result",
+ "status",
+ "type"
+ ]
+ }
+ },
+ "run_id": {
+ "description": "It is the runid for the run",
+ "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
+ "type": "string"
+ },
+ "run_list": {
+ "description": "It is the runlist for the run",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "source": {
+ "description": "The tool / client mode that initiated the action. Note that 'chef_solo' includes Chef Solo Mode and Chef Solo Legacy Mode.",
+ "type": "string",
+ "enum": ["chef_solo", "chef_client"]
+ },
+ "start_time": {
+ "description": "It is the ISO timestamp of when the run started",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
+ "type": "string"
+ },
+ "status": {
+ "description": "It gives the status of the run",
+ "type": "string",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "total_resource_count": {
+ "description": "It is the total number of resources for the run",
+ "type": "integer",
+ "minimum": 0
+ },
+ "updated_resource_count": {
+ "description": "It is the number of updated resources during the course of the run",
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "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"
+ ],
+ "title": "RunEndSchema",
+ "type": "object"
+}
+```
+
+## Technical Implementation
+
+The remainder of document will focus entirely on the nuts and bolts of the Data Collector.
+
+### Action Collection Integration
+
+Most of the work is done by a separate Action Collection to track the actions of Chef resources.
+If the Data Collector is not enabled, it never registers with the Action Collection and no work will be done by the Action Collection to track resources.
+
+### Additional Collected Information
+
+The Data Collector also collects:
+
+- the expanded run list
+- deprecations
+- the node
+- formatted error output for exceptions
+
+Most of this is done through hooking events directly in the Data Collector itself. The ErrorHandlers module is broken out into a module, which is directly mixed into the Data Collector, to separate that concern out into a different file. This ErrorHandlers module is straightforward with fairly little state, but involves a lot of hooked methods.
+
+### Basic Configuration Modes
+
+#### Configured for Automate
+
+Do nothing. The URL is constructed from the base `Chef::Config[:chef_server_url]`, `auth` is just Chef Server API authentication, and the default behavior is that it is configured.
+
+#### Configured to Log to a File
+
+Setup a file output location, no token is necessary:
+
+```
+Chef::Config[:data_collector][:output_locations] = { files: [ "/Users/lamont/data_collector.out" ] }
+```
+
+Note the fact that you can't assign to `Chef::Config[:data_collector][:output_locations][:files]` and will NoMethodError if you try.
+
+#### Configured to Log to a Non-Chef Server Endpoint
+
+Setup a server url, requiring a token:
+
+```
+Chef::Config[:data_collector][:server_url] = "https://chef.acme.local/myendpoint.html"
+Chef::Config[:data_collector][:token] = "mytoken"
+```
+
+This works for chef-clients, which are configured to hit a Chef server, but use a custom non-Chef-Automate endpoint for reporting, or for chef-solo/zero users.
+
+XXX: There is also the `Chef::Config[:data_collector][:output_locations] = { urls: [ "https://chef.acme.local/myendpoint.html" ] }` method, which will behave differently, particularly on non-chef-solo use cases.
+In that case, the Data Collector `server_url` will still be automatically derived from the `chef_server_url` and the Data Collector will attempt to contact that endpoint.
+But with the token being supplied, the Data Collector will use that token and will not use Chef Server authentication.
+Thus, the server should 403 back.
+Also, if `raise_on_failure` is left to the default of `false`, then the Data Collector will simply drop that failure and continue without raising, which will appear to work, and output will be send to the configured `output_locations`.
+
+Note that the presence of a token flips all external URIs to using the token. So it is **not** possible to use this feature to talk to both a Chef Automate endpoint and a custom URI reporting endpoint.
+This would seem to be the most useful of an incredibly marginally useful feature and it does not work.
+But given how hopelessly complicated this is, the recommendation is to use the `server_url` and to avoid using any `url` options in the `output_locations` since that feature is fairly poorly designed at this point in time.
+
+### Resiliency to Failures
+
+The Data Collector in Chef >= 15.0 is resilient to failures that occur anywhere in the main loop of the `Chef::Client#run` method.
+In order to do this, there is a lot of defensive coding around internal data structures that may be `nil`. (e.g. failures before the node is loaded will result in the node being nil.)
+The spec tests for the Data Collector now run through a large sequence of events -- which must, unfortunately, be manually kept in sync with the events in the Chef::Client if those events are ever 'moved' around -- which should catch any issues in the Data Collector with early failures.
+The specs should also serve as documentation for what the messages will look like under different failure conditions.
+The goal was to keep the format of the messages to look as near as possible to the same schema as possible, even in the presence of failures, but some data structures will be entirely empty.
+
+When the Data Collector fails extraordinarily early, it still sends both a start and an end message.
+This will happen if it fails so early that it would not normally have sent a start message.
+
+### Decision to Be Enabled
+
+This is complicated due to over-design and is encapsulated in the `#should_be_enabled?` method and the ConfigValidation module. The `#should_be_enabled?` message and
+ConfigValidation should probably be merged into one renamed Config module to isolate the concern of processing the Chef::Config options and of doing the correct thing.
+
+### Run Start and Run End Message modules
+
+These are separated out into their own modules, which are very deliberately not mixed into the main Data Collector.
+They use the Data Collector and Action Collection public interfaces.
+They are stateless themselves.
+This keeps the collaboration between them and the Data Collector very easy to understand.
+The start message is relatively simple and straightforwards.
+The complication of the end message is mostly due to walking through the Action Collection and all the collected action records from the entire run, along with a lot of defensive programming to deal with early errors.
+
+### Relevant Event Sequence
+
+As it happens in the actual chef-client run:
+
+1. `events.register(data_collector)`
+2. `events.register(action_collection)`
+3. `run_status.run_id = request_id`
+4. `events.run_start(Chef::VERSION, run_status)`
+ * failures during registration will cause `registration_failed(node_name, exception, config)` here and skip to #13
+ * failures during node loading will cause `node_load_failed(node_name, exception, config)` here and skip to #13
+5. `events.node_load_success(node)`
+6. `run_status.node = node`
+ * failures during run list expansion will cause `run_list_expand_failed(node, exception)` here and skip to #13
+7. `events.run_list_expanded(expansion)`
+8. `run_status.start_clock`
+9. `events.run_started(run_status)`
+ * failures during cookbook resolution will cause `events.cookbook_resolution_failed(node, exception)` here and skip to #13
+ * failures during cookbook synch will cause `events.cookbook_sync_failed(node, exception)` and skip to #13
+10. `events.cookbook_compilation_start(run_context)`
+11. < the resource events happen here, which hit the Action Collection, may throw any of the other failure events >
+12. `events.converge_complete` or `events.converge_failed(exception)`
+13. `run_status.stop_clock`
+14. `run_status.exception = exception` if it failed
+15. `events.run_completed(node, run_status)` or `events.run_failed(exception, run_status)`
diff --git a/docs/dev/design_documents/deprecations_in_resources.md b/docs/dev/design_documents/deprecations_in_resources.md
new file mode 100644
index 0000000000..367f234256
--- /dev/null
+++ b/docs/dev/design_documents/deprecations_in_resources.md
@@ -0,0 +1,68 @@
+# Deprecation Warnings Within Custom Resources
+
+In Chef 12, we introduced deprecation warnings within the chef-client. This allowed us to communicate future breaking changes to users. The warnings and integration within Test Kitchen have become a powerful tool, allowing users to future-proof their cookbooks and ease chef-client migration projects.
+
+This design extends the deprecation functionality to custom resources, allowing authors to warn consumers of future breaking changes to their resources.
+
+## Motivation
+
+ As an author of custom resources,
+ I want to warn resource consumers of future breaking changes to resources,
+ so that they can update their wrapper cookbooks before my next release.
+
+ As an author of custom resources,
+ I want to provide immediate backwards compatibility in property names while still warning users,
+ so that they can update their wrapper cookbooks before my next release.
+
+ As an author of custom resources,
+ I want to entirely deprecate a resource that users are consuming,
+ so that they can update their wrapper cookbooks before my next release.
+
+ As a consumer of custom resources,
+ I want to be warned when I use deprecated functionality,
+ so that I can update my wrapper cookbooks.
+
+## Specification
+
+### deprecated method for resources
+
+This new method will let authors communicate to consumers that a resource is going away in the future. Right now, we rely on readme or changelog entries, which are not a very effective way to communicate to consumers. This method will accept a string, which becomes the warning message.
+
+#### Example
+
+in resources/example.rb
+
+```ruby
+deprecated 'This resource will be removed in the 3.0 release of the example cookbook in April 2018. You should use example_ng instead. See the readme for additional information.'
+```
+
+### deprecated method for properties
+
+This new option for properties will let authors communicate to consumers that an individual property is going away in the future. Right now, we rely on readme or changelog entries, which are not a very effective way to communicate to consumers. This method will accept a string, which becomes the warning message.
+
+#### Example
+
+in resources/example.rb
+
+```ruby
+property :destroy_everything,
+ kind_of: [true, false],
+ default: true,
+ deprecated: 'Turns out destroying everything was a bad idea. This property will be removed in the 3.0 release of this cookbook in April 2018 and will throw an error if set at that time.'
+```
+
+### deprecated_property_alias
+
+Currently if a resource author decides to change the name of a property, they have two options:
+ - Use `alias_method`, which silently routes old properties to the new names, or
+ - Define both properties in the resource, and include functionality to set the new value while using the old value and warning the user.
+
+`alias_method` doesn't alert cookbook consumers to the change, and writing your own code to perform deprecation warnings is cumbersome and rarely done. A new `deprecated_property_alias` would behave similar to a `alias_method`, but throw deprecation warnings while providing backwards compatibility. It would accept and optional `String` value that would be used in place of a generic deprecation message.
+
+#### Example
+
+in resources/example.rb
+
+```ruby
+deprecated_property_alias :set, :set_impact, 'The set property has been renamed to set_impact. Set will be removed from this cookbook in the next release in April 2018.'
+```
diff --git a/docs/dev/design_documents/event_handler_recipe_dsl.md b/docs/dev/design_documents/event_handler_recipe_dsl.md
new file mode 100644
index 0000000000..ec6adbc165
--- /dev/null
+++ b/docs/dev/design_documents/event_handler_recipe_dsl.md
@@ -0,0 +1,67 @@
+# Recipe DSL method for event handler hooks
+
+Allow cookbook authors to easily add custom logic on Chef events.
+
+## Motivation
+
+Chef has an extensive [event dispatch mechanism](https://github.com/chef/chef/blob/master/lib/chef/event_dispatch/base.rb).
+But incorporating some custom logic against any of the events is an onerous process, which involves
+subclassing the based event handler and adding it via the config. This RFC
+proposes a recipe DSL method to ease this. For new Chef users, this will reduce
+the entry barrier.
+
+## Specification
+
+Currently, `chef-client` sets up couple of default handlers -- e.g. doc, base -- during
+initialization. An additional empty event handler -- a subclass
+of the base handler without any custom logic -- can be added alongside the
+existing handlers, which will used as a placeholder for user-specific hooks.
+
+A top level (::Chef) method will be introduced (`event_handler`) to wrap the
+main event handler DSL (`on`). Users can tap into one of the event types
+-- as specified in base dispatcher -- using this DSL to execute their custom logic.
+
+The additional top level method(`Chef.event_handler`) will allow the handler
+DSL usage in and outside of recipes, and also ease writing backward-compatible
+changes for the `on` method if need be.
+
+The following is an example of sending hipchat notification on chef run failure.
+
+```ruby
+Chef.event_handler do
+ on :run_failed do |exception|
+ hipchat_notify exception.message
+ end
+end
+```
+
+The following is another example of taking a distributed lock via `etcd`, to
+prevent concurrent chef runs in different nodes.
+
+```ruby
+lock_key = "#{node.chef_environment}/#{node.name}"
+
+Chef.event_handler do
+ on :converge_start do |run_context|
+ Etcd.lock_acquire(lock_key)
+ end
+end
+
+Chef.event_handler do
+ on :converge_complete do
+ Etcd.lock_release(lock_key)
+ end
+end
+```
+
+The following is another example of sending a hipchat alert on a key config change.
+
+```ruby
+Chef.event_handler do
+ on :resource_updated do |resource, action|
+ if resource.to_s == 'template[/etc/nginx/nginx.conf]'
+ Helper.hipchat_message("#{resource} was updated by chef")
+ end
+ end
+end
+``` \ No newline at end of file
diff --git a/docs/dev/design_documents/gem_installation_via_metadata.md b/docs/dev/design_documents/gem_installation_via_metadata.md
new file mode 100644
index 0000000000..4409bb4c69
--- /dev/null
+++ b/docs/dev/design_documents/gem_installation_via_metadata.md
@@ -0,0 +1,30 @@
+# Enable gem dependencies in cookbook metadata
+
+Support a 'gem' DSL method for cookbook metadata to create a dependency on a rubygem. The
+gem will be installed via `chef_gem` after all the cookbooks are synchronized, but before any
+other cookbook loading is done.
+
+## Motivation
+
+ As a Chef User,
+ I want to be able to use additional gems in libraries, attributes and resources,
+ and to avoid complex workarounds and double-run converges.
+
+## Specification
+
+Allow users to specify additional gem dependencies like:
+
+```ruby
+gem "poise"
+gem "chef-sugar"
+gem "chef-provisioning"
+```
+
+In the `Chef::RunContext::CookbookCompiler#compile` method, a phase will be added before `compile_libraries`. This phase will install all of the gem declarations from all of the synchronized cookbooks before any other cookbook code is compiled.
+
+The implementation will use an in-memory bundler Gemfile which is constructed against all gem statements in all cookbooks which are in the `run_list`, solved
+at the same time. The syntax of the 'gem' statement will support the bundler gem syntax, with the qualification of since the syntax is compiled into metadata.json, that arbitrary ruby code will be expanded at cookbook upload time.
+
+The resulting gemset bundle will be installed into the LIBPATH of the running chef-client. This may either be directly into the base ruby libraries (per current `chef_gem` behavior), or into a custom location with the LIBPATH of the chef-client extended to use that location--as an open implementation question.
+
+The normal Gemfile `requires` tag may be used by users to autoload files out of gems.
diff --git a/docs/dev/design_documents/how_chef_is_tested_and_built.md b/docs/dev/design_documents/how_chef_is_tested_and_built.md
new file mode 100644
index 0000000000..fda2ac6156
--- /dev/null
+++ b/docs/dev/design_documents/how_chef_is_tested_and_built.md
@@ -0,0 +1,39 @@
+# How Chef Infra Client is Tested and Built
+
+This document describes the process that a change flows through from opened PR to omnibus build in our current channel.
+
+## User Opens Pull Request
+
+Here's where all the magic begins. You have a great idea, so you decide to contribute. You fork the chef repository, make a change, and open a pull request in GitHub.
+
+## Tests Validate the Pull request
+
+We can't just merge any old change, so we run various checks against your submitted PR to make sure it's ready to review. We use GitHub's protected branches feature in the chef repo, so each of these checks must pass before a change can be approved:
+ - **Expeditor Config**: At Chef, we use an internal tool named Expeditor to manage our overall release process, including bumping chef versions, adding changelog entries, kicking off CI/CD jobs, and promoting artifacts. It's important that we don't break this critical component of our release process, so each PR will be checked by Expeditor to make sure the config is valid. If anything doesn't pass, Expeditor will link you to remediation steps in the Expeditor docs.
+ - **DCO sign-off**: Our Expeditor tool will check to make sure that every commit in your pull request has been signed off for the Developer Certificate of Origin (DCO). This gives us added legal protection above the Apache-2.0 license that we need to accept your contribution. Without this sign-off, we are unable to merge a PR.
+ - **Buildkite**: Buildkite is where we run the majority of our publicly available tests. Buildkite allows us to run tests in our own infrastructure while exposing the results to community members. We run 18 separate tests against each submitted PR. This includes the following tests:
+ - Unit, functional, and integration tests on all supported Ruby releases. These run on Ubuntu, CentOS and openSUSE so we can make sure our functional / integration tests run on the platforms where users consume Chef Infra.
+ - Chefstyle Ruby linting
+ - Unit tests from chef-sugar, chef-zero, cheffish, chefspec, and knife-windows against the chef code in your PR
+ - Full Test Kitchen integration tests that covers common Chef usage on various different Linux Distros. Those integration tests run using the kitchen-dokken plugin and the dokken-images Docker containers, which do their best to replicate a real Linux system. You can see exactly what we test here: https://github.com/chef/chef/blob/master/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
+ - **Appveyor**: Buildkite does a great job of testing PRs against Linux hosts, but many rspec tests require a Windows system to run, and for this, we test in Appveyor. In Appveyor, we run our unit, integration, and functional specs against our supported Ruby releases, but this time, we do it on Windows.
+
+## PR is Reviewed and Merged
+
+Once your pull request passes the above tests, it will need to be reviewed by at least two members of the Chef Infra project owners, approvers, or reviewers groups. For a list of owners, approvers, and reviewers, see https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md. GitHub will automatically assign these groups as reviewers. Reviews will happen ad hoc as members in those groups have time, or during our weekly issue triage. Check the above team doc link for information on when that review takes place.
+
+Your PR will be reviewed for Chef and Ruby correctness, overall design, and likelihood of impact on the community. We do our best to make sure all the changes made to Chef are high quality and easy to maintain going forward, while also having the lowest chance of negatively impacting our end users. If your PR meets that criteria, we'll merge the change into our master branch.
+
+## Version Bump and Changelog Update
+
+Every commit that we merge into Chef Infra should be ready to release. In order to ensure that's possible, each merged PR increments the patch version of the application and is noted in the changelog. This is performed automatically by our Expeditor tool. If you'd like to avoid one or both of these steps, there are Expeditor GitHub issue labels that can be applied to skip either.
+
+## Buildkite Build / Test Pipeline
+
+Once the version has been incremented, Expeditor will begin a build matrix in our internal Buildkite pipeline. In Buildkite, we build omnibus-based system packages of Chef Infra for multiple platforms, distros, and architectures. Each of the platforms we build are those which will eventually be available on our downloads site if this build is successful and later promoted. Upon completion, builds are promoted to the `unstable` channel and are available to any system supporting our Omnitruck API (Test Kitchen, mixlib-install, etc).
+
+Once the builds complete, Buildkite will move to the test phase where each of these builds will be verified on all the platforms that Chef officially supports. For many build artifacts, this means the artifact tests on multiple versions of the same platform. For example, Chef is built on Windows 2012 R2, yet tests are run on 2012, 2012 R2, and 2016 to ensure full compatibility. In total, this phase includes nearly three dozen test nodes. Assuming all tests pass, the build will be promoted to the `current` channel, which is available on the downloads site, in addition to being available via the Omnitruck API.
+
+## Releasing Chef
+
+Once a build has been blessed by our Buildkite gauntlet and everyone has agreed that we are ready to release, an artifact can be promoted from current to stable channels. For the complete release process see [Releasing Chef Infra Client](../how_to/releasing_chef_infra.md).
diff --git a/docs/dev/design_documents/ohai_cookbook_segment.md b/docs/dev/design_documents/ohai_cookbook_segment.md
new file mode 100644
index 0000000000..4cbc387aac
--- /dev/null
+++ b/docs/dev/design_documents/ohai_cookbook_segment.md
@@ -0,0 +1,36 @@
+# Add Ohai cookbook segment
+
+Support Ohai plugins under an `ohai` top level directory in cookbooks. Load all
+Ohai plugins in all synchronized cookbooks after cookbook synchronization.
+
+## Motivation
+
+ As a Chef User,
+ I want to have my custom Ohai plugins loaded on first bootstrap,
+ So I don't have to run chef twice.
+
+ As a Chef User,
+ I want my Ohai plugins loaded before attributes and compile/converge mode,
+ So I can use them without worrying about cookbook execution ordering.
+
+ As a Chef User,
+ I want my Ohai plugins synchronized with my cookbooks,
+ So that I don't incur more unavoidable round-trips to the Chef Server.
+
+ As a Chef Developer,
+ I want Ohai plugins as a first-class object,
+ So that I don't have to compile recipes to discover templates that drop plugins.
+
+## Specification
+
+The "segments" of a cookbook will be extended to include an "ohai" segment. In this segment, there will be plugins, which are intended to be copied to the Ohai `plugin_path`. All files in this segment will be copied, recursively, maintaining directory structure.
+
+In the `Chef::RunContext::CookbookCompiler#compile` method, a phase will be added after `compile_libraries` and before `compile_attributes`, which will copy the Ohai plugins from the cookbook segment and will load all of the discovered plugins.
+
+The plugins will be copied from `<cookbookname>/ohai` into `/etc/chef/ohai/cookbook-plugins/<cookbookname>` as their top level directory (recursively). The state of the entire
+subdirectory tree under `/etc/chef/ohai/cookbook-plugins` will be managed fully by chef-client so that any files which are not synchronized by chef-client will be removed, so
+that removal of a plugin from a cookbook or removal of the cookbook from `run_list` will result in the plugin being removed on the target host.
+
+The plugins directory will work similarly to libraries and other directions, in that there will be no control over the inclusion of plugins below the level of the inclusion of the cookbook itself in the `run_list`.
+
+When plugins override other plugins on loading, and in particular when cookbook plugins override core plugins, they should WARN the user. This will address the case where a user has included a custom plugin and Ohai is later extended with similar functionality in the same namespace. The custom plugin should take precedence for backwards compatibility. There should be a way to silence the warning with a DSL method added to the custom plugin. \ No newline at end of file
diff --git a/docs/dev/design_documents/resource_before_notifications.md b/docs/dev/design_documents/resource_before_notifications.md
new file mode 100644
index 0000000000..cfae566289
--- /dev/null
+++ b/docs/dev/design_documents/resource_before_notifications.md
@@ -0,0 +1,77 @@
+# Before Notifications
+
+Let users trigger another resource prior to an action happening.
+
+## Motivation
+
+ As a Chef user,
+ I want to make one resource action a precondition for another,
+ So that when two resources depend on each other, I can make an update succeed.
+
+ As a Chef user,
+ I want this action only to happen if the other resource *does* update,
+ So that I don't unnecessarily run my preconditions on every single Chef run.
+
+ As a Chef user,
+ I want Chef to run an action if a resource updates,
+ So that I don't have to implement the resource test logic in my recipe.
+
+## Specification
+
+We add a new `:before` timing which causes a notification to happen
+*before* the resource actually updates. If the resource will not actually update,
+this event does not fire.
+
+The events you can specify would become:
+
+- `:before` - before the resource updates, but *only* if an update will occur.
+- `:immediately` - after the resource updates, but *only* if an update occurred.
+- `:delayed` - after the resource updates, at the end of the run.
+
+```ruby
+package "foo" do
+ action :upgrade
+ # The package upgrade will fail if we try to upgrade while it runs!!!
+ notifies :stop, "service[blah]", :before
+end
+
+service "blah" do
+end
+```
+
+This will work for both `subscribes` and `notifies`.
+
+### Backwards Compatibility
+
+This will only affect resources, which have `:before` on them, and will
+not modify any existing resources or recipes.
+
+### Implementation
+
+There is a tricky implementation detail here, because both the test part -- "is my
+package version lower than the latest?" -- and the set part -- "call rpm and update the
+package"-- of a resource are both part of the action. Chef only runs one
+action to run at a time, and it seems ill-advised to change that without a lot
+of extra thinking.
+
+To get around this without breaking the model, we propose that resources with an
+`:before` action run a why-run test of the action and trigger off of
+that, before running the action for real. The flow of this resource:
+
+```ruby
+package "foo" do
+ action :upgrade
+ notifies :stop, "service[blah]", :before
+end
+```
+
+The execution of the package upgrade looks like this:
+
+1. If `:before` events are on the resource:
+ a. raise an error if the resource does not support `why-run`.
+ b. Turn on `why-run` temporarily.
+ c. Run the action.
+ d. Send the notification if `updated_by_last_action?` is true.
+ e. Turn off `why-run`.
+2. Run the action (for real!).
+3. Send `:immediate` or `:delayed` notifications if `updated_by_last_action?` is true.
diff --git a/docs/dev/design_documents/resource_file_content_verification.md b/docs/dev/design_documents/resource_file_content_verification.md
new file mode 100644
index 0000000000..f813e57c72
--- /dev/null
+++ b/docs/dev/design_documents/resource_file_content_verification.md
@@ -0,0 +1,110 @@
+# File Content Verification
+
+File-based resources should be able to verify a file's content via
+user-supplied instructions before deploying the new content.
+
+## Specification
+
+The `verify` attribute of the `file`, `template`, `cookbook_file`, and
+`remote_file` resources will take a user-provided block or string. At
+converge time, a block will be passed the path to a temporary file
+holding the proposed content for the file. If the block returns `true`,
+the provider will continue to update the file on disk as
+appropriate. If the block returns `false`, the provider will raise an
+error.
+
+If a string argument to verify is passed, it will then be executed as
+a system command. If the command's return code indicates success -- 0 on
+unix-like system -- the provider will continue to update the file on
+disk as appropriate. If the command's return code indicates failure,
+the provider will raise an error.
+
+The path to the temporary file with the proposed content will be
+available by using Ruby's sprintf formatting:
+
+ "%{path}"
+
+Other variables may be made available to commands in the future.
+
+If no verification block or string is supplied by the user, the
+provider assumes the content is valid.
+
+Multiple verify blocks may be provided by the user. All given verify
+block must pass before the content is deployed.
+
+As an example:
+
+```ruby
+# This should succeed
+template "/tmp/foo" do
+ verify do |path|
+ true
+ end
+end
+
+# This should succeed on most systems
+template "/tmp/wombat" do
+ verify "/usr/bin/true"
+end
+
+# This should raise an error
+template "/tmp/bar" do
+ verify do |path|
+ false
+ end
+end
+
+# This should raise an error on most systems
+template "/tmp/turtle" do
+ verify "/usr/bin/false"
+end
+
+# This should pass
+template "/tmp/baz" do
+ verify { true }
+ verify { 1 == 1 }
+end
+
+# This should raise an error
+template "/tmp/bat" do
+ verify { true }
+ verify { 1 == 0 }
+end
+```
+
+Users could use this feature to shell out to tools, which check the
+configuration:
+
+```ruby
+template "/etc/nginx.conf" do
+ verify "nginx -t -c %{path}"
+end
+```
+
+Chef may ship built-in verifiers for common checks, such as
+content-type verification. Built-in verifiers can be used by passing
+well-known symbols to the verify attribute:
+
+```ruby
+template "/etc/config.json" do
+ verify :json
+end
+```
+
+## Motivation
+
+Typos and bugs in a template can lead Chef to render invalid
+configuration files on a node. In some cases, this will cause the
+related service to fail a notified restart and bring down the user's
+application. One hopes to catch such errors in testing, but that is
+not always possible.
+
+Many applications provide a means to verify a configuration file, but
+it is currently difficult to use these tools to verify a template
+without an elaborate series of resources chained together with
+notifications.
+
+## Related BUGS
+
+https://tickets.opscode.com/browse/CHEF-4416
+https://tickets.opscode.com/browse/CHEF-3634
diff --git a/docs/dev/design_documents/resource_guard_interpreters.md b/docs/dev/design_documents/resource_guard_interpreters.md
new file mode 100755
index 0000000000..37db4e761a
--- /dev/null
+++ b/docs/dev/design_documents/resource_guard_interpreters.md
@@ -0,0 +1,534 @@
+# Resource-based Guard Interpreters for Chef
+
+The *guard interpreter* is a feature of Chef resources that
+allows authors to specify their choice of Chef resource classes to evaluate a guard expression (i.e. `only_if` or
+`not_if` block). The goal of this capability is to reduce the complexity in both the number of languages
+and the boilerplate code found within a Chef recipe.
+
+Guard interpreter customization makes the Chef DSL *Delightful(tm)*.
+
+## Motivation
+
+The original impetus for guard interpreters involved a common user expectation
+that when guard expressions were present in a `script` resource, the same
+interpreter used to evaluate the `script` resource (e.g. `bash`, `csh`,
+`powershell`) would be used to evaluate the guard expression. It turns out
+this is not the case -- more on this later -- and thus user expectations were not
+being met.
+
+An open source ticket for the Chef project describes a typical instance of
+this problem at [CHEF-4553](https://tickets.opscode.com/browse/CHEF-4553). In particular, that
+ticket posits that Windows users of the `powershell_script` resource expect
+that guards (i.e. the `only_if` and `not_if` conditionals) evaluated in the context of a `powershell_script`
+block use the `powershell_script` interpreter, not the cmd.exe (batch file)
+interpreter. This is a change from the current state of affairs, since in general there is no link between the interpreter used by a
+script resource. This is an issue that affects both Windows and *nix users.
+
+Further detail and motivation for adding these features are given in sections at the
+end of the document.
+
+## Problems addressed through guard interpreters
+
+The guard interpreter and related improvements discussed in the document address
+the following use cases:
+
+* CHEF-4553: Users of the `powershell_script` resource are forced to execute
+ script guards with `CMD`'s batch language instead of the PowerShell
+ language already in use in the `powershell_script` resource.
+* Users of the bash resource who want to use bash in script guards must explicitly invoke bash with properly quoted
+ command arguments in the guard
+* Windows users of the `powershell_script` resource do not have a way to use
+ PowerShell in script guards in a concise, intuitive, quasi-Boolean fashion,
+ while users of the `script`, `csh`, `bash`, and other resources have this
+* On Windows, script guards are always executed with the 32-bit process architecture and
+ will be affected by the absence of system state exposed only to 64-bit
+ processes
+
+## Definitions
+
+This document assumes familiarity with the Chef resource DSL, which is
+documented at <https://docs.chef.io/dsl_recipe/>.
+
+These definitions are used throughout the discussion:
+
+* **Chef resource or resource:** an element of the Chef DSL that represents configuration, system components, or any other aspect of system state to be managed by Chef.
+ The resource contains attributes that define the desired state, and an
+ action that can be taken by a provider evaluating the resource such as one
+ that changes the actual state of the system the desired state. State such as
+ files, scripts, and software packages are examples of system that that can
+ be modeled as a Chef resource.
+* **guard:** An expression given as an attribute of a Chef resource in the
+ form of a string to be executed by a shell or a Ruby block. Such an
+ expression is evaluated before running the resource's action, and
+ depending on whether it results in a true or false value, will control
+ whether or not the resource's action is executed or skipped.
+* **script guard:** A guard expression that is given as a string to be
+ evaluated by a shell command interpreter. When the interpreter's execution
+ of the script results in a successful (i.e. non-zero) process exit code, the guard's value
+ is `true`. Otherwise, it is `false`.
+* **block guard:** A guard to which a Ruby block is passed rather than a string.
+* **guard parameter:** Any Ruby expression passed as additional information to
+ the shell interpreter used to modify execution context such as the current
+ working directory, environment variables, user identity, etc.
+* **guard interpreter resource:** A Chef resource that is not part of a Chef run
+ context and is expressed within a block guard's block. The guard interpreter
+ resource is simply used to assess a true or false value (e.g. whether a
+ script that tests system state in a relevant way returns a success or
+ failure process status) inside of a block guard.
+
+## Overview
+Guard expressions for all resources have been extended to include an attribute
+named `guard_interpreter` that takes the short name symbol of a Chef resource to be
+used to evaluate script guards. This is useful for testing conditions to ensure idempotence for non-idempotent resources such as script resources. The goals in doing this are:
+
+* To address [CHEF-4553](https://tickets.opscode.com/browse/CHEF-4553) -- simplify convoluted expressions such as that below for
+Windows users
+```
+not_if 'powershell -noninteractive -noprofile -command "exit [int32]((Get-ExecutionPolicy -scope localmachine) -eq 'RemoteSigned')"'
+```
+* For guard expressions, to allow Unix and Windows users to make use of familiar modern shells such as
+ bash and PowerShell rather than ancient interpreters like `sh` or `cmd.exe` with
+ limited or obscure syntax
+* To make Chef interactions with OS interfaces such as shells as natural for
+ users of the OS as possible
+
+## Behavioral impact on Chef resources
+At a high level, here are the changes proposed and now accepted to simplify conditional execution of resource actions:
+
+* Add a `guard_interpreter` attribute to the `Chef::Resource` class that can
+ take a symbol that corresponds to the name of a Chef resource derived from
+ `Chef::Resource::Script`. This guard interpreter resource will be used to evaluate the script command passed to the guard.
+* Truth or falsehood of such a guard is determined by whether the resource
+ evaluating the script guard is updated, i.e. runs the script without raising
+ an exception and without the script returning a non-success code (0 is the
+ default expected success exit code of the script interpreter).
+* Parameters passed in hash format after the guard command string are
+ interpreted as attributes to be set for the guard interpreter resource instance.
+* The guard interpreter resource is executed outside of the containing resource's
+ run context.
+* Enable inheritance of attributes from a given resource A for any resource B
+ executed as part of a block passed to a guard attribute of resource
+* Add a `convert_boolean_return` attribute to the `powershell_script` resource
+ so that Chef interprets PowerShell `boolean` expressions for PowerShell code
+ executed by the `powershell_script` resource such that it returns `boolean`
+ values the same way that Unix shells like bash do when they evaluate
+ "Boolean-like" statements through commands such as the `test` command
+* Make `convert_boolean_return` default to `false` to provide for behavior
+ identical to versions of Chef that did not have this feature, but make it
+ default to `true` when used to evaluate a guard via the `guard_interpreter`
+ attribute to make guard expressions more concise and natural.
+
+## Guard interpreter code examples
+The following examples demonstrate the intended use cases around guard
+interpreters.
+Concepts such as inheritance are introduced in the examples which are explained in subsequent sections.
+
+### Custom interpreter for script resources
+
+```ruby
+
+# This resource will run without errors because the guard uses
+# the bash interpreter; if we had passed the same string
+# directly to the only_if, this would have failed the
+# Chef run since that string is not valid for /bin/sh
+bash "Use bash for only_if" do
+ guard_interpreter :bash
+ code "echo I am $SHELL"
+ only_if '[[ 1 == 1 ]]' # won't work outside of bash
+end
+```
+
+### Inheritance is your friend
+
+```ruby
+
+# This resource will run because the cwd of the guard
+# is the same as that of the parent resource
+bash "My cwd gets inherited" do
+ guard_interpreter :bash
+ code 'echo inherit me'
+ cwd '/opt'
+ only_if '[[ $PWD == "/opt" ]]' # Glad I didn't have to add cwd
+end
+```
+
+### Setting guard parameters
+
+```ruby
+
+# The normal command string syntax for guards lets you
+# specify parameters like cwd, etc. -- you can do the same
+# here by specifying those parameters in the guard expression
+bash "Override my guard attributes" do
+ guard_interpreter :bash
+ code 'echo override me'
+ cwd '/var'
+ only_if '[[ $PWD == "/opt" ]]', :cwd => '/opt' # Don't try to put me in my place
+end
+```
+
+### `powershell_script` default behavior examples
+The examples below are changes to the `powershell_script` resource that take
+advantage of guard interpreter resource support.
+
+#### `powershell_script` guard interpreter default example
+
+```ruby
+
+# Here is the fix for CHEF-4553 -- use guard_interpreter to
+# execute the script with powershell, not cmd
+powershell_script "defaultguard" do
+ guard_interpreter :powershell_script
+ code 'new-smbshare systemshare $env:systemdrive\'
+ not_if 'get-smbshare systemshare' # This uses powershell, not cmd
+end
+```
+
+#### `powershell_script` Boolean behavior
+
+```ruby
+
+# What if guards evaluated powershell script code that powershell
+# evaluates as a boolean type as the actual boolean value of the guard
+# itself? You can avoid extra script code to translate the boolean into
+# a process exit code that results in the right true / false behavior
+# for the guard. Guards already work this way on Linux systems...
+powershell_script "set execution policy" do
+ guard_interpreter :powershell_script
+ code "set-executionpolicy remotesigned"
+ not_if "(get-executionpolicy -scope localmachine) -eq 'remotesigned'" # Like I barely left Ruby -- wow!
+end
+```
+
+#### `powershell_script` architecture inheritance
+
+```ruby
+do
+# And look, the not_if will run as an :i386 process because of the
+# architecture attribute for the parent resource which powershell_script
+# guard interpreter resources will inherit from the enclosing resource
+powershell_script "set i386 execution policy" do
+ guard_interpreter :powershell_script
+ architecture :i386
+ code "set-executionpolicy remotesigned"
+ not_if "(get-executionpolicy -scope localmachine) -eq 'remotesigned'"
+end
+```
+
+## Guard interpreter formal specification
+
+The documented behavior for guards can be found at
+<https://docs.chef.io/resource_common/>. Guards are expressed via the optional
+`not_if` and `only_if` attributes. The expression following the attribute
+may be either a block or a string.
+
+### Guard conditional semantics overview
+
+Guards allow for conditional execution of a resource. Before executing the action for the resource, Chef will evaluate the expression to produce a Ruby `true` or
+`false` value that is utilized in determining whether to execute the resource's
+action or to skip it:
+
+ 1. If the **guard_interpreter resource** is **not** specified for the resource, when a string is passed to a guard, the existing implementation executes the `/bin/sh` interpreter on Unix or `cmd.exe` on Windows with that string to be evaluated as a script by the interpreter. Chef will execute the interpreter with the code supplied to the string; if the interpreter exits with a 0 (success) code, this is interpreted as a Ruby `true` value, otherwise it is `false`.
+ 2. When a block is passed to a guard, the code in the block will be executed,
+ and the value of the last line of code executed by the block will be the
+ Boolean value of the block, converted to a Boolean value in a manner
+ consistent with the Ruby `!!` operator, resulting in either the value `true`
+ or `false`.
+ 3. If the aforementioned string or block expression was supplied to an `only_if` attribute, the action of the resource containing the attribute will be skipped if the expression evaluated to `false` and executed if it evaluated to `true`.
+ 4. If the expression was supplied to a `not_if` attribute, the behavior of the resource is the inverse of that for `only_if`; the resource action is executed if the expression evaluated to `false` and skipped if it evaluated to `true`.
+
+This specification of guard behavior is accurate without the inclusion of
+`guard_interpreter` features described in this document. The
+`guard_interpreter` attribute allows for the interpreter to be something other
+than `/bin/sh` or `cmd.exe` and is described below.
+
+### Conditional semantics with the guard_interpreter attribute
+
+In Chef Client versions 11.12.0 and later, the `guard_interpreter` attribute
+was introduced, which provides the following behavior:
+
+ 1. When the `guard_interpreter` attribute is specified in the resource as a value other than `:default`, a **guard interpreter resource** of the type specified in the `guard_interpreter` attribute is created with its `code` attribute set to the value of the string passed to the guard attribute. The guard interpreter resource's action will be executed to produce a truth value.
+ 2. If the resource action updates the resource, the value is `true`. Resources can only be updated if the interpreter used by the resource specified in the `guard_interpreter` attribute returns a success code, `0` by default, though this can be overridden in attributes specified to the resource as guard arguments. Anything other than a success code results in the guard evaluating as `false`.
+
+### script resource conditional semantics
+To enable the usage as guard resources of resources derived from `Chef::Resource::Script`, known colloquially as script resources, all such resources when executed as guard resources will handle the exception `Mixlib::Shellout::ShellCommandFailed`.
+
+By doing this, usage of script resources has the same conditional and exception behavior as the case described earlier when a string is passed to a `not_if` or `only_if` guard attribute since this exception is raised precisely in the case where a string passed as a guard would have been evaluated by /bin/sh or cmd.exe as exiting with a failure status code.
+
+This gives any script resource -- for example, bash -- the ability to behave like the string argument usage for guards, except that an alternative interpreter to `/bin/sh` is used to execute the command. This extends the range of shell script languages that may be used in guard expressions.
+
+### `powershell_script` guard_interpreter example
+
+Use of `guard_interpreter` for the `powershell_script` resource addresses [CHEF-4553](https://tickets.opscode.com/browse/CHEF-4553). Without `guard_interpreter`, a user of the `powershell_script` resource who would like to use the same PowerShell language in the expression passed to the guard resource to the following cumbersome solution:
+
+```ruby
+# Yuk. Let me look up all the right cli args to powershell.exe.
+# Oh, do I have to quote my cmd -- what kind of quotes again?
+# So much fun for me. This is CHEF-4553.
+powershell_script "oldguard" do
+ code 'new-smbshare systemshare $env:systemdrive'
+ not_if 'powershell.exe -inputformat none -noprofile -nologo -noninteractive -command get-smbshare systemshare'
+end
+```
+
+With the `guard_interpreter` attribute, we have the following more concise, less cumbersome, and less error-prone expression for the same `powershell_script` use case given above:
+
+```ruby
+# So PowerShell. Such short.
+powershell_script "newguard" do
+ guard_interpreter :powershell_script
+ code 'new-smbshare systemshare $env:systemdrive'
+ not_if 'get-smbshare systemshare'
+end
+```
+
+### Guard attribute inheritance
+A new change is that a resource used within the context of a guard may inherit some attributes from the resource that contains the guard.
+
+Inheritance follows these rules:
+
+* An attribute in a guard interpreter resource is inherited from the parent resource only if the attribute is in a set of inheritable attributes defined by the type of the guard resource
+* To be inherited from the parent, the attribute must not have been specified as a parameter to the guard command -- whatever is passed to the guard command will override any parent specification of the attribute.
+* The Chef `script` resource, i.e. `Chef::Resource::Script`, and all resources derived from it, including `bash`, `python`, and `powershell_script`, inherit the following attributes from the parent resource:
+
+ `:cwd`
+ `:environment`
+ `:group`
+ `:path`
+ `:user`
+ `:umask`
+
+* Resource types may define additional rules for inheritance -- the `powershell_script` resource has additional behaviors described in a subsequent section.
+
+In general, the utility of inheritance derives from a common case where setting system configuration through a Chef resource requires some external state such as an environment variable, alternate user identity, or current directory, and testing the current state to ensure idempotence through a guard requires the same state. Inheritance allows that state to be expressed no more than once through the Chef DSL.
+
+### Simplification through attribute inheritance
+
+Consider the following example:
+
+```ruby
+script "javatooling" do
+ environment {"JAVA_HOME" => '/usr/lib/java/jdk1.7/home'}
+ code 'java-based-daemon-ctl.sh -start'
+ not_if 'java-based-daemon-ctl.sh -test-started', :environment =>
+ {"JAVA_HOME" => '/usr/lib/java/jdk1.7/home'}
+end
+```
+
+In the `not_if` attribute, the same hash of environment variables specified for
+the resource must also be specified for the guard, both of which use a shell script
+to that relies on the `JAVA_HOME` environment variable. With inheritance,
+the second environment variable specification (along with the possibility of
+an incorrect specification) can be eliminated with this simplified version:
+
+```ruby
+script "javatooling" do
+ guard_interpreter :csh
+ environment {"JAVA_HOME" => '/usr/lib/java/jdk1.7/home'}
+ code 'java-based-daemon-ctl.sh -start'
+ not_if 'java-based-daemon-ctl.sh -test-started'
+end
+```
+#### `powershell_script` inheritance rules
+
+* For the `powershell_script` resource, an additional attribute is inherited
+ when this resource is used as a guard resource:
+
+ `:architecture`
+
+* When a guard attribute of `powershell_script` is given a string rather than a
+ block, unlike other resources, inheritance of attributes occurs. The
+ behavior of the PowerShell interpreter when executing that string is the same
+ as if a `powershell_script` resource has been passed instead with the
+ `code` attribute set to the value of the string.
+* Inherited attributes in this case may be overridden by specifying those same
+ attributes as guard parameters using the existing guard parameter syntax
+
+This results in a more concise expression of the resource compared
+to the situation without inheritance for string arguments. For example,
+without allowing the architecture attribute to be inherited with a string
+guard, here is the recipe fragment we'd need to set the PowerShell execution
+policy for the x86 PowerShell interpreter:
+
+```ruby
+# This is what we'd write if we couldn't inherit the architecture
+# attribute when a string is passed to a guard -- we'd repeat
+# the architecture attribute twice.
+powershell_script "set i386 execution policy" do
+ guard_interpreter :powershell_script
+ architecture :i386
+ code "set-executionpolicy remotesigned"
+ not_if "(get-executionpolicy -scope localmachine) -eq 'remotesigned'", :architecture => :i386
+end
+```
+
+By allowing inheritance, the expression is more compact, requires less
+up-front consideration of options, and provides the least surprising behavior:
+
+```ruby
+# Much more concise -- architecture attribute is inherited by the guard
+powershell_script "set i386 execution policy" do
+ guard_interpreter :powershell_script
+ architecture :i386
+ code "set-executionpolicy remotesigned"
+ not_if "(get-executionpolicy -scope localmachine) -eq 'remotesigned'"
+end
+```
+
+### `powershell_script` Boolean result code interpretation
+
+Boolean result code interpretation allows guards that make use of the
+`powershell_script` resource to treat PowerShell Boolean expressions as if they
+were Ruby boolean expressions as in the code below:
+
+```ruby
+powershell_script "backup-dc" do
+ guard_interpreter :powershell_script
+ code "backup-domain-controller.ps1"
+ only_if "[Security.Principal.WindowsIdentity]::GetCurrent().IsSystem()"
+end
+```
+
+More formally, the value of guard conditionals for `powershell_script` gets the following
+modification:
+
+* The process exit code for a PowerShell script fragment executed by the
+ `powershell_script` resource will support passing the value of a *Boolean*
+ expression from the script through the interpreter's exit code.
+* The attribute `convert_boolean_return` is introduced for the
+ `powershell_script` resource to control this behavior -- it may have the
+ value `true` or `false`.
+* The default value of `convert_boolean_return` is `false` for
+ `powershell_script` resource instances that are not being evaluated as a
+ guard interpreter resource -- this means that recipes using
+ `powershell_script` prior to this change will behave identically after it.
+* However, if the `powershell_script` instance exists as the result of
+ evaluating a guard expression because the `guard_interpreter` attribute was
+ set to `:powershell_script`, the value of `convert_boolean_return` is set to
+ `true`. There is no backward compatibility issue for this default because the
+ guard_interpreter resource was not available prior to versions of Chef
+ with the boolean interpolation feature.
+* Boolean interpolation only occurs if the script fragment could have been
+ executed as the definition of a PowerShell function with a return type of
+ `bool`, a PowerShell type analogous to a typical Boolean data type **AND**
+ if the `convert_boolean_return` attribute of the resource executing the
+ script is set to `true`.
+* In this case, if the function return value is the PowerShell value `$true`,
+ the exit code is 0 (overloaded with 'success'), otherwise the function return
+ value is `$false` and the exit code is 1.
+* In cases where the hypothetical PowerShell function raises an exception or returns a
+ type other than PowerShell's `bool` type, preexisting exit code rules hold.
+
+This behavior for `powershell_script` when `convert_boolean_return` is set to
+`true` is functionally equivalent to the behavior of the bash shell
+when it evaluates quasi-boolean commands such as the `test` command and
+related commands.
+
+## Detailed motivation on guard improvements
+Particularly for PowerShell users on Windows, the behavior of guards before
+Chef 11.12.0 was not delightful. Prior to Chef 11.12.0, when a
+string was supplied to a guard, on Unix it was **always** evaluated with
+`/bin/sh`, even if the guard was being executed in the context of a script
+resource that executes code using something other than sh, like the `bash`
+resource. On Windows, there is no `/bin/sh`, so `cmd.exe` was always used for guards.
+
+Both Unix and Windows experiences could have been better in multiple respects. For Windows, `cmd.exe` is
+guaranteed to exist on the system, but that's about as much good as you can
+say for it. It's a vestigial component that still shows signs of its 1970's
+CP/M heritage even in 2014, and as Windows admins turned to PowerShell or were
+nudged toward it (often by Microsoft itself), it was asking a lot for Chef users to know
+how to use legacy `cmd.exe` to accomplish tasks. Most likely, users of `powershell_script`
+would choose to run powershell.exe in the `not_if` and `only_if` blocks. Since
+that was the common case for `powershell_script` users, the guards should have
+had some way to allow that, or to
+provide guard execution via PowerShell in a more natural fashion.
+
+Even for Unix users, however, there was still room to be delightful since
+`/bin/sh`, while not the antediluvian relic that is `cmd.exe` on Windows, is
+certainly not a modern shell. Thus guards require users of, say, the `bash` resource,
+to use two different shell dialects. The bash dialect is a modern and familiar one for the code to be
+executed by the script resource, and `sh` is a more limited one for the guards. It's confusing behavior
+for new users. And even for those who are experienced,
+it requires awkward workarounds like explicitly running bash with some set of
+switches and/or researching workarounds for missing features in `sh`. Overall,
+it decreases the efficiency of using resources like `bash` -- one might just as
+well use the generic script or execute resources if knowledge of the best way
+to a given interpreter cannot be contained in the resource.
+
+So the addition of the `guard_interpreter` attribute as adopted via this
+design document lets users choose to adopt a more natural way of expressing
+idempotence that lets you embed shell-specific expressions in the clean Chef
+DSL without all of the awkwardness and corner cases described earlier. The
+result is an uncluttered description of infrastructure that doesn't sacrifice
+on the shell or underlying platform's native descriptive and functional capabilities.
+
+### Boolean result code interpolation details
+Consider the Chef DSL fragment below where a string passed to an `only_if` guard performs a
+Boolean test using the sh "[" command:
+
+```ruby
+bash "systemrestart" do
+ code '~/rebootnow.sh'
+ only_if '[ "$USER" == "root" ]'
+end
+```
+
+This results in the bash script 'rebootnow.sh' being executed only when this
+code is executed with chef-client running as root. The Boolean-like expression
+in the sh script passed to the guard is treated as a Boolean result for the
+guard, resulting in a natural way of using the sh interpreter from within Chef
+and Ruby.
+
+A similar mapping between Boolean results for strings passed to guards on the
+Windows platform does not exist. This partially due to guards always being
+executed with cmd.exe. However, the behavior shown on Unix guards that
+interpret script strings is actually present in the script resources
+themselves when the same Boolean-like code is executed as part of the `code`
+attribute. Here's an example:
+
+```ruby
+bash "myfail" do
+ code '[ "$USER" == "root" ]'
+end
+```
+
+If this resource is run as the root user, it will succeed and subsequent
+resources in the recipe can be executed. If the user is not root, this will
+result in /bin/sh returning a non-zero exit code, and the execution will fail,
+terminating any chef-client run.
+
+While the utility of translating Boolean values to interpreter exit codes is debatable within a resource executed
+at recipe scope, it is consistent with the much more useful guard behavior
+described in the previous example.
+
+Contrast this to the existing `powershell_script` resource, which does not interpolate
+Boolean results of scripts to exit codes consistent with truth or falsehood in
+any context. The added interpolation for `powershell_script` rectifies the
+deficiency in this resource compared to bash and the other Unix shell-based resources.
+
+### PowerShell Boolean symmetry with Unix shells
+This boolean interpolation behavior is similar to the `bash` or `sh`
+interpreters' behavior in certain contexts, where the
+Boolean-like result of the test command causes the interpreter process to exit with 0 if
+the test command resulted in a true result, 1 otherwise, assuming the test
+command was the last line of the script.
+
+This enables cases where a test that can be expressed very cleanly with
+the PowerShell language can be used directly within a guard expression with no
+need to try to generate a process exit code that Chef will interpret as a true
+or false value. For example, the true or false value of a PowerShell
+expression like
+
+ ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).
+ IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
+
+or
+
+ (gi WSMan:\localhost\Shell\MaxMemoryPerShellMB).value -ge 300
+
+can be passed directly to Ruby and evaluated as true or false by the guard
+without specifying any additional PowerShell code. This interpolation of
+Boolean return values also happens when a string of code is passed to a guard
+in a `powershell_script` resource, a scenario that builds on top of the
+previously described switch to the PowerShell language as the script interpreter of
+strings passed to guards in the `powershell_script` resource.
diff --git a/docs/dev/design_documents/resource_load_and_converge_methods.md b/docs/dev/design_documents/resource_load_and_converge_methods.md
new file mode 100644
index 0000000000..00b074578b
--- /dev/null
+++ b/docs/dev/design_documents/resource_load_and_converge_methods.md
@@ -0,0 +1,235 @@
+# Easy Resource Load And Converge Methods
+
+With the introduction of `action` on resources, it becomes useful to have a
+blessed way to get the actual value of the resource. This proposal adds
+`load_current_value` and `converge_if_changed` to help with this purpose, enabling:
+
+- Low-ceremony load methods (as easy to write as we can make it)
+- A super easy converge model that automatically compares current vs. desired
+ values and prints green text
+
+## Motivation
+
+ As a Chef resource writer,
+ I want to be able to read the current value of my resource at converge time,
+ so that it is easy to tell the difference between current and desired value.
+
+ As a Chef resource writer,
+ I want a converge model that compares current and desired values for me,
+ So that the easiest converge to write is the most correct one.
+
+## Specification
+
+### `load_current_value`: in-place resource load
+
+When using `action`, one needs a way to load the *actual* system value of the resource, so that it can be compared to the desired value, and a decision made as to whether to change anything.
+
+When the resource writer defines `load_current_value` on the resource class, it can be called to load the real system value into the resource. Before any action runs, this will be used by `load_current_resource` to load the resource. `action` will do some important work before calling the new method:
+
+1. Create a new instance of the resource with the same name.
+2. Copy all non-desired-state values from the desired resource into the new instance.
+3. Call `load_current_value` on the new instance.
+
+```ruby
+class File < Chef::Resource
+ property :path, name_attribute: true
+ property :mode, default: 0666
+ property :content
+
+ load_current_value do
+ current_value_does_not_exist! unless File.exist?(path)
+ mode File.stat(path).mode
+ content IO.read(path)
+ end
+
+ action :create do
+ converge_if_changed do
+ File.chmod(mode, path)
+ IO.write(path, content)
+ end
+ end
+end
+
+file '/x.txt' do
+ # Before the change, the above code would have modified `mode` to be `0666`.
+ # After, it leaves `mode` alone.
+ content 'Hello World'
+end
+```
+
+#### Non-existence
+
+To appropriately handle actual value loading, the user needs a way to specify that the actual value legitimately does not exist, rather than simply not filling in the object and getting `nil`s in it. If `load_current_value` raises `Chef::Exceptions::ActualValueDoesNotExist`, the new resource will be discarded and `current_resource` becomes `nil`. The `current_value_does_not_exist!` method can be called to raise this.
+
+NOTE: The alternative was to have users return `false` if the resource does not exist, but I didn't want users to be forced into the ceremony of a trailing `true` line.
+
+```ruby
+ load_current_value do
+ # Check for existence before doing anything else.
+ current_value_does_not_exist! if !File.exist?(path)
+
+ # Set "mode" on the resource.
+ mode File.stat(path).mode
+ end
+```
+
+The block will also be passed the original (desired) resource as a parameter, in case it is needed.
+
+#### Inheritance
+
+`super` in `load_current_value!` will call the superclass's `load_current_value!` method.
+
+#### Handling Multi-Key Resources
+
+The new resource is created with all properties copied over *except* desired state properties (properties in `ResourceClass.state_properties`). This means `name`, and properties with `identity: true` or `desired_state: false` are copied over. Normal `property` and `attribute` are not.
+
+```ruby
+class DataBagItem < Chef::Resource
+ # Copied
+ attribute :item_name, name_attribute: true
+ attribute :data_bag_name, identity: true
+ attribute :recursively_delete, desired_state: false
+ # Not copied:
+ attribute :data
+ def load_current_value!
+ data Chef::DataBagItem.new(data_bag_name, item_name).data
+ end
+end
+```
+
+### `converge_if_changed`: automatic test-and-set
+
+The new `converge_if_changed do ... end` syntax is added to actions, which enables a *lot* of help for resource writers to make safe, effective resources. It performs several key tasks common to nearly every resource (which are often not done correctly):
+
+- Goes through all attributes on the resource and checks whether the desired
+ value is different from the current value.
+- If any attributes are different, prints appropriate green text.
+- Honors why-run (and does not call the `converge_if_changed` block if why-run is enabled).
+
+```ruby
+class File < Chef::Resource
+ property :path, name_attribute: true
+ property :content
+
+ load_current_value do
+ current_value_does_not_exist! unless File.exist?(path)
+ content IO.read(path)
+ end
+
+ action :create do
+ converge_if_changed do
+ IO.write(path, content)
+ end
+ end
+end
+```
+
+#### Side-by-side: new and old
+
+Here is a sample `converge_if_changed` statement from a hypothetical FooBarBaz resource with properties `foo`, `bar` and `baz`:
+
+```ruby
+converge_if_changed do
+ if current_resource
+ FooBarBaz.update(new_resource.id, new_resource.foo, new_resource.bar, new_resource.baz)
+ else
+ FooBarBaz.create(new_resource.id, new_resource.foo, new_resource.bar, new_resource.baz)
+ end
+end
+```
+
+This is what you would have to write to do the equivalent:
+
+```ruby
+if current_resource
+ # We're updating; look for properties that the user wants to change (do the "test" part of test-and-set)
+ differences = []
+ if (new_resource.property_is_set?(:foo) && new_resource.foo != current_resource.foo)
+ differences << "foo = #{new_resource.foo}"
+ end
+ if (new_resource.property_is_set?(:bar) && new_resource.bar != current_resource.bar)
+ differences << "bar = #{new_resource.bar}"
+ end
+ if (new_resource.property_is_set?(:baz) && new_resource.baz != current_resource.baz)
+ differences << "baz = #{new_resource.baz}"
+ end
+
+ if !differences.empty?
+ converge_by "updating FooBarBaz #{new_resource.id}, setting #{differences.join(", ")}" do
+ FooBarBaz.create(new_resource.id, new_resource.foo, new_resource.bar, new_resource.baz)
+ end
+ end
+
+else
+ # If the current resource doesn't exist, we're definitely creating it
+ converge_by "creating FooBarBaz #{new_resource.id} with foo = #{new_resource.foo}, bar = #{new_resource.bar}, baz = #{new_resource.baz}" do
+ FooBarBaz.update(new_resource.id, new_resource.foo, new_resource.bar, new_resource.baz)
+ end
+end
+```
+
+#### Desired value = actual value
+
+> The easiest way to write a resource must be the most correct one.
+
+There is a subtle pitfall when updating a resource, where the user has set *some* values, but not all. One can easily end up writing a resource, which will overwrite perfectly good system properties with their defaults, and can cause instability. If the user does not specify a property, it is generally preferable to preserve its existing value rather than overwrite it.
+
+To prevent this, referencing the bare property in an `action` will now yield the *actual* value if load_current_value succeeded, and the *default* value if we are creating a new resource (if `load_current_value` raised `ActualValueDoesNotExist`).
+
+```ruby
+class File < Chef::Resource
+ property :path, name_attribute: true
+ property :mode, default: 0666
+ property :content
+
+ load_current_value do
+ current_value_does_not_exist! unless File.exist?(path)
+ mode File.stat(path).mode
+ content IO.read(path)
+ end
+
+ action :create do
+ converge_if_changed do
+ File.chmod(mode, path)
+ IO.write(path, content)
+ end
+ end
+end
+
+file '/x.txt' do
+ # Before the change, the above code would have modified `mode` to be `0666`.
+ # After, it leaves `mode` alone.
+ content 'Hello World'
+end
+```
+
+There will be times when the old behavior of overwriting with defaults is desired. The resource writer can still find out whether `mode` was set with `property_is_set?(:mode)`, and can still access the default value with `new_resource.mode` if it is not set.
+
+There are no backwards-compatibility issues with this because it only applies to `action`, which has not been released yet.
+
+#### Compound Resource Convergence
+
+Some resources perform several different (possibly expensive) operations, depending on what is set. `converge_if_changed :attribute1, :attribute2, ... do` allows the user to target different groups of changes based on exactly which attributes have changed:
+
+```ruby
+class File < Chef::Resource
+ property :path, name_attribute: true
+ property :mode
+ property :content
+
+ load_current_value do
+ current_value_does_not_exist! unless File.exist?(path)
+ mode File.stat(path).mode
+ content IO.read(path)
+ end
+
+ action :create do
+ converge_if_changed :mode do
+ File.chmod(mode, path)
+ end
+ converge_if_changed :content do
+ IO.write(path, content)
+ end
+ end
+end
+``` \ No newline at end of file
diff --git a/docs/dev/design_documents/resource_property_validation_messaging.md b/docs/dev/design_documents/resource_property_validation_messaging.md
new file mode 100644
index 0000000000..20ead5e625
--- /dev/null
+++ b/docs/dev/design_documents/resource_property_validation_messaging.md
@@ -0,0 +1,29 @@
+# Resource Validation Messaging
+
+Custom resources provide multiple property validators, allowing authors to control property input beyond just simple data types. Authors can expect strings to match predefined strings, match a regex, or return true from a callback method. This gives the author great control over the input data, but doesn't provide the consumer with much information when the validator fails. This RFC provides the author with the ability to control the error text when the validator fails.
+
+## Motivation
+
+ As an author of custom resources,
+ I want to control property inputs while providing useful error messaging on failure,
+ so that users can easily understand why input data is not acceptable
+
+ As a consumer of custom resources,
+ I want detailed errors when I pass incorrect data to a resource,
+ so that I quickly resolve failed chef-client runs.
+
+## Specification
+
+This design specifies a `validation_message` option for properties, which accepts a string. This message will be shown on failure in place of a generic failure message.
+
+### Example
+
+in resources/example.rb
+
+```ruby
+property :version,
+ kind_of: String,
+ default: '8.0.35'
+ regex: [/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/]
+ validation_message: 'Version must be a X.Y.Z format String type'
+``` \ No newline at end of file
diff --git a/docs/dev/design_documents/self_documenting_resources.md b/docs/dev/design_documents/self_documenting_resources.md
new file mode 100644
index 0000000000..d18d86111d
--- /dev/null
+++ b/docs/dev/design_documents/self_documenting_resources.md
@@ -0,0 +1,80 @@
+# Self Documenting Resources
+
+Chef has allowed organizations to embrace infrastructure as code, but with codified infrastructure comes the need for accurate documentation for that codebase. This RFC aims to improve the ability to document resources within Chef code, so that we can ensure documentation is accurate and automatically generated. This is applicable to both resources within chef-client and those which ship in cookbooks.
+
+## Motivation
+
+```
+As an author of custom resources,
+I want to manage code and documentation in a single location
+so that I can have up to date documentation with minimal work
+
+As a maintainer of chef
+I want docs to automatically update when new chef-client releases are made
+so that manual release steps and mistakes can be reduced
+
+As a consumer of custom resources
+I want accurate and up to date documentation
+so that I can easily write cookbooks utilizing custom resources
+```
+
+## Specification
+
+This design specifies 4 documentation methods in custom resources:
+
+### description (resource level)
+
+Description is a String value that allows the user to describe the resource and its functionality. This information would be similar to what you would expect to find in a readme or the Chef Docs site describing the usage of a resource.
+
+### introduced (resource level)
+
+Introduced is a String value that documents when the resource was introduced. In a cookbook, this would be a particular cookbook release. In the chef-client itself, this would be a chef-client release.
+
+### examples (resource level)
+
+Examples is a String value containing examples for how to use the resource. This allows the author to show and describe various ways the resource can be used.
+
+### description (property level)
+
+Description is a String value that documents the usage of the individual property. Useful information here would be allowed values, validation regexes, or input coercions.
+
+### description (action level)
+
+Description is a String that describes the functionality of the action.
+
+## Example
+
+```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'
+
+ cookbook_file "/etc/apparmor.d/#{new_resource.name}" do
+ cookbook new_resource.source_cookbook if new_resource.source_cookbook
+ source new_resource.source_filename if new_resource.source_filename
+ owner 'root'
+ group 'root'
+ mode '0644'
+ notifies :reload, 'service[apparmor]', :immediately
+ end
+
+ service 'apparmor' do
+ supports status: true, restart: true, reload: true
+ action [:nothing]
+ end
+end
+```
+
+## Reasons for not using YARD
+
+The goal of introducing minimal DSL changes is to extend the existing data already contained within each resource to include the necessary information to fully document resources. Documenting resources in YARD would require significant duplication of documentation, which most users probably won't do. Out of the box, even without these new DSL extensions, we can already document resources fairly well. These new extensions incentivize users to provide us with a small amount of additional information that would fully fill out the resource documentation. Within our own configuration management industry, other projects have gone different routes to document their equivalence of resources. One project uses a hybrid comment / code method, which feels bolted on and overly complex. The other project fully documents code in comments, which results in near 100% duplication of effort. Simple DSL extensions seem like they are more likely to be utilized and provide a better user experience. \ No newline at end of file
diff --git a/docs/dev/design_documents/server_enforced_recipes.md b/docs/dev/design_documents/server_enforced_recipes.md
new file mode 100644
index 0000000000..3441c43621
--- /dev/null
+++ b/docs/dev/design_documents/server_enforced_recipes.md
@@ -0,0 +1,101 @@
+# Server Enforced Recipe
+
+## Description
+
+Chef Server will provide an endpoint that MAY serve a Chef recipe file. Chef
+Client will attempt to fetch the recipe during run context setup. If
+no user action is taken to configure the feature, the endpoint returns 404
+and Client behavior will be unaffected. When the feature is enabled, the
+endpoint returns the configured recipe file. Chef Client will evaluate and
+converge the recipe.
+
+## Rationale
+
+The motivation for this feature is to allow the operator of the Chef Server to
+enforce limited desired client-side configuration using Chef. Intended use
+cases include:
+
+* Allow cloud-based vendors to install an agent necessary for correct operation
+ of the service
+* Allow Chef Customer Development Partners to efficiently install experimental
+ client-side software during feature development
+* Allow organizations that operate as internal service providers to enforce
+ standard configurations
+
+This feature is targeted at expert level practitioners who are delivering
+isolated configuration changes to the target systems, such as self-contained
+agent software. Users who wish to deliver more comprehensive configuration
+changes should not use this mechanism to deliver those changes directly, but
+could configure an additional Chef Client identity (i.e., node name, client
+key, organization/server url) to deliver those changes via this feature.
+
+As this feature is intended to be used in a manner that is as unobtrusive as
+possible, and in cases where the Chef Server is administrated by a vendor on
+behalf of the user, existing approaches to enforcing client-side configuration
+are not sufficient.
+
+The enforced policy is limited to a single recipe instead of a full cookbook or
+secondary run list for several reasons:
+
+* Cookbooks are Chef Server objects that are organization-scoped and subject to
+ authorization restrictions. Allowing some cookbooks to be global requires
+ additional complexity, which is not needed for the intended uses.
+* Cookbooks have versions and dependencies, which have to be solved. There are
+ several ways this could be addressed, but all options introduce unneeded
+ complexity into the solution.
+* Attributes are not usable for the intended use case, since the author(s) of
+ the enforced recipe code may have no control over the node data, roles, JSON
+ files, or policyfiles used by the nodes being managed.
+* Other cookbook features, such as libraries and the various flavors of
+ resources and providers, set ruby constants, which could interfere with the
+ correct operation of the end user's cookbooks.
+* Templates and cookbook files would be useful, but expert practitioners will
+ be able to be effective without them.
+
+## Motivation
+
+ As a Chef Server Service Provider,
+ I want to enforce a recipe to run on client systems,
+ so that I can ensure client systems are correctly configured.
+
+## Specification
+
+### Enforced Recipe Endpoint
+
+Chef Server shall expose an organization-scoped endpoint for the enforced
+recipe. If the feature has not been configured by the Chef Server
+administrator, the endpoint shall return a 404 response. If the feature is
+enabled by the Chef Server administrator, the endpoint shall return a 200
+response with the recipe content as the response body.
+
+The endpoint shall authenticate the request via Chef Server's usual
+authentication mechanism.
+
+No authorization mechanism is provided. Any user or client with API access to
+any organization on the Chef Server will have read-only access to the enforced
+recipe.
+
+The URL path of the endpoint relative to the organization base path will be
+determined at a future time.
+
+### Chef Server Configuration
+
+The interface for configuring the feature is to be determined.
+
+Though the initial implementation will likely only support a standalone Chef
+Server deployment, the configuration interface will be written such that it can
+be extended to support tiered and HA configurations.
+
+### Chef Run
+
+During the setup phase of the Chef Client run, Chef Client shall make a HTTP
+GET request to the enforced recipe endpoint. If the Chef Server returns a 404
+response, Chef Client will continue the Chef Client run normally. If the Chef
+Server returns a 200 response, Chef Client will store the recipe file in its
+cache directory. Chef Client will then evaluate and converge the recipe using a
+mechanism to be decided.
+
+One possible implementation is to add the recipe to the list of
+`specific_recipes` which is currently populated only via CLI arguments to
+`chef-client --local-mode`. In this case, enforced recipes would be evaluated
+and converged after the primary run list. \ No newline at end of file
diff --git a/docs/dev/how_to/adding_new_platforms_to_infra.md b/docs/dev/how_to/adding_new_platforms_to_infra.md
new file mode 100644
index 0000000000..99a2544710
--- /dev/null
+++ b/docs/dev/how_to/adding_new_platforms_to_infra.md
@@ -0,0 +1,57 @@
+# Adding New Platforms to Chef Infra
+
+Adding a new platform to Chef Infra involves not just adding the platform to the `chef/chef` repo in GitHub, but also ensuring that the platform is supported in all of our ecosystem tooling. This document breaks down all the places we need to update to bring in a new platform.
+
+## Adding the Build to Chef/Chef
+
+### Pull Request Testing
+
+When we add a new platform, if possible, we want to ensure that we test that platform on all Pull Requests to the `chef/chef` GitHub repository. There are a few places to update to accomplish this.
+
+#### RubyDistros
+
+We run RSpec tests against all Pull Requests to `chef/chef` using containers built from our [RubyDistros project](https://github.com/chef/rubydistros). These containers are built on various Linux distribution releases and target the last few Ruby releases. This allows us to run RSpec tests on common Linux platforms like CentOS 8 with Ruby releases such as 2.7 or 3.0.
+
+For major new Linux or Windows distribution releases, you'll want to add these to the RubyDistros repository. You can test these builds locally in Docker and then set the builds to run on DockerHub. If you're not a member of the [RubyDistros DockerHub org](https://hub.docker.com/orgs/rubydistros) then ask #releng-support to add you to that. From there you can add a new repository matching the existing repository setups. Make sure you configure the automated builds to point to the correct Dockerfile path with each Ruby version as a tag. See https://hub.docker.com/repository/docker/rubydistros/opensuse-15 for an example of how we set this up.
+
+Note: Windows builds can't be built on DockerHub and have to be pushed manually from your workstation instead.
+
+Once you've added the distro to RubyDistros and the image is pushed to DockerHub, you can add that new distro to the `chef/chef` [verify.pipeline.yml](https://github.com/chef/chef/blob/master/.expeditor/verify.pipeline.yml).
+
+#### Kitchen Dokken Images
+
+We utilize the kitchen-dokken Test Kitchen plugin to test the contents of `chef/chef` against various Linux distributions. This works best when run against containers that look more like VMs and less like slim containers. The [dokken-images](https://github.com/test-kitchen/dokken-images) repository defines many Docker images for common Linux distributions. These are all based on the official distro Docker images but contain additional packages to make them behave more like full systems.
+
+Similar to the RubyDistros setup, these are defined in GitHub and built-in DockerHub. You'll need to be a member of the [dokken DockerHub organization](https://hub.docker.com/orgs/dokken). If you don't have access to that please ask #releng-support to add you. Once you've added a distro to the GitHub repo you can add a new repository to that DockerHub Organization. Make sure to copy the automated builds settings and specify the correct path to the Dockerfile. Once that is complete and the image is pushed to DockerHub you can add the new test to `chef/chef`. You'll need to edit the [kitchen-test/kitchen.yml](https://github.com/chef/chef/blob/master/kitchen-tests/kitchen.yml) file and then add that new platform to the [verify.pipeline.yml](https://github.com/chef/chef/blob/master/.expeditor/verify.pipeline.yml) config.
+
+#### Azure Pipelines tests
+
+COMING SOON!
+
+### Omnibus Pipeline
+
+With the new platform tested in Pull Requests, you'll now want to ensure that we build and test these packages in our Buildkite Omnibus pipeline.
+
+HOW TO DO THIS COMING SOON!
+
+### Code Changes
+
+With builds in place, we also want to make sure we support this new platform within the Chef Infra Client itself. Most of this is not something that can be documented here. You'll just need to understand where in Chef Infra Client we need to support a new distro. The two most common places to add support are `Ohai` and `chef-utils`:
+
+#### chef-utils
+
+chef-utils provides a large number of helpers for making cookbook authoring simpler. One of the most useful sets of helpers is helpers for platforms and platform families, which will need to be updated if we added new distros. Make sure new platform are supported in `platform.rb` / `platform_family.rb` files in [chef-utils/lib/chef-utils/dsl](https://github.com/chef/chef/tree/master/chef-utils/lib/chef-utils/dsl). In order to test these changes you'll most likely need to update the Fauxhai data. See the section below for instructions on updating that data.
+
+#### Ohai
+
+Ohai powers all system configuration detection for Chef Infra Client. New distributions have pretty far-reaching impacts on Ohai and should be evaluated carefully. The most basic task when adding a new distro is to make sure that the platform is properly mapped to the appropriate platform_family value. On Linux systems this is performed in the [Linux Platform Plugin](https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb).
+
+## Additional Ecosystem Updates
+
+### Bento Boxes
+
+Bento boxes are used by the kitchen-vagrant driver by customers for cookbook testing. When we add a new platform we need to make sure that customers can use it in their testing processes. The [bento repository](https://github.com/chef/bento/) contains packer templates for each platform / platform version, a [builds.yml](https://github.com/chef/bento/blob/master/builds.yml) file which defines the systems to build, and a ruby application which helps with building systems. Bento boxes are generally build for VirtualBox, VMware Fusion, Parallels Desktop, and Hyper-V so you will need both a macOS and Windows system to build all the systems. Once this is complete you can use the `bento upload` command to push these to Vagrant Cloud. You'll need to be added to the Bento Vagrant Cloud org, which #releng-support can assist you with.
+
+### Fauxhai Dumps
+
+Fauxhai is used by ChefSpec for unit testing of cookbooks and we also use it directly in `chef/chef` to test the helpers in the `chef-utils` gem. When we add support for new platforms we need to make sure we update the Fauxhai dumps as well. The [fauxhai repository](https://github.com/chefspec/fauxhai/) contains the actual dump files, and the [fauxhai_generator](https://github.com/chefspec/fauxhai_generator) repository contains a `kitchen.yml` file with a custom provisioner for gathering dump data. These spins up hosts in AWS and gathers their dumps. Keep in mind this only works on Linux hosts and sometimes it requires tracking down custom AMIs. You can see how messy this gets by looking at the values in the `kitchen.yml`. Running this driver using `bundle exec kitchen test FOO` will dump a file locally. You'll then need to use a json sorting tool to sort this JSON. Once it's sorted stick it in the appropriate directory in the Fauxhai project, run `rake documentation:update_platforms` to generate the `PLATFORMS.md` file and commit the change.
diff --git a/docs/dev/how_to/branching_and_backporting.md b/docs/dev/how_to/branching_and_backporting.md
new file mode 100644
index 0000000000..8929c926e9
--- /dev/null
+++ b/docs/dev/how_to/branching_and_backporting.md
@@ -0,0 +1,21 @@
+# Branching and Backporting
+
+## Branch Structure
+
+We develop and ship the current release of Chef off the master branch of this repository. Our goal is that `master` should always be in a shippable state. Previous stable releases of Chef are developed on their own branches named by the major version (ex: chef-14 or chef-13). We do not perform direct development on these stable branches, except to resolve build failures. Instead, we backport fixes from our master branch to these stable branches. Stable branches receive critical bugfixes and security releases, and stable Chef releases are made as necessary for security purposes.
+
+## Backporting Fixes to Stable Releases
+
+If there is a critical fix that you believe should be backported from master to a stable branch, please follow these steps to backport your change:
+
+1. Ask in the #chef-dev channel on [Chef Community Slack](https://community-slack.chef.io/) if this is an appropriate change to backport.
+3. Inspect the Git history and find the `SHA`(s) associated with the fix.
+4. Backport the fix to a branch via cherry-pick:
+ 1. Check out the stable release branch: `git checkout chef-14`
+ 2. Create a branch for your backport: `git checkout -b my_great_bug_backport`
+ 3. Cherry Pick the SHA with the fix: `git cherry-pick SHA`
+ 4. Address any conflicts (if necessary)
+ 5. Push the new branch to your origin: `git push origin`
+5. Open a PR for your backport
+ 1. The PR title should be `Backport: ORIGINAL_PR_TEXT`
+ 2. The description should link to the original PR and include a description of why it needs to be backported
diff --git a/docs/dev/how_to/building_and_installing.md b/docs/dev/how_to/building_and_installing.md
new file mode 100644
index 0000000000..ecdc3cb25f
--- /dev/null
+++ b/docs/dev/how_to/building_and_installing.md
@@ -0,0 +1,46 @@
+# Building and Installing
+
+Chef Infra can be built and installed in two distinct ways:
+
+- A gem built from this repository and installed into your own Ruby installation
+- A system native package containing its own Ruby built using our Omnibus packaging system
+
+**NOTE:** As an end user, please download the [Chef Infra package](https://downloads.chef.io/chef) for managing systems, or the [Chef Workstation package](https://downloads.chef.io/chef-workstation) for authoring Chef cookbooks and administering your Chef infrastructure.
+
+We do not recommend for end users to install Chef from gems or build from source. The following instructions apply only to those developing the Chef Infra client.
+
+## Building the Gem
+
+### Prerequisite
+
+- git
+- C compiler, header files, etc.
+- Ruby 2.6 or later
+
+**NOTE:** Chef supports a large number of platforms, and there are many different ways to manage Ruby installs on each of those platforms. We assume you will install Ruby in a way appropriate for your development platform, but do not provide instructions for setting up Ruby.
+
+### Building / Installing
+
+Once you have your development environment configured, you can clone the Chef repository and install Chef:
+
+```bash
+git clone https://github.com/chef/chef.git
+cd chef
+bundle install
+bundle exec rake install
+```
+
+## Building the Full System Native Package
+
+To build Chef as a standalone package, we use the [omnibus](omnibus/README.md) packaging 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 in this repository. See the [Omnibus project repository](https://github.com/chef/omnibus) for additional details.
diff --git a/docs/dev/how_to/bumping_minor_or_major_versions.md b/docs/dev/how_to/bumping_minor_or_major_versions.md
new file mode 100644
index 0000000000..79cfa9075e
--- /dev/null
+++ b/docs/dev/how_to/bumping_minor_or_major_versions.md
@@ -0,0 +1,16 @@
+# How To Bump Minor / Major Versions
+
+## When to Bump Versions
+
+After performing the monthly minor release of Chef, we should wait several days, and then bump the version for the next month's release. Why wait? We don't want to bump the version until we are sure we do not need to perform an emergency release for a regression. Once we're fairly confident we won't be performing a regression release, we want all new builds for the current channel to have the next month's version. This makes it very clear what version of Chef users are testing within the current channel.
+
+Bumping for the yearly major release is a bit different. We can bump for the new major release once we create a stable branch for the current major version number. Once this branch and version bump occurs, all development on master will be for the upcoming major release, and anything going into the stable release will need to be backported. See [Branching and Backporting](branching_and_backporting.md) for more information on how we branch and backport to stable.
+
+See [Bumping The Major Version](bumping_the_major_version.md) for the major version bump process.
+
+### How to Bump
+
+Chef's Expeditor tool includes functionality to bump the minor or major version of the product. By using Expeditor to bump versions, we can perform a bump without directly editing the version files. A version bump is performed when a PR is merged with either of these two labels applied:
+
+- Expeditor: Bump Version Minor
+- Expeditor: Bump Version Major
diff --git a/docs/dev/how_to/bumping_the_major_version.md b/docs/dev/how_to/bumping_the_major_version.md
new file mode 100644
index 0000000000..7247b3d470
--- /dev/null
+++ b/docs/dev/how_to/bumping_the_major_version.md
@@ -0,0 +1,61 @@
+# Bumping Major Version Number
+
+This document outlines the process for bumping the major version of Chef Infra Client for the yearly release.
+
+## Preparing Ohai
+
+Chef consumes Ohai from GitHub as both a runtime dependency and a testing dependency for Test Kitchen validations in Buildkite. Ohai's version is tightly coupled to that of Chef Infra Client so the first step in bumping the major release in the chef/chef repo is to bump the major version of Ohai.
+
+### Create a new stable branch of Ohai
+
+1. Edit the Expeditor config for the new branch, which you'll create shortly:
+
+ - Example config change commit: https://github.com/chef/ohai/commit/1ad8c5946606a7f08ffb841e3682ae2d4991077f
+
+2. On your local machine fork the current master branch to a new stable branch. For example: `git checkout -b 16-stable`.
+
+3. Push the branch `git push --set-upstream origin 16-stable`
+
+### Bump Ohai master to the new major version
+
+Starting from the master branch create a PR which:
+
+- Edits the `VERSION` file in the root of the repository to the new major release
+- Updates the `chef-config` and `chef-utils` dependencies to allow for the new major release of Chef Infra in `ohai.gemspec`
+- Update the `chef-config` and `chef-utils` deps in the Gemfile to point the the yet to be created chef-XYZ stable branch in the `chef/chef` repo. Note: This is going to fail for now. We have to start somewhere.
+
+## Update chef/chef
+
+### Prep master branch for forking
+
+- In ./expeditor/config.yml add the version_constraint for the new branch, update the version_constraint for master to match the new planned major version and add a constraint for the new stable version / branch
+
+### Fork Chef master to a stable branch
+
+Before bumping the major version of Chef Infra we want to fork off the current master to a new stable branch, which will be used to build hotfix releases. We support the N-1 version of Chef Infra Client for a year after the release of a new major version. For example Chef Infra Client 16 was released in April 2020, at which point Chef Infra Client 15 became the N-1 release. Chef Infra Client 15 will then be maintained with critical bug and security fixes until April 2021.
+
+On your local machine fork the current master branch to a new stable branch. For example: `git checkout -b chef-15` and `git push --set-upstream origin chef-15`, which can then be pushed up to GitHub.
+
+### Create a branch to fixup your new stable branch for release
+
+Once you've forked to a new stable branch such as `chef-15` you'll want to create a new branch so you can build a PR, which will get this branch ready for release:
+
+- In ./expeditor/config.yml remove all the update_dep.sh subscriptions which don't work against stable branches.
+- In readme.md update the buildkite badge to point to the new stable branch image and link instead of pointing to master.
+- In kitchen-tests/Gemfile update the Ohai branch to point to the new Ohai stable
+- In kitchen-tests/kitchen.yml update chef_version to be your new stable version and not current. Ex: 15
+- In tasks/bin/run_external_test update the ohai branch to point to your new stable ohai branch
+- In Gemfile set ohai to pull from the ohai stable branch
+- Run `rake dependencies:update`
+
+Example PR for Chef 15: https://github.com/chef/chef/pull/9236
+
+Note: Make sure you're making this PR against the new stable branch and not master!
+
+## Bump master for the new major release
+
+Create a PR that performs the following:
+
+- Update the version in the VERSION file
+- Update chef.gemspec to point to the new ohai major release
+- run `rake dependencies:update`
diff --git a/docs/dev/how_to/releasing_chef_infra.md b/docs/dev/how_to/releasing_chef_infra.md
new file mode 100644
index 0000000000..b0db85432e
--- /dev/null
+++ b/docs/dev/how_to/releasing_chef_infra.md
@@ -0,0 +1,69 @@
+# Releasing Chef Infra
+
+## Steps to validate that we are ready to ship
+
+ 1. Has the version number been bumped for releasing? This can be done by merging in a PR that has the "Expeditor: Bump Version Minor" label applied.
+ 2. Are there any outstanding community PRs that should be merged? Ideally we don't make a community member wait multiple months for a code contribution to ship. Merge anything that's been reviewed.
+ 3. Are new resource "introduced" fields using the correct version? From time to time, we incorrectly merge a PR that has the wrong "introduced" version in a new resource or new resource property. If we added new resources or properties, make sure these fields match the version we are about to ship.
+ 4. Have any changes in Ohai been shipped to rubygems?
+ 5. Do we have a build in the `current` channel? If not, you might wanna fix that.
+
+## Prepare the Release
+
+### Write the release notes
+
+The importance of our release notes cannot be understated. As developers, we understand changes between releases and we are accustomed to reading git history. Users are not, and if we don't call out new functionality, they will not find it on their own. We need to take the time and effort to write quality release notes that give a compelling reason to upgrade and also properly warn of any potential breaking changes. Make sure to involve the docs team so we can make sure our English is legible.
+
+#### Overall Release Notes Structure
+
+1. `Major new features`: Document new features with a high level bullet. This is a great opportunity to show off our work and sell users on new workflows.
+2. `Updated InSpec Releases`: We should always call out the updated Chef InSpec release and include a description of new functionality.
+3. `New Resources`: If we ship new resources, we want to make sure to brag about those resources. Use this section to give the elevator pitch for the new resource, including an example of how it might be used if available.
+4. `Updated Resources`: It's important to let users know about new functionality in resources they may already be using. Cover any important bug fixes or new properties/actions here.
+5. `Security Updates`: Call out any updated components we are shipping and include links to the CVEs if available.
+
+### Update the Docs Site
+
+If there are any new or updated resources, the docs site will need to be updated. This is a `partially` automated process. If you are making a single resource update or changing wording, it may just be easier to do it by hand.
+
+#### Resource Documentation Automation
+
+1. Run `rake docs_site:resources` to generate content to a `docs_site` directory
+2. Compare the relevant generated files to the content in the `content/resources` directory within the [chef-web-docs repo](https://github.com/chef/chef-web-docs/). The generated files are missing some content, such as action descriptions, and don't have perfect formatting, so this is a bit of an art form.
+
+## Release Chef Infra Client
+
+### Promote the build
+
+Chef employees can promote a build to stable from Slack. This is done with expeditor using a chatops command in the following format:
+
+`/expeditor promote chef/chef:master 17.1.9`
+
+or for a previous release branch:
+
+`/expeditor promote chef/chef:chef-16 16.13.9`
+
+### Announce the Build
+
+We want to make sure to announce the build on Discourse. It is helpful that these announcements come from real people because people like people and not machines. You can copy a previous release announcement, and change the version numbers and release notes content.
+
+Also, make sure to announce the build on any social media platforms that you occupy if you feel comfortable doing so. It's great to make an announcement in `#sous-chefs` and `#general` in Community Slack, as well as on Twitter, where we tend to get a good response.
+
+### Update homebrew Content
+
+Many of our users consume Chef via Homebrew using our casks. Expeditor will create a pull request to update the Chef Homebrew cask, which will need to be merged here: https://github.com/chef/homebrew-chef
+
+### Update Chocolatey Packages
+
+Many Windows users consume our packages via Chocolatey. Make sure to update the various version strings and sha checksums here: https://github.com/chef/chocolatey-packages
+
+Once this is updated, you'll need to build / push the artifact to the Chocolatey site from a Windows host:
+
+ 1. `choco pack .\chef-client\chef-client.nuspec`
+ 2. `choco push .\chef-client.15.1.9.nupkg --key API_KEY_HERE`
+
+Note: In order to push the artifact, you will need to be added as a maintainer on [Chocolatey.org](https://chocolatey.org/).
+
+### Relax
+
+You're done. You have a month to relax.
diff --git a/docs/dev/how_to/running_adhoc_builds.md b/docs/dev/how_to/running_adhoc_builds.md
new file mode 100644
index 0000000000..ec05994502
--- /dev/null
+++ b/docs/dev/how_to/running_adhoc_builds.md
@@ -0,0 +1,45 @@
+# Running Buildkite Ad Hoc Builds
+
+Buildkite ad hoc builds are a great way to ensure that complex changes or changes that impact package creation will build across all supported platforms.
+
+## What's wrong with just PR testing
+
+The testing of Chef Infra Client occurs at the PR level and the build level, each incrementally improving developer confidence in the safety of a change.
+
+Pull Request testing in GitHub is our first layer of defense but is far from perfect. It's optimized for speed and to work within the constraints of GitHub and public CI instances. This means this testing occurs against Ruby containers and no builds are created. Any change to build dependencies or configuration untested at the PR level.
+
+Build testing occurs post-merge and involves the creation of packages for the platforms we support and the execution of RSpec tests using those packages. Since these RSpec tests are performed within the packages, we test against the dependencies we ship to the customer.
+
+When making changes that impact the package you always want to validate the build process by running an ad hoc Buildkite build. Ad hoc builds take the same path as merged changes but without the need to merge code.
+
+## Starting the Build
+
+Ad hoc Buildkite builds can be created against any branch in the `chef/chef` GitHub repository by Chef employees with access to the private Buildkite account. If you don't have access to this account or the ability to push branches to `chef/chef` ask in `#releng-support`.
+
+Buildkite pipelines for running ad hoc builds are automatically created for each branch configured in the Expeditor `config.yml` file. They follow a standard naming system:
+
+### Master
+
+https://buildkite.com/chef/chef-chef-master-omnibus-adhoc/
+
+### Chef 16
+
+https://buildkite.com/chef/chef-chef-chef-16-omnibus-adhoc/
+
+Once in the ad hoc pipeline page, select "New Build" in the upper right hand corner. Then provide a message to display in the Buildkite UI, the commit, and branch name.
+
+## Downloading the Build
+
+Once your build completes you can download it from either Artifactory or via mixlib-install.
+
+### Artifactory
+
+Builds are available in Artifactory at http://artifactory.chef.co/ui/builds/chef
+
+### mixlib-install
+
+Successful ad hoc builds are promoted to the unstable channel and can be downloaded using mixlib-install (part of Chef Workstation)
+
+`mixlib-install download chef -c unstable`
+
+Keep in mind this just downloads the last build so you may end up the wrong build if the ad hoc isn't the last build to promote to unstable.
diff --git a/docs/dev/how_to/updating_dependencies.md b/docs/dev/how_to/updating_dependencies.md
new file mode 100644
index 0000000000..638d409e4c
--- /dev/null
+++ b/docs/dev/how_to/updating_dependencies.md
@@ -0,0 +1,13 @@
+# 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:
+
+* [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 order to update everything, run `rake dependencies`. Note that the [Gemfile.lock](Gemfile.lock) pins Windows platform gems, 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 x64-mingw32 x86-mingw32
+```
diff --git a/docs/dev/how_to/upgrading_from_chef_12.md b/docs/dev/how_to/upgrading_from_chef_12.md
new file mode 100644
index 0000000000..50dccaad2d
--- /dev/null
+++ b/docs/dev/how_to/upgrading_from_chef_12.md
@@ -0,0 +1,239 @@
+---
+title: Upgrading From Chef 12
+---
+
+# Purpose
+
+This is not a general guide on how to upgrade from Chef Infra 12. There already exists documentation on:
+
+* [How to upgrade from the command line](https://docs.chef.io/upgrade_client/)
+* [How to use the `chef_client_updater` cookbook](https://supermarket.chef.io/cookbooks/chef_client_updater)
+* [All of the possible Deprecations and remediation steps](https://docs.chef.io/chef_deprecations_client/)
+
+This is strictly expert-level documentation on what the large scale focus should be and what kind of otherwise
+undocumented gotchas can occur.
+
+# Deprecations
+
+In order to see all deprecation warnings, users must upgrade through every major version and must run the
+last version of chef-client for each major version.
+
+That means that a user on Chef Infra 12.11.18 must first upgrade to Chef Infra 12.21.31, then to Chef Infra 13.12.14, then
+(as of this writing) to Chef Infra 14.13.11 before upgrading to the latest Chef Infra 15.
+
+It is always the rule that the prior minor version of Chef Infra Client has all the deprecation warnings that are necessary
+to be addressed for the next major version. Once we begin development on the next major version of the Client we delete
+all the code and all the deprecation warnings. Old cookbook code can no longer receive warnings, it will simply wind up
+on new code paths and fail hard. The old Chef Infra Client code which issued the deprecation warning has necessarily been
+removed as part of cleaning up and changing to the new behavior. This makes it impossible to skip versions from
+Chef Infra Client 12 directly to 14 and still address all the deprecations.
+
+It is not necessary to upgrade the entire production environment to those versions of Chef Infra Client, but
+test-kitchen must be run on those versions of Chef Infra Client, and the deprecations must be fixed in all the
+cookbooks.
+
+The `treat_deprecation_warnings_as_errors` flag to the test-kitchen provisioner may be useful to accomplish this:
+
+```
+provisioner:
+ name: chef_zero
+ client.rb:
+ treat_deprecation_warnings_as_errors: true
+```
+
+# CHEF-3694 Deprecation Warnings
+
+An very notable exception to the above rule that all deprecation warnings must be addressed is the old CHEF-3694
+deprecation warnings.
+
+Not all of these warnings must be fixed. It was not possible to determine in code which of them were important to fix
+and which of them could be ignored. In actual fact most of them can be ignored without any impact.
+
+The only way to test which ones need fixing, though, is to run the cookbooks through Chef Infra 13 or later and test
+the behavior. If the cookbooks work, then the warnings can be ignored. All the deprecation warnings do in this case
+are warn that there might some risk of behavior change on upgrade.
+
+The technical details of the issue is that with resource cloning the properties of resources that are declared multiple
+times is that the properties of the prior research are merged with the newly declared properties and that becomes
+the resource. That accumulated state goes away when upgrading from Chef Infra 12. The problem is that determining if
+that state was important or not to the specific resource being merged requires knowledge of the semantic meaning of
+the properties being merged. They may be important or they may not, and it requires the user to make that
+determination. In most cases the merged resources are fairly trivial and the merged properties do not substantially
+change any behavior that is meaningful and the cookbooks will still work correctly.
+
+To ignore these errors while still treating deprecations as error you can use the `silence_deprecation_warnings` config
+in test-kitchen:
+
+```
+provisioner:
+ name: chef_zero
+ client.rb:
+ treat_deprecation_warnings_as_errors: true
+ silence_deprecation_warnings:
+ - chef-3694
+```
+
+# Converting to Custom Resource Style is Unnecessary
+
+Chef Infra 15 largely supports the same resource styles as Chef Infra 11 did. It is not necessary to convert all providers files
+or all library-resources to the fused style with the actions declared directly in the resources file. That style is
+*preferable* to older ways of writing resources, but it should never be a blocker to getting off of unsupported Chef Infra 12.
+Upgrading should always take priority over code cleanup.
+
+There are a few necessary changes to resources which need to occur, but minimal changes should be required.
+
+# All Providers or Custom Resources should declare `use_inline_resources` before upgrading.
+
+Introduced in Chef Infra 11.0 this became the way to write providers in Chef Infra 13.0. Existing Chef Infra 12 code should always declare
+`use_inline_resources` and should be run through test-kitchen and deployed in preparation for upgrading.
+
+The only problem with introducing this change would be resources which expect to be able to modify the resources declared
+in outer scopes. Another name for this is the `accumulator` pattern of writing chef resources. Those kinds of resources
+will break once they are placed in the sub-`run_context` that `use_inline_resources` creates.
+
+Those kinds of resources should use the `with_run_context :root` helper in order to access those resources in the outer
+`run_context`. The use of the `resource_collection` editing utilities `find_resource` and `edit_resource` will also
+be useful for addressing those problems.
+
+Since the vast majority of chef resources do not do this kind of editing of the resource collection, the vast majority
+of chef resources will run successfully with `use_inline_resources` declared.
+
+Once the entire infrastructure is off of Chef Infra 12 then the `use_inline_resources` lines may be deleted.
+
+# Creating a Current Resource
+
+The automatic naming of classes after the DSL name of the resource was removed in Chef Infra 13.0, as a result in order to
+implement a `load_current_resource` function the construction of the `current_resource` must change.
+
+Old code:
+
+```ruby
+def load_current_resource
+ @current_resource = Chef::Resource::MyResource.new(@new_resource.name)
+ [ ... rest of implementation ... ]
+end
+```
+
+New code:
+
+```ruby
+def load_current_resource
+ @current_resource = new_resource.class.new(@new_resource.name)
+ [ ... rest of implementation ... ]
+end
+```
+
+Resources may be rewritten instead to use `load_current_value`, but that is not required to upgrade.
+
+# Constructing Providers or Looking Up Classes
+
+The way to look up the class of a Resource:
+
+```ruby
+ @klass = Chef::Resource.resource_for_node(:package, node)
+```
+
+The way to get an instance of a Provider:
+
+```ruby
+ @klass = Chef::Resource.resource_for_node(:package, node)
+ @provider = klass.provider_for_action(:install)
+```
+
+Do not inject provider classes via the `provider` method on a resource. It should not be necessary to get the class of
+the provider for injecting in a `provider` method on a resource.
+
+This code is brittle as of Chef Infra 12 and becomes worse in Chef Infra 13 and beyond:
+
+```ruby
+ package_class = if should_do_local?
+ Chef::Provider::Package::Rpm
+ else
+ Chef::Provider::Package::Yum
+ end
+
+ package "lsof" do
+ provider package_class
+ action :upgrade
+ end
+```
+
+This actually constructs a `Chef::Resource::YumPackage` resource wrapping a `Chef::Provider::Package::Rpm` object if
+`should_do_local?` is true, which is not a consistent set of objects and will ultimately cause bugs and problems.
+
+The correct way to accomplish this is by dynamically constructing the correct kind of resource (ultimately via the
+`Chef::ResourceResolver`) rather than manually trying to construct a hybrid object:
+
+```ruby
+ package_resource = if should_do_local?
+ :rpm_package
+ else
+ :yum_package
+ end
+
+ declare_resource(package_resource, "lsof") do
+ action :upgrade
+ end
+```
+
+This section is an uncommon need. Very few cookbooks should be dynamically declaring providers. The requirement to look up
+resource classes and provider instances would likely also only occur for sites which do roll-your-own `rspec` testing of
+resources (similar to the way resources are tested in core chef) instead of `chefspec` or `test-kitchen` testing.
+
+# Notifications From Custom Resources
+
+A behavior change which occurred in Chef Infra 12.21.3 which later was recognized to potentially be breaking is that custom
+resources now have their own delayed notification phase. If it is necessary to create a resource, like a service resource,
+in a custom resource and then send a delayed notification to it which is executed at the end of the entire chef client
+run (and not at the end of the execution of the custom resource's action) then the resource needs to be declared in
+the outer "root" or "recipe" run context.
+
+This code in Chef Infra before 12.21.3 would restart the service at the end of the run:
+
+```ruby
+use_inline_resources
+
+action :doit do
+ # this creates the service resource in the run_context of the custom resource
+ service "whateverd" do
+ action :nothing
+ end
+
+ # under Chef-12 this will send a delayed notification which will run at the end of the chef-client run
+ file "/etc/whatever.d/whatever.conf" do
+ contents "something"
+ notifies :restart, "service[whateverd]", :delayed
+ end
+end
+```
+
+To preserve this exact behavior in version of Chef Infra Client of 12.21.3 or later:
+
+```ruby
+use_inline_resources
+
+action :doit do
+ # this creates the resource in the outermost run_context using find_resource's API which is
+ # is "find-or-create" (use edit_resource with a block to get "create-or-update" semantics).
+ with_run_context :root do
+ find_resource(:service, "whateverd") do
+ action :nothing
+ end
+ end
+
+ # this will now send a notification to the outer run context and will restart the service
+ # at the very end of the chef client run
+ file "/etc/whatever.d/whatever.conf" do
+ contents "something"
+ notifies :restart, "service[whateverd]", :delayed
+ end
+end
+```
+
+This behavior is not backwards compatible, and the code which executes properly on 12.21.3 will not run properly on
+version before that, and vice versa. There are no deprecation warnings for this behavior, it was not anticipated at
+the time that this would produce any backwards incompatibility and by the time it was understood there was no way
+to retroactively change behavior or add any warnings. It is not a common issue. The new behavior is more typically
+preferable since it asserts that the once the configuration for the service is updated that the service is restarted
+by the delayed action which occurs at the end of the custom resource's action and then future recipes can rely on
+the service being in a correctly running state.
diff --git a/docs/dev/license_acceptance.md b/docs/dev/license_acceptance.md
new file mode 100644
index 0000000000..bb6e8a4e07
--- /dev/null
+++ b/docs/dev/license_acceptance.md
@@ -0,0 +1,13 @@
+# License Acceptance
+
+Starting with Chef Client 15 users are required to accept the [Chef
+EULA](https://www.chef.io/end-user-license-agreement/) to use the Chef Software distribution. This document aims to
+explain how the `license-acceptance` gem and the `chef` gem interact.
+
+The overall goal is that the license acceptance flow is invoked as early as possible in the binary (EG, `chef-client`)
+execution. Failure to accept the license causes the binary to immediately exit with code `172`.
+
+For an explanation of how this is achieved please see the [Ruby
+README](https://github.com/chef/license-acceptance/tree/master/components/ruby) in the license-acceptance repo. For an
+overall view of how the license-acceptance gem works, its specification, how marker files are stored, etc. please see
+the [repo README](https://github.com/chef/license-acceptance).
diff --git a/docs/dev/policy/release_and_support_schedule.md b/docs/dev/policy/release_and_support_schedule.md
new file mode 100644
index 0000000000..c3d8ea531b
--- /dev/null
+++ b/docs/dev/policy/release_and_support_schedule.md
@@ -0,0 +1,77 @@
+# Chef Infra Release and Support Schedule
+
+## Versioning Scheme
+
+Chef Infra releases follow a `MAJOR.MINOR.PATCH` versioning scheme based on [Semantic Versioning](https://semver.org).
+
+Given a version number `MAJOR.MINOR.PATCH`:
+
+ * **MAJOR** version releases (e.g. 15.x -> 16.x) will include breaking or backwards-incompatible changes.
+ * _Example: When changing the load order of any cookbook segments_
+ * **MINOR** version releases (e.g. 15.1 -> 15.2) will include new features, bug fixes, and will be backwards-compatible to the best of the maintainers' abilities.
+ * _Example: When adding support to the mount provider for special filesystem types that were previously unsupported._
+ * _Example: Major version bump of a software dependency._
+ * **PATCH** version releases (e.g. 15.1.1 -> 15.1.2) will include backwards-compatible bug fixes.
+ * _Example: Minor version bump of a software dependency._
+
+When incrementing a version, the following conditions will apply:
+
+ * When **MAJOR** increases, **MINOR** and **PATCH** will be reset to zero (e.g. 11.X.X -> 12.0.0)
+ * _Note: New features that did not exist in version 1.1.0 may be released in 2.0.0 without any intermediary releases._
+ * When **MINOR** increases, **PATCH** will be reset to zero (e.g. 11.3.x -> 11.4.0)
+
+### Auto-bumping PATCH versions
+
+Chef projects are managed by our Expeditor release tooling application. This application is executed each time a GitHub Pull Request is merged and increments the patch version of the software before running the change through our internal CI/CD pipeline. As not all builds will make it successfully through the CI/CD pipeline, the versions available for public consumption might have gaps (e.g. 1.2.1, 1.2.10, 1.2.11, 1.2.12, 1.2.20), but all versions have been built and tested.
+
+## Support Schedule
+
+Chef currently supports the current **major version** release as well as the previous **major version** release. These releases each fall into one of three distinct lifecycle stages:
+
+ - **Generally Available (GA)**
+ - **Deprecated**
+ - **End of Life (EOL)**
+
+### Generally Available (GA)
+
+This stage indicates that the release version is in active development.
+
+ - Releases occur per our regular release schedule
+ - New features as well as bug fixes ship in each new release
+ - When a new major version release of Chef Infra ships it becomes the new GA release and the previous release moves to the Deprecated stage
+
+### Deprecated
+
+This stage indicates that a release version is no longer in active development and will eventually move to end of life status.
+
+ - Releases do not follow our regular release schedule, but instead only occur as necessary for critical bugs or security vulnerabilities.
+ - After a year releases transition from Deprecated to End of Life status when a new major release is made.
+
+### End of Life (EOL)
+
+This stage indicates a previously deprecated release version, which is no longer supported
+
+ - No additional releases will be made
+ - Documentation will be archived
+ - Cookbooks and other community tooling may no longer function using this version of Chef Infra
+
+## Release Schedule
+
+### Generally Available Release Schedule
+
+ - **Minor releases**: 2nd week of each month
+ - **Major releases**: Once a year in April
+
+### Deprecated Release Schedule
+
+ - Adhoc released for critical bugs and security vulnerabilities only
+
+## Release Announcements
+
+Each release will be announced to the "chef-release" [Chef Mailing List](https://discourse.chef.io) category, notifying users of the new stable release.
+
+### Example Release / Support Cycle
+
+April 2019: Chef 15.0 released as GA
+April 2020: Chef 15 becomes Deprecated when Chef 16 ships
+April 2021: Chef 15 becomes End of Life when Chef 17 ships
diff --git a/ext/win32-eventlog/Rakefile b/ext/win32-eventlog/Rakefile
index 5b1186cf13..6addd36091 100644
--- a/ext/win32-eventlog/Rakefile
+++ b/ext/win32-eventlog/Rakefile
@@ -1,6 +1,8 @@
require "rubygems"
require "rake"
require "mkmf"
+require "erb"
+require "chef-utils/dist"
desc "Building event log dll"
@@ -12,18 +14,24 @@ def ensure_present(commands)
end
end
-EVT_MC_FILE = "chef-log.man"
-EVT_RC_FILE = "chef-log.rc"
-EVT_RESOURCE_OBJECT = "resource.o"
-EVT_SHARED_OBJECT = "chef-log.dll"
-MC = "windmc"
-RC = "windres"
-CC = "gcc"
+# Templating the windows event log messages
+# So we can inject distro constants in there
+template = ERB.new(IO.read("chef-log.man.erb"))
+chef_log_man = template.result
+File.open("chef-log.man", "w") { |f| f.write(chef_log_man) }
+
+EVT_MC_FILE = "chef-log.man".freeze
+EVT_RC_FILE = "chef-log.rc".freeze
+EVT_RESOURCE_OBJECT = "resource.o".freeze
+EVT_SHARED_OBJECT = "chef-log.dll".freeze
+MC = "windmc".freeze
+RC = "windres".freeze
+CC = "gcc".freeze
ensure_present [MC, RC, CC]
-task :build => [EVT_RESOURCE_OBJECT, EVT_SHARED_OBJECT]
-task :default => [:build, :register]
+task build: [EVT_RESOURCE_OBJECT, EVT_SHARED_OBJECT]
+task default: %i{build register}
file EVT_RC_FILE => EVT_MC_FILE do
sh "#{MC} #{EVT_MC_FILE}"
@@ -37,15 +45,15 @@ file EVT_SHARED_OBJECT => EVT_RESOURCE_OBJECT do
sh "#{CC} -o #{EVT_SHARED_OBJECT} -shared #{EVT_RESOURCE_OBJECT}"
end
-task :register => EVT_SHARED_OBJECT do
+task register: EVT_SHARED_OBJECT do
require "win32/eventlog"
dll_file = File.expand_path(EVT_SHARED_OBJECT)
begin
Win32::EventLog.add_event_source(
- :source => "Application",
- :key_name => "Chef",
- :event_message_file => dll_file,
- :category_message_file => dll_file
+ source: "Application",
+ key_name: ChefUtils::Dist::Infra::SHORT,
+ event_message_file: dll_file,
+ category_message_file: dll_file
)
rescue Errno::EIO => e
puts "Skipping event log registration due to missing privileges: #{e}"
diff --git a/ext/win32-eventlog/chef-log.man b/ext/win32-eventlog/chef-log.man
deleted file mode 100644
index 10c28e739f..0000000000
--- a/ext/win32-eventlog/chef-log.man
+++ /dev/null
@@ -1,56 +0,0 @@
-MessageId=10000
-SymbolicName=RUN_START
-Language=English
-Starting Chef Client run v%1
-.
-
-MessageId=10001
-SymbolicName=RUN_STARTED
-Language=English
-Started Chef Client run %1
-.
-
-MessageId=10002
-SymbolicName=RUN_COMPLETED
-Language=English
-Completed Chef Client run %1 in %2 seconds
-.
-
-MessageId=10003
-SymbolicName=RUN_FAILED
-Language=English
-Failed Chef Client run %1 in %2 seconds.%n
-Exception type: %3%n
-Exception message: %4%n
-Exception backtrace: %5%n
-.
-
-MessageId=10100
-SymbolicName=INFO
-Language=English
-[INFO] %1
-.
-
-MessageId=10101
-SymbolicName=WARN
-Language=English
-[WARN] %1
-.
-
-MessageId=10102
-SymbolicName=DEBUG
-Language=English
-[DEBUG] %1
-.
-
-MessageId=10103
-SymbolicName=ERROR
-Language=English
-[ERROR] %1
-.
-
-MessageId=10104
-SymbolicName=FATAL
-Language=English
-[FATAL] %1
-.
diff --git a/ext/win32-eventlog/chef-log.man.erb b/ext/win32-eventlog/chef-log.man.erb
new file mode 100644
index 0000000000..83be49b4d2
--- /dev/null
+++ b/ext/win32-eventlog/chef-log.man.erb
@@ -0,0 +1,56 @@
+MessageId=10000
+SymbolicName=RUN_START
+Language=English
+Starting <%= ChefUtils::Dist::Infra::PRODUCT %> run v%1
+.
+
+MessageId=10001
+SymbolicName=RUN_STARTED
+Language=English
+Started <%= ChefUtils::Dist::Infra::PRODUCT %> run %1
+.
+
+MessageId=10002
+SymbolicName=RUN_COMPLETED
+Language=English
+Completed <%= ChefUtils::Dist::Infra::PRODUCT %> run %1 in %2 seconds
+.
+
+MessageId=10003
+SymbolicName=RUN_FAILED
+Language=English
+Failed <%= ChefUtils::Dist::Infra::PRODUCT %> run %1 in %2 seconds.%n
+Exception type: %3%n
+Exception message: %4%n
+Exception backtrace: %5%n
+.
+
+MessageId=10100
+SymbolicName=INFO
+Language=English
+[INFO] %1
+.
+
+MessageId=10101
+SymbolicName=WARN
+Language=English
+[WARN] %1
+.
+
+MessageId=10102
+SymbolicName=DEBUG
+Language=English
+[DEBUG] %1
+.
+
+MessageId=10103
+SymbolicName=ERROR
+Language=English
+[ERROR] %1
+.
+
+MessageId=10104
+SymbolicName=FATAL
+Language=English
+[FATAL] %1
+.
diff --git a/habitat/config/client.rb b/habitat/config/client.rb
new file mode 100644
index 0000000000..3dd6d67e1b
--- /dev/null
+++ b/habitat/config/client.rb
@@ -0,0 +1,19 @@
+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}}"
+local_mode "{{cfg.local_mode}}"
+{{#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..3b002f1499
--- /dev/null
+++ b/habitat/default.toml
@@ -0,0 +1,42 @@
+pid_file = "chef.pid"
+run_list = ""
+
+local_mode = true
+
+# Positive License Acceptance. See https://docs.chef.io/chef_license_accept/
+chef_license = "donotaccept"
+
+# Path to the chef client config on disk. If blank, will default to the one built by habitat.
+config_path = ""
+
+# 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-client run.
+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..598d609d7f
--- /dev/null
+++ b/habitat/hooks/run
@@ -0,0 +1,16 @@
+#!/bin/sh
+exec 2>&1
+
+export SSL_CERT_FILE="{{pkgPathFor "core/cacerts"}}/ssl/cert.pem"
+
+if [[ -z "{{cfg.config_path}}" ]]; then
+ CLIENT_CONFIG="{{pkg.svc_config_path}}"
+else
+ CLIENT_CONFIG="{{cfg.config_path}}"
+fi
+
+if [[ "${CLIENT_CONFIG##*.}" != "rb" ]]; then
+ CLIENT_CONFIG=${CLIENT_CONFIG}/client.rb
+fi
+
+exec chef-client --fork -c ${CLIENT_CONFIG} --chef-license {{cfg.chef_license}}
diff --git a/habitat/plan.ps1 b/habitat/plan.ps1
new file mode 100644
index 0000000000..8269e436a5
--- /dev/null
+++ b/habitat/plan.ps1
@@ -0,0 +1,151 @@
+$pkg_name="chef-infra-client"
+$pkg_origin="chef"
+$pkg_version=(Get-Content $PLAN_CONTEXT/../VERSION)
+$pkg_description="Chef Infra Client is an agent that runs locally on every node that is under management by Chef Infra. This package is binary-only to provide Chef Infra Client executables. It does not define a service to run."
+$pkg_maintainer="The Chef Maintainers <maintainers@chef.io>"
+$pkg_upstream_url="https://github.com/chef/chef"
+$pkg_license=@("Apache-2.0")
+$pkg_filename="${pkg_name}-${pkg_version}.zip"
+$pkg_bin_dirs=@(
+ "bin"
+ "vendor/bin"
+)
+$pkg_deps=@(
+ "core/cacerts"
+ "chef/ruby27-plus-devkit"
+ "chef/chef-powershell-shim"
+)
+
+function Invoke-Begin {
+ [Version]$hab_version = (hab --version).split(" ")[1].split("/")[0]
+ if ($hab_version -lt [Version]"0.85.0" ) {
+ Write-Warning "(╯°□°)╯︵ ┻━┻ I CAN'T WORK UNDER THESE CONDITIONS!"
+ Write-Warning ":habicat: I'm being built with $hab_version. I need at least Hab 0.85.0, because I use the -IsPath option for setting/pushing paths in SetupEnvironment."
+ throw "unable to build: required minimum version of Habitat not installed"
+ } else {
+ Write-BuildLine ":habicat: I think I have the version I need to build."
+ }
+}
+
+function Invoke-SetupEnvironment {
+ Push-RuntimeEnv -IsPath GEM_PATH "$pkg_prefix/vendor"
+
+ Set-RuntimeEnv APPBUNDLER_ALLOW_RVM "true" # prevent appbundler from clearing out the carefully constructed runtime GEM_PATH
+ Set-RuntimeEnv FORCE_FFI_YAJL "ext" # Always use the C-extensions because we use MRI on all the things and C is fast.
+ Set-RuntimeEnv -IsPath SSL_CERT_FILE "$(Get-HabPackagePath cacerts)/ssl/cert.pem"
+ Set-RuntimeEnv LANG "en_US.UTF-8"
+ Set-RuntimeEnv LC_CTYPE "en_US.UTF-8"
+}
+
+function Invoke-Download() {
+ Write-BuildLine " ** Locally creating archive of latest repository commit at ${HAB_CACHE_SRC_PATH}/${pkg_filename}"
+ # 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
+ try {
+ Push-Location (Resolve-Path "$PLAN_CONTEXT/../").Path
+ git archive --format=zip --output="${HAB_CACHE_SRC_PATH}/${pkg_filename}" HEAD
+ if (-not $?) { throw "unable to create archive of source" }
+ } finally {
+ Pop-Location
+ }
+}
+
+function Invoke-Verify() {
+ Write-BuildLine " ** Skipping checksum verification on the archive we just created."
+ return 0
+}
+
+function Invoke-Prepare {
+ $env:GEM_HOME = "$pkg_prefix/vendor"
+
+ try {
+ Push-Location "${HAB_CACHE_SRC_PATH}/${pkg_dirname}"
+
+ Write-BuildLine " ** Configuring bundler for this build environment"
+ bundle config --local without server docgen maintenance pry travis integration ci chefstyle
+ if (-not $?) { throw "unable to configure bundler to restrict gems to be installed" }
+ bundle config --local jobs 4
+ bundle config --local retry 5
+ bundle config --local silence_root_warning 1
+ } finally {
+ Pop-Location
+ }
+}
+
+function Invoke-Build {
+ try {
+ Push-Location "${HAB_CACHE_SRC_PATH}/${pkg_dirname}"
+
+ $env:_BUNDER_WINDOWS_DLLS_COPIED = "1"
+
+ Write-BuildLine " ** Using bundler to retrieve the Ruby dependencies"
+ bundle install --jobs=3 --retry=3
+ if (-not $?) { throw "unable to install gem dependencies" }
+ Write-BuildLine " ** 'rake install' any gem sourced as a git reference so they'll look like regular gems."
+ foreach($git_gem in (Get-ChildItem "$env:GEM_HOME/bundler/gems")) {
+ try {
+ Push-Location $git_gem
+ Write-BuildLine " -- installing $git_gem"
+ rake install # this needs to NOT be 'bundle exec'd else bundler complains about dev deps not being installed
+ if (-not $?) { throw "unable to install $git_gem as a plain old gem" }
+ } finally {
+ Pop-Location
+ }
+ }
+ Write-BuildLine " ** Running the chef project's 'rake install' to install the path-based gems so they look like any other installed gem."
+ bundle exec rake install # this needs to be 'bundle exec'd because a Rakefile makes reference to Bundler
+ if (-not $?) {
+ Write-Warning " -- That didn't work. Let's try again."
+ bundle exec rake install # this needs to be 'bundle exec'd because a Rakefile makes reference to Bundler
+ if (-not $?) { throw "unable to install the gems that live in directories within this repo" }
+ }
+ } finally {
+ Pop-Location
+ }
+}
+
+function Invoke-Install {
+ try {
+ Push-Location $pkg_prefix
+ $env:BUNDLE_GEMFILE="${HAB_CACHE_SRC_PATH}/${pkg_dirname}/Gemfile"
+
+ foreach($gem in ("chef-bin", "chef", "inspec-core-bin", "ohai")) {
+ Write-BuildLine "** generating binstubs for $gem with precise version pins"
+ appbundler.bat "${HAB_CACHE_SRC_PATH}/${pkg_dirname}" $pkg_prefix/bin $gem
+ if (-not $?) { throw "Failed to create appbundled binstubs for $gem"}
+ }
+ Remove-StudioPathFrom -File $pkg_prefix/vendor/gems/chef-$pkg_version*/Gemfile
+ } finally {
+ Pop-Location
+ }
+}
+
+function Invoke-After {
+ # Trim the fat before packaging
+
+ # We don't need the cache of downloaded .gem files ...
+ Remove-Item $pkg_prefix/vendor/cache -Recurse -Force
+ # ... or bundler's cache of git-ref'd gems
+ Remove-Item $pkg_prefix/vendor/bundler -Recurse -Force
+
+ # We don't need the gem docs.
+ Remove-Item $pkg_prefix/vendor/doc -Recurse -Force
+ # We don't need to ship the test suites for every gem dependency,
+ # only Chef's for package verification.
+ Get-ChildItem $pkg_prefix/vendor/gems -Filter "spec" -Directory -Recurse -Depth 1 `
+ | Where-Object -FilterScript { $_.FullName -notlike "*chef-$pkg_version*" } `
+ | Remove-Item -Recurse -Force
+ # Remove the byproducts of compiling gems with extensions
+ Get-ChildItem $pkg_prefix/vendor/gems -Include @("gem_make.out", "mkmf.log", "Makefile") -File -Recurse `
+ | Remove-Item -Force
+}
+
+function Remove-StudioPathFrom {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [String]
+ $File
+ )
+ (Get-Content $File) -replace ($env:FS_ROOT -replace "\\","/"),"" | Set-Content $File
+}
diff --git a/habitat/plan.sh b/habitat/plan.sh
new file mode 100644
index 0000000000..64f424dfac
--- /dev/null
+++ b/habitat/plan.sh
@@ -0,0 +1,145 @@
+_chef_client_ruby="core/ruby27"
+pkg_name="chef-infra-client"
+pkg_origin="chef"
+pkg_maintainer="The Chef Maintainers <humans@chef.io>"
+pkg_description="The Chef Infra Client"
+pkg_license=('Apache-2.0')
+pkg_bin_dirs=(
+ bin
+ vendor/bin
+)
+pkg_build_deps=(
+ core/make
+ core/gcc
+ core/git
+)
+pkg_deps=(
+ core/glibc
+ $_chef_client_ruby
+ core/libxml2
+ core/libxslt
+ core/libiconv
+ core/xz
+ core/zlib
+ core/openssl
+ core/cacerts
+ core/libffi
+ core/coreutils
+ core/libarchive
+)
+pkg_svc_user=root
+
+pkg_version() {
+ cat "${SRC_PATH}/VERSION"
+}
+
+do_before() {
+ do_default_before
+ update_pkg_version
+ # We must wait until we update the pkg_version to use the pkg_version
+ pkg_filename="${pkg_name}-${pkg_version}.tar.gz"
+}
+
+do_download() {
+ build_line "Locally creating archive of latest repository commit at ${HAB_CACHE_SRC_PATH}/${pkg_filename}"
+ # 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 "${SRC_PATH}" || exit_with "unable to enter hab-src directory" 1
+ 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_setup_environment() {
+ push_runtime_env GEM_PATH "${pkg_prefix}/vendor"
+
+ set_runtime_env APPBUNDLER_ALLOW_RVM "true" # prevent appbundler from clearing out the carefully constructed runtime GEM_PATH
+ set_runtime_env SSL_CERT_FILE "$(pkg_path_for cacerts)/ssl/cert.pem"
+ set_runtime_env LANG "en_US.UTF-8"
+ set_runtime_env LC_CTYPE "en_US.UTF-8"
+}
+
+do_prepare() {
+ export GEM_HOME="${pkg_prefix}/vendor"
+ 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"
+ export CPPFLAGS="${CPPFLAGS} ${CFLAGS}"
+
+ ( cd "$CACHE_PATH"
+ bundle config --local build.nokogiri "--use-system-libraries \
+ --with-zlib-dir=$(pkg_path_for zlib) \
+ --with-xslt-dir=$(pkg_path_for libxslt) \
+ --with-xml2-include=$(pkg_path_for libxml2)/include/libxml2 \
+ --with-xml2-lib=$(pkg_path_for libxml2)/lib"
+ bundle config --local jobs "$(nproc)"
+ bundle config --local without server docgen maintenance pry travis integration ci chefstyle
+ bundle config --local shebang "$(pkg_path_for "$_chef_client_ruby")/bin/ruby"
+ bundle config --local retry 5
+ bundle config --local silence_root_warning 1
+ )
+
+ build_line "Setting link for /usr/bin/env to 'coreutils'"
+ if [ ! -f /usr/bin/env ]; then
+ ln -s "$(pkg_interpreter_for core/coreutils bin/env)" /usr/bin/env
+ fi
+}
+
+do_build() {
+ ( cd "$CACHE_PATH" || exit_with "unable to enter hab-cache directory" 1
+ build_line "Installing gem dependencies ..."
+ bundle install --jobs=3 --retry=3
+ build_line "Installing this project's gems ..."
+ bundle exec rake install
+ for gem in $GEM_HOME/bundler/gems/*; do
+ ( cd $gem
+ build_line "Installing gems from git repos properly ..."
+ rake install
+ )
+ done
+ )
+}
+
+do_install() {
+ ( cd "$pkg_prefix" || exit_with "unable to enter pkg prefix directory" 1
+ export BUNDLE_GEMFILE="${CACHE_PATH}/Gemfile"
+ build_line "** fixing binstub shebangs"
+ fix_interpreter "${pkg_prefix}/vendor/bin/*" "$_chef_client_ruby" bin/ruby
+ export BUNDLE_GEMFILE="${CACHE_PATH}/Gemfile"
+ for gem in chef-bin chef inspec-core-bin ohai; do
+ build_line "** generating binstubs for $gem with precise version pins"
+ appbundler $CACHE_PATH $pkg_prefix/bin $gem
+ done
+ )
+}
+
+do_after() {
+ build_line "Trimming the fat ..."
+
+ # We don't need the cache of downloaded .gem files ...
+ rm -r "$pkg_prefix/vendor/cache"
+ # ... or bundler's cache of git-ref'd gems
+ rm -r "$pkg_prefix/vendor/bundler"
+ # We don't need the gem docs.
+ rm -r "$pkg_prefix/vendor/doc"
+ # We don't need to ship the test suites for every gem dependency,
+ # only Chef's for package verification.
+ find "$pkg_prefix/vendor/gems" -name spec -type d | grep -v "chef-${pkg_version}" \
+ | while read spec_dir; do rm -r "$spec_dir"; done
+}
+
+do_end() {
+ if [ "$(readlink /usr/bin/env)" = "$(pkg_interpreter_for core/coreutils bin/env)" ]; then
+ build_line "Removing the symlink we created for '/usr/bin/env'"
+ rm /usr/bin/env
+ fi
+}
+
+do_strip() {
+ return 0
+}
diff --git a/habitat/tests/spec.ps1 b/habitat/tests/spec.ps1
new file mode 100644
index 0000000000..34b3a07beb
--- /dev/null
+++ b/habitat/tests/spec.ps1
@@ -0,0 +1,25 @@
+param (
+ [Parameter()]
+ [string]$PackageIdentifier = $(throw "Usage: test.ps1 [test_pkg_ident] e.g. test.ps1 ci/user-windows/1.0.0/20190812103929")
+)
+
+# some of the functional tests require that winrm be configured
+winrm quickconfig -quiet
+
+$chef_gem_root = (hab pkg exec $PackageIdentifier gem.cmd which chef | Split-Path | Split-Path)
+try {
+ Push-Location $chef_gem_root
+ $env:PATH = "C:\hab\bin;$env:PATH"
+
+ # Put chef's GEM_PATH in the machine environment so that the windows service
+ # tests will be able to consume the win32-service gem
+ $pkgEnv = hab pkg env $PackageIdentifier
+ $gemPath = $pkgEnv | Where-Object { $_.StartsWith("`$env:GEM_PATH=") }
+ SETX GEM_PATH $($gemPath.Split("=")[1]) /m
+
+ hab pkg binlink --force $PackageIdentifier
+ /hab/bin/rspec --tag ~executables --tag ~choco_installed spec/functional
+ if (-not $?) { throw "functional testing failed"}
+} finally {
+ Pop-Location
+}
diff --git a/habitat/tests/test.pester.ps1 b/habitat/tests/test.pester.ps1
new file mode 100644
index 0000000000..56f31e9a2f
--- /dev/null
+++ b/habitat/tests/test.pester.ps1
@@ -0,0 +1,68 @@
+param(
+ [Parameter()]
+ [string]$PackageIdentifier = $(throw "Usage: test.ps1 [test_pkg_ident] e.g. test.ps1 ci/user-windows-default/1.0.0/20190812103929")
+)
+
+$PackageVersion = $PackageIdentifier.split('/')[2]
+
+Describe "chef-infra-client" {
+ Context "chef-client" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier chef-client.bat --version
+ $? | Should be $true
+ }
+
+ <#
+ At some point hab's argument parsing changed and it started interpreting the trailing `--version` as being
+ an argument passed to hab instead of an argument to the command passed to `hab pkg exec`.
+
+ Powershell 5.1 and 7 appear to differ in how they treat following arguments as well, such that these two
+ versions of the command fail in powershell 5.1 (which is currently what is running in the windows machines
+ in Buildkite) but pass in powershell 7 (which is currently what is running in a stock Windows 10 VM).
+
+ $the_version = (hab pkg exec $PackageIdentifier chef-client.bat '--version' | Out-String).split(':')[1].Trim()
+ $the_version = (hab pkg exec $PackageIdentifier chef-client.bat --version | Out-String).split(':')[1].Trim()
+
+ This version of the command passes in powershell 5.1 but fails in powershell 7.
+ #>
+ It "is the expected version" {
+ $the_version = (hab pkg exec $PackageIdentifier chef-client.bat -- --version | Out-String).split(':')[1].Trim()
+ $the_version | Should be $PackageVersion
+ }
+ }
+
+ Context "ohai" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier ohai.bat --version
+ $? | Should be $true
+ }
+ }
+
+ Context "chef-shell" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier chef-shell.bat --version
+ $? | Should be $true
+ }
+ }
+
+ Context "chef-apply" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier chef-apply.bat --version
+ $? | Should be $true
+ }
+ }
+
+ Context "knife" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier knife.bat --version
+ $? | Should be $true
+ }
+ }
+
+ Context "chef-solo" {
+ It "is an executable" {
+ hab pkg exec $PackageIdentifier chef-solo.bat --version
+ $? | Should be $true
+ }
+ }
+}
diff --git a/habitat/tests/test.ps1 b/habitat/tests/test.ps1
new file mode 100644
index 0000000000..640507428a
--- /dev/null
+++ b/habitat/tests/test.ps1
@@ -0,0 +1,23 @@
+param (
+ [Parameter()]
+ [string]$PackageIdentifier = $(throw "Usage: test.ps1 [test_pkg_ident] e.g. test.ps1 ci/user-windows/1.0.0/20190812103929")
+)
+
+# ensure Pester is available for test use
+if (-Not (Get-Module -ListAvailable -Name Pester)){
+ hab pkg install core/pester
+ Import-Module "$(hab pkg path core/pester)\module\pester.psd1"
+}
+
+Write-Host "--- :fire: Smokish Pestering"
+# Pester the Package
+$__dir=(Get-Item $PSScriptRoot)
+$test_result = Invoke-Pester -Strict -PassThru -Script @{
+ Path = "$__dir/test.pester.ps1";
+ Parameters = @{PackageIdentifier=$PackageIdentifier}
+}
+if ($test_result.FailedCount -ne 0) { Exit $test_result.FailedCount }
+
+Write-Host "--- :alembic: Functional Tests"
+powershell -File "./habitat/tests/spec.ps1" -PackageIdentifier $PackageIdentifier
+if (-not $?) { throw "functional spec suite failed" }
diff --git a/habitat/tests/test.sh b/habitat/tests/test.sh
new file mode 100755
index 0000000000..c28ab8b2cf
--- /dev/null
+++ b/habitat/tests/test.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+export CHEF_LICENSE="accept-no-persist"
+export HAB_LICENSE="accept-no-persist"
+export HAB_NONINTERACTIVE="true"
+
+project_root="$(git rev-parse --show-toplevel)"
+pkg_ident="$1"
+
+# print error message followed by usage and exit
+error () {
+ local message="$1"
+
+ echo -e "\nERROR: ${message}\n" >&2
+
+ exit 1
+}
+
+[[ -n "$pkg_ident" ]] || error 'no hab package identity provided'
+
+package_version=$(awk -F / '{print $3}' <<<"$pkg_ident")
+
+cd "${project_root}"
+
+echo "--- :mag_right: Testing ${pkg_ident} executables"
+actual_version=$(hab pkg exec "${pkg_ident}" chef-client -- --version | sed 's/.*: //')
+[[ "$package_version" = "$actual_version" ]] || error "chef-client is not the expected version. Expected '$package_version', got '$actual_version'"
+
+for executable in 'chef-client' 'ohai' 'chef-shell' 'chef-apply' 'knife' 'chef-solo'; do
+ echo -en "\t$executable = "
+ hab pkg exec "${pkg_ident}" "${executable}" -- --version || error "${executable} failed to execute properly"
+done
+
+echo "--- :mag_right: Testing ${pkg_ident} functionality"
+hab pkg exec "${pkg_ident}" rspec --tag ~executables spec/functional || error 'failures during rspec tests'
diff --git a/kitchen-tests/.kitchen.travis.yml b/kitchen-tests/.kitchen.travis.yml
deleted file mode 100644
index 9c5854d923..0000000000
--- a/kitchen-tests/.kitchen.travis.yml
+++ /dev/null
@@ -1,118 +0,0 @@
----
-driver:
- name: dokken
- privileged: true
- chef_version: latest
-
-transport:
- name: dokken
-
-provisioner:
- name: chef_github
- 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'] %>
- 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
-
-verifier:
- name: inspec
- format: progress
-
-platforms:
-- name: debian-7
- driver:
- image: debian:7
- pid_one_command: /sbin/init
- intermediate_instructions:
- - RUN /usr/bin/apt-get update
- - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
- - RUN /bin/mkdir /var/run/sshd
-
-- name: debian-8
- driver:
- image: debian:8
- pid_one_command: /bin/systemd
- intermediate_instructions:
- - RUN /usr/bin/apt-get update
- - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
-
-- name: centos-5
- driver:
- image: centos:5
- platform: rhel
- run_command: /sbin/init
- intermediate_instructions:
- - RUN yum clean all
- - RUN yum install -y which initscripts net-tools sudo wget
- - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
-
-- name: centos-6
- driver:
- image: centos:6
- run_command: /sbin/init
- intermediate_instructions:
- - RUN yum clean all
- - RUN yum -y install which initscripts net-tools sudo wget
- - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
-
-- name: centos-7
- driver:
- image: centos:7
- pid_one_command: /usr/lib/systemd/systemd
- intermediate_instructions:
- - RUN yum clean all
- - RUN yum -y install which initscripts net-tools sudo wget
- - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
-
-- name: fedora-23
- driver:
- image: fedora:23
- pid_one_command: /usr/lib/systemd/systemd
- intermediate_instructions:
- - RUN dnf -y install yum which initscripts rpm-build zlib-devel net-tools sudo wget
- - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
-
-- name: ubuntu-12.04
- driver:
- image: ubuntu-upstart:12.04
- pid_one_command: /sbin/init
- intermediate_instructions:
- - RUN /usr/bin/apt-get update
- - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
-
-- name: ubuntu-14.04
- driver:
- image: ubuntu-upstart:14.04
- pid_one_command: /sbin/init
- intermediate_instructions:
- - RUN /usr/bin/apt-get update
- - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
-
-- name: ubuntu-16.04
- driver:
- image: ubuntu:16.04
- pid_one_command: /bin/systemd
- intermediate_instructions:
- - RUN /usr/bin/apt-get update
- - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
-
-- name: opensuse-13.2
- driver:
- image: opensuse:13.2
- pid_one_command: /bin/systemd
- intermediate_instructions:
- - RUN zypper refresh
-
-suites:
- - name: webapp
- run_list:
- - recipe[base::default]
-# - recipe[webapp::default]
diff --git a/kitchen-tests/.kitchen.yml b/kitchen-tests/.kitchen.yml
deleted file mode 100644
index 314857663e..0000000000
--- a/kitchen-tests/.kitchen.yml
+++ /dev/null
@@ -1,37 +0,0 @@
----
-driver:
- name: vagrant
- customize:
- 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"
- refname: <%= %x(git rev-parse HEAD) %>
- data_path: test/fixtures
- client_rb:
- diff_disabled: true
-
-platforms:
- - name: ubuntu-12.04
- - name: ubuntu-14.04
- - name: ubuntu-16.04
- - name: centos-7.2
- - name: centos-6.7
- # needs fixing for 5.11
- # - name: centos-5.11
-
-suites:
- - name: webapp
- run_list:
- - recipe[base::default]
-# - recipe[webapp::default]
- attributes:
diff --git a/kitchen-tests/Berksfile b/kitchen-tests/Berksfile
index 407b685236..50e09ebf3b 100644
--- a/kitchen-tests/Berksfile
+++ b/kitchen-tests/Berksfile
@@ -1,8 +1,3 @@
-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"
-
-cookbook "resolver", github: "chef-cookbooks/resolver"
+cookbook "end_to_end", path: "cookbooks/end_to_end"
diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock
deleted file mode 100644
index 16d32a31d9..0000000000
--- a/kitchen-tests/Berksfile.lock
+++ /dev/null
@@ -1,108 +0,0 @@
-DEPENDENCIES
- base
- path: cookbooks/base
- php (~> 1.5.0)
- resolver
- git: https://github.com/chef-cookbooks/resolver.git
- revision: 8bf9034dabc47d29a07870e4059c32114f2c820a
- webapp
- path: cookbooks/webapp
-
-GRAPH
- apache2 (3.2.2)
- apt (4.0.2)
- compat_resource (>= 12.10)
- aws (3.4.1)
- ohai (>= 2.1.0)
- base (0.1.0)
- apt (>= 0.0.0)
- build-essential (>= 0.0.0)
- chef-client (>= 0.0.0)
- chef_hostname (>= 0.0.0)
- logrotate (>= 0.0.0)
- multipackage (>= 0.0.0)
- nscd (>= 0.0.0)
- ntp (>= 0.0.0)
- openssh (>= 0.0.0)
- resolver (>= 0.0.0)
- selinux (>= 0.0.0)
- sudo (>= 0.0.0)
- ubuntu (>= 0.0.0)
- users (>= 0.0.0)
- build-essential (6.0.5)
- compat_resource (>= 12.14)
- mingw (>= 1.1)
- seven_zip (>= 0.0.0)
- chef-client (5.0.0)
- cron (>= 1.7.0)
- logrotate (>= 1.9.0)
- windows (>= 1.42.0)
- chef-sugar (3.4.0)
- chef_hostname (0.4.1)
- compat_resource (>= 0.0.0)
- compat_resource (12.14.2)
- cron (1.7.6)
- 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 (5.0.0)
- windows (>= 1.34.6)
- iptables (2.2.0)
- logrotate (2.1.0)
- compat_resource (>= 0.0.0)
- mingw (1.2.4)
- compat_resource (>= 0.0.0)
- seven_zip (>= 0.0.0)
- multipackage (3.0.28)
- compat_resource (>= 0.0.0)
- 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)
- nscd (4.1.0)
- compat_resource (>= 0.0.0)
- ntp (3.0.0)
- windows (>= 1.38.0)
- ohai (4.2.1)
- compat_resource (>= 12.14)
- openssh (2.0.0)
- iptables (>= 1.0)
- 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)
- resolver (1.3.1)
- selinux (0.9.0)
- seven_zip (2.0.2)
- windows (>= 1.2.2)
- sudo (3.0.0)
- ubuntu (2.0.0)
- apt (>= 0.0.0)
- users (3.0.0)
- webapp (0.1.0)
- apache2 (~> 3.2.2)
- database (~> 2.3.1)
- mysql (~> 5.6.3)
- php (~> 1.5.0)
- windows (2.0.2)
- xfs (2.0.1)
- xml (3.0.0)
- build-essential (>= 0.0.0)
- yum (4.0.0)
- yum-epel (1.0.1)
- yum (>= 3.6)
- yum-mysql-community (1.0.0)
- yum (>= 3.2)
diff --git a/kitchen-tests/Gemfile b/kitchen-tests/Gemfile
index ad89269a75..bc100ffb62 100644
--- a/kitchen-tests/Gemfile
+++ b/kitchen-tests/Gemfile
@@ -1,11 +1,11 @@
source "https://rubygems.org"
-gem "berkshelf"
-gem "kitchen-appbundle-updater"
-gem "kitchen-dokken"
-gem "kitchen-ec2"
-gem "kitchen-inspec"
-gem "kitchen-vagrant"
-gem "ridley"
-gem "test-kitchen"
-gem "vagrant-wrapper"
+gem "rake" # required to build some native extensions
+gem "chef", path: ".."
+gem "ohai", git: "https://github.com/chef/ohai.git", branch: "master" # avoids failures when we bump chef major
+gem "berkshelf", git: "https://github.com/berkshelf/berkshelf.git", branch: "master"
+gem "kitchen-dokken", "~> 2.0"
+gem "kitchen-inspec", git: "https://github.com/chef/kitchen-inspec.git", branch: "master"
+gem "inspec"
+gem "test-kitchen", git: "https://github.com/test-kitchen/test-kitchen.git", branch: "master"
+gem "kitchen-azurerm", git: "https://github.com/test-kitchen/kitchen-azurerm.git", branch: "master" \ No newline at end of file
diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock
deleted file mode 100644
index 36571ec015..0000000000
--- a/kitchen-tests/Gemfile.lock
+++ /dev/null
@@ -1,245 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- addressable (2.4.0)
- artifactory (2.3.3)
- aws-sdk (2.5.11)
- aws-sdk-resources (= 2.5.11)
- aws-sdk-core (2.5.11)
- jmespath (~> 1.0)
- aws-sdk-resources (2.5.11)
- aws-sdk-core (= 2.5.11)
- berkshelf (5.0.0)
- addressable (~> 2.3, >= 2.3.4)
- berkshelf-api-client (>= 2.0.2, < 4.0)
- buff-config (~> 2.0)
- buff-extensions (~> 2.0)
- buff-shell_out (~> 0.1)
- cleanroom (~> 1.0)
- faraday (~> 0.9)
- httpclient (~> 2.7)
- minitar (~> 0.5, >= 0.5.4)
- mixlib-archive (~> 0.1)
- octokit (~> 4.0)
- retryable (~> 2.0)
- ridley (~> 5.0)
- solve (> 2.0, < 4.0)
- thor (~> 0.19)
- berkshelf-api-client (3.0.0)
- faraday (~> 0.9)
- httpclient (~> 2.7)
- ridley (>= 4.5, < 6.0)
- 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 (0.1.0)
- buff-shell_out (0.2.0)
- buff-ruby_engine (~> 0.1.0)
- builder (3.2.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.13.37)
- 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.31.0)
- excon (>= 0.38.0)
- json
- erubis (2.7.0)
- excon (0.52.0)
- faraday (0.9.2)
- multipart-post (>= 1.2, < 3)
- ffi (1.9.14)
- ffi (1.9.14-x86-mingw32)
- fuzzyurl (0.9.0)
- gssapi (1.2.0)
- ffi (>= 1.0.1)
- gyoku (1.3.1)
- builder (>= 2.1.2)
- hashie (3.4.4)
- hitimes (1.2.4)
- hitimes (1.2.4-x86-mingw32)
- httpclient (2.8.2.4)
- inspec (0.34.0)
- hashie (~> 3.4)
- json (>= 1.8, < 3.0)
- method_source (~> 0.8)
- mixlib-log
- parallel (~> 1.9)
- pry (~> 0)
- rainbow (~> 2)
- rspec (~> 3)
- rspec-its (~> 1.2)
- rubyzip (~> 1.1)
- sslshake (~> 1)
- thor (~> 0.19)
- train (>= 0.19.0, < 1.0)
- jmespath (1.3.1)
- json (2.0.2)
- kitchen-appbundle-updater (0.1.2)
- kitchen-dokken (1.0.0)
- docker-api (~> 1.29)
- test-kitchen (~> 1.5)
- kitchen-ec2 (1.1.0)
- aws-sdk (~> 2)
- excon
- multi_json
- retryable (~> 2.0)
- test-kitchen (~> 1.4, >= 1.4.1)
- kitchen-inspec (0.15.1)
- inspec (>= 0.22.0, < 1.0.0)
- test-kitchen (~> 1.6)
- kitchen-vagrant (0.20.0)
- test-kitchen (~> 1.4)
- little-plugger (1.1.4)
- logging (2.1.0)
- little-plugger (~> 1.1)
- multi_json (~> 1.10)
- method_source (0.8.2)
- minitar (0.5.4)
- mixlib-archive (0.2.0)
- mixlib-log
- mixlib-authentication (1.4.1)
- mixlib-log
- mixlib-config (2.2.4)
- mixlib-install (1.1.0)
- artifactory
- mixlib-shellout
- mixlib-versioning
- mixlib-log (1.7.1)
- mixlib-shellout (2.2.7)
- mixlib-shellout (2.2.7-universal-mingw32)
- win32-process (~> 0.8.2)
- wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- molinillo (0.5.0)
- multi_json (1.12.1)
- multipart-post (2.0.0)
- net-scp (1.2.1)
- net-ssh (>= 2.6.5)
- net-ssh (3.2.0)
- net-ssh-gateway (1.2.0)
- net-ssh (>= 2.6.5)
- nio4r (1.2.1)
- nori (2.6.0)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- parallel (1.9.0)
- pry (0.10.4)
- coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- rainbow (2.1.0)
- retryable (2.0.4)
- ridley (5.0.0)
- addressable
- buff-config (~> 2.0)
- buff-extensions (~> 2.0)
- buff-ignore (~> 1.2)
- 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 (~> 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.3)
- rspec-support (~> 3.5.0)
- rspec-expectations (3.5.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-its (1.2.0)
- rspec-core (>= 3.0.0)
- rspec-expectations (>= 3.0.0)
- rspec-mocks (3.5.0)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-support (3.5.0)
- rubyntlm (0.6.0)
- rubyzip (1.2.0)
- safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- semverse (2.0.0)
- slop (3.6.0)
- solve (3.0.1)
- molinillo (~> 0.4)
- semverse (>= 1.1, < 3.0)
- sslshake (1.0.12)
- test-kitchen (1.12.0)
- mixlib-install (~> 1.0, >= 1.0.4)
- mixlib-shellout (>= 1.2, < 3.0)
- net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-gateway (~> 1.2.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
- thor (0.19.1)
- timers (4.0.4)
- hitimes
- train (0.19.0)
- docker-api (~> 1.26)
- json (>= 1.8, < 3.0)
- mixlib-shellout (~> 2.0)
- net-scp (~> 1.2)
- net-ssh (>= 2.9, < 4.0)
- winrm (~> 2.0)
- winrm-fs (~> 1.0)
- vagrant-wrapper (2.0.3)
- varia_model (0.6.0)
- buff-extensions (~> 2.0)
- hashie (>= 2.0.2, < 4.0.0)
- win32-process (0.8.3)
- ffi (>= 1.0.0)
- winrm (2.0.1)
- 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 (1.0.0)
- erubis (~> 2.7)
- logging (>= 1.6.1, < 3.0)
- rubyzip (~> 1.1)
- winrm (~> 2.0)
- wmi-lite (1.0.0)
-
-PLATFORMS
- ruby
- x86-mingw32
-
-DEPENDENCIES
- berkshelf
- kitchen-appbundle-updater
- kitchen-dokken
- kitchen-ec2
- kitchen-inspec
- kitchen-vagrant
- ridley
- test-kitchen
- vagrant-wrapper
-
-BUNDLED WITH
- 1.12.5
diff --git a/kitchen-tests/README.md b/kitchen-tests/README.md
index 2d03b19b62..99ab9b0cfe 100644
--- a/kitchen-tests/README.md
+++ b/kitchen-tests/README.md
@@ -1,18 +1,16 @@
-# End-To-End Testing for Chef Client
-Here we seek to provide end-to-end testing of Chef Client through cookbooks which
-exercise many of the available resources, providers, and common patterns. The cookbooks
-here are designed to ensure certain capabilities remain functional with updates
-to the client code base.
+# End-To-End Testing for Chef Infra Client
+
+Here we seek to provide end-to-end testing of Chef Infra Client through cookbooks which exercise many of the available resources, providers, and common patterns. The cookbooks here are designed to ensure certain capabilities remain functional with updates to the client code base.
## Getting started
-All the gems needed to run these tests can be installed with Bundler.
+
+These tests run in Docker containers, so make sure to install Docker on your workstation. Once Docker is installed, all the gems needed to run these tests can be installed with Bundler.
```shell
chef/kitchen-tests$ bundle install
```
-To ensure everything is working properly, and to see which platforms can have tests
-executed on them, run
+To ensure everything is working properly, and to see which platforms can have tests executed on them, run
```shell
chef/kitchen-tests$ bundle exec kitchen list
@@ -21,69 +19,55 @@ chef/kitchen-tests$ bundle exec kitchen list
You should see output similar to
```shell
-Instance Driver Provisioner Last Action
-webapp-ubuntu-1204 Vagrant ChefSolo <Not Created>
+Instance Driver Provisioner Verifier Transport Last Action Last Error
+end-to-end-amazonlinux Dokken Dokken Inspec Dokken <Not Created> <None>
```
## Testing
-We use Test Kitchen to build instances, test client code, and destroy instances. If
-you are unfamiliar with Test Kitchen we recommend checking out the [tutorial](http://kitchen.ci/)
-along with the `kitchen-vagrant` [driver documentation](https://github.com/test-kitchen/kitchen-vagrant).
-Test Kitchen is configured to manipulate instances using [Vagrant](http://www.vagrantup.com/)
-when testing locally, and [Amazon EC2](http://aws.amazon.com/ec2/) when testing
-pull requests on [Travis CI](https://travis-ci.com).
+
+We use Test Kitchen to build instances, test client code, and destroy instances. If you are unfamiliar with Test Kitchen, we recommend checking out the [tutorial](http://kitchen.ci/) along with the `kitchen-dokken` [driver documentation](https://github.com/someara/kitchen-dokken). Test Kitchen is configured to manipulate instances using [Docker](https://www.docker.com/) when testing locally, and when testing, pull requests on [Buildkite](https://buildkite.com/chef-oss/chef-chef-master-verify).
### Commands
-Kitchen instances are led through a series of states. The instance states, and the actions
-taken to transition into each state, are in order:
-* `destroy`: Delete all information for and terminate one or more instances.
- * This is equivalent to running `vagrant destroy` to stop and delete a Vagrant machine.
-* `create`: Start one or more instances.
- * This is equivalent to running `vagrant up --no-provision` to start a Vagrant instance.
-* `converge`: Use a provisioner to configure one or more instances.
- * By default, Test Kitchen is configured to use the `ChefSolo` provisioner which:
- * Prepares local files for transfer,
- * Installs the latest release of Chef Omnibus,
- * Downloads Chef Client source code from the prescribed GitHub repository and reference,
- * Builds and installs a `chef` gem from the downloaded source,
- * Runs `chef-client`.
-* `setup`: Prepare to run automated tests. Installs `busser` and related gems on one or more instances.
-* `verify`: Run automated tests on one or more instances.
-
-When transitioning between states, actions for any and all intermediate states will performed.
-Executing the `create` then the `verify` commands is equivalent to executing `create`, `converge`,
-`setup`, and `verify` one-by-one and in order. The only exception is `destroy`, which will
-immediately transfer that machine's state to destroyed.
-
-The `test` command takes one or more instances through all the states, in order: `destroy`, `create`,
-`converge`, `setup`, `verify`, `destroy`.
-
-To see a list of available commands, type `bundle exec kitchen help`. To see more information
-about a particular command, type `bundle exec kitchen help <command>`.
+
+Kitchen instances are led through a series of states. The instance states, and the actions taken to transition into each state, are in order:
+
+- `destroy`: Delete all information for and terminate one or more instances.
+ - This is equivalent to running `vagrant destroy` to stop and delete a Vagrant machine.
+- `create`: Start one or more instances.
+ - This is equivalent to running `vagrant up --no-provision` to start a Vagrant instance.
+- `converge`: Use a provisioner to configure one or more instances.
+ - By default, Test Kitchen is configured to use the `ChefSolo` provisioner which:
+ - Prepares local files for transfer,
+ - Installs the latest release of Chef Omnibus,
+ - Downloads Chef Client source code from the prescribed GitHub repository and reference,
+ - Builds and installs a `chef` gem from the downloaded source,
+ - Runs `chef-client`.
+- `setup`: Prepare the instance to run automated tests.
+- `verify`: Run automated tests on one or more instances.
+
+When transitioning between states, actions for any and all intermediate states will performed. Executing the `create` then the `verify` commands is equivalent to executing `create`, `converge`, `setup`, and `verify` one-by-one and in order. The only exception is `destroy`, which will immediately transfer that machine's state to destroyed.
+
+The `test` command takes one or more instances through all the states, in order: `destroy`, `create`, `converge`, `setup`, `verify`, `destroy`.
+
+To see a list of available commands, type `bundle exec kitchen help`. To see more information about a particular command, type `bundle exec kitchen help <command>`.
### Configuring your tests
-Test Kitchen is configured for local testing in the `.kitchen.yml` file which resides in this directory.
-You will need to configure the provisioner before running the tests.
-The provisioner can be configured to pull client source code from a GitHub repository using any
-valid Git reference. You are encouraged to modify any of these settings, but please return them
-to their original values before submitting a pull request for review (unless, of course, your
-changes are enhancements to the default provisioner settings).
+Test Kitchen is configured in the `kitchen.yml` file, which resides in this directory. You will need to configure the provisioner before running the tests.
+
+The provisioner can be configured to pull client source code from a GitHub repository using any valid Git reference. You are encouraged to modify any of these settings, but please return them to their original values before submitting a pull request for review (unless, of course, your changes are enhancements to the default provisioner settings).
-By default, the provisioner is configured to pull your most recent commit to `opscode/chef`. You
-can change this by modifying the `github` and `branch` provisioner options:
-* `github`: Set this to `"<your_username>/<your_chef_repo>"`. The default is `"opscode/chef"`.
-* `branch`: This can be any valid git reference (e.g., branch name, tag, or commit SHA). If omitted, it defaults to `master`.
+By default, the provisioner is configured to pull your most recent commit to `chef/chef`. You can change this by modifying the `github` and `branch` provisioner options:
+
+- `github`: Set this to `"<your_username>/<your_chef_repo>"`. The default is `"chef/chef"`.
+- `branch`: This can be any valid git reference (e.g., branch name, tag, or commit SHA). If omitted, it defaults to `master`.
The branch you choose must be accessible on GitHub. You cannot use a local commit at this time.
### Testing pull requests
-These end-to-end tests are also configured to run with Travis on EC2 instances when you submit a pull request
-to `opscode/chef`. Kitchen is configured to pull chef client source code from the branch it is testing. There
-is no need to modify `.kitchen.travis.yml` unless you are contributing tests.
+
+These end-to-end tests are also configured to run on Buildkite with Docker containers when you submit a pull request to `chef/chef`. Kitchen is configured to pull chef client source code from the branch it is testing. There is no need to modify `kitchen.yml` unless you are contributing tests.
## Contributing
-We would love to fill out our end-to-end testing coverage! If you have cookbooks and tests that you would
-like to see become a part of client testing, we encourage you to submit a pull request with your additions.
-We request that you do not add platforms to `.kitchen.travis.yml`. Please file a request to add a
-platform under [Issues](https://github.com/opscode/chef/issues).
+
+We would love to fill out our end-to-end testing coverage! If you have cookbooks and tests that you would like to see become a part of client testing, we encourage you to submit a pull request with your additions. We request that you do not add platforms to `kitchen.yml`. Please file a request to add a platform under [Issues](https://github.com/chef/chef/issues).
diff --git a/kitchen-tests/cookbooks/audit_test/.gitignore b/kitchen-tests/cookbooks/audit_test/.gitignore
deleted file mode 100644
index 1e074046f0..0000000000
--- a/kitchen-tests/cookbooks/audit_test/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-.vagrant
-*~
-*#
-.#*
-\#*#
-.*.sw[a-z]
-*.un~
-
-# Bundler
-Gemfile.lock
-bin/*
-.bundle/*
-
-.kitchen/
-.kitchen.local.yml
diff --git a/kitchen-tests/cookbooks/audit_test/.kitchen.yml b/kitchen-tests/cookbooks/audit_test/.kitchen.yml
deleted file mode 100644
index be11e33081..0000000000
--- a/kitchen-tests/cookbooks/audit_test/.kitchen.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-driver:
- name: vagrant
-
-provisioner:
- name: chef_zero
-
-platforms:
- - name: ubuntu-12.04
- - name: centos-6.5
-
-suites:
- - name: default
- run_list:
- - recipe[audit_test::default]
- attributes:
diff --git a/kitchen-tests/cookbooks/audit_test/Berksfile b/kitchen-tests/cookbooks/audit_test/Berksfile
deleted file mode 100644
index 0ac9b78cf7..0000000000
--- a/kitchen-tests/cookbooks/audit_test/Berksfile
+++ /dev/null
@@ -1,3 +0,0 @@
-source "https://supermarket.getchef.com"
-
-metadata
diff --git a/kitchen-tests/cookbooks/audit_test/Berksfile.lock b/kitchen-tests/cookbooks/audit_test/Berksfile.lock
deleted file mode 100644
index ef9f28a3be..0000000000
--- a/kitchen-tests/cookbooks/audit_test/Berksfile.lock
+++ /dev/null
@@ -1,7 +0,0 @@
-DEPENDENCIES
- audit_test
- path: .
- metadata: true
-
-GRAPH
- audit_test (0.1.0)
diff --git a/kitchen-tests/cookbooks/audit_test/README.md b/kitchen-tests/cookbooks/audit_test/README.md
deleted file mode 100644
index 75e2f44808..0000000000
--- a/kitchen-tests/cookbooks/audit_test/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# audit_test
-
-This cookbook has some basic recipes to test audit mode.
-
-In order to run these tests on your dev box:
-
-```
-$ bundle install
-$ bundle exec chef-client -c kitchen-tests/.chef/client.rb -z -o audit_test::default -l debug
-```
-
-Expected JSON output for the tests will be printed to `debug` log.
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
deleted file mode 100644
index 3fbda5dbe1..0000000000
--- a/kitchen-tests/cookbooks/audit_test/metadata.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-name "audit_test"
-maintainer "The Authors"
-maintainer_email "you@example.com"
-license "all_rights"
-description "Installs/Configures audit_test"
-long_description "Installs/Configures audit_test"
-version "0.1.0"
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/default.rb b/kitchen-tests/cookbooks/audit_test/recipes/default.rb
deleted file mode 100644
index 886c2cd0ac..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/default.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: default
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-control_group "basic control group" do
- control "basic math" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
- end
-end
-
-control_group "control group without top level control" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
-end
-
-control_group "control group with empty control" do
- control "empty"
-end
-
-control_group "empty control group with block" do
-end
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/error_duplicate_control_groups.rb b/kitchen-tests/cookbooks/audit_test/recipes/error_duplicate_control_groups.rb
deleted file mode 100644
index 2b5b8b5d22..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/error_duplicate_control_groups.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: error_duplicate_control_groups
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-control_group "basic control group" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
-end
-
-control_group "basic control group" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
-end
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/error_no_block.rb b/kitchen-tests/cookbooks/audit_test/recipes/error_no_block.rb
deleted file mode 100644
index e4c444e8bc..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/error_no_block.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: error_no_block
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-control_group "empty control group without block"
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/error_orphan_control.rb b/kitchen-tests/cookbooks/audit_test/recipes/error_orphan_control.rb
deleted file mode 100644
index 61d809a7c0..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/error_orphan_control.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: error_orphan_control
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-control_group "basic control group" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
-end
-
-control "orphan control"
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/failed_specs.rb b/kitchen-tests/cookbooks/audit_test/recipes/failed_specs.rb
deleted file mode 100644
index 9e6adfe2e9..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/failed_specs.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: failed_specs
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-control_group "basic control group" do
- control "basic math" do
- # Can not write a good control :(
- it "should pass" do
- expect(2 - 0).to eq(0)
- end
- end
-end
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/serverspec_collision.rb b/kitchen-tests/cookbooks/audit_test/recipes/serverspec_collision.rb
deleted file mode 100644
index 36dd714cef..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/serverspec_collision.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: serverspec_collision
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-file "/tmp/audit_test_file" do
- action :create
- content "Welcome to audit mode."
-end
-
-control_group "file auditing" do
- describe "test file" do
- it "says welcome" do
- expect(file("/tmp/audit_test_file")).to contain("Welcome")
- end
- end
-end
-
-file "/tmp/audit_test_file_2" do
- action :create
- content "Bye to audit mode."
-end
-
-control_group "end file auditing" do
- describe "end file" do
- it "says bye" do
- expect(file("/tmp/audit_test_file_2")).to contain("Bye")
- end
- end
-end
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/serverspec_support.rb b/kitchen-tests/cookbooks/audit_test/recipes/serverspec_support.rb
deleted file mode 100644
index fc44fb334e..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/serverspec_support.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: serverspec_support
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-file "/tmp/audit_test_file" do
- action :create
- content "Welcome to audit mode."
-end
-
-# package "curl" do
-# action :install
-# end
-
-control_group "serverspec helpers with types" do
- control "file helper" do
- it "says welcome" do
- expect(file("/tmp/audit_test_file")).to contain("Welcome")
- end
- end
-
- control service("com.apple.CoreRAID") do
- it { is_expected.to be_enabled }
- it { is_expected.not_to be_running }
- end
-
- # describe "package helper" do
- # it "works" do
- # expect(package("curl")).to be_installed
- # end
- # end
-
- control package("postgresql") do
- it { is_expected.to_not be_installed }
- end
-end
diff --git a/kitchen-tests/cookbooks/audit_test/recipes/with_include_recipe.rb b/kitchen-tests/cookbooks/audit_test/recipes/with_include_recipe.rb
deleted file mode 100644
index 7727c573c7..0000000000
--- a/kitchen-tests/cookbooks/audit_test/recipes/with_include_recipe.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Cookbook Name:: audit_test
-# Recipe:: with_include_recipe
-#
-# Copyright 2014-2016, The Authors, All Rights Reserved.
-
-include_recipe "audit_test::serverspec_collision"
-
-control_group "basic example" do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
-end
-
-include_recipe "audit_test::serverspec_collision"
-include_recipe "audit_test::default"
diff --git a/kitchen-tests/cookbooks/base/Berksfile b/kitchen-tests/cookbooks/base/Berksfile
deleted file mode 100644
index 4b6079016e..0000000000
--- a/kitchen-tests/cookbooks/base/Berksfile
+++ /dev/null
@@ -1,5 +0,0 @@
-source "https://api.berkshelf.com"
-
-metadata
-
-cookbook "apt"
diff --git a/kitchen-tests/cookbooks/base/README.md b/kitchen-tests/cookbooks/base/README.md
deleted file mode 100644
index f19ab46735..0000000000
--- a/kitchen-tests/cookbooks/base/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# webapp
-
-TODO: Enter the cookbook description here.
diff --git a/kitchen-tests/cookbooks/base/attributes/default.rb b/kitchen-tests/cookbooks/base/attributes/default.rb
deleted file mode 100644
index ef273c969c..0000000000
--- a/kitchen-tests/cookbooks/base/attributes/default.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-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"]["rsa_authentication"] = "no"
-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,blowfish-cbc,3des-cbc,cast128-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"]["rhosts_rsa_authentication"] = "no"
-default["openssh"]["server"]["rsa_authentication"] = "no"
-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"]["use_privilege_separation"] = "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"
-
-#
-# sudo cookbook overrides
-#
-
-default["authorization"]["sudo"]["passwordless"] = true
-default["authorization"]["sudo"]["users"] = %w{vagrant centos ubuntu}
-
-#
-# 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
deleted file mode 100644
index 90d02a361f..0000000000
--- a/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 32ea03916f..0000000000
--- a/kitchen-tests/cookbooks/base/metadata.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-name "base"
-maintainer ""
-maintainer_email ""
-license ""
-description "Installs/Configures base"
-long_description "Installs/Configures base"
-version "0.1.0"
-
-gem "chef-sugar"
-
-depends "apt"
-depends "build-essential"
-depends "chef-client"
-depends "chef_hostname"
-depends "logrotate"
-depends "multipackage"
-depends "nscd"
-depends "ntp"
-depends "openssh"
-depends "resolver"
-depends "selinux"
-depends "sudo"
-depends "ubuntu"
-depends "users"
diff --git a/kitchen-tests/cookbooks/base/recipes/default.rb b/kitchen-tests/cookbooks/base/recipes/default.rb
deleted file mode 100644
index 397d50c016..0000000000
--- a/kitchen-tests/cookbooks/base/recipes/default.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Cookbook Name:: webapp
-# Recipe:: default
-#
-# Copyright (C) 2014
-#
-
-hostname "chef-travis-ci.chef.io"
-
-if node["platform_family"] == "debian"
- include_recipe "ubuntu"
- apt_update "packages"
-end
-
-if %w{rhel fedora}.include?(node["platform_family"])
- 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 { node["platform_family"] == "rhel" }
-end
-
-include_recipe "build-essential"
-
-include_recipe "::packages"
-
-include_recipe "ntp"
-
-include_recipe "resolver"
-
-include_recipe "users::sysadmins"
-
-include_recipe "sudo"
-
-include_recipe "chef-client::delete_validation"
-include_recipe "chef-client::config"
-include_recipe "chef-client"
-
-# hack needed for debian-7 on docker
-directory "/var/run/sshd"
-
-include_recipe "openssh"
-
-include_recipe "nscd"
-
-include_recipe "logrotate"
-
-include_recipe "::tests"
diff --git a/kitchen-tests/cookbooks/base/recipes/packages.rb b/kitchen-tests/cookbooks/base/recipes/packages.rb
deleted file mode 100644
index 1ca8d81968..0000000000
--- a/kitchen-tests/cookbooks/base/recipes/packages.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-pkgs = %w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversion git traceroute htop tmux s3cmd sysbench }
-
-# 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 aws-sdk}
-
-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
deleted file mode 100644
index 9d9d813865..0000000000
--- a/kitchen-tests/cookbooks/base/recipes/tests.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Cookbook Name:: webapp
-# Recipe:: default
-#
-# Copyright (C) 2014
-#
-
-#
-# 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/end_to_end/README.md b/kitchen-tests/cookbooks/end_to_end/README.md
new file mode 100644
index 0000000000..f9bc150e61
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/README.md
@@ -0,0 +1,3 @@
+# end_to_end
+
+A standard chef "base" cookbook that performs various base system configuration tasks using common community cookbooks.
diff --git a/kitchen-tests/cookbooks/end_to_end/attributes/default.rb b/kitchen-tests/cookbooks/end_to_end/attributes/default.rb
new file mode 100644
index 0000000000..e910b9e7d4
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/attributes/default.rb
@@ -0,0 +1,80 @@
+puts "CHEF UTILS THINKS WE ARE ON UBUNTU" if ubuntu?
+puts "CHEF UTILS THINKS WE ARE ON RHEL" if rhel?
+puts "CHEF UTILS THINKS WE ARE ON MACOS" if macos?
+puts "CHEF UTILS THINKS WE ARE ON WINDOWS" if windows?
+
+#
+# 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"
+# everyone 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
+
+default["chef_client"]["chef_license"] = "accept-no-persist"
+
+#
+# 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" unless platform_family?("suse") # this breaks SLES 15
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/GlobalSignRootCA.pem b/kitchen-tests/cookbooks/end_to_end/files/certs/GlobalSignRootCA.pem
new file mode 100644
index 0000000000..58537e9a94
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/GlobalSignRootCA.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/base64-cert2.cer b/kitchen-tests/cookbooks/end_to_end/files/certs/base64-cert2.cer
new file mode 100644
index 0000000000..44d4e39200
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/base64-cert2.cer
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
+MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
+YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
+MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
+ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
+MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
+ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
+PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
+wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
+EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
+avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
+sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
+/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
+IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
+OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
+TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
+dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
+ReYNnyicsbkqWletNw+vHX/bvZ8=
+-----END CERTIFICATE-----
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/der-cert1.cer b/kitchen-tests/cookbooks/end_to_end/files/certs/der-cert1.cer
new file mode 100644
index 0000000000..3cdad66612
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/der-cert1.cer
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.cer b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.cer
new file mode 100644
index 0000000000..86b1e8595e
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.cer
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pfx b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pfx
new file mode 100644
index 0000000000..7815cd5806
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pfx
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pvk b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pvk
new file mode 100644
index 0000000000..d32189f9dd
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test-cert.pvk
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test_cert.crt b/kitchen-tests/cookbooks/end_to_end/files/certs/test_cert.crt
new file mode 100644
index 0000000000..1e6967febb
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test_cert.crt
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test_der.der b/kitchen-tests/cookbooks/end_to_end/files/certs/test_der.der
new file mode 100644
index 0000000000..1e6967febb
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test_der.der
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/certs/test_p7b.p7b b/kitchen-tests/cookbooks/end_to_end/files/certs/test_p7b.p7b
new file mode 100644
index 0000000000..c380514b1b
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/certs/test_p7b.p7b
@@ -0,0 +1,22 @@
+-----BEGIN PKCS7-----
+MIIDpgYJKoZIhvcNAQcCoIIDlzCCA5MCAQExADALBgkqhkiG9w0BBwGgggN5MIID
+dTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UE
+BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3Qg
+Q0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBa
+Fw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxT
+aWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWdu
+IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZjc6j
+40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0S
+y6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrj
+sok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUO
+hugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Q
+zns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo
+7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1h
+TdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38Nf
+lNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEV
+tQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUadDKqC
+5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbMEHMUf
+pIBvFSDJ3gyICh3WZlXi/EjJKSZp4KEAMQA=
+-----END PKCS7-----
diff --git a/kitchen-tests/cookbooks/end_to_end/files/io.chef.testing.fake.plist b/kitchen-tests/cookbooks/end_to_end/files/io.chef.testing.fake.plist
new file mode 100644
index 0000000000..570c736041
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/io.chef.testing.fake.plist
@@ -0,0 +1,25 @@
+<?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>
+ <key>Label</key>
+
+ <string>io.chef.testing.fake</string>
+
+ <key>ProgramArguments</key>
+
+ <array>
+ <string>/bin/sleep</string>
+ <string>60</string>
+ </array>
+
+ <key>StartCalendarInterval</key>
+
+ <dict>
+ <key>Hour</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+</plist>
diff --git a/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.gz b/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.gz
new file mode 100644
index 0000000000..a03da304d3
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.gz
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.xz b/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.xz
new file mode 100644
index 0000000000..de37cea21e
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/tourism.tar.xz
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/files/tourism.zip b/kitchen-tests/cookbooks/end_to_end/files/tourism.zip
new file mode 100644
index 0000000000..8c578e691d
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/files/tourism.zip
Binary files differ
diff --git a/kitchen-tests/cookbooks/end_to_end/metadata.rb b/kitchen-tests/cookbooks/end_to_end/metadata.rb
new file mode 100644
index 0000000000..fca8dc9c9f
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/metadata.rb
@@ -0,0 +1,27 @@
+name "end_to_end"
+license "Apache-2.0"
+description "Installs/Configures base"
+version "1.0.0"
+
+gem "chef-sugar"
+
+depends "logrotate"
+depends "multipackage"
+depends "nscd"
+depends "ntp"
+depends "openssh"
+depends "resolver"
+depends "selinux"
+depends "users"
+depends "git"
+
+supports "ubuntu"
+supports "debian"
+supports "centos"
+supports "opensuseleap"
+supports "fedora"
+supports "amazon"
+
+chef_version ">= 16"
+issues_url "https://github.com/chef/chef/issues"
+source_url "https://github.com/chef/chef"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_alternatives.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_alternatives.rb
new file mode 100644
index 0000000000..8e0a0bb178
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_alternatives.rb
@@ -0,0 +1,51 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: alternatives
+#
+
+file "/usr/local/sample-binary-1" do
+ content '#!/bin/bash
+ echo sample-binary-v1
+ '
+ mode "500"
+end
+
+file "/usr/local/sample-binary-2" do
+ content '#!/bin/bash
+ echo sample-binary-v2
+ '
+ mode "550"
+end
+
+alternatives "sample-binary v1" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-1"
+ priority 100
+ action :install
+end
+
+alternatives "sample-binary v2" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-2"
+ priority 101
+ action :install
+end
+
+alternatives "set sample-binary v1" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-1"
+ action :set
+end
+
+alternatives "sample-binary-test v1" do
+ link_name "sample-binary-test"
+ path "/usr/local/sample-binary-1"
+ priority 100
+ action :install
+end
+
+alternatives "sample-binary-test v1" do
+ link_name "sample-binary-test"
+ path "/usr/local/sample-binary-1"
+ action :remove
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_apt.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_apt.rb
new file mode 100644
index 0000000000..901cd8c36c
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_apt.rb
@@ -0,0 +1,24 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: apt_preference
+#
+
+apt_update "periodic apt update" do
+ frequency 86400
+ action :periodic
+end
+
+apt_preference "dotdeb" do
+ glob "*"
+ pin "origin packages.dotdeb.org"
+ pin_priority "700"
+end
+
+apt_preference "a_fake_package" do
+ pin "version 5.1.49-3"
+ pin_priority "700"
+end
+
+apt_preference "libmysqlclient16" do
+ action :remove
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_chef-vault.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_chef-vault.rb
new file mode 100644
index 0000000000..8f1d40ff49
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_chef-vault.rb
@@ -0,0 +1,43 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: chef-vault
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+chef_data_bag "creds"
+
+openssl_rsa_private_key "/root/bob_bobberson.pem" do
+ key_length 2048
+ action :create
+end
+
+chef_client "bob_bobberson" do
+ source_key_path "/root/bob_bobberson.pem"
+end
+
+chef_node "bob_bobberson"
+
+chef_vault_secret "super_secret_1" do
+ data_bag "creds"
+ raw_data("auth" => "1234")
+ admins "bob_bobberson"
+ search "*:*"
+end
+
+chef_vault_secret "super_secret_2" do
+ data_bag "creds"
+ raw_data("auth" => "4321")
+ admins "bob_bobberson"
+end
+
+ruby_block "load vault item" do
+ block do
+
+ chef_vault_item("creds", "super_secret_1")
+ rescue ChefVault::Exceptions::SecretDecryption
+ puts "Not authorized for this key!"
+
+ end
+ action :run
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_config.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_config.rb
new file mode 100644
index 0000000000..0e68582af0
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_config.rb
@@ -0,0 +1,11 @@
+chef_client_config "Create chef-client's client.rb" do
+ chef_server_url "https://localhost"
+ chef_license "accept"
+ additional_config <<~CONFIG
+ begin
+ require 'aws-sdk'
+ rescue LoadError
+ Chef::Log.warn "Failed to load aws-sdk."
+ end
+ CONFIG
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_trusted_certificate.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_trusted_certificate.rb
new file mode 100644
index 0000000000..486d5f36c6
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_chef_client_trusted_certificate.rb
@@ -0,0 +1,30 @@
+chef_client_trusted_certificate "self-signed.badssl.com" do
+ certificate <<~CERT
+ -----BEGIN CERTIFICATE-----
+ MIIDeTCCAmGgAwIBAgIJAPziuikCTox4MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
+ c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0x
+ OTEwMDkyMzQxNTJaFw0yMTEwMDgyMzQxNTJaMGIxCzAJBgNVBAYTAlVTMRMwEQYD
+ VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK
+ DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB
+ BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2
+ PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW
+ hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A
+ xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve
+ ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY
+ QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T
+ BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI
+ hvcNAQELBQADggEBAGlwCdbPxflZfYOaukZGCaxYK6gpincX4Lla4Ui2WdeQxE95
+ w7fChXvP3YkE3UYUE7mupZ0eg4ZILr/A0e7JQDsgIu/SRTUE0domCKgPZ8v99k3A
+ vka4LpLK51jHJJK7EFgo3ca2nldd97GM0MU41xHFk8qaK1tWJkfrrfcGwDJ4GQPI
+ iLlm6i0yHq1Qg1RypAXJy5dTlRXlCLd8ufWhhiwW0W75Va5AEnJuqpQrKwl3KQVe
+ wGj67WWRgLfSr+4QG1mNvCZb2CkjZWmxkGPuoP40/y7Yu5OFqxP5tAjj4YixCYTW
+ EVA0pmzIzgBg+JIe3PdRy27T0asgQW/F4TY61Yk=
+ -----END CERTIFICATE-----
+ CERT
+end
+
+# see if we can fetch from our new trusted domain
+remote_file ::File.join(Chef::Config[:file_cache_path], "index.html") do
+ source "https://self-signed.badssl.com/index.html"
+end \ No newline at end of file
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_cron.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_cron.rb
new file mode 100644
index 0000000000..b2575923ea
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_cron.rb
@@ -0,0 +1,67 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: cron
+#
+
+#
+# cron_d resource
+#
+
+cron_d "noop" do
+ hour "5"
+ minute "0"
+ command "/bin/true"
+end
+
+cron_d "name_of_cron_entry" do
+ minute "0"
+ hour "8"
+ weekday "6"
+ mailto "admin@example.com"
+ command "/bin/true"
+ action :create
+end
+
+cron_d "name_of_cron_entry" do
+ minute "0"
+ hour "20"
+ day "*"
+ month "11"
+ weekday "1-5"
+ command "/bin/true"
+ action :create
+end
+
+cron_d "job_to_remove" do
+ action :delete
+end
+
+#
+# cron_access resource
+#
+
+cron_access "alice" do
+ action :allow
+end
+
+cron_access "bob"
+
+# legacy resource name
+cron_manage "Bill breaks things. Take away cron" do
+ user "bill"
+ action :deny
+end
+
+#
+# cron resource
+#
+
+cron "some random cron job" do
+ minute 0
+ hour 23
+ command "/usr/bin/true"
+end
+
+cron "remove_a_job" do
+ action :delete
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_dmg_package.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_dmg_package.rb
new file mode 100644
index 0000000000..27dad15c80
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_dmg_package.rb
@@ -0,0 +1,21 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: _dmg_package
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+dmg_package "LittleSecrets" do
+ source "https://www.mani.de/download/littlesecrets/LittleSecrets1.9.4.dmg"
+ checksum "8281c1f648c038b296a02940126c29032ff387b90a880d63834e303e1b3a5ff7"
+ action :install
+end
+
+dmg_package "virtualbox" do
+ app "virtualbox"
+ source "http://download.virtualbox.org/virtualbox/6.1.8/VirtualBox-6.1.8-137981-OSX.dmg"
+ checksum "569e91eb3c7cb002d407b236a7aa71ac610cf2ad1afa03730dab11fbd4b89e7c"
+ type "pkg"
+ accept_eula true
+ allow_untrusted true
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_ifconfig.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_ifconfig.rb
new file mode 100644
index 0000000000..2a02636ac2
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_ifconfig.rb
@@ -0,0 +1,15 @@
+return if fedora? && node["platform_version"] >= "33" # ifconfig does not support the new network manager keyfile format
+
+execute "create virtual interface for testing" do
+ command "ifconfig eth0:0 123.123.22.22"
+end
+
+ifconfig "33.33.33.80" do
+ bootproto "dhcp"
+ device "eth0:0"
+end
+
+ifconfig "Set eth1 to DHCP" do
+ device "eth0:0"
+ bootproto "dhcp"
+end \ No newline at end of file
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_launchd.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_launchd.rb
new file mode 100644
index 0000000000..4a1e910b51
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_launchd.rb
@@ -0,0 +1,13 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: launchd
+#
+
+file "/Library/LaunchDaemons/io.chef.testing.fake.plist" do
+ path "io.chef.testing.fake.plist"
+ mode "644"
+end
+
+launchd "io.chef.testing.fake" do
+ source "io.chef.testing.fake"
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_macos_userdefaults.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_macos_userdefaults.rb
new file mode 100644
index 0000000000..80cbfddfcc
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_macos_userdefaults.rb
@@ -0,0 +1,75 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: _macos_userdefaults
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+# test that we can autodetect the type
+mac_os_x_userdefaults "Disable fast user switching" do
+ domain "/Library/Preferences/.GlobalPreferences"
+ key "MultipleSessionEnabled"
+ value 0
+end
+
+# test full path to the domain
+macos_userdefaults "Enable macOS firewall" do
+ domain "/Library/Preferences/com.apple.alf"
+ key "globalstate"
+ value "1"
+ type "int"
+end
+
+# test short domain name
+macos_userdefaults "Set the dock size" do
+ domain "com.apple.dock"
+ type "int"
+ key "tilesize"
+ value "20"
+end
+
+# test that we can properly handle spaces
+macos_userdefaults "Value with space" do
+ domain "/Library/Preferences/ManagedInstalls"
+ type "string"
+ key "LogFile"
+ value "/Library/Managed Installs/Logs/ManagedSoftwareUpdate2.log"
+end
+
+# test that we can set an array
+macos_userdefaults "Bogus key with array value" do
+ domain "/Library/Preferences/ManagedInstalls"
+ type "array"
+ key "LogFileArray"
+ value [ "/Library/Managed Installs/fake.log", "/Library/Managed Installs/also_fake.log"]
+end
+
+# test that we can set a dict
+macos_userdefaults "Bogus key with dict value" do
+ domain "/Library/Preferences/ManagedInstalls"
+ type "dict"
+ key "LogFileDict"
+ value "User": "/Library/Managed Installs/way_fake.log"
+end
+
+# test that we can set a bool
+macos_userdefaults "Bogus key with boolean value" do
+ domain "/Library/Preferences/ManagedInstalls"
+ key "LoggingIsTheThingToDoRight"
+ value "yes"
+ type "bool"
+end
+
+# test that we can handle the 2nd client run with :delete
+macos_userdefaults "bogus key" do
+ domain "/Library/Preferences/com.apple.alf"
+ key "GlobalStateNope"
+ action :delete
+end
+
+# try to delete a key we known is there
+macos_userdefaults "delete a key" do
+ domain "/Library/Preferences/ManagedInstalls"
+ key "LoggingIsTheThingToDoRight"
+ action :delete
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb
new file mode 100644
index 0000000000..207ef9d1fb
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb
@@ -0,0 +1,20 @@
+mount "/proc" do
+ device "proc"
+ fstype "proc"
+ options %w{bind rw}
+ action %i{ mount enable }
+end
+
+mount "/mnt" do
+ device "/tmp"
+ fstype "ext4"
+ options %w{bind rw}
+ action %i{ mount enable }
+end
+
+mount "/mnt" do
+ device "/etc"
+ fstype "ext4"
+ options %w{bind rw}
+ action %i{ mount enable }
+end \ No newline at end of file
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_ohai_hint.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_ohai_hint.rb
new file mode 100644
index 0000000000..29b97daae4
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_ohai_hint.rb
@@ -0,0 +1,13 @@
+ohai_hint "hint_at_compile_time"
+
+ohai_hint "not_at_compile_time" do
+ compile_time false
+end
+
+ohai_hint "hint_with_content" do
+ content Hash[:a, "test_content"]
+end
+
+ohai_hint "hint_without_content"
+
+ohai_hint "hint_with_json_in_resource_name.json"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_openssl.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_openssl.rb
new file mode 100644
index 0000000000..dba01e368f
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_openssl.rb
@@ -0,0 +1,258 @@
+# leaving this for the future where we test windows too
+base = if platform_family?("windows")
+ 'C:\ssl_test'
+ else
+ "/etc/ssl_test"
+ end
+
+# Create directory if not already present
+directory base do
+ recursive true
+end
+
+#
+# DHPARAM HERE
+#
+
+# Generate new key and certificate
+openssl_dhparam "#{base}/dhparam.pem" do
+ key_length 1024
+ action :create
+end
+
+#
+# RSA KEYS HERE
+#
+
+# Generate new key with des3 cipher using the new resource name
+openssl_rsa_private_key "#{base}/rsakey_des3.pem" do
+ key_length 2048
+ action :create
+end
+
+# Generate new key with aes-128-cbc cipher with the old resource name
+openssl_rsa_key "#{base}/rsakey_aes128cbc.pem" do
+ key_length 1024
+ key_cipher "aes-128-cbc"
+ action :create
+end
+
+# we need to do this with a file resource so that chefspec stepping
+# into openssl_rsa_public_key can function. It's :(
+file "#{base}/private_key.pem" do
+ content "-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,1F2FDA436115C4EE W24gBmtq/Eik2FkSdBh3hF3th3gFq2lMZqSLbho/JVbHFpAQynDbcS9qH5x1fRkt Y7o4A/Sh7noy9kzC1eVIPaQpKFJu5da+uf3t1KxpVMqibzeIE33P9WI+5PzzOm5W xs9shvv/0anU6UMsqBqI+0cmQQ8lw3myTTpO9yWKav2FdTnx7svd+P6BmFknGQaM DYomD0qiB/JzjXbYHLgFspPQXHdyQGhe/YFMlvmjKE0Nut18XJsNwUTWjBA4nRj4 JdlE8XOkWrzIsWKfrBhuhx9bTD0ZVvgssYl2QEh26mv0P0nxx4V/zYx+9U5j0L7q tV4FXfQTgFyctKySuBNi8IT1HFqG9LQps14p8q0XeRigFsRUOVuR0S3eHqg7xiiW QVdF+LgYPpdVNX2mHOSFnHMpFdKLHs8VCNjcGwMNK7avKbne/TJ2NRcL4uhgpsX/ 4tg1kQlwIwtp8MlMqkcinHJ3fjIhWGgjNBVe85NJPVogRDy+c80SqBenaJSavwVA ytmiQOCeon4zhZdscESki+KmsyOWkPB9/zQK76E4ni2IVOL6ZYBMJNTkP47WmA9d Etv7UMxQMI6EYMEH43czvbe4bNCC+hlYotJUM2B52Al7I79W9sSy8cmYi3YZEl0G xtKgY7XwstUBD2XjMuaNyUT0EDjcoa0GhLJSCQkvgn8//BGKaLEyb+Lr+dmHGvxM phCnUKLkfZn9hAFempSJuW4iSaeBKIU3KgYOkBooTuYhXqbN2McoxH6Ec/gnAM5e TIaLiDaHY8IPI4Et5l0sr7v+YF3ZGKC1fL6k4eInNRlhy8oWsFMe79jKkh5wRflt WifTbEdy3D53pVH5lbXyJwpBIOjKJ0OqGWGegu02P5JTsAsniKD+jxNUS8iSOAXL gtpMe4jtqj38hb9D7pBir85Hm+uDqeEuwUqSXAiI+P2F/Jf4ep3h+ek8dcgZtkJQ 3iz92ic2g3M7HW+EE0JcBX+KBwU7yI+UJbWvNQmTXUAYbpoQOLIVm/TrFdGzZ6e9 t0T5wmkE2cS9C3QYiEc7D81nTcTadZChZJDURzUk2REwRGjnunQggHUsj/JKVWqO EPZbpgyDhCaIAkkloWK/SgKny4irMZClhVdeq+v55vDf9nbKR9bgHUb2ZNwp6DQc CPs1BteYthiLtILYzzasMKhlfdoUjEaYziYLGkAQca5XwvwEp0qWg0sMCUUL9pbW 9WzFELBvqNQ1WyIcjb4clcvM0fJdGZ2nKbCAw6zbeSQcGd50NzvTra0xE/J2q6Jo 0V6AGr1Zmu4bJ+tGZCdAIteEO2TosNfS6nrFy15DAe4M4+77ZUGJ8rcwOBopa9qI w7aAyPlfAhrtdSrbOLLp0kRP9EwzSIjSoqc/YJINaNMN8WM7JgnfklmPToT2AqPc 6MOX/Uktag6AXzjcQDtIZSQox326emX1o/huw+7z3/lSXgTdxm3brew/is+9iaQh 5katqPtbec+K/4qydINZSRRFPaoVkg27+6OXvd1AbVS7jmUGHL20xyzA0A9c1csN dm460w4eqbjJEUtDucyIhLPhtYJwPODoRitRmIrzF5DSPrgmSiG93TPiDpRfVPPU -----END RSA PRIVATE KEY-----"
+end
+
+openssl_rsa_public_key "#{base}/rsakey_des3.pub" do
+ private_key_path "#{base}/rsakey_des3.pem"
+ private_key_pass "something"
+ action :create
+end
+
+openssl_rsa_public_key "#{base}/rsakey_2.pub" do
+ private_key_pass "something"
+ private_key_content "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,5EE0AE9A5FE3342E\n\nyb930kj5/4/nd738dPx6XdbDrMCvqkldaz0rHNw8xsWvwARrl/QSPwROG3WY7ROl\nEUttVlLaeVaqRPfQbmTUfzGI8kTMmDWKjw52gJUx2YJTYRgMHAB0dzYIRjeZAaeS\nypXnEfouVav+jKTmmehr1WuVKbzRhQDBSalzeUwsPi2+fb3Bfuo1dRW6xt8yFuc4\nAkv1hCglymPzPHE2L0nSGjcgA2DZu+/S8/wZ4E63442NHPzO4VlLvpNvJrYpEWq9\nB5mJzcdXPeOTjqd13olNTlOZMaKxu9QShu50GreCTVsl8VRkK8NtwbWuPGBZlIFa\njzlS/RaLuzNzfajaKMkcIYco9t7gN2DwnsACHKqEYT8248Ii3NQ+9/M5YcmpywQj\nWGr0UFCSAdCky1lRjwT+zGQKohr+dVR1GaLem+rSZH94df4YBxDYw4rjsKoEhvXB\nv2Vlx+G7Vl2NFiZzxUKh3MvQLr/NDElpG1pYWDiE0DIG13UqEG++cS870mcEyfFh\nSF2SXYHLWyAhDK0viRDChJyFMduC4E7a2P9DJhL3ZvM0KZ1SLMwROc1XuZ704GwO\nYUqtCX5OOIsTti1Z74jQm9uWFikhgWByhVtu6sYL1YTqtiPJDMFhA560zp/k/qLO\nFKiM4eUWV8AI8AVwT6A4o45N2Ru8S48NQyvh/ADFNrgJbVSeDoYE23+DYKpzbaW9\n00BD/EmUQqaQMc670vmI+CIdcdE7L1zqD6MZN7wtPaRIjx4FJBGsFoeDShr+LoTD\nrwbadwrbc2Rf4DWlvFwLJ4pvNvdtY3wtBu79UCOol0+t8DVVSPVASsh+tp8XncDE\nKRljj88WwBjX7/YlRWvQpe5y2UrsHI0pNy8TA1Xkf6GPr6aS2TvQD5gOrAVReSse\n/kktCzZQotjmY1odvo90Zi6A9NCzkI4ZLgAuhiKDPhxZg61IeLppnfFw0v3H4331\nV9SMYgr1Ftov0++x7q9hFPIHwZp6NHHOhdHNI80XkHqtY/hEvsh7MhFMYCgSY1pa\nK/gMcZ/5Wdg9LwOK6nYRmtPtg6fuqj+jB3Rue5/p9dt4kfom4etCSeJPdvP1Mx2I\neNmyQ/7JN9N87FsfZsIj5OK9OB0fPdj0N0m1mlHM/mFt5UM5x39u13QkCt7skEF+\nyOptXcL629/xwm8eg4EXnKFk330WcYSw+sYmAQ9ZTsBxpCMkz0K4PBTPWWXx63XS\nc4J0r88kbCkMCNv41of8ceeGzFrC74dG7i3IUqZzMzRP8cFeps8auhweUHD2hULs\nXwwtII0YQ6/Fw4hgGQ5//0ASdvAicvH0l1jOQScHzXC2QWNg3GttueB/kmhMeGGm\nsHOJ1rXQ4oEckFvBHOvzjP3kuRHSWFYDx35RjWLAwLCG9odQUApHjLBgFNg9yOR0\njW9a2SGxRvBAfdjTa9ZBBrbjlaF57hq7mXws90P88RpAL+xxCAZUElqeW2Rb2rQ6\nCbz4/AtPekV1CYVodGkPutOsew2zjNqlNH+M8XzfonA60UAH20TEqAgLKwgfgr+a\nc+rXp1AupBxat4EHYJiwXBB9XcVwyp5Z+/dXsYmLXzoMOnp8OFyQ9H8R7y9Y0PEu\n-----END RSA PRIVATE KEY-----\n"
+ action :create
+end
+
+#
+# EC KEYS HERE
+#
+
+# Generate a new ec key with key_curve prime256v1 and des3 cipher
+openssl_ec_private_key "#{base}/eckey_prime256v1_des3.pem" do
+ key_curve "prime256v1"
+ key_pass "something"
+ action :create
+end
+
+openssl_ec_public_key "#{base}/eckey_prime256v1_des3.pub" do
+ private_key_path "#{base}/eckey_prime256v1_des3.pem"
+ private_key_pass "something"
+ action :create
+end
+
+openssl_ec_public_key "#{base}/eckey_prime256v1_des3_2.pub" do
+ private_key_content "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEII2VAU9re44mAUzYPWCg+qqwdmP8CplsEg0b/DYPXLg2oAoGCCqGSM49\nAwEHoUQDQgAEKkpMCbIQ2C6Qlp/B+Odp1a9Y06Sm8yqPvCVIkWYP7M8PX5+RmoIv\njGBVf/+mVBx77ji3NpTilMUt2KPZ87lZ3w==\n-----END EC PRIVATE KEY-----\n"
+ action :create
+end
+
+#
+# X509_CERTIFICATE HERE
+#
+
+# Generate new key and certificate
+openssl_x509 "#{base}/mycert.crt" do
+ common_name "mycert.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ subject_alt_name ["IP:127.0.0.1", "DNS:localhost.localdomain"]
+end
+
+# Generate a new certificate from an existing key
+openssl_x509 "#{base}/mycert2.crt" do
+ common_name "mycert2.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ key_file "#{base}/mycert.key"
+end
+
+# Generate a new CA certificate
+openssl_x509 "#{base}/my_ca.crt" do
+ common_name "CA"
+ expire 3650
+ extensions(
+ "keyUsage" => {
+ "values" => %w{
+ keyCertSign
+ keyEncipherment
+ digitalSignature
+ cRLSign},
+ "critical" => true,
+ }
+ )
+end
+
+# Generate and sign a certificate with the CA
+openssl_x509_certificate "#{base}/my_signed_cert.crt" do
+ common_name "mysignedcert.example.com"
+ ca_key_file "#{base}/my_ca.key"
+ ca_cert_file "#{base}/my_ca.crt"
+ expire 365
+ extensions(
+ "keyUsage" => {
+ "values" => %w{
+ keyEncipherment
+ digitalSignature},
+ "critical" => true,
+ },
+ "extendedKeyUsage" => {
+ "values" => %w{serverAuth},
+ "critical" => false,
+ }
+ )
+ subject_alt_name ["IP:127.0.0.1", "DNS:localhost.localdomain"]
+end
+
+# Generate CA with CSR and EC key
+openssl_ec_private_key "#{base}/my_ca2.key" do
+ mode "0400"
+ key_curve "secp521r1"
+end
+
+openssl_x509_request "The my_ca2.csr cert" do
+ path "#{base}/my_ca2.csr"
+ common_name "CA2"
+ key_file "#{base}/my_ca2.key"
+ action :create
+end
+
+openssl_x509_certificate "#{base}/my_ca2.crt" do
+ csr_file "#{base}/my_ca2.csr"
+ ca_key_file "#{base}/my_ca2.key"
+ expire 3650
+ extensions(
+ "keyUsage" => {
+ "values" => %w{
+ keyCertSign
+ keyEncipherment
+ digitalSignature
+ cRLSign},
+ "critical" => true,
+ }
+ )
+end
+
+# Generate key, csr & sign it with CA
+openssl_ec_private_key "#{base}/my_signed_cert2.key"
+
+openssl_x509_request "#{base}/my_signed_cert2.csr" do
+ common_name "mysignedcert2.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ key_file "#{base}/my_signed_cert2.key"
+end
+
+openssl_x509_certificate "#{base}/my_signed_cert2.crt" do
+ csr_file "#{base}/my_signed_cert2.csr"
+ ca_key_file "#{base}/my_ca2.key"
+ ca_cert_file "#{base}/my_ca2.crt"
+ expire 365
+ extensions(
+ "keyUsage" => {
+ "values" => %w{
+ keyEncipherment
+ digitalSignature},
+ "critical" => true,
+ },
+ "extendedKeyUsage" => {
+ "values" => %w{serverAuth},
+ "critical" => false,
+ }
+ )
+ subject_alt_name ["IP:127.0.0.1", "DNS:localhost.localdomain"]
+end
+
+#
+# X509_CRL HERE
+#
+
+openssl_x509_crl "#{base}/my_ca2.crl" do
+ ca_cert_file "#{base}/my_ca2.crt"
+ ca_key_file "#{base}/my_ca2.key"
+ expire 1
+end
+
+openssl_x509_crl "#{base}/my_ca2.crl" do
+ ca_cert_file "#{base}/my_ca2.crt"
+ ca_key_file "#{base}/my_ca2.key"
+ renewal_threshold 2
+end
+
+openssl_x509_crl "#{base}/my_ca2.crl" do
+ ca_cert_file "#{base}/my_ca2.crt"
+ ca_key_file "#{base}/my_ca2.key"
+ serial_to_revoke "C7BCB6602A2E4251EF4E2827A228CB52BC0CEA2F"
+end
+
+#
+# X509_REQUEST HERE
+#
+
+# Generate new ec key and csr
+openssl_x509_request "#{base}/my_ec_request.csr" do
+ common_name "myecrequest.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+end
+
+# Generate a new csr from an existing ec key
+openssl_x509_request "#{base}/my_ec_request2.csr" do
+ common_name "myecrequest2.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ key_file "#{base}/my_ec_request.key"
+end
+
+# Generate new rsa key and csr
+openssl_x509_request "#{base}/my_rsa_request.csr" do
+ common_name "myrsarequest.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ key_type "rsa"
+end
+
+# Generate a new certificate from an existing rsa key
+openssl_x509_request "#{base}/my_rsa_request2.csr" do
+ common_name "myrsarequest2.example.com"
+ org "Test Kitchen Example"
+ org_unit "Kitchens"
+ country "UK"
+ key_file "#{base}/my_rsa_request.key"
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_packages.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_packages.rb
new file mode 100644
index 0000000000..58ede61f52
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_packages.rb
@@ -0,0 +1,24 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: packages
+#
+# Copyright:: Copyright (c) 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{chef-ruby-lvm community_cookbook_releaser}
+
+gems.each do |gem|
+ chef_gem gem do
+ compile_time false
+ end
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_snap.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_snap.rb
new file mode 100644
index 0000000000..0a4ba48a58
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_snap.rb
@@ -0,0 +1,14 @@
+package "snapd" do
+ action :upgrade
+end
+
+service "snapd" do
+ action :start
+end
+
+execute "sleep 5"
+
+snap_package "black" do
+ action :upgrade
+ channel "beta"
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_sudo.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_sudo.rb
new file mode 100644
index 0000000000..4f08405ae9
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_sudo.rb
@@ -0,0 +1,63 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: sudo
+#
+
+sudo "sysadmins" do
+ users "bob_bobberson"
+ groups "sysadmins, superusers"
+ nopasswd true
+end
+
+sudo "tomcat" do
+ user "%tomcat"
+ runas "app_user"
+ commands ["/etc/init.d/tomcat restart", "/etc/init.d/tomcat stop", "/etc/init.d/tomcat start"]
+ defaults ["!requiretty", "env_reset"]
+end
+
+sudo "bob" do
+ user "bob"
+end
+
+sudo "invalid.user" do
+ user "bob"
+end
+
+sudo "tilde-invalid~user" do
+ user "bob"
+ action :create
+end
+
+# Like above, but ensure the tilde at the front gets munged as well
+sudo "~bob" do
+ user "bob"
+end
+
+sudo "alice" do
+ user "alice"
+ command_aliases [{ name: "STARTSSH", command_list: ["/etc/init.d/ssh start", "/etc/init.d/ssh restart", "! /etc/init.d/ssh stop"] }]
+ commands ["STARTSSH"]
+end
+
+sudo "git" do
+ user "git"
+ runas "phabricator"
+ nopasswd true
+ setenv true
+ commands ["/usr/bin/git-upload-pack", "/usr/bin/git-receive-pack"]
+end
+
+sudo "jane" do
+ user "jane"
+ noexec true
+ commands ["/usr/bin/less"]
+end
+
+sudo "rbenv" do
+ env_keep_add %w{PATH RBENV_ROOT RBENV_VERSION}
+end
+
+sudo "java_home" do
+ env_keep_subtract ["JAVA_HOME"]
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_sysctl.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_sysctl.rb
new file mode 100644
index 0000000000..7bc3ae84d2
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_sysctl.rb
@@ -0,0 +1,21 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: sysctl
+#
+
+sysctl "vm.swappiness" do
+ value 19
+end
+
+sysctl "kernel.msgmax" do
+ value 9000
+end
+
+sysctl "kernel.msgmax" do
+ action :remove
+end
+
+sysctl_param "bogus.sysctl_val" do
+ value 9000
+ action :remove
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_tests.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_tests.rb
new file mode 100644
index 0000000000..7f51361693
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_tests.rb
@@ -0,0 +1,30 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: tests
+#
+# Copyright:: Copyright (c) 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.
+# cspell:disable-next-line
+file "/tmp/chef-test-\xFDmlaut" do
+ content "testing illegal UTF-8 char in the filename"
+end
+
+node["network"]["interfaces"].each do |interface_data|
+ interface = interface_data[0]
+ sysctl_param "net/ipv4/conf/#{interface}/rp_filter" do
+ value 0
+ ignore_failure true
+ end
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_yum.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_yum.rb
new file mode 100644
index 0000000000..af36038ca7
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_yum.rb
@@ -0,0 +1,16 @@
+bash "disable yum metadata caching" do
+ code <<-EOH
+ echo http_caching=packages >> /etc/yum.conf
+ EOH
+ only_if { File.exist?("/etc/yum.conf") && File.readlines("/etc/yum.conf").grep(/http_caching=packages/).empty? }
+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 { rhel? }
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb
new file mode 100644
index 0000000000..0acca8e271
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb
@@ -0,0 +1,13 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: _zypper
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+zypper_repository "nginx repo" do
+ baseurl "https://nginx.org/packages/sles/15"
+ gpgkey "https://nginx.org/keys/nginx_signing.key"
+end
+
+zypper_package "nginx"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
new file mode 100644
index 0000000000..20971a0333
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
@@ -0,0 +1,10 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: default
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+include_recipe "::linux" if linux?
+include_recipe "::macos" if macos?
+include_recipe "::windows" if windows?
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
new file mode 100644
index 0000000000..8991cf1594
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
@@ -0,0 +1,130 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: linux
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+hostname "chef-bk-ci.chef.io"
+
+apt_update
+
+chef_sleep "2"
+
+execute "sleep 1"
+
+execute "sleep 1 second" do
+ command "sleep 1"
+ live_stream true
+end
+
+execute "sensitive sleep" do
+ command "sleep 1"
+ sensitive true
+end
+
+timezone "America/Los_Angeles"
+
+include_recipe "::_yum" if platform_family?("rhel")
+
+if platform_family?("rhel", "fedora", "amazon")
+ include_recipe "selinux::disabled"
+end
+
+build_essential do
+ raise_if_unsupported true
+end
+
+include_recipe "::_packages"
+
+include_recipe "ntp"
+
+include_recipe "resolver"
+
+users_manage "sysadmin" do
+ group_id 2300
+ action [:create]
+end
+
+ssh_known_hosts_entry "github.com"
+
+include_recipe "openssh"
+
+include_recipe "nscd"
+
+include_recipe "logrotate"
+
+include_recipe "git"
+
+# test various archive formats in the archive_file resource
+%w{tourism.tar.gz tourism.tar.xz tourism.zip}.each do |archive|
+ cookbook_file File.join(Chef::Config[:file_cache_path], archive) do
+ source archive
+ end
+
+ archive_file archive do
+ path File.join(Chef::Config[:file_cache_path], archive)
+ extract_to File.join(Chef::Config[:file_cache_path], archive.tr(".", "_"))
+ end
+end
+
+user_ulimit "tomcat" do
+ filehandle_soft_limit 8192
+ filehandle_hard_limit 8192
+ process_soft_limit 61504
+ process_hard_limit 61504
+ memory_limit 1024
+ core_limit 2048
+ core_soft_limit 1024
+ core_hard_limit "unlimited"
+ stack_soft_limit 2048
+ stack_hard_limit 2048
+ rtprio_soft_limit 60
+ rtprio_hard_limit 60
+end
+
+include_recipe "::_chef_client_config"
+include_recipe "::_chef_client_trusted_certificate"
+
+chef_client_cron "Run chef-client as a cron job"
+
+chef_client_cron "Run chef-client with base recipe" do
+ minute 0
+ hour "0,12"
+ job_name "chef-client-base"
+ log_directory "/var/log/custom_chef_client_dir/"
+ log_file_name "chef-client-base.log"
+ daemon_options ["--override-runlist mycorp_base::default"]
+end
+
+chef_client_systemd_timer "Run chef-client as a systemd timer" do
+ interval "1hr"
+ cpu_quota 50
+ only_if { systemd? }
+end
+
+chef_client_systemd_timer "a timer that does not exist" do
+ action :remove
+end
+
+locale "set system locale" do
+ lang "en_US.UTF-8"
+ only_if { debian? }
+end
+
+include_recipe "::_apt" if platform_family?("debian")
+include_recipe "::_zypper" if suse?
+include_recipe "::_chef-vault" unless includes_recipe?("end_to_end::chef-vault")
+include_recipe "::_sudo"
+include_recipe "::_sysctl"
+include_recipe "::_alternatives"
+include_recipe "::_cron"
+include_recipe "::_ohai_hint"
+include_recipe "::_openssl"
+include_recipe "::_tests"
+include_recipe "::_mount"
+include_recipe "::_ifconfig"
+
+# at the moment these do not run properly in docker
+# we need to investigate if this is a snap on docker issue or a chef issue
+# include_recipe "::_snap" if platform?("ubuntu")
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb b/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb
new file mode 100644
index 0000000000..d6a3b40bf8
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb
@@ -0,0 +1,91 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: macos
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+chef_sleep "2"
+
+execute "sleep 1"
+
+execute "sleep 1 second" do
+ command "sleep 1"
+ live_stream true
+end
+
+execute "sensitive sleep" do
+ command "sleep 1"
+ sensitive true
+end
+
+timezone "America/Los_Angeles"
+
+include_recipe "ntp"
+
+include_recipe "resolver"
+
+users_manage "remove sysadmin" do
+ group_name "sysadmin"
+ group_id 2300
+ action [:remove]
+end
+
+users_manage "create sysadmin" do
+ group_name "sysadmin"
+ group_id 2300
+ action [:create]
+end
+
+ssh_known_hosts_entry "github.com"
+
+include_recipe "::_chef_client_config"
+include_recipe "::_chef_client_trusted_certificate"
+
+chef_client_launchd "Every 30 mins Infra Client run" do
+ interval 30
+ action :enable
+end
+
+include_recipe "git"
+
+# test various archive formats in the archive_file resource
+%w{tourism.tar.gz tourism.tar.xz tourism.zip}.each do |archive|
+ cookbook_file File.join(Chef::Config[:file_cache_path], archive) do
+ source archive
+ end
+
+ archive_file archive do
+ path File.join(Chef::Config[:file_cache_path], archive)
+ extract_to File.join(Chef::Config[:file_cache_path], archive.tr(".", "_"))
+ end
+end
+
+osx_profile "Remove screensaver profile" do
+ identifier "com.company.screensaver"
+ action :remove
+end
+
+build_essential
+
+launchd "io.chef.testing.fake" do
+ source "io.chef.testing.fake.plist"
+ action "enable"
+end
+
+homebrew_update "update" do
+ action :update
+end
+
+homebrew_package "nethack"
+
+homebrew_package "nethack" do
+ action :purge
+end
+
+homebrew_cask "do-not-disturb"
+
+include_recipe "::_dmg_package"
+include_recipe "::_macos_userdefaults"
+include_recipe "::_ohai_hint"
+include_recipe "::_openssl"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb b/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb
new file mode 100644
index 0000000000..c6cd597fe0
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb
@@ -0,0 +1,136 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: windows
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+# hostnames on windows cannot contain a '.'
+# hostname on windows requires a reboot
+# hostname "chef-bk-ci"
+
+chef_sleep "2"
+
+execute "dir"
+
+powershell_script "sleep 1 second" do
+ code "Start-Sleep -s 1"
+ live_stream true
+end
+
+powershell_script "sensitive sleep" do
+ code "Start-Sleep -s 1"
+ sensitive true
+end
+
+timezone "Pacific Standard time"
+
+include_recipe "ntp"
+
+windows_security_policy "NewGuestName" do
+ secvalue "down_with_guests"
+ action :set
+end
+
+windows_security_policy "EnableGuestAccount" do
+ secvalue "1"
+ action :set
+end
+
+windows_security_policy "LockoutBadCount" do
+ secvalue "15"
+ action :set
+end
+
+windows_security_policy "LockoutDuration" do
+ secvalue "30"
+ action :set
+end
+
+windows_security_policy "ResetLockoutCount" do
+ secvalue "15"
+ action :set
+end
+
+windows_firewall_profile "Domain" do
+ default_inbound_action "Allow"
+ default_outbound_action "Allow"
+ action :enable
+end
+
+windows_firewall_profile "Public" do
+ action :disable
+end
+
+windows_audit_policy "Update Some Advanced Audit Policies to Success and Failure" do
+ subcategory ["Application Generated", "Application Group Management", "Audit Policy Change"]
+ success true
+ failure true
+end
+
+windows_audit_policy "Update Some Advanced Audit Policies to Success only" do
+ subcategory ["Authentication Policy Change", "Authorization Policy Change"]
+ success true
+ failure false
+end
+
+windows_audit_policy "Update Some Advanced Audit Policies to Failure only" do
+ subcategory ["Central Policy Staging", "Certification Services", "Computer Account Management"]
+ success false
+ failure true
+end
+
+windows_audit_policy "Update Some Advanced Audit Policies to No Auditing" do
+ subcategory ["Credential Validation", "DPAPI Activity", "Detailed File Share"]
+ success false
+ failure false
+end
+
+users_manage "remove sysadmin" do
+ group_name "sysadmin"
+ group_id 2300
+ action [:remove]
+end
+
+# FIXME: create is not idempotent. it fails with a windows error if this already exists.
+users_manage "create sysadmin" do
+ group_name "sysadmin"
+ group_id 2300
+ action [:create]
+end
+
+include_recipe "::_chef_client_config"
+include_recipe "::_chef_client_trusted_certificate"
+
+include_recipe "git"
+
+# test various archive formats in the archive_file resource
+%w{tourism.tar.gz tourism.tar.xz tourism.zip}.each do |archive|
+ cookbook_file File.join(Chef::Config[:file_cache_path], archive) do
+ source archive
+ end
+
+ archive_file archive do
+ path File.join(Chef::Config[:file_cache_path], archive)
+ extract_to File.join(Chef::Config[:file_cache_path], archive.tr(".", "_"))
+ end
+end
+
+locale "set system locale" do
+ lang "en_US.UTF-8"
+ only_if { debian? }
+end
+
+include_recipe "::_ohai_hint"
+
+hostname "new-hostname" do
+ windows_reboot false
+end
+
+user "phil" do
+ uid "8019"
+end
+
+user "phil" do
+ action :remove
+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 5124aa4f6f..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", "~> 3.2.2"
-depends "database", "~> 2.3.1"
-depends "mysql", "~> 5.6.3"
-depends "php", "~> 1.5.0"
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/mysql.json b/kitchen-tests/data_bags/passwords/mysql.json
deleted file mode 100644
index af02a6a858..0000000000
--- a/kitchen-tests/data_bags/passwords/mysql.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "id": "mysql",
- "server_root_password": "ilikerandompasswordstoo",
- "server_repl_password": "itoolikerandompasswords"
-}
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/kitchen.azure.yml b/kitchen-tests/kitchen.azure.yml
new file mode 100644
index 0000000000..c44c76579b
--- /dev/null
+++ b/kitchen-tests/kitchen.azure.yml
@@ -0,0 +1,39 @@
+---
+driver:
+ name: azurerm
+
+driver_config:
+ subscription_id: 80b824de-ec53-4116-9868-3deeab10b0cd
+ location: West US2
+ machine_size: Standard_B4ms
+
+provisioner:
+ name: chef_zero
+ deprecations_as_errors: true
+ chef_license: accept-no-persist
+ product_name: chef
+ client_rb:
+ diff_disabled: true
+ always_dump_stacktrace: true
+
+transport:
+ name: winrm
+
+verifier:
+ name: inspec
+ format: progress
+
+platforms:
+- name: windows-10
+ driver:
+ image_id: /subscriptions/80b824de-ec53-4116-9868-3deeab10b0cd/resourceGroups/EDM_Master_Storage_Resource_Group/providers/Microsoft.Compute/images/testkitchen-win-10
+ use_managed_disk: true
+ winrm_powershell_script: |-
+ Set-WSManQuickConfig -Force -SkipNetworkProfileCheck
+ netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any
+ Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
+
+suites:
+ - name: end-to-end
+ run_list:
+ - recipe[end_to_end::windows]
diff --git a/kitchen-tests/kitchen.yml b/kitchen-tests/kitchen.yml
new file mode 100644
index 0000000000..d1780f94f2
--- /dev/null
+++ b/kitchen-tests/kitchen.yml
@@ -0,0 +1,153 @@
+---
+driver:
+ name: dokken
+ privileged: true
+ chef_image: chef/chef
+ chef_version: current
+
+transport:
+ name: dokken
+
+provisioner:
+ name: dokken
+ client_rb:
+ diff_disabled: true
+ always_dump_stacktrace: true
+ chef_license: "accept-no-persist"
+
+lifecycle:
+ pre_converge:
+ - remote: echo "Chef container's Chef / Ohai release:"
+ - remote: /opt/chef/bin/chef-client -v
+ - remote: /opt/chef/bin/ohai -v
+ - remote: /opt/chef/embedded/bin/gem install appbundler appbundle-updater --no-doc
+ - remote: /opt/chef/embedded/bin/appbundle-updater chef ohai <%= File.readlines('../Gemfile.lock', File.expand_path(File.dirname(__FILE__))).find { |l| l =~ /^\s+ohai \((\d+\.\d+\.\d+)\)/ }; 'v' + $1 %> --tarball --github chef/ohai
+ - remote: /opt/chef/embedded/bin/appbundle-updater chef chef <%= ENV['BUILDKITE_COMMIT'] || %x(git rev-parse HEAD).chomp %> --tarball --github chef/chef
+ - remote: echo "Installed Chef / Ohai release:"
+ - remote: /opt/chef/bin/chef-client -v
+ - remote: /opt/chef/bin/ohai -v
+
+verifier:
+ name: inspec
+ format: progress
+
+platforms:
+- name: amazonlinux-2
+ driver:
+ image: dokken/amazonlinux-2
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- 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 install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource
+
+- name: debian-10
+ driver:
+ image: dokken/debian-10
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource
+
+- name: debian-11
+ driver:
+ image: dokken/debian-11
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource
+
+- name: centos-6
+ driver:
+ image: dokken/centos-6
+ pid_one_command: /sbin/init
+ intermediate_instructions:
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+ - RUN wget -O /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo https://www.centos.org/keys/RPM-GPG-KEY-CentOS-SIG-SCLo
+ - RUN printf "[centos-sclo-rh]\nname=CentOS-6 - SCLo rh\nbaseurl=http://vault.centos.org/centos/6/sclo/x86_64/rh\ngpgcheck=1\nenabled=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo" > /etc/yum.repos.d/CentOS-SCLo-rh.repo
+ - RUN yum install -y devtoolset-7
+ - RUN scl enable devtoolset-7 bash
+
+- name: centos-7
+ driver:
+ image: dokken/centos-7
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install e2fsprogs
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: centos-8
+ driver:
+ image: dokken/centos-8
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install e2fsprogs
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: oraclelinux-7
+ driver:
+ image: dokken/oraclelinux-7
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install e2fsprogs
+ - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- name: oraclelinux-8
+ driver:
+ image: dokken/oraclelinux-8
+ pid_one_command: /usr/lib/systemd/systemd
+ intermediate_instructions:
+ - RUN yum -y install e2fsprogs
+ - RUN yum -y reinstall systemd
+ - RUN mkdir /etc/sysconfig/network-scripts # missing from the oracle image
+ - 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 sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+
+- 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 install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource
+
+- name: ubuntu-20.04
+ driver:
+ image: dokken/ubuntu-20.04
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource testing
+
+- name: ubuntu-21.04
+ driver:
+ image: dokken/ubuntu-21.04
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - RUN /usr/bin/apt-get install ifupdown -y # we need this for /etc/network/interfaces & ifconfig resource testing
+
+- name: opensuse-leap-15
+ driver:
+ image: dokken/opensuse-leap-15
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/zypper --non-interactive update
+ - RUN /usr/bin/zypper --non-interactive install net-tools-deprecated # we need this for /etc/network/interfaces & ifconfig resource testing
+
+suites:
+ - name: end-to-end
+ run_list:
+ - recipe[end_to_end::default]
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/end-to-end/_chef_client_config.rb b/kitchen-tests/test/integration/end-to-end/_chef_client_config.rb
new file mode 100644
index 0000000000..4cc1459b7b
--- /dev/null
+++ b/kitchen-tests/test/integration/end-to-end/_chef_client_config.rb
@@ -0,0 +1,11 @@
+client_rb = if os.windows?
+ 'C:\chef\client.rb'
+ else
+ "/etc/chef/client.rb"
+ end
+
+describe file(client_rb) do
+ its("content") { should match(%r{chef_server_url "https://localhost"}) }
+ its("content") { should match(/chef_license "accept"/) }
+ its("content") { should match(/require 'aws-sdk'/) }
+end
diff --git a/kitchen-tests/test/integration/webapp/default_spec.rb b/kitchen-tests/test/integration/webapp/default_spec.rb
deleted file mode 100644
index ec23a57998..0000000000
--- a/kitchen-tests/test/integration/webapp/default_spec.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-#describe port(80) do
-# it { should be_listening }
-# its('processes') {should include 'http'}
-#end
-#
-#describe command("curl http://localhost/index.html") do
-# its("stdout") { should match /Hello, World!/ }
-#end
-
-case os[:family]
-when "debian", "ubuntu"
- ssh_package = "openssh-client"
- ssh_service = "ssh"
- ntp_service = "ntp"
-when "centos", "redhat", "fedora"
- ssh_package = "openssh-clients"
- ssh_service = "sshd"
- ntp_service = "ntpd"
-else
- raise "i don't know the family #{os[:family]}"
-end
-
-describe package("nscd") do
- it { should be_installed }
-end
-
-describe service("nscd") do
- # broken?
- # it { should be_enabled }
- it { should be_installed }
- it { should be_running }
-end
-
-describe package(ssh_package) do
- it { should be_installed }
-end
-
-describe service(ssh_service) do
- it { should be_enabled }
- it { should be_installed }
- it { should be_running }
-end
-
-describe sshd_config do
- its("Protocol") { should cmp 2 }
- its("GssapiAuthentication") { should cmp "no" }
- its("UseDns") { should cmp "no" }
-end
-
-describe ssh_config do
- its("StrictHostKeyChecking") { should cmp "no" }
- its("GssapiAuthentication") { should cmp "no" }
-end
-
-describe package("ntp") do
- it { should be_installed }
-end
-
-describe service(ntp_service) do
- # broken?
- # it { should be_enabled }
- it { should be_installed }
- it { should be_running }
-end
-
-describe service("chef-client") do
- it { should be_enabled }
- it { should be_installed }
- it { should be_running }
-end
-
-describe file("/etc/resolv.conf") do
- its("content") { should match /search\s+chef.io/ }
- its("content") { should match /nameserver\s+8.8.8.8/ }
- its("content") { should match /nameserver\s+8.8.4.4/ }
-end
-
-describe package("gcc") do
- it { should be_installed }
-end
-
-describe package("flex") do
- it { should be_installed }
-end
-
-describe package("bison") do
- it { should be_installed }
-end
-
-describe package("autoconf") do
- it { should be_installed }
-end
-
-%w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversion git traceroute htop tmux s3cmd sysbench }.each do |pkg|
- describe package pkg do
- it { should be_installed }
- end
-end
-
-describe etc_group.where(group_name: "sysadmin") do
- its("users") { should include "adam" }
- its("gids") { should eq [2300] }
-end
-
-describe passwd.users("adam") do
- its("uids") { should eq ["666"] }
-end
-
-describe ntp_conf do
- its("server") { should_not eq nil }
-end
-
-# busted inside of docker containers?
-describe port(22) do
- it { should be_listening }
- its("protocols") { should include "tcp" }
- its("processes") { should eq ["sshd"] }
-end
diff --git a/kitchen-tests/vendor/bundle/bundler/gems/kitchen-ec2-fec3f199a646 b/kitchen-tests/vendor/bundle/bundler/gems/kitchen-ec2-fec3f199a646
deleted file mode 160000
-Subproject fec3f199a646980dc289ac6db9f90e9a9e4b0f6
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/already_exists_error.rb b/lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb
deleted file mode 100644
index 6d9973e8a4..0000000000
--- a/lib-backcompat/chef/chef_fs/file_system/already_exists_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/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..b76dab67ac 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +16,19 @@
# limitations under the License.
#
-require "chef/version"
-require "chef/nil_argument"
-require "chef/mash"
-require "chef/exceptions"
-require "chef/log"
-require "chef/config"
-require "chef/providers"
-require "chef/resources"
-require "chef/shell_out"
+require_relative "chef/version"
-require "chef/daemon"
+require_relative "chef/mash"
+require_relative "chef/exceptions"
+require_relative "chef/log"
+require_relative "chef/config"
+require_relative "chef/providers"
+require_relative "chef/resources"
-require "chef/run_status"
-require "chef/handler"
-require "chef/handler/json_file"
-require "chef/event_dispatch/dsl"
-require "chef/chef_class"
+require_relative "chef/daemon"
+
+require_relative "chef/run_status"
+require_relative "chef/handler"
+require_relative "chef/handler/json_file"
+require_relative "chef/event_dispatch/dsl"
+require_relative "chef/chef_class"
diff --git a/lib/chef/action_collection.rb b/lib/chef/action_collection.rb
new file mode 100644
index 0000000000..1ac47630a9
--- /dev/null
+++ b/lib/chef/action_collection.rb
@@ -0,0 +1,275 @@
+#
+# Copyright:: Copyright (c) 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 "event_dispatch/base"
+
+class Chef
+ class ActionCollection < EventDispatch::Base
+ include Enumerable
+ extend Forwardable
+
+ class ActionRecord
+
+ # @return [Chef::Resource] The declared resource state.
+ #
+ attr_accessor :new_resource
+
+ # @return [Chef::Resource] The current_resource object (before-state). This can be nil
+ # for non-why-run-safe resources in why-run mode, or if load_current_resource itself
+ # threw an exception (which should be considered a bug in that load_current_resource
+ # implementation, but must be handled), or for unprocessed resources.
+ attr_accessor :current_resource
+
+ # @return [Chef::Resource] the after_resource object (after-state). This can be nil for
+ # non custom-resources or resources that do not implement load_after_resource.
+ attr_accessor :after_resource
+
+ # @return [Symbol] # The action that was run (or scheduled to run in the case of "unprocessed" resources).
+ attr_accessor :action
+
+ # @return [Exception] The exception that was thrown
+ attr_accessor :exception
+
+ # @return [Hash] JSON-formatted error description from the Chef::Formatters::ErrorMapper
+ attr_accessor :error_description
+
+ # @return [Numeric] The elapsed time in seconds with machine precision
+ attr_accessor :elapsed_time
+
+ # @return [Chef::Resource::Conditional] The conditional that caused the resource to be skipped
+ attr_accessor :conditional
+
+ # The status of the resource:
+ # - updated: ran and converged
+ # - up_to_date: skipped due to idempotency
+ # - skipped: skipped due to a conditional
+ # - failed: failed with an exception
+ # - unprocessed: resources that were not touched by a run that failed
+ #
+ # @return [Symbol] status
+ #
+ attr_accessor :status
+
+ # The "nesting" level. Outer resources in recipe context are 0 here, while for every
+ # sub-resource_collection inside of a custom resource this number is incremented by 1.
+ # Resources that are fired via build-resource or manually creating and firing
+ #
+ # @return [Integer]
+ #
+ attr_accessor :nesting_level
+
+ def initialize(new_resource, action, nesting_level)
+ @new_resource = new_resource
+ @action = action
+ @nesting_level = nesting_level
+ end
+
+ # @return [Boolean] true if there was no exception
+ def success?
+ !exception
+ end
+ end
+
+ attr_reader :action_records
+ attr_reader :pending_updates
+ attr_reader :run_context
+ attr_reader :consumers
+ attr_reader :events
+
+ def initialize(events, run_context = nil, action_records = [])
+ @action_records = action_records
+ @pending_updates = []
+ @consumers = []
+ @events = events
+ @run_context = run_context
+ end
+
+ def_delegators :@action_records, :each, :last
+
+ # Allows getting at the action_records collection filtered by nesting level and status.
+ #
+ # TODO: filtering by resource type+name
+ #
+ # @return [Chef::ActionCollection]
+ #
+ def filtered_collection(max_nesting: nil, up_to_date: true, skipped: true, updated: true, failed: true, unprocessed: true)
+ subrecords = action_records.select do |rec|
+ ( max_nesting.nil? || rec.nesting_level <= max_nesting ) &&
+ ( rec.status == :up_to_date && up_to_date ||
+ rec.status == :skipped && skipped ||
+ rec.status == :updated && updated ||
+ rec.status == :failed && failed ||
+ rec.status == :unprocessed && unprocessed )
+ end
+ self.class.new(events, run_context, subrecords)
+ end
+
+ # This hook gives us the run_context immediately after it is created so that we can wire up this object to it.
+ #
+ # This also causes the action_collection_registration event to fire, all consumers that have not yet registered with the
+ # action_collection must register via this callback. This is the latest point before resources actually start to get
+ # evaluated.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def cookbook_compilation_start(run_context)
+ run_context.action_collection = self
+ # fire the action_colleciton_registration hook after cookbook_compilation_start -- last chance for consumers to register
+ run_context.events.enqueue(:action_collection_registration, self)
+ @run_context = run_context
+ end
+
+ # Consumers must call register -- either directly or through the action_collection_registration hook. If
+ # nobody has registered any interest, then no action tracking will be done.
+ #
+ # @params object [Object] callers should call with `self`
+ #
+ def register(object)
+ consumers << object
+ end
+
+ # End of an unsuccessful converge used to fire off detect_unprocessed_resources.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def converge_failed(exception)
+ return if consumers.empty?
+
+ detect_unprocessed_resources
+ end
+
+ # Hook to start processing a resource. May be called within processing of an outer resource
+ # so the pending_updates array forms a stack that sub-resources are popped onto and off of.
+ # This is always called.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_action_start(new_resource, action, notification_type = nil, notifier = nil)
+ return if consumers.empty?
+
+ pending_updates << ActionRecord.new(new_resource, action, pending_updates.length)
+ end
+
+ # Hook called after a current resource is loaded. If load_current_resource fails, this hook will
+ # not be called and current_resource will be nil, and the resource_failed hook will be called.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_current_state_loaded(new_resource, action, current_resource)
+ return if consumers.empty?
+
+ current_record.current_resource = current_resource
+ end
+
+ # Hook called after an after resource is loaded. If load_after_resource fails, this hook will
+ # not be called and after_resource will be nil, and the resource_failed hook will be called.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_after_state_loaded(new_resource, action, after_resource)
+ return if consumers.empty?
+
+ current_record.after_resource = after_resource
+ end
+
+ # Hook called after an action is determined to be up to date.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_up_to_date(new_resource, action)
+ return if consumers.empty?
+
+ current_record.status = :up_to_date
+ end
+
+ # Hook called after an action is determined to be skipped due to a conditional.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_skipped(resource, action, conditional)
+ return if consumers.empty?
+
+ current_record.status = :skipped
+ current_record.conditional = conditional
+ end
+
+ # Hook called after an action modifies the system and is marked updated.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_updated(new_resource, action)
+ return if consumers.empty?
+
+ current_record.status = :updated
+ end
+
+ # Hook called after an action fails.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_failed(new_resource, action, exception)
+ return if consumers.empty?
+
+ current_record.status = :failed
+ current_record.exception = exception
+ current_record.error_description = Formatters::ErrorMapper.resource_failed(new_resource, action, exception).for_json
+ end
+
+ # Hook called after an action is completed. This is always called, even if the action fails.
+ #
+ # (see EventDispatch::Base#)
+ #
+ def resource_completed(new_resource)
+ return if consumers.empty?
+
+ current_record.elapsed_time = new_resource.elapsed_time
+
+ # 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
+ # XXX?: what about sensitive data in the current_resource?
+ # FIXME: this needs to be display-logic
+ if current_record.new_resource.sensitive
+ klass = current_record.new_resource.class
+ resource_name = current_record.new_resource.name
+ current_record.new_resource = klass.new(resource_name)
+ end
+
+ action_records << pending_updates.pop
+ end
+
+ private
+
+ # @return [Chef::ActionCollection::ActionRecord] the current record we are working on at the top of the stack
+ def current_record
+ pending_updates[-1]
+ end
+
+ # If the chef-client run fails in the middle, we are left with a half-completed resource_collection, this
+ # method is responsible for adding all of the resources which have not yet been touched. They are marked
+ # as being "unprocessed".
+ #
+ def detect_unprocessed_resources
+ run_context.resource_collection.all_resources.select { |resource| resource.executed_by_runner == false }.each do |resource|
+ Array(resource.action).each do |action|
+ record = ActionRecord.new(resource, action, 0)
+ record.status = :unprocessed
+ action_records << record
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/api_client.rb b/lib/chef/api_client.rb
index 3c1ef7a2b6..75eee6883c 100644
--- a/lib/chef/api_client.rb
+++ b/lib/chef/api_client.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,13 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/search/query"
-require "chef/server_api"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "search/query"
+require_relative "server_api"
# DEPRECATION NOTE
#
@@ -47,62 +47,62 @@ 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(
:name,
arg,
- :regex => /^[\-[:alnum:]_\.]+$/
+ regex: /^[\-[:alnum:]_\.]+$/
)
end
# 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(
:admin,
arg,
- :kind_of => [ TrueClass, FalseClass ]
+ kind_of: [ TrueClass, FalseClass ]
)
end
# 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(
:public_key,
arg,
- :kind_of => String
+ kind_of: String
)
end
# 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)
set_or_return(
:validator,
arg,
- :kind_of => [TrueClass, FalseClass]
+ kind_of: [TrueClass, FalseClass]
)
end
# 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(
:private_key,
arg,
- :kind_of => [String, FalseClass]
+ kind_of: [String, FalseClass]
)
end
@@ -110,7 +110,7 @@ class Chef
# Private key is included if available.
#
# @return [Hash]
- def to_hash
+ def to_h
result = {
"name" => @name,
"public_key" => @public_key,
@@ -123,11 +123,13 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
# The JSON representation of the object.
#
# @return [String] the JSON string.
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def self.from_hash(o)
@@ -140,17 +142,12 @@ 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
def self.http_api
- Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" })
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0" })
end
def self.reregister(name)
@@ -160,9 +157,9 @@ class Chef
def self.list(inflate = false)
if inflate
- response = Hash.new
+ response = {}
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
@@ -174,7 +171,7 @@ class Chef
# Load a client by name via the API
def self.load(name)
response = http_api.get("clients/#{name}")
- if response.kind_of?(Chef::ApiClient)
+ if response.is_a?(Chef::ApiClient)
response
else
from_hash(response)
@@ -188,20 +185,18 @@ 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::HTTPClientException => 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
def reregister
- reregistered_self = http_api.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ reregistered_self = http_api.put("clients/#{name}", { name: name, admin: admin, validator: validator, private_key: true })
if reregistered_self.respond_to?(:[])
private_key(reregistered_self["private_key"])
else
@@ -226,7 +221,7 @@ class Chef
end
def http_api
- @http_api ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" })
+ @http_api ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0" })
end
end
diff --git a/lib/chef/api_client/registration.rb b/lib/chef/api_client/registration.rb
index e8ab0149e8..b05a2852a8 100644
--- a/lib/chef/api_client/registration.rb
+++ b/lib/chef/api_client/registration.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,10 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/server_api"
-require "chef/exceptions"
+require_relative "../config"
+require_relative "../server_api"
+require_relative "../exceptions"
+require "fileutils" unless defined?(FileUtils)
class Chef
class ApiClient
@@ -59,6 +60,7 @@ class Chef
rescue Net::HTTPFatalError => e
# HTTPFatalError implies 5xx.
raise if retries <= 0
+
retries -= 1
Chef::Log.warn("Failed to register new client, #{retries} tries remaining")
Chef::Log.warn("Response: HTTP #{e.response.code} - #{e}")
@@ -69,8 +71,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)
+ unless File.exist?(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.exist?(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
@@ -85,10 +94,11 @@ class Chef
def create_or_update
create
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# If create fails because the client exists, attempt to update. This
# requires admin privileges.
raise unless e.response.code == "409"
+
update
end
@@ -131,7 +141,7 @@ class Chef
end
def put_data
- base_put_data = { :name => name, :admin => false }
+ base_put_data = { name: name, admin: false }
if self_generate_keys?
base_put_data[:public_key] = generated_public_key
else
@@ -141,19 +151,18 @@ class Chef
end
def post_data
- post_data = { :name => name, :admin => false }
+ post_data = { name: name, admin: false }
post_data[:public_key] = generated_public_key if self_generate_keys?
post_data
end
def http_api
@http_api ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url],
- {
- :api_version => "0",
- :client_name => Chef::Config[:validation_client_name],
- :signing_key_filename => Chef::Config[:validation_key],
- }
- )
+ {
+ api_version: "0",
+ client_name: Chef::Config[:validation_client_name],
+ signing_key_filename: Chef::Config[:validation_key],
+ })
end
# Whether or not to generate keys locally and post the public key to the
diff --git a/lib/chef/api_client_v1.rb b/lib/chef/api_client_v1.rb
index 47b0cd1c53..6178cb91c3 100644
--- a/lib/chef/api_client_v1.rb
+++ b/lib/chef/api_client_v1.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,16 +17,16 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/search/query"
-require "chef/exceptions"
-require "chef/mixin/api_version_request_handling"
-require "chef/server_api"
-require "chef/api_client"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "search/query"
+require_relative "exceptions"
+require_relative "mixin/api_version_request_handling"
+require_relative "server_api"
+require_relative "api_client"
# COMPATIBILITY NOTE
#
@@ -44,7 +44,7 @@ class Chef
include Chef::Mixin::ParamsValidate
include Chef::Mixin::ApiVersionRequestHandling
- SUPPORTED_API_VERSIONS = [0, 1]
+ SUPPORTED_API_VERSIONS = [0, 1].freeze
# Create a new Chef::ApiClientV1 object.
def initialize
@@ -57,88 +57,88 @@ class Chef
end
def chef_rest_v0
- @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0", :inflate_json_class => false })
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0", inflate_json_class: false })
end
def chef_rest_v1
- @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "1", :inflate_json_class => false })
+ @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "1", inflate_json_class: false })
end
def self.http_api
- Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "1", :inflate_json_class => false })
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "1", inflate_json_class: false })
end
# 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(
:name,
arg,
- :regex => /^[\-[:alnum:]_\.]+$/
+ regex: /^[\-[:alnum:]_\.]+$/
)
end
# 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(
:admin,
arg,
- :kind_of => [ TrueClass, FalseClass ]
+ kind_of: [ TrueClass, FalseClass ]
)
end
# 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(
:public_key,
arg,
- :kind_of => String
+ kind_of: String
)
end
# 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)
set_or_return(
:validator,
arg,
- :kind_of => [TrueClass, FalseClass]
+ kind_of: [TrueClass, FalseClass]
)
end
# 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(
:private_key,
arg,
- :kind_of => [String, TrueClass, FalseClass]
+ kind_of: [String, TrueClass, FalseClass]
)
end
# 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(
:create_key,
arg,
- :kind_of => [ TrueClass, FalseClass ]
+ kind_of: [ TrueClass, FalseClass ]
)
end
@@ -146,7 +146,7 @@ class Chef
# Private key is included if available.
#
# @return [Hash]
- def to_hash
+ def to_h
result = {
"name" => @name,
"validator" => @validator,
@@ -159,11 +159,13 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
# The JSON representation of the object.
#
# @return [String] the JSON string.
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def self.from_hash(o)
@@ -188,9 +190,9 @@ class Chef
def self.list(inflate = false)
if inflate
- response = Hash.new
+ response = {}
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 +202,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,29 +215,27 @@ 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::HTTPClientException => e
+ # If that fails, go ahead and try and update it
+ if e.response.code == "404"
+ create
+ else
+ raise e
end
end
def reregister
# Try API V0 and if it fails due to V0 not being supported, raise the proper error message.
# reregister only supported in API V0 or lesser.
- reregistered_self = chef_rest_v0.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ reregistered_self = chef_rest_v0.put("clients/#{name}", { name: name, admin: admin, validator: validator, private_key: true })
if reregistered_self.respond_to?(:[])
private_key(reregistered_self["private_key"])
else
private_key(reregistered_self.private_key)
end
self
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# if there was a 406 related to versioning, give error explaining that
# only API version 0 is supported for reregister command
if e.response.code == "406" && e.response["x-ops-server-api-version"]
@@ -255,7 +256,7 @@ class Chef
# it was never implemented, we will simply ignore that functionality
# as it is being deprecated.
# Delete this comment after V0 support is dropped.
- payload = { :name => name }
+ payload = { name: name }
payload[:validator] = validator unless validator.nil?
# DEPRECATION
@@ -265,10 +266,11 @@ class Chef
begin
new_client = chef_rest_v1.put("clients/#{name}", payload)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# rescue API V0 if 406 and the server supports V0
supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
raise e unless supported_versions && supported_versions.include?(0)
+
new_client = chef_rest_v0.put("clients/#{name}", payload)
end
@@ -278,11 +280,11 @@ class Chef
# Create the client via the REST API
def create
payload = {
- :name => name,
- :validator => validator,
+ name: name,
+ validator: validator,
# this field is ignored in API V1, but left for backwards-compat,
# can remove after OSC 11 support is finished?
- :admin => admin,
+ admin: admin,
}
begin
# try API V1
@@ -302,7 +304,7 @@ class Chef
new_client.delete("chef_key")
end
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# rescue API V0 if 406 and the server supports V0
supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
raise e unless supported_versions && supported_versions.include?(0)
@@ -313,7 +315,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_h.merge(new_client))
end
# As a string
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index f9735a3769..117f498831 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "pp"
-require "socket"
-require "chef/config"
-require "chef/config_fetcher"
-require "chef/exceptions"
-require "chef/local_mode"
-require "chef/log"
-require "chef/platform"
-require "mixlib/cli"
-require "tmpdir"
-require "rbconfig"
-require "chef/application/exit_code"
-require "yaml"
+require "pp" unless defined?(PP)
+require "socket" unless defined?(Socket)
+require_relative "config"
+require_relative "exceptions"
+require_relative "local_mode"
+require_relative "log"
+require_relative "platform"
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require "tmpdir" unless defined?(Dir.mktmpdir)
+require "rbconfig" unless defined?(RbConfig)
+require_relative "application/exit_code"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+module LicenseAcceptance
+ autoload :Acceptor, "license_acceptance/acceptor"
+end
class Chef
class Application
@@ -39,13 +41,17 @@ class Chef
@chef_client = nil
@chef_client_json = nil
+ end
- # Always switch to a readable directory. Keeps subsequent Dir.chdir() {}
- # from failing due to permissions when launched as a less privileged user.
+ # 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
@@ -53,10 +59,11 @@ class Chef
end
# Get this party started
- def run
+ def run(enforce_license: false)
setup_signal_handlers
reconfigure
setup_application
+ check_license_acceptance if enforce_license
run_application
end
@@ -69,30 +76,61 @@ class Chef
Chef::Application.fatal!("SIGTERM received, stopping", Chef::Exceptions::SigTerm.new)
end
- unless Chef::Platform.windows?
+ unless ChefUtils.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
+ begin
+ load_config_file
+ rescue Exception => e
+ Chef::Application.fatal!(e.message, Chef::Exceptions::ConfigurationError.new)
+ end
+ 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
+ require_relative "config_fetcher"
+ 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
@@ -100,128 +138,102 @@ 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 instead.")
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 instead.")
+ logger.warn("*****************************************")
else
config_content = config_fetcher.read_config
apply_config(config_content, config[:config_file])
end
extra_config_options = config.delete(:config_option)
- Chef::Config.merge!(config)
- if extra_config_options
- extra_parsed_options = extra_config_options.inject({}) do |memo, option|
- # Sanity check value.
- Chef::Application.fatal!("Unparsable config option #{option.inspect}") if option.empty? || !option.include?("=")
- # 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
- Chef::Config.merge!(extra_parsed_options)
- end
+ 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)
+ end
+
+ # Set the specific recipes to Chef::Config if the recipes are valid
+ # otherwise log a fatal error message and exit the application.
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.is_a?(Array) &&
+ (cli_arguments.empty? || cli_arguments.all? { |file| File.file?(file) } )
+ chef_config[:specific_recipes] =
+ cli_arguments.map { |file| File.expand_path(file) }
+ else
+ Chef::Application.fatal!("Invalid argument; could not find the following recipe files: \"" +
+ cli_arguments.select { |file| !File.file?(file) }.join('", "') + '"')
+ end
end
- # Initialize and configure the logger.
- # === Loggers and Formatters
- # In Chef 10.x and previous, the Logger was the primary/only way that Chef
- # communicated information to the user. In Chef 10.14, a new system, "output
- # formatters" was added, and in Chef 11.0+ it is the default when running
- # chef in a console (detected by `STDOUT.tty?`). Because output formatters
- # are more complex than the logger system and users have less experience with
- # them, the config option `force_logger` is provided to restore the Chef 10.x
- # behavior.
- #
- # Conversely, for users who want formatter output even when chef is running
- # 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.
- #
- # === 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]))
- if want_additional_logger?
- configure_stdout_logger
+ logger.init(MonoLogger.new(chef_config[:log_location][0]))
+ chef_config[:log_location][1..].each do |log_location|
+ logger.loggers << MonoLogger.new(log_location)
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})")
+ 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.
+ # merge Chef::Config[:log_location] and config[:log_location_cli]
+ # - the nil default value of log_location_cli means STDOUT
+ # - the nil default value of log_location is removed
+ # - Arrays are supported
+ # - syslog + winevt are converted to those specific logger objects
+ #
def configure_log_location
- log_location = Chef::Config[:log_location]
- return unless log_location.respond_to?(:to_sym)
-
- Chef::Config[:log_location] =
- case log_location.to_sym
- when :syslog then Chef::Log::Syslog.new
- when :win_evt then Chef::Log::WinEvt.new
- else log_location # Probably a path; let MonoLogger sort it out
+ log_location_cli = [ config[:log_location_cli] ].flatten.map { |log_location| log_location.nil? ? STDOUT : log_location }
+
+ chef_config[:log_location] = [ chef_config[:log_location], log_location_cli ].flatten.compact.uniq
+
+ chef_config[:log_location].map! do |log_location|
+ case log_location
+ when :syslog, "syslog"
+ force_force_logger
+ logger::Syslog.new
+ when :win_evt, "win_evt"
+ force_force_logger
+ logger::WinEvt.new
+ else
+ # should be a path or STDOUT
+ log_location
end
+ 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])
+ # Force the logger by default for the :winevt and :syslog loggers. Since we do not and cannot
+ # support multiple log levels in a mix-and-match situation with formatters and loggers, and the
+ # formatters do not support syslog, we force the formatter off by default and the log level is
+ # thus info by default. Users can add `--force-formatter -l info` to get back formatter output
+ # on STDOUT along with syslog logging.
+ #
+ def force_force_logger
+ chef_config[:force_logger] = true unless chef_config[:force_formatter]
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)
+ # 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
- end
-
- # if log_level is `:auto`, convert it to :warn (when using output formatter)
- # or :info (no output formatter). See also +using_output_formatter?+
+ # The :auto formatter defaults to :warn with the formatter and :info with the logger
def resolve_log_level
- if auto_log_level?
- if using_output_formatter?
- :warn
- else
- :info
- end
+ if chef_config[:log_level] == :auto
+ using_output_formatter? ? :warn : :info
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
@@ -229,6 +241,15 @@ class Chef
raise Chef::Exceptions::Application, "#{self}: you must override setup_application"
end
+ def check_license_acceptance
+ LicenseAcceptance::Acceptor.check_and_persist!(
+ "infra-client",
+ Chef::VERSION.to_s,
+ logger: logger,
+ provided: Chef::Config[:chef_license]
+ )
+ end
+
# Actually run the application
def run_application
raise Chef::Exceptions::Application, "#{self}: you must override run_application"
@@ -242,12 +263,12 @@ class Chef
Chef::LocalMode.with_server_connectivity do
override_runlist = config[:override_runlist]
- override_runlist ||= [] if specific_recipes.size > 0
@chef_client = Chef::Client.new(
@chef_client_json,
override_runlist: override_runlist,
specific_recipes: specific_recipes,
- runlist: config[:runlist]
+ runlist: config[:runlist],
+ logger: logger
)
@chef_client_json = nil
@@ -269,7 +290,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) && !ChefUtils.windows?
end
# Run chef-client once and then exit. If TERM signal is received, ignores the
@@ -277,7 +298,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
@@ -286,52 +307,54 @@ class Chef
end
def fork_chef_client
- Chef::Log.info "Forking chef instance to converge..."
+ logger.info "Forking #{ChefUtils::Dist::Infra::PRODUCT} 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] ? ChefUtils::Dist::Solo::EXEC : ChefUtils::Dist::Infra::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)
+ 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 #{ChefUtils::Dist::Infra::CLIENT} 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
def handle_child_exit(pid_and_status)
status = pid_and_status[1]
return true if status.success?
+
message = if status.signaled?
- "Chef run process terminated by signal #{status.termsig} (#{Signal.list.invert[status.termsig]})"
+ "#{ChefUtils::Dist::Infra::PRODUCT} run process terminated by signal #{status.termsig} (#{Signal.list.invert[status.termsig]})"
else
- "Chef run process exited unsuccessfully (exit code #{status.exitstatus})"
+ "#{ChefUtils::Dist::Infra::PRODUCT} run process exited unsuccessfully (exit code #{status.exitstatus})"
end
raise Exceptions::ChildConvergeError, message
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}'", error)
+ filtered_trace.each { |line| logger.fatal(" " + line ) }
+ raise Chef::Exceptions::ConfigurationError.new("Aborting due to error in '#{config_file_path}': #{error}")
+ # Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", Chef::Exceptions::ConfigurationError.new(error))
end
# This is a hook for testing
@@ -339,18 +362,12 @@ 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)
- while cause != nil
+ 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
@@ -358,10 +375,14 @@ class Chef
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)
+ Chef::FileCache.store("#{ChefUtils::Dist::Infra::SHORT}-stacktrace.out", chef_stacktrace_out)
+ logger.fatal("Stacktrace dumped to #{Chef::FileCache.load("#{ChefUtils::Dist::Infra::SHORT}-stacktrace.out", false)}")
+ logger.fatal("Please provide the contents of the stacktrace.out file if you file a bug report")
+ if Chef::Config[:always_dump_stacktrace]
+ logger.fatal(message)
+ else
+ logger.debug(message)
+ end
true
end
@@ -371,12 +392,15 @@ class Chef
# Log a fatal error message to both STDERR and the Logger, exit the application
def fatal!(msg, err = nil)
- Chef::Log.fatal(msg)
+ if Chef::Config[:always_dump_stacktrace]
+ msg << "\n#{err.backtrace.join("\n")}"
+ end
+ logger.fatal(msg)
Process.exit(normalize_exit_code(err))
end
def exit!(msg, err = nil)
- Chef::Log.debug(msg)
+ 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 3e3fb58448..3559f8e416 100644
--- a/lib/chef/application/apply.rb
+++ b/lib/chef/application/apply.rb
@@ -17,101 +17,122 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef"
-require "chef/application"
-require "chef/client"
-require "chef/config"
-require "chef/log"
-require "fileutils"
-require "tempfile"
-require "chef/providers"
-require "chef/resources"
+require_relative "../../chef"
+require_relative "../application"
+require_relative "../client"
+require_relative "../config"
+require_relative "../config_fetcher"
+require_relative "../log"
+require "fileutils" unless defined?(FileUtils)
+require "tempfile" unless defined?(Tempfile)
+require_relative "../providers"
+require_relative "../resources"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "license_acceptance/cli_flags/mixlib_cli"
class Chef::Application::Apply < Chef::Application
+ include LicenseAcceptance::CLIFlags::MixlibCLI
- banner "Usage: chef-apply [RECIPE_FILE | -e RECIPE_TEXT | -s] [OPTIONS]"
+ banner "Usage: #{ChefUtils::Dist::Apply::EXEC} [RECIPE_FILE | -e RECIPE_TEXT | -s] [OPTIONS]"
option :execute,
- :short => "-e RECIPE_TEXT",
- :long => "--execute RECIPE_TEXT",
- :description => "Execute resources supplied in a string",
- :proc => nil
+ short: "-e RECIPE_TEXT",
+ long: "--execute RECIPE_TEXT",
+ description: "Execute resources supplied in a string.",
+ proc: nil
option :stdin,
- :short => "-s",
- :long => "--stdin",
- :description => "Execute resources read from STDIN",
- :boolean => true
+ short: "-s",
+ long: "--stdin",
+ description: "Execute resources read from STDIN.",
+ boolean: true
option :json_attribs,
- :short => "-j JSON_ATTRIBS",
- :long => "--json-attributes JSON_ATTRIBS",
- :description => "Load attributes from a JSON file or URL",
- :proc => nil
+ short: "-j JSON_ATTRIBS",
+ long: "--json-attributes JSON_ATTRIBS",
+ description: "Load attributes from a JSON file or URL.",
+ proc: nil
option :force_logger,
- :long => "--force-logger",
- :description => "Use logger output instead of formatter output",
- :boolean => true,
- :default => false
+ long: "--force-logger",
+ description: "Use logger output instead of formatter output.",
+ boolean: true,
+ default: false
option :force_formatter,
- :long => "--force-formatter",
- :description => "Use formatter output instead of logger output",
- :boolean => true,
- :default => false
+ long: "--force-formatter",
+ description: "Use formatter output instead of logger output.",
+ boolean: true,
+ default: false
option :formatter,
- :short => "-F FORMATTER",
- :long => "--format FORMATTER",
- :description => "output format to use",
- :proc => lambda { |format| Chef::Config.add_formatter(format) }
+ short: "-F FORMATTER",
+ long: "--format FORMATTER",
+ description: "The output format to use.",
+ proc: lambda { |format| Chef::Config.add_formatter(format) }
option :log_level,
- :short => "-l LEVEL",
- :long => "--log_level LEVEL",
- :description => "Set the log level (debug, info, warn, error, fatal)",
- :proc => lambda { |l| l.to_sym }
+ short: "-l LEVEL",
+ long: "--log_level LEVEL",
+ description: "Set the log level (trace, debug, info, warn, error, fatal).",
+ proc: lambda { |l| l.to_sym }
+
+ option :log_location_cli,
+ short: "-L LOGLOCATION",
+ long: "--logfile LOGLOCATION",
+ description: "Set the log file location, defaults to STDOUT - recommended for daemonizing."
+
+ option :always_dump_stacktrace,
+ long: "--[no-]always-dump-stacktrace",
+ boolean: true,
+ default: false,
+ description: "Always dump the stacktrace regardless of the log_level setting."
option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true,
- :show_options => true,
- :exit => 0
+ short: "-h",
+ long: "--help",
+ description: "Show this help message.",
+ on: :tail,
+ boolean: true,
+ show_options: true,
+ exit: 0
option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
+ short: "-v",
+ long: "--version",
+ description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version.",
+ boolean: true,
+ proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
option :why_run,
- :short => "-W",
- :long => "--why-run",
- :description => "Enable whyrun mode",
- :boolean => true
+ short: "-W",
+ long: "--why-run",
+ description: "Enable whyrun mode.",
+ boolean: true
+
+ option :yaml,
+ long: "--yaml",
+ description: "Parse recipe as YAML",
+ boolean: true,
+ default: false
option :profile_ruby,
- :long => "--[no-]profile-ruby",
- :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
- :boolean => true,
- :default => false
+ long: "--[no-]profile-ruby",
+ description: "Dump complete Ruby call graph stack of entire #{ChefUtils::Dist::Infra::PRODUCT} run (expert only).",
+ boolean: true,
+ default: false
option :color,
- :long => "--[no-]color",
- :boolean => true,
- :default => true,
- :description => "Use colored output, defaults to enabled"
+ long: "--[no-]color",
+ boolean: true,
+ default: true,
+ description: "Use colored output, defaults to enabled."
option :minimal_ohai,
- :long => "--minimal-ohai",
- :description => "Only run the bare minimum ohai plugins chef needs to function",
- :boolean => true
+ long: "--minimal-ohai",
+ description: "Only run the bare minimum Ohai plugins #{ChefUtils::Dist::Infra::PRODUCT} needs to function.",
+ boolean: true
attr_reader :json_attribs
@@ -160,7 +181,7 @@ class Chef::Application::Apply < Chef::Application
else
Chef::RunContext.new(@chef_client.node, {}, @chef_client.events)
end
- recipe = Chef::Recipe.new("(chef-apply cookbook)", "(chef-apply recipe)", run_context)
+ recipe = Chef::Recipe.new("(#{ChefUtils::Dist::Apply::EXEC} cookbook)", "(#{ChefUtils::Dist::Apply::EXEC} recipe)", run_context)
[recipe, run_context]
end
@@ -181,7 +202,7 @@ class Chef::Application::Apply < Chef::Application
@recipe_text = STDIN.read
temp_recipe_file
else
- if !ARGV[0]
+ unless ARGV[0]
puts opt_parser
Chef::Application.exit! "No recipe file provided", Chef::Exceptions::RecipeNotFound.new
end
@@ -189,32 +210,38 @@ class Chef::Application::Apply < Chef::Application
@recipe_text, @recipe_fh = read_recipe_file @recipe_filename
end
recipe, run_context = get_recipe_and_run_context
- recipe.instance_eval(@recipe_text, @recipe_filename, 1)
+ if config[:yaml] || File.extname(@recipe_filename) == ".yml"
+ logger.info "Parsing recipe as YAML"
+ recipe.from_yaml(@recipe_text)
+ else
+ recipe.instance_eval(@recipe_text, @recipe_filename, 1)
+ end
runner = Chef::Runner.new(run_context)
- begin
+ catch(:end_client_run_early) do
+
runner.converge
ensure
@recipe_fh.close
+
end
Chef::Platform::Rebooter.reboot_if_needed!(runner)
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}", e)
- 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
- def run
+ # Get this party started
+ def run(enforce_license: false)
reconfigure
+ check_license_acceptance if enforce_license
run_application
end
diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb
new file mode 100644
index 0000000000..ad8e8b69c2
--- /dev/null
+++ b/lib/chef/application/base.rb
@@ -0,0 +1,455 @@
+#
+# Copyright:: Copyright (c) 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 "../application"
+require_relative "../client"
+require_relative "../log"
+require_relative "../config"
+require_relative "../mixin/shell_out"
+require_relative "../config_fetcher"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "../daemon"
+require "chef-config/mixin/dot_d"
+require "license_acceptance/cli_flags/mixlib_cli"
+module Mixlib
+ autoload :Archive, "mixlib/archive"
+end
+
+# This is a temporary class being used as a part of an effort to reduce duplication
+# between Chef::Application::Client and Chef::Application::Solo.
+#
+# If you are looking to make edits to the Client/Solo behavior please make changes here.
+#
+# If you are looking to reference or subclass this class, use Chef::Application::Client
+# instead. This base class will be removed once the work is complete and external code
+# will break.
+#
+# @deprecated use Chef::Application::Client instead, this will be removed in Chef-16
+#
+class Chef::Application::Base < Chef::Application
+ include Chef::Mixin::ShellOut
+ include ChefConfig::Mixin::DotD
+ include LicenseAcceptance::CLIFlags::MixlibCLI
+
+ # Mimic self_pipe sleep from Unicorn to capture signals safely
+ SELF_PIPE = [] # rubocop:disable Style/MutableConstant
+
+ option :config_option,
+ long: "--config-option OPTION=VALUE",
+ description: "Override a single configuration option.",
+ proc: lambda { |option, existing|
+ (existing ||= []) << option
+ existing
+ }
+
+ option :once,
+ long: "--once",
+ description: "Cancel any interval or splay options, run #{ChefUtils::Dist::Infra::PRODUCT} once and exit.",
+ boolean: true
+
+ option :formatter,
+ short: "-F FORMATTER",
+ long: "--format FORMATTER",
+ description: "The output format to use.",
+ proc: lambda { |format| Chef::Config.add_formatter(format) }
+
+ option :force_logger,
+ long: "--force-logger",
+ description: "Use logger output instead of formatter output.",
+ boolean: true,
+ default: false
+
+ option :force_formatter,
+ long: "--force-formatter",
+ description: "Use formatter output instead of logger output.",
+ boolean: true,
+ default: false
+
+ option :profile_ruby,
+ long: "--[no-]profile-ruby",
+ description: "Dump complete Ruby call graph stack of entire #{ChefUtils::Dist::Infra::PRODUCT} run (expert only).",
+ boolean: true,
+ default: false
+
+ option :color,
+ long: "--[no-]color",
+ boolean: true,
+ default: true,
+ description: "Use colored output, defaults to enabled."
+
+ option :log_level,
+ short: "-l LEVEL",
+ long: "--log_level LEVEL",
+ description: "Set the log level (auto, trace, debug, info, warn, error, fatal).",
+ proc: lambda { |l| l.to_sym }
+
+ option :log_location_cli,
+ short: "-L LOGLOCATION",
+ long: "--logfile LOGLOCATION",
+ description: "Set the log file location, defaults to STDOUT - recommended for daemonizing."
+
+ option :always_dump_stacktrace,
+ long: "--[no-]always-dump-stacktrace",
+ boolean: true,
+ default: false,
+ description: "Always dump the stacktrace regardless of the log_level setting."
+
+ option :help,
+ short: "-h",
+ long: "--help",
+ description: "Show this help message.",
+ on: :tail,
+ boolean: true,
+ show_options: true,
+ exit: 0
+
+ option :user,
+ short: "-u USER",
+ long: "--user USER",
+ description: "User to set privilege to.",
+ proc: nil
+
+ option :group,
+ short: "-g GROUP",
+ long: "--group GROUP",
+ description: "Group to set privilege to.",
+ proc: nil
+
+ option :lockfile,
+ long: "--lockfile LOCKFILE",
+ description: "Set the lockfile location. Prevents multiple client processes from converging at the same time.",
+ proc: nil
+
+ option :interval,
+ short: "-i SECONDS",
+ long: "--interval SECONDS",
+ description: "Run #{ChefUtils::Dist::Infra::PRODUCT} periodically, in seconds.",
+ proc: lambda { |s| s.to_i }
+
+ option :json_attribs,
+ short: "-j JSON_ATTRIBS",
+ long: "--json-attributes JSON_ATTRIBS",
+ description: "Load attributes from a JSON file or URL.",
+ proc: nil
+
+ option :node_name,
+ short: "-N NODE_NAME",
+ long: "--node-name NODE_NAME",
+ description: "The node name for this client.",
+ proc: nil
+
+ option :splay,
+ short: "-s SECONDS",
+ long: "--splay SECONDS",
+ description: "The splay time for running at intervals, in seconds.",
+ proc: lambda { |s| s.to_i }
+
+ option :environment,
+ short: "-E ENVIRONMENT",
+ long: "--environment ENVIRONMENT",
+ description: "Set the #{ChefUtils::Dist::Infra::PRODUCT} environment on the node."
+
+ option :client_fork,
+ short: "-f",
+ long: "--[no-]fork",
+ description: "Fork #{ChefUtils::Dist::Infra::PRODUCT} process."
+
+ option :why_run,
+ short: "-W",
+ long: "--why-run",
+ description: "Enable whyrun mode.",
+ boolean: true
+
+ option :override_runlist,
+ short: "-o RunlistItem,RunlistItem...",
+ long: "--override-runlist RunlistItem,RunlistItem...",
+ description: "Replace current run list with specified items for a single run.",
+ proc: lambda { |items|
+ items = items.split(",")
+ items.compact.map do |item|
+ Chef::RunList::RunListItem.new(item)
+ end
+ }
+
+ option :run_lock_timeout,
+ long: "--run-lock-timeout SECONDS",
+ description: "Set maximum duration to wait for another client run to finish, default is indefinitely.",
+ proc: lambda { |s| s.to_i }
+
+ option :version,
+ short: "-v",
+ long: "--version",
+ description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version.",
+ boolean: true,
+ proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
+
+ option :minimal_ohai,
+ long: "--minimal-ohai",
+ description: "Only run the bare minimum Ohai plugins #{ChefUtils::Dist::Infra::PRODUCT} needs to function.",
+ boolean: true
+
+ option :delete_entire_chef_repo,
+ long: "--delete-entire-chef-repo",
+ description: "DANGEROUS: does what it says, only useful with --recipe-url.",
+ boolean: true
+
+ option :ez,
+ long: "--ez",
+ description: "A memorial for Ezra Zygmuntowicz.",
+ boolean: true
+
+ option :target,
+ short: "-t TARGET",
+ long: "--target TARGET",
+ description: "Target #{ChefUtils::Dist::Infra::PRODUCT} against a remote system or device",
+ proc: lambda { |target|
+ Chef::Log.warn "-- EXPERIMENTAL -- Target mode activated, resources and dsl may change without warning -- EXPERIMENTAL --"
+ target
+ }
+
+ option :disable_config,
+ long: "--disable-config",
+ description: "Refuse to load a config file and use defaults. This is for development and not a stable API.",
+ boolean: true
+
+ if Chef::Platform.windows?
+ option :fatal_windows_admin_check,
+ short: "-A",
+ long: "--fatal-windows-admin-check",
+ description: "Fail the run when #{ChefUtils::Dist::Infra::CLIENT} doesn't have administrator privileges on Windows.",
+ boolean: true
+ end
+
+ option :fips,
+ long: "--[no-]fips",
+ description: "Enable FIPS mode.",
+ boolean: true
+
+ option :solo_legacy_mode,
+ long: "--legacy-mode",
+ description: "Run in legacy mode.",
+ boolean: true
+
+ option :chef_server_url,
+ short: "-S CHEFSERVERURL",
+ long: "--server CHEFSERVERURL",
+ description: "The #{ChefUtils::Dist::Server::PRODUCT} URL.",
+ proc: nil
+
+ option :validation_key,
+ short: "-K KEY_FILE",
+ long: "--validation_key KEY_FILE",
+ description: "Set the validation key file location, used for registering new clients.",
+ proc: nil
+
+ option :client_key,
+ short: "-k KEY_FILE",
+ long: "--client_key KEY_FILE",
+ description: "Set the client key file location.",
+ proc: nil
+
+ option :enable_reporting,
+ short: "-R",
+ long: "--enable-reporting",
+ description: "(#{ChefUtils::Dist::Infra::CLIENT} only) reporting data collection for runs.",
+ boolean: true
+
+ option :local_mode,
+ short: "-z",
+ long: "--local-mode",
+ description: "Point at local repository.",
+ boolean: true
+
+ option :chef_zero_host,
+ long: "--chef-zero-host HOST",
+ description: "Host to start #{ChefUtils::Dist::Zero::PRODUCT} on."
+
+ option :chef_zero_port,
+ long: "--chef-zero-port PORT",
+ description: "Port (or port range) to start #{ChefUtils::Dist::Zero::PRODUCT} on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
+
+ option :listen,
+ long: "--[no-]listen",
+ description: "Whether a local mode (-z) server binds to a port.",
+ boolean: false
+
+ option :skip_cookbook_sync,
+ long: "--[no-]skip-cookbook-sync",
+ description: "(#{ChefUtils::Dist::Infra::CLIENT} only) Use cached cookbooks without overwriting local differences from the #{ChefUtils::Dist::Server::PRODUCT}.",
+ boolean: false
+
+ option :named_run_list,
+ short: "-n NAMED_RUN_LIST",
+ long: "--named-run-list NAMED_RUN_LIST",
+ description: "Use a policyfile's named run list instead of the default run list."
+
+ IMMEDIATE_RUN_SIGNAL = "1".freeze
+ RECONFIGURE_SIGNAL = "H".freeze
+
+ attr_reader :chef_client_json
+
+ def setup_application
+ Chef::Daemon.change_privilege
+ end
+
+ def setup_signal_handlers
+ super
+
+ unless Chef::Platform.windows?
+ SELF_PIPE.replace IO.pipe
+
+ trap("USR1") do
+ 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
+
+ # Run the chef client, optionally daemonizing or looping at intervals.
+ def run_application
+ if Chef::Config[:version]
+ puts "#{ChefUtils::Dist::Infra::PRODUCT} version: #{::Chef::VERSION}"
+ end
+
+ if !Chef::Config[:client_fork] || Chef::Config[:once]
+ begin
+ # run immediately without interval sleep, or splay
+ run_chef_client(Chef::Config[:specific_recipes])
+ rescue SystemExit
+ raise
+ rescue Exception => e
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
+ end
+ else
+ interval_run_chef_client
+ end
+ end
+
+ private
+
+ def windows_interval_error_message
+ "Windows #{ChefUtils::Dist::Infra::PRODUCT} interval runs are not supported in #{ChefUtils::Dist::Infra::PRODUCT} 15 and later." +
+ "\nConfiguration settings:" +
+ ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
+ "\nPlease manage #{ChefUtils::Dist::Infra::PRODUCT} as a scheduled task instead."
+ end
+
+ def unforked_interval_error_message
+ "Unforked #{ChefUtils::Dist::Infra::PRODUCT} interval runs are disabled by default." +
+ "\nConfiguration settings:" +
+ ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
+ "\nEnable #{ChefUtils::Dist::Infra::PRODUCT} interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
+ end
+
+ def fetch_recipe_tarball(url, path)
+ require "open-uri" unless defined?(OpenURI)
+ Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
+ if File.exist?(url)
+ FileUtils.cp(url, path)
+ elsif URI::DEFAULT_PARSER.make_regexp.match?(url)
+ File.open(path, "wb") do |f|
+ open(url) do |r|
+ f.write(r.read)
+ end
+ end
+ 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
+
+ def interval_run_chef_client
+ if Chef::Config[:daemonize]
+ Chef::Daemon.daemonize(ChefUtils::Dist::Infra::PRODUCT)
+
+ # Start first daemonized run after configured number of seconds
+ if Chef::Config[:daemonize].is_a?(Integer)
+ sleep_then_run_chef_client(Chef::Config[:daemonize])
+ end
+ end
+
+ loop do
+ sleep_then_run_chef_client(time_to_sleep)
+ Chef::Application.exit!("Exiting", 0) unless Chef::Config[:interval]
+ end
+ end
+
+ def sleep_then_run_chef_client(sleep_sec)
+ 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.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
+ retry
+ end
+
+ Chef::Application.fatal!("#{e.class}: #{e.message}", e)
+ end
+
+ def time_to_sleep
+ duration = 0
+ duration += rand(Chef::Config[:splay]) if Chef::Config[:splay]
+ duration += Chef::Config[:interval] if Chef::Config[:interval]
+ duration
+ end
+
+ # sleep and handle queued signals
+ def interval_sleep(sec)
+ unless SELF_PIPE.empty?
+ # 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
+ sleep(sec)
+ end
+ end
+
+ def for_ezra
+ puts <<~EOH
+ For Ezra Zygmuntowicz:
+ The man who brought you Chef Solo
+ Early contributor to Chef
+ Kind hearted open source advocate
+ Rest in peace, Ezra.
+ EOH
+ end
+
+end
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index cbaa494b71..39ae7adaac 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,295 +17,58 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/application"
-require "chef/client"
-require "chef/config"
-require "chef/daemon"
-require "chef/log"
-require "chef/config_fetcher"
-require "chef/handler/error_report"
-require "chef/workstation_config_loader"
-require "chef/mixin/shell_out"
-require "chef-config/mixin/dot_d"
-require "mixlib/archive"
-
-class Chef::Application::Client < Chef::Application
- include Chef::Mixin::ShellOut
- include ChefConfig::Mixin::DotD
+require_relative "base"
+require_relative "../handler/error_report"
+require_relative "../workstation_config_loader"
+autoload :URI, "uri"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+module Mixlib
+ module Authentication
+ autoload :Log, "mixlib/authentication"
+ end
+end
+autoload :Train, "train"
- # Mimic self_pipe sleep from Unicorn to capture signals safely
- SELF_PIPE = []
+# DO NOT MAKE EDITS, see Chef::Application::Base
+#
+# External code may call / subclass or make references to this class.
+#
+class Chef::Application::Client < Chef::Application::Base
option :config_file,
- :short => "-c CONFIG",
- :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",
- :description => "output format to use",
- :proc => lambda { |format| Chef::Config.add_formatter(format) }
-
- option :force_logger,
- :long => "--force-logger",
- :description => "Use logger output instead of formatter output",
- :boolean => true,
- :default => false
-
- option :force_formatter,
- :long => "--force-formatter",
- :description => "Use formatter output instead of logger output",
- :boolean => true,
- :default => false
-
- option :profile_ruby,
- :long => "--[no-]profile-ruby",
- :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
- :boolean => true,
- :default => false
-
- option :color,
- :long => "--[no-]color",
- :boolean => true,
- :default => true,
- :description => "Use colored output, defaults to enabled"
-
- option :log_level,
- :short => "-l LEVEL",
- :long => "--log_level LEVEL",
- :description => "Set the log level (auto, debug, info, warn, error, fatal)",
- :proc => lambda { |l| l.to_sym }
-
- option :log_location,
- :short => "-L LOGLOCATION",
- :long => "--logfile LOGLOCATION",
- :description => "Set the log file location, defaults to STDOUT - recommended for daemonizing",
- :proc => nil
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ description: "The configuration file to use."
- option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true,
- :show_options => true,
- :exit => 0
-
- option :user,
- :short => "-u USER",
- :long => "--user USER",
- :description => "User to set privilege to",
- :proc => nil
-
- option :group,
- :short => "-g GROUP",
- :long => "--group GROUP",
- :description => "Group to set privilege to",
- :proc => nil
-
- unless Chef::Platform.windows?
+ unless ChefUtils.windows?
option :daemonize,
- :short => "-d [WAIT]",
- :long => "--daemonize [WAIT]",
- :description =>
- "Daemonize the process. Accepts an optional integer which is the " \
+ short: "-d [WAIT]",
+ long: "--daemonize [WAIT]",
+ description: "Daemonize the process. Accepts an optional integer which is the " \
"number of seconds to wait before the first daemonized run.",
- :proc => lambda { |wait| wait =~ /^\d+$/ ? wait.to_i : true }
+ proc: lambda { |wait| /^\d+$/.match?(wait) ? wait.to_i : true }
end
option :pid_file,
- :short => "-P PID_FILE",
- :long => "--pid PIDFILE",
- :description => "Set the PID file location, for the chef-client daemon process. Defaults to /tmp/chef-client.pid",
- :proc => nil
-
- option :lockfile,
- :long => "--lockfile LOCKFILE",
- :description => "Set the lockfile location. Prevents multiple client processes from converging at the same time",
- :proc => nil
-
- option :interval,
- :short => "-i SECONDS",
- :long => "--interval SECONDS",
- :description => "Run chef-client periodically, in seconds",
- :proc => lambda { |s| s.to_i }
-
- option :once,
- :long => "--once",
- :description => "Cancel any interval or splay options, run chef once and exit",
- :boolean => true
-
- option :json_attribs,
- :short => "-j JSON_ATTRIBS",
- :long => "--json-attributes JSON_ATTRIBS",
- :description => "Load attributes from a JSON file or URL",
- :proc => nil
-
- option :node_name,
- :short => "-N NODE_NAME",
- :long => "--node-name NODE_NAME",
- :description => "The node name for this client",
- :proc => nil
-
- option :splay,
- :short => "-s SECONDS",
- :long => "--splay SECONDS",
- :description => "The splay time for running at intervals, in seconds",
- :proc => lambda { |s| s.to_i }
-
- option :chef_server_url,
- :short => "-S CHEFSERVERURL",
- :long => "--server CHEFSERVERURL",
- :description => "The chef server URL",
- :proc => nil
-
- option :validation_key,
- :short => "-K KEY_FILE",
- :long => "--validation_key KEY_FILE",
- :description => "Set the validation key file location, used for registering new clients",
- :proc => nil
-
- option :client_key,
- :short => "-k KEY_FILE",
- :long => "--client_key KEY_FILE",
- :description => "Set the client key file location",
- :proc => nil
-
- option :named_run_list,
- :short => "-n NAMED_RUN_LIST",
- :long => "--named-run-list NAMED_RUN_LIST",
- :description => "Use a policyfile's named run list instead of the default run list"
-
- option :environment,
- :short => "-E ENVIRONMENT",
- :long => "--environment ENVIRONMENT",
- :description => "Set the Chef Environment on the node"
-
- option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
-
- option :override_runlist,
- :short => "-o RunlistItem,RunlistItem...",
- :long => "--override-runlist RunlistItem,RunlistItem...",
- :description => "Replace current run list with specified items for a single run",
- :proc => lambda {|items|
- items = items.split(",")
- items.compact.map do |item|
- Chef::RunList::RunListItem.new(item)
- end
- }
+ short: "-P PID_FILE",
+ long: "--pid PIDFILE",
+ description: "Set the PID file location, for the #{ChefUtils::Dist::Infra::CLIENT} daemon process. Defaults to /tmp/chef-client.pid.",
+ proc: nil
option :runlist,
- :short => "-r RunlistItem,RunlistItem...",
- :long => "--runlist RunlistItem,RunlistItem...",
- :description => "Permanently replace current run list with specified items",
- :proc => lambda {|items|
+ short: "-r RunlistItem,RunlistItem...",
+ long: "--runlist RunlistItem,RunlistItem...",
+ description: "Permanently replace current run list with specified items.",
+ proc: lambda { |items|
items = items.split(",")
items.compact.map do |item|
Chef::RunList::RunListItem.new(item)
end
}
- option :why_run,
- :short => "-W",
- :long => "--why-run",
- :description => "Enable whyrun mode",
- :boolean => true
-
- option :client_fork,
- :short => "-f",
- :long => "--[no-]fork",
- :description => "Fork client",
- :boolean => true
option :recipe_url,
- :long => "--recipe-url=RECIPE_URL",
- :description => "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode."
-
- option :enable_reporting,
- :short => "-R",
- :long => "--enable-reporting",
- :description => "Enable reporting data collection for chef runs",
- :boolean => true
-
- option :local_mode,
- :short => "-z",
- :long => "--local-mode",
- :description => "Point chef-client at local repository",
- :boolean => true
-
- option :chef_zero_host,
- :long => "--chef-zero-host HOST",
- :description => "Host to start chef-zero on"
-
- option :chef_zero_port,
- :long => "--chef-zero-port PORT",
- :description => "Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
-
- option :disable_config,
- :long => "--disable-config",
- :description => "Refuse to load a config file and use defaults. This is for development and not a stable API",
- :boolean => true
-
- option :run_lock_timeout,
- :long => "--run-lock-timeout SECONDS",
- :description => "Set maximum duration to wait for another client run to finish, default is indefinitely.",
- :proc => lambda { |s| s.to_i }
-
- if Chef::Platform.windows?
- option :fatal_windows_admin_check,
- :short => "-A",
- :long => "--fatal-windows-admin-check",
- :description => "Fail the run when chef-client doesn't have administrator privileges on Windows",
- :boolean => true
- end
-
- option :audit_mode,
- :long => "--audit-mode MODE",
- :description => "Enable audit-mode with `enabled`. Disable audit-mode with `disabled`. Skip converge and only perform audits with `audit-only`",
- :proc => lambda { |mo| mo.tr("-", "_").to_sym }
-
- option :minimal_ohai,
- :long => "--minimal-ohai",
- :description => "Only run the bare minimum ohai plugins chef needs to function",
- :boolean => true
-
- option :listen,
- :long => "--[no-]listen",
- :description => "Whether a local mode (-z) server binds to a port",
- :boolean => true
-
- option :fips,
- :long => "--fips",
- :description => "Enable fips mode",
- :boolean => true
-
- option :delete_entire_chef_repo,
- :long => "--delete-entire-chef-repo",
- :description => "DANGEROUS: does what it says, only useful with --recipe-url",
- :boolean => true
-
- option :skip_cookbook_sync,
- :long => "--[no-]skip-cookbook-sync",
- :description => "Use cached cookbooks without overwriting local differences from the server",
- :boolean => false
-
- IMMEDIATE_RUN_SIGNAL = "1".freeze
-
- attr_reader :chef_client_json
+ long: "--recipe-url=RECIPE_URL",
+ description: "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode."
# Reconfigure the chef client
# Re-open the JSON attributes and load them into the node
@@ -316,40 +79,52 @@ class Chef::Application::Client < Chef::Application
set_specific_recipes
- Chef::Config[:fips] = config[:fips] if config.has_key? :fips
+ Chef::Config[:fips] = config[:fips] if config.key? :fips
- Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url
+ Chef::Config[:chef_server_url] = config[:chef_server_url] if config.key? :chef_server_url
- Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode)
+ Chef::Config.local_mode = config[:local_mode] if config.key?(:local_mode)
- if Chef::Config.has_key?(:chef_repo_path) && Chef::Config.chef_repo_path.nil?
+ if Chef::Config.key?(:chef_repo_path) && Chef::Config.chef_repo_path.nil?
Chef::Config.delete(:chef_repo_path)
Chef::Log.warn "chef_repo_path was set in a config file but was empty. Assuming #{Chef::Config.chef_repo_path}"
end
- if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
+ if Chef::Config.local_mode && !Chef::Config.key?(:cookbook_path) && !Chef::Config.key?(:chef_repo_path)
Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd)
end
if Chef::Config[:recipe_url]
if !Chef::Config.local_mode
- Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode")
+ Chef::Application.fatal!("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"
- FileUtils.rm_rf(recipes_path, :secure => true)
+ Chef::Log.trace "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it"
+ FileUtils.rm_rf(Chef::Config.chef_repo_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)
Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
+ config_path = File.join(Chef::Config.chef_repo_path, "#{ChefUtils::Dist::Infra::USER_CONF_DIR}/config.rb")
+ Chef::Config.from_string(IO.read(config_path), config_path) if File.file?(config_path)
end
end
Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host]
Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port]
+ if config[:target] || Chef::Config.target
+ Chef::Config.target_mode.host = config[:target] || Chef::Config.target
+ if URI.parse(Chef::Config.target_mode.host).scheme
+ train_config = Train.unpack_target_from_uri(Chef::Config.target_mode.host)
+ Chef::Config.target_mode = train_config
+ end
+ Chef::Config.target_mode.enabled = true
+ Chef::Config.node_name = Chef::Config.target_mode.host unless Chef::Config.node_name
+ end
+
if Chef::Config[:daemonize]
Chef::Config[:interval] ||= 1800
end
@@ -359,29 +134,31 @@ class Chef::Application::Client < Chef::Application
Chef::Config[:splay] = nil
end
- if !Chef::Config[:client_fork] && Chef::Config[:interval] && !Chef::Platform.windows?
- Chef::Application.fatal!(unforked_interval_error_message)
+ # 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[:interval]
+ if Chef::Platform.windows?
+ Chef::Application.fatal!(windows_interval_error_message)
+ elsif !Chef::Config[:client_fork]
+ Chef::Application.fatal!(unforked_interval_error_message)
+ end
end
if Chef::Config[:json_attribs]
config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs])
@chef_client_json = config_fetcher.fetch_json
end
-
- if mode = config[:audit_mode] || Chef::Config[:audit_mode]
- expected_modes = [:enabled, :disabled, :audit_only]
- unless expected_modes.include?(mode)
- Chef::Application.fatal!(unrecognized_audit_mode(mode))
- end
- end
end
def load_config_file
- if !config.has_key?(:config_file) && !config[:disable_config]
+ if !config.key?(:config_file) && !config[:disable_config]
if config[:local_mode]
config[:config_file] = Chef::WorkstationConfigLoader.new(nil, Chef::Log).config_location
else
- config[:config_file] = Chef::Config.platform_specific_path("/etc/chef/client.rb")
+ config[:config_file] = Chef::Config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/client.rb")
end
end
@@ -398,131 +175,4 @@ class Chef::Application::Client < Chef::Application
Ohai::Log.use_log_devices( Chef::Log )
end
- def setup_application
- Chef::Daemon.change_privilege
- end
-
- def setup_signal_handlers
- super
-
- unless Chef::Platform.windows?
- SELF_PIPE.replace IO.pipe
-
- trap("USR1") do
- Chef::Log.info("SIGUSR1 received, waking up")
- SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select
- end
- end
- end
-
- # Run the chef client, optionally daemonizing or looping at intervals.
- def run_application
- if Chef::Config[:version]
- puts "Chef version: #{::Chef::VERSION}"
- end
-
- if !Chef::Config[:client_fork] || Chef::Config[:once]
- begin
- # run immediately without interval sleep, or splay
- run_chef_client(Chef::Config[:specific_recipes])
- rescue SystemExit
- raise
- rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
- else
- interval_run_chef_client
- end
- end
-
- private
-
- def interval_run_chef_client
- if Chef::Config[:daemonize]
- Chef::Daemon.daemonize("chef-client")
-
- # Start first daemonized run after configured number of seconds
- if Chef::Config[:daemonize].is_a?(Integer)
- sleep_then_run_chef_client(Chef::Config[:daemonize])
- end
- end
-
- loop do
- sleep_then_run_chef_client(time_to_sleep)
- Chef::Application.exit!("Exiting", 0) if !Chef::Config[:interval]
- end
- 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
-
- run_chef_client(Chef::Config[:specific_recipes])
- 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")}")
- retry
- end
-
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
-
- def test_signal
- @signal = interval_sleep(0)
- end
-
- def time_to_sleep
- duration = 0
- duration += rand(Chef::Config[:splay]) if Chef::Config[:splay]
- duration += Chef::Config[:interval] if Chef::Config[:interval]
- duration
- end
-
- def interval_sleep(sec)
- unless SELF_PIPE.empty?
- client_sleep(sec)
- 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:" +
- "#{"\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]}" +
- "\nEnable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- end
-
- def audit_mode_settings_explanation
- "\n* To enable audit mode after converge, use command line option `--audit-mode enabled` or set `audit_mode :enabled` in your config file." +
- "\n* To disable audit mode, use command line option `--audit-mode disabled` or set `audit_mode :disabled` in your config file." +
- "\n* To only run audit mode, use command line option `--audit-mode audit-only` or set `audit_mode :audit_only` in your config file." +
- "\nAudit mode is disabled by default."
- end
-
- def unrecognized_audit_mode(mode)
- "Unrecognized setting #{mode} for audit mode." + audit_mode_settings_explanation
- 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)
- end
- end
- end
end
diff --git a/lib/chef/application/exit_code.rb b/lib/chef/application/exit_code.rb
index 753f1a0d80..26c181fa3d 100644
--- a/lib/chef/application/exit_code.rb
+++ b/lib/chef/application/exit_code.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Murawski (<smurawski@chef.io>)
-# Copyright:: Copyright 2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@ class Chef
# These are the exit codes defined in Chef RFC 062
# https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md
class ExitCode
+ require "chef-utils/dist" unless defined?(ChefUtils::Dist)
# -1 is defined as DEPRECATED_FAILURE in RFC 062, so it is
# not enumerated in an active constant.
@@ -34,73 +35,36 @@ class Chef
REBOOT_SCHEDULED: 35,
REBOOT_NEEDED: 37,
REBOOT_FAILED: 41,
- AUDIT_MODE_FAILURE: 42,
- }
+ # 42 was used by audit mode and should not be reused
+ CONFIG_FAILURE: 43,
+ CLIENT_UPGRADED: 213,
+ }.freeze
DEPRECATED_RFC_062_EXIT_CODES = {
DEPRECATED_FAILURE: -1,
- }
+ }.freeze
class << self
def normalize_exit_code(exit_code = nil)
- if normalization_not_configured?
- normalize_legacy_exit_code_with_warning(exit_code)
- elsif normalization_disabled?
- normalize_legacy_exit_code(exit_code)
+ normalized_exit_code = normalize_legacy_exit_code(exit_code)
+ if valid_exit_codes.include? normalized_exit_code
+ normalized_exit_code
else
- normalize_exit_code_to_rfc(exit_code)
+ Chef::Log.warn(non_standard_exit_code_warning(normalized_exit_code))
+ VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
end
end
- def enforce_rfc_062_exit_codes?
- !normalization_disabled? && !normalization_not_configured?
- end
-
- def notify_reboot_exit_code_deprecation
- return if normalization_disabled?
- notify_on_deprecation(reboot_deprecation_warning)
- end
-
- def notify_deprecated_exit_code
- return if normalization_disabled?
- notify_on_deprecation(deprecation_warning)
- end
-
private
- def normalization_disabled?
- Chef::Config[:exit_status] == :disabled
- end
-
- def normalization_not_configured?
- Chef::Config[:exit_status].nil?
- end
-
- def normalize_legacy_exit_code_with_warning(exit_code)
- normalized_exit_code = normalize_legacy_exit_code(exit_code)
- unless valid_exit_codes.include? normalized_exit_code
- notify_on_deprecation(deprecation_warning)
- end
- normalized_exit_code
- end
-
def normalize_legacy_exit_code(exit_code)
case exit_code
- when Fixnum
+ when Integer
exit_code
when Exception
lookup_exit_code_by_exception(exit_code)
else
- default_exit_code
- end
- end
-
- def normalize_exit_code_to_rfc(exit_code)
- normalized_exit_code = normalize_legacy_exit_code_with_warning(exit_code)
- if valid_exit_codes.include? normalized_exit_code
- normalized_exit_code
- else
VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
end
end
@@ -110,34 +74,21 @@ class Chef
VALID_RFC_062_EXIT_CODES[:SIGINT_RECEIVED]
elsif sigterm_received?(exception)
VALID_RFC_062_EXIT_CODES[:SIGTERM_RECEIVED]
- elsif normalization_disabled? || normalization_not_configured?
- if legacy_exit_code?(exception)
- # We have lots of "Chef::Application.fatal!('', 2)
- # This maintains that behavior at initial introduction
- # and when the RFC exit_status compliance is disabled.
- VALID_RFC_062_EXIT_CODES[:SIGINT_RECEIVED]
- else
- VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
- end
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 configuration_failure?(exception)
+ VALID_RFC_062_EXIT_CODES[:CONFIG_FAILURE]
+ elsif client_upgraded?(exception)
+ VALID_RFC_062_EXIT_CODES[:CLIENT_UPGRADED]
else
VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
end
end
- def legacy_exit_code?(exception)
- resolve_exception_array(exception).any? do |e|
- e.is_a? Chef::Exceptions::DeprecatedExitCode
- end
- end
-
def reboot_scheduled?(exception)
resolve_exception_array(exception).any? do |e|
e.is_a? Chef::Exceptions::Reboot
@@ -156,9 +107,15 @@ class Chef
end
end
- def audit_failure?(exception)
+ def configuration_failure?(exception)
+ resolve_exception_array(exception).any? do |e|
+ e.is_a? Chef::Exceptions::ConfigurationError
+ end
+ end
+
+ def client_upgraded?(exception)
resolve_exception_array(exception).any? do |e|
- e.is_a? Chef::Exceptions::AuditError
+ e.is_a? Chef::Exceptions::ClientUpgraded
end
end
@@ -189,34 +146,17 @@ class Chef
end
def notify_on_deprecation(message)
- begin
- Chef.log_deprecation(message)
- rescue Chef::Exceptions::DeprecatedFeatureError
- # Have to rescue this, otherwise this unhandled error preempts
- # the current exit code assignment.
- end
- end
-
- def deprecation_warning
- "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
- " In a future release, non-standard exit codes will be redefined as" \
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
+ 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 reboot_deprecation_warning
- "Per RFC 062 (https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md)" \
- ", when a reboot is requested Chef Client will exit with an exit code of 35, REBOOT_SCHEDULED." \
- " To maintain the current behavior (an exit code of 0), you will need to set `exit_status` to" \
- " `:disabled` in your client.rb"
- end
-
- def default_exit_code
- if normalization_disabled? || normalization_not_configured?
- return DEPRECATED_RFC_062_EXIT_CODES[:DEPRECATED_FAILURE]
- else
- VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE]
- end
+ def non_standard_exit_code_warning(exit_code)
+ "#{ChefUtils::Dist::Infra::CLIENT} attempted to exit with a non-standard exit code of #{exit_code}." \
+ " The #{ChefUtils::Dist::Infra::PRODUCT} Exit Codes design document (https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md)" \
+ " defines the exit codes that should be used with #{ChefUtils::Dist::Infra::CLIENT}. Chef::Application::ExitCode defines" \
+ " valid exit codes Non-standard exit codes are redefined as GENERIC_FAILURE."
end
end
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
index c80d0245f1..7906ce6eaa 100644
--- a/lib/chef/application/knife.rb
+++ b/lib/chef/application/knife.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,141 +15,150 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/knife"
-require "chef/application"
+require_relative "../knife"
+require_relative "../application"
require "mixlib/log"
require "ohai/config"
-require "chef/monkey_patches/net_http.rb"
+module Net
+ autoload :HTTP, "net/http"
+end
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef::Application::Knife < Chef::Application
- NO_COMMAND_GIVEN = "You need to pass a sub-command (e.g., knife SUB-COMMAND)\n"
+ NO_COMMAND_GIVEN = "You need to pass a sub-command (e.g., knife SUB-COMMAND)\n".freeze
banner "Usage: knife sub-command (options)"
option :config_file,
- :short => "-c CONFIG",
- :long => "--config CONFIG",
- :description => "The configuration file to use",
- :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ 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|
+ 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",
- :long => "--verbose",
- :description => "More verbose output. Use twice for max verbosity",
- :proc => Proc.new { verbosity_level += 1 },
- :default => 0
+ short: "-V",
+ long: "--verbose",
+ description: "More verbose output. Use twice (-VV) for additional verbosity and three times (-VVV) for maximum verbosity.",
+ proc: Proc.new { verbosity_level += 1 },
+ default: 0
option :color,
- :long => "--[no-]color",
- :boolean => true,
- :default => true,
- :description => "Use colored output, defaults to enabled"
+ long: "--[no-]color",
+ boolean: true,
+ default: true,
+ description: "Use colored output, defaults to enabled."
option :environment,
- :short => "-E ENVIRONMENT",
- :long => "--environment ENVIRONMENT",
- :description => "Set the Chef environment (except for in searches, where this will be flagrantly ignored)"
+ short: "-E ENVIRONMENT",
+ long: "--environment ENVIRONMENT",
+ description: "Set the #{ChefUtils::Dist::Infra::PRODUCT} environment (except for in searches, where this will be flagrantly ignored)."
option :editor,
- :short => "-e EDITOR",
- :long => "--editor EDITOR",
- :description => "Set the editor to use for interactive commands",
- :default => ENV["EDITOR"]
+ short: "-e EDITOR",
+ long: "--editor EDITOR",
+ description: "Set the editor to use for interactive commands.",
+ default: ENV["EDITOR"]
option :disable_editing,
- :short => "-d",
- :long => "--disable-editing",
- :description => "Do not open EDITOR, just accept the data as is",
- :boolean => true,
- :default => false
+ short: "-d",
+ long: "--disable-editing",
+ description: "Do not open EDITOR, just accept the data as is.",
+ boolean: true,
+ default: false
option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true
+ short: "-h",
+ long: "--help",
+ description: "Show this help message.",
+ on: :tail,
+ boolean: true
option :node_name,
- :short => "-u USER",
- :long => "--user USER",
- :description => "API Client Username"
+ short: "-u USER",
+ long: "--user USER",
+ description: "#{ChefUtils::Dist::Server::PRODUCT} API client username."
option :client_key,
- :short => "-k KEY",
- :long => "--key KEY",
- :description => "API Client Key",
- :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
+ short: "-k KEY",
+ long: "--key KEY",
+ description: "#{ChefUtils::Dist::Server::PRODUCT} API client key.",
+ proc: lambda { |path| File.expand_path(path, Dir.pwd) }
option :chef_server_url,
- :short => "-s URL",
- :long => "--server-url URL",
- :description => "Chef Server URL"
+ short: "-s URL",
+ long: "--server-url URL",
+ description: "#{ChefUtils::Dist::Server::PRODUCT} URL."
option :yes,
- :short => "-y",
- :long => "--yes",
- :description => "Say yes to all prompts for confirmation"
+ short: "-y",
+ long: "--yes",
+ description: "Say yes to all prompts for confirmation."
option :defaults,
- :long => "--defaults",
- :description => "Accept default values for all questions"
+ long: "--defaults",
+ description: "Accept default values for all questions."
option :print_after,
- :long => "--print-after",
- :description => "Show the data after a destructive operation"
+ long: "--print-after",
+ description: "Show the data after a destructive operation."
option :format,
- :short => "-F FORMAT",
- :long => "--format FORMAT",
- :description => "Which format to use for output",
- :default => "summary"
+ short: "-F FORMAT",
+ long: "--format FORMAT",
+ description: "Which format to use for output.",
+ in: %w{summary text json yaml pp},
+ default: "summary"
option :local_mode,
- :short => "-z",
- :long => "--local-mode",
- :description => "Point knife commands at local repository instead of server",
- :boolean => true
+ short: "-z",
+ long: "--local-mode",
+ description: "Point knife commands at local repository instead of #{ChefUtils::Dist::Server::PRODUCT}.",
+ boolean: true
option :chef_zero_host,
- :long => "--chef-zero-host HOST",
- :description => "Host to start chef-zero on"
+ long: "--chef-zero-host HOST",
+ description: "Host to start #{ChefUtils::Dist::Zero::PRODUCT} on."
option :chef_zero_port,
- :long => "--chef-zero-port PORT",
- :description => "Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
+ long: "--chef-zero-port PORT",
+ description: "Port (or port range) to start #{ChefUtils::Dist::Zero::PRODUCT} on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
option :listen,
- :long => "--[no-]listen",
- :description => "Whether a local mode (-z) server binds to a port",
- :boolean => true
+ long: "--[no-]listen",
+ description: "Whether a local mode (-z) server binds to a port.",
+ boolean: false
option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
+ short: "-v",
+ long: "--version",
+ description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version.",
+ boolean: true,
+ proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
option :fips,
- :long => "--[no-]fips",
- :description => "Enable fips mode",
- :boolean => true,
- :default => nil
+ long: "--[no-]fips",
+ description: "Enable FIPS mode.",
+ boolean: true,
+ default: nil
+
+ option :profile,
+ long: "--profile PROFILE",
+ description: "The credentials profile to select."
# Run knife
def run
+ ChefConfig::PathHelper.per_tool_home_environment = "KNIFE_HOME"
Mixlib::Log::Formatter.show_time = false
validate_and_parse_options
quiet_traps
@@ -203,11 +212,20 @@ 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
+
+ if want_help?
+ puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{Chef::VERSION}"
+ puts
+ puts "Docs: #{ChefUtils::Dist::Org::KNIFE_DOCS}"
+ puts "Patents: #{ChefUtils::Dist::Org::PATENTS}"
+ puts
+ end
+
+ 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 446a0f007d..8264393bb9 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,211 +16,45 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-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"
+require_relative "base"
+require_relative "../../chef"
+require_relative "client"
+require "fileutils" unless defined?(FileUtils)
+require "pathname" unless defined?(Pathname)
+require "chef-utils" unless defined?(ChefUtils::CANARY)
-class Chef::Application::Solo < Chef::Application
- include Chef::Mixin::ShellOut
- include ChefConfig::Mixin::DotD
+# DO NOT MAKE EDITS, see Chef::Application::Base
+#
+# Do not reference this class it will be removed in Chef-16
+#
+# @deprecated use Chef::Application::Client instead, this will be removed in Chef-16
+#
+class Chef::Application::Solo < Chef::Application::Base
option :config_file,
- :short => "-c CONFIG",
- :long => "--config CONFIG",
- :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",
- :description => "output format to use",
- :proc => lambda { |format| Chef::Config.add_formatter(format) }
-
- option :force_logger,
- :long => "--force-logger",
- :description => "Use logger output instead of formatter output",
- :boolean => true,
- :default => false
-
- option :force_formatter,
- :long => "--force-formatter",
- :description => "Use formatter output instead of logger output",
- :boolean => true,
- :default => false
-
- option :profile_ruby,
- :long => "--[no-]profile-ruby",
- :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
- :boolean => true,
- :default => false
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ default: Chef::Config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/solo.rb"),
+ description: "The configuration file to use."
- option :color,
- :long => "--[no-]color",
- :boolean => true,
- :default => !Chef::Platform.windows?,
- :description => "Use colored output, defaults to enabled"
-
- option :log_level,
- :short => "-l LEVEL",
- :long => "--log_level LEVEL",
- :description => "Set the log level (debug, info, warn, error, fatal)",
- :proc => lambda { |l| l.to_sym }
-
- option :log_location,
- :short => "-L LOGLOCATION",
- :long => "--logfile LOGLOCATION",
- :description => "Set the log file location, defaults to STDOUT",
- :proc => nil
-
- option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true,
- :show_options => true,
- :exit => 0
-
- option :user,
- :short => "-u USER",
- :long => "--user USER",
- :description => "User to set privilege to",
- :proc => nil
-
- option :group,
- :short => "-g GROUP",
- :long => "--group GROUP",
- :description => "Group to set privilege to",
- :proc => nil
-
- unless Chef::Platform.windows?
+ unless ChefUtils.windows?
option :daemonize,
- :short => "-d",
- :long => "--daemonize",
- :description => "Daemonize the process",
- :proc => lambda { |p| true }
+ short: "-d",
+ long: "--daemonize",
+ description: "Daemonize the process.",
+ proc: lambda { |p| true }
end
- option :lockfile,
- :long => "--lockfile LOCKFILE",
- :description => "Set the lockfile location. Prevents multiple processes from converging at the same time",
- :proc => nil
-
- option :interval,
- :short => "-i SECONDS",
- :long => "--interval SECONDS",
- :description => "Run chef-client periodically, in seconds",
- :proc => lambda { |s| s.to_i }
-
- option :json_attribs,
- :short => "-j JSON_ATTRIBS",
- :long => "--json-attributes JSON_ATTRIBS",
- :description => "Load attributes from a JSON file or URL",
- :proc => nil
-
- option :node_name,
- :short => "-N NODE_NAME",
- :long => "--node-name NODE_NAME",
- :description => "The node name for this client",
- :proc => nil
-
- option :splay,
- :short => "-s SECONDS",
- :long => "--splay SECONDS",
- :description => "The splay time for running at intervals, in seconds",
- :proc => lambda { |s| s.to_i }
-
option :recipe_url,
- :short => "-r RECIPE_URL",
- :long => "--recipe-url RECIPE_URL",
- :description => "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache."
-
- option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
-
- option :override_runlist,
- :short => "-o RunlistItem,RunlistItem...",
- :long => "--override-runlist RunlistItem,RunlistItem...",
- :description => "Replace current run list with specified items",
- :proc => lambda {|items|
- items = items.split(",")
- items.compact.map do |item|
- Chef::RunList::RunListItem.new(item)
- end
- }
-
- option :client_fork,
- :short => "-f",
- :long => "--[no-]fork",
- :description => "Fork client",
- :boolean => true
-
- option :why_run,
- :short => "-W",
- :long => "--why-run",
- :description => "Enable whyrun mode",
- :boolean => true
-
- option :ez,
- :long => "--ez",
- :description => "A memorial for Ezra Zygmuntowicz",
- :boolean => true
-
- option :environment,
- :short => "-E ENVIRONMENT",
- :long => "--environment ENVIRONMENT",
- :description => "Set the Chef Environment on the node"
-
- option :run_lock_timeout,
- :long => "--run-lock-timeout SECONDS",
- :description => "Set maximum duration to wait for another client run to finish, default is indefinitely.",
- :proc => lambda { |s| s.to_i }
-
- option :minimal_ohai,
- :long => "--minimal-ohai",
- :description => "Only run the bare minimum ohai plugins chef needs to function",
- :boolean => true
-
- option :delete_entire_chef_repo,
- :long => "--delete-entire-chef-repo",
- :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
+ short: "-r RECIPE_URL",
+ long: "--recipe-url RECIPE_URL",
+ description: "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache."
# Get this party started
- def run
+ def run(enforce_license: false)
setup_signal_handlers
reconfigure
+ check_license_acceptance if enforce_license
for_ezra if Chef::Config[:ez]
if !Chef::Config[:solo_legacy_mode]
Chef::Application::Client.new.run
@@ -237,27 +71,23 @@ class Chef::Application::Solo < Chef::Application
set_specific_recipes
- Chef::Config[:solo] = true
+ Chef::Config[:fips] = config[:fips] if config.key? :fips
- 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::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")
- # -r means something entirely different in chef-client land, so let's replace it with a "safe" value
- if dash_r = ARGV.index("-r")
- ARGV[dash_r] = "--recipe-url"
- end
-
# For back compat reasons, we need to ensure that we try and use the cache_path as a repo first
- Chef::Log.debug "Current chef_repo_path is #{Chef::Config.chef_repo_path}"
+ 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)
+ if !Chef::Config.key?(:cookbook_path) && !Chef::Config.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
@@ -268,112 +98,40 @@ class Chef::Application::Solo < Chef::Application
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
+
+ if Chef::Config[:interval]
+ if Chef::Platform.windows?
+ Chef::Application.fatal!(windows_interval_error_message)
+ elsif !Chef::Config[:client_fork]
+ Chef::Application.fatal!(unforked_interval_error_message)
+ end
+ end
if Chef::Config[:recipe_url]
- cookbooks_path = Array(Chef::Config[:cookbook_path]).detect { |e| Pathname.new(e).cleanpath.to_s =~ /\/cookbooks\/*$/ }
+ cookbooks_path = Array(Chef::Config[:cookbook_path]).detect { |e| Pathname.new(e).cleanpath.to_s =~ %r{/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"
- FileUtils.rm_rf(recipes_path, :secure => true)
+ 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)
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.
+ # json_attribs should be fetched after recipe_url tarball is unpacked.
# Otherwise it may fail if points to local file from tarball.
if Chef::Config[:json_attribs]
config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs])
@chef_client_json = config_fetcher.fetch_json
end
-
- # Disable auditing for solo
- Chef::Config[:audit_mode] = :disabled
- end
-
- def setup_application
- Chef::Daemon.change_privilege
- end
-
- def run_application
- if !Chef::Config[:client_fork] || Chef::Config[:once]
- # Run immediately without interval sleep or splay
- begin
- run_chef_client(Chef::Config[:specific_recipes])
- rescue SystemExit
- raise
- rescue Exception => e
- Chef::Application.fatal!("#{e.class}: #{e.message}", e)
- end
- else
- interval_run_chef_client
- end
- end
-
- private
-
- def for_ezra
- puts <<-EOH
-For Ezra Zygmuntowicz:
- The man who brought you Chef Solo
- Early contributor to Chef
- Kind hearted open source advocate
- Rest in peace, Ezra.
-EOH
- end
-
- def interval_run_chef_client
- if Chef::Config[:daemonize]
- Chef::Daemon.daemonize("chef-client")
- end
-
- loop do
- begin
-
- sleep_sec = 0
- 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")
- sleep(sleep_sec)
- end
-
- run_chef_client
- if !Chef::Config[:interval]
- Chef::Application.exit! "Exiting", 0
- end
- 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")}")
- retry
- else
- 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}")
- File.open(path, "wb") do |f|
- open(url) do |r|
- f.write(r.read)
- end
- end
end
- def unforked_interval_error_message
- "Unforked chef-client interval runs are disabled in Chef 12." +
- "\nConfiguration settings:" +
- "#{"\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]}" +
- "\nEnable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
- end
end
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
index 2f1456ac45..8975556f75 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,20 @@
# limitations under the License.
#
-require "chef"
-require "chef/monologger"
-require "chef/application"
-require "chef/client"
-require "chef/config"
-require "chef/handler/error_report"
-require "chef/log"
-require "chef/http"
-require "mixlib/cli"
-require "socket"
-require "uri"
+require_relative "../../chef"
+require_relative "../monologger"
+require_relative "../application"
+require_relative "../client"
+require_relative "../config"
+require_relative "../handler/error_report"
+require_relative "../log"
+require_relative "../http"
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require "socket" unless defined?(Socket)
+require "uri" unless defined?(URI)
require "win32/daemon"
-require "chef/mixin/shell_out"
+require_relative "../mixin/shell_out"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Application
@@ -37,36 +38,36 @@ class Chef
include Chef::Mixin::ShellOut
option :config_file,
- :short => "-c CONFIG",
- :long => "--config CONFIG",
- :default => "#{ENV['SYSTEMDRIVE']}/chef/client.rb",
- :description => ""
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ default: "#{Chef::Config.etc_chef_dir}/client.rb",
+ description: "The configuration file to use for #{ChefUtils::Dist::Infra::PRODUCT} runs."
option :log_location,
- :short => "-L LOGLOCATION",
- :long => "--logfile LOGLOCATION",
- :description => "Set the log file location"
+ short: "-L LOGLOCATION",
+ long: "--logfile LOGLOCATION",
+ description: "Set the log file location."
option :splay,
- :short => "-s SECONDS",
- :long => "--splay SECONDS",
- :description => "The splay time for running at intervals, in seconds",
- :proc => lambda { |s| s.to_i }
+ short: "-s SECONDS",
+ long: "--splay SECONDS",
+ description: "The splay time for running at intervals, in seconds.",
+ proc: lambda { |s| s.to_i }
option :interval,
- :short => "-i SECONDS",
- :long => "--interval SECONDS",
- :description => "Set the number of seconds to wait between chef-client runs",
- :proc => lambda { |s| s.to_i }
+ short: "-i SECONDS",
+ long: "--interval SECONDS",
+ description: "Set the number of seconds to wait between #{ChefUtils::Dist::Infra::PRODUCT} runs.",
+ proc: lambda { |s| s.to_i }
- DEFAULT_LOG_LOCATION ||= "#{ENV['SYSTEMDRIVE']}/chef/client.log"
+ DEFAULT_LOG_LOCATION ||= "#{Chef::Config.c_chef_dir}/client.log".freeze
def service_init
@service_action_mutex = Mutex.new
@service_signal = ConditionVariable.new
reconfigure
- Chef::Log.info("Chef Client Service initialized")
+ Chef::Log.info("#{ChefUtils::Dist::Infra::CLIENT} Service initialized")
end
def service_main(*startup_parameters)
@@ -77,41 +78,41 @@ class Chef
while running?
# Grab the service_action_mutex to make a chef-client run
@service_action_mutex.synchronize do
- begin
- Chef::Log.info("Next chef-client run will happen in #{timeout} seconds")
- @service_signal.wait(@service_action_mutex, timeout)
-
- # Continue only if service is RUNNING
- next if state != RUNNING
-
- # Reconfigure each time through to pick up any changes in the client file
- Chef::Log.info("Reconfiguring with startup parameters")
- reconfigure(startup_parameters)
- timeout = Chef::Config[:interval]
-
- # Honor splay sleep config
- timeout += rand Chef::Config[:splay]
-
- # run chef-client only if service is in RUNNING state
- next if state != RUNNING
-
- Chef::Log.info("Chef-Client service is starting a chef-client run...")
- run_chef_client
- rescue SystemExit => e
- # Do not raise any of the errors here in order to
- # prevent service crash
- Chef::Log.error("#{e.class}: #{e}")
- rescue Exception => e
- Chef::Log.error("#{e.class}: #{e}")
- end
+
+ Chef::Log.info("Next #{ChefUtils::Dist::Infra::CLIENT} run will happen in #{timeout} seconds")
+ @service_signal.wait(@service_action_mutex, timeout)
+
+ # Continue only if service is RUNNING
+ next if state != RUNNING
+
+ # Reconfigure each time through to pick up any changes in the client file
+ Chef::Log.info("Reconfiguring with startup parameters")
+ reconfigure(startup_parameters)
+ timeout = Chef::Config[:interval]
+
+ # Honor splay sleep config
+ timeout += rand Chef::Config[:splay]
+
+ # run chef-client only if service is in RUNNING state
+ next if state != RUNNING
+
+ Chef::Log.info("#{ChefUtils::Dist::Infra::CLIENT} service is starting a #{ChefUtils::Dist::Infra::CLIENT} run...")
+ run_chef_client
+ rescue SystemExit => e
+ # Do not raise any of the errors here in order to
+ # prevent service crash
+ Chef::Log.error("#{e.class}: #{e}")
+ rescue Exception => e
+ Chef::Log.error("#{e.class}: #{e}")
+
end
end
# 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
################################################################################
@@ -130,12 +131,12 @@ class Chef
break
else
unless run_warning_displayed
- Chef::Log.info("Currently a chef run is happening on this system.")
- Chef::Log.info("Service will stop when run is completed.")
+ Chef::Log.info("Currently a #{ChefUtils::Dist::Infra::PRODUCT} run is happening on this system.")
+ Chef::Log.info("Service will stop when run is completed.")
run_warning_displayed = true
end
- Chef::Log.debug("Waiting for chef-client run...")
+ Chef::Log.trace("Waiting for #{ChefUtils::Dist::Infra::PRODUCT} run...")
sleep 1
end
end
@@ -149,7 +150,7 @@ class Chef
# since this is a PAUSE signal.
if @service_action_mutex.locked?
- Chef::Log.info("Currently a chef-client run is happening.")
+ Chef::Log.info("Currently a #{ChefUtils::Dist::Infra::PRODUCT} run is happening.")
Chef::Log.info("Service will pause once it's completed.")
else
Chef::Log.info("Service is pausing....")
@@ -183,39 +184,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)
- Your chef-client run timed out. You can increase the time chef-client is given
+
+ Chef::Log.info "Starting #{ChefUtils::Dist::Infra::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(
+ "#{ChefUtils::Dist::Infra::CLIENT}.bat #{config_params}",
+ timeout: Chef::Config[:windows_service][:watchdog_timeout],
+ logger: Chef::Log
+ )
+ Chef::Log.trace (result.stdout).to_s
+ Chef::Log.trace (result.stderr).to_s
+ rescue Mixlib::ShellOut::CommandTimeout => e
+ Chef::Log.error "#{ChefUtils::Dist::Infra::CLIENT} timed out\n(#{e})"
+ Chef::Log.error(<<-EOF)
+ Your #{ChefUtils::Dist::Infra::CLIENT} run timed out. You can increase the time #{ChefUtils::Dist::Infra::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
+ EOF
+ rescue Mixlib::ShellOut::ShellCommandFailed => e
+ Chef::Log.warn "Not able to start #{ChefUtils::Dist::Infra::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)
@@ -229,7 +229,7 @@ class Chef
configure_chef startup_parameters
configure_logging
- Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url
+ Chef::Config[:chef_server_url] = config[:chef_server_url] if config.key? :chef_server_url
unless Chef::Config[:exception_handlers].any? { |h| Chef::Handler::ErrorReport === h }
Chef::Config[:exception_handlers] << Chef::Handler::ErrorReport.new
end
@@ -257,13 +257,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?
@@ -307,23 +307,23 @@ class Chef
begin
case config[:config_file]
- when /^(http|https):\/\//
+ when %r{^(http|https)://}
Chef::HTTP.new("").streaming_request(config[:config_file]) { |f| apply_config(f.path) }
else
::File.open(config[:config_file]) { |f| apply_config(f.path) }
end
rescue Errno::ENOENT
Chef::Log.warn("*****************************************")
- Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.")
+ Chef::Log.warn("Did not find config file: #{config[:config_file]}. Using command line options instead.")
Chef::Log.warn("*****************************************")
Chef::Config.merge!(config)
rescue SocketError
- Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", Chef::Exceptions::DeprecatedExitCode.new)
+ 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}", Chef::Exceptions::DeprecatedExitCode.new)
+ 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}", Chef::Exceptions::DeprecatedExitCode.new)
+ 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..4f0de26411 100644
--- a/lib/chef/application/windows_service_manager.rb
+++ b/lib/chef/application/windows_service_manager.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,12 @@
# limitations under the License.
#
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
require "win32/service"
end
-require "chef/config"
-require "mixlib/cli"
+require_relative "../config"
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Application
@@ -37,38 +38,38 @@ class Chef
include Mixlib::CLI
option :action,
- :short => "-a ACTION",
- :long => "--action ACTION",
- :default => "status",
- :description => "Action to carry out on chef-service (install, uninstall, status, start, stop, pause, or resume)"
+ short: "-a ACTION",
+ long: "--action ACTION",
+ default: "status",
+ description: "Action to carry out on #{ChefUtils::Dist::Infra::SHORT}-service (install, uninstall, status, start, stop, pause, or resume)."
option :config_file,
- :short => "-c CONFIG",
- :long => "--config CONFIG",
- :default => "#{ENV['SYSTEMDRIVE']}/chef/client.rb",
- :description => "The configuration file to use for chef runs"
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ default: "#{ChefConfig::Config.c_chef_dir}/client.rb",
+ description: "The configuration file to use for #{ChefUtils::Dist::Infra::PRODUCT} runs."
option :log_location,
- :short => "-L LOGLOCATION",
- :long => "--logfile LOGLOCATION",
- :description => "Set the log file location for chef-service"
+ short: "-L LOGLOCATION",
+ long: "--logfile LOGLOCATION",
+ description: "Set the log file location for #{ChefUtils::Dist::Infra::SHORT}-service."
option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true,
- :show_options => true,
- :exit => 0
+ short: "-h",
+ long: "--help",
+ description: "Show this help message.",
+ on: :tail,
+ boolean: true,
+ show_options: true,
+ exit: 0
option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
+ short: "-v",
+ long: "--version",
+ description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version.",
+ boolean: true,
+ proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
def initialize(service_options)
# having to call super in initialize is the most annoying
@@ -77,10 +78,10 @@ class Chef
raise ArgumentError, "Service definition is not provided" if service_options.nil?
- required_options = [:service_name, :service_display_name, :service_description, :service_file_path]
+ required_options = %i{service_name service_display_name service_description service_file_path}
required_options.each do |req_option|
- if !service_options.has_key?(req_option)
+ unless service_options.key?(req_option)
raise ArgumentError, "Service definition doesn't contain required option #{req_option}"
end
end
@@ -114,22 +115,24 @@ class Chef
cmd = "\"#{ruby}\" \"#{@service_file_path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR)
::Win32::Service.new(
- :service_name => @service_name,
- :display_name => @service_display_name,
- :description => @service_description,
+ service_name: @service_name,
+ display_name: @service_display_name,
+ description: @service_description,
# Prior to 0.8.5, win32-service creates interactive services by default,
# and we don't want that, so we need to override the service type.
- :service_type => ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
- :start_type => ::Win32::Service::SERVICE_AUTO_START,
- :binary_path_name => cmd,
- :service_start_name => @service_start_name,
- :password => @password,
- :dependencies => @dependencies
+ service_type: ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
+ start_type: ::Win32::Service::SERVICE_AUTO_START,
+ binary_path_name: cmd,
+ service_start_name: @service_start_name,
+ 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"
@@ -161,12 +164,12 @@ class Chef
private
# Just some state constants
- STOPPED = "stopped"
- RUNNING = "running"
- PAUSED = "paused"
+ STOPPED = "stopped".freeze
+ RUNNING = "running".freeze
+ PAUSED = "paused".freeze
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/applications.rb b/lib/chef/applications.rb
index 97c896e12e..a30b765c77 100644
--- a/lib/chef/applications.rb
+++ b/lib/chef/applications.rb
@@ -1,4 +1,4 @@
-require "chef/application/client"
-require "chef/application/knife"
-require "chef/application/solo"
-require "chef/application/apply"
+require_relative "application/client"
+require_relative "application/knife"
+require_relative "application/solo"
+require_relative "application/apply"
diff --git a/lib/chef/attribute_allowlist.rb b/lib/chef/attribute_allowlist.rb
new file mode 100644
index 0000000000..18665ce835
--- /dev/null
+++ b/lib/chef/attribute_allowlist.rb
@@ -0,0 +1,86 @@
+
+require_relative "exceptions"
+
+class Chef
+ class AttributeAllowlist
+
+ # filter takes two arguments - the data you want to filter, and an array of
+ # keys you want included. 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.
+ #
+ # AttributeAllowlist.filter(
+ # { "filesystem" => {
+ # "/dev/disk" => {
+ # "size" => "10mb"
+ # },
+ # "map - autohome" => {
+ # "size" => "10mb"
+ # }
+ # },
+ # "network" => {
+ # "interfaces" => {
+ # "eth0" => {...},
+ # "eth1" => {...}
+ # }
+ # }
+ # },
+ # ["network/interfaces/eth0", ["filesystem", "/dev/disk"]])
+ # will capture the eth0 and /dev/disk subtrees.
+ def self.filter(data, allowlist = nil)
+ return data if allowlist.nil?
+
+ new_data = {}
+ allowlist.each do |item|
+ add_data(data, new_data, item)
+ end
+ new_data
+ end
+
+ # Walk the data has according to the keys provided by the allowlisted item
+ # and add the data to the allowlisting result.
+ def self.add_data(data, new_data, item)
+ parts = to_array(item)
+
+ all_data = data
+ filtered_data = new_data
+ parts[0..-2].each do |part|
+ unless all_data.key?(part)
+ Chef::Log.warn("Could not find allowlist attribute #{item}.")
+ return nil
+ end
+
+ filtered_data[part] ||= {}
+ filtered_data = filtered_data[part]
+ all_data = all_data[part]
+ end
+
+ # Note: You can't do all_data[parts[-1]] here because the value
+ # may be false-y
+ unless all_data.key?(parts[-1])
+ Chef::Log.warn("Could not find allowlist attribute #{item}.")
+ return nil
+ end
+
+ filtered_data[parts[-1]] = all_data[parts[-1]]
+ new_data
+ end
+
+ private_class_method :add_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.is_a? 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/attribute_blocklist.rb b/lib/chef/attribute_blocklist.rb
new file mode 100644
index 0000000000..929d3dfa36
--- /dev/null
+++ b/lib/chef/attribute_blocklist.rb
@@ -0,0 +1,81 @@
+
+require_relative "exceptions"
+
+class Chef
+ class AttributeBlocklist
+
+ # filter takes two arguments - the data you want to filter, and an array
+ # of keys you want discarded. 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.
+ #
+ # AttributeBlocklist.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, blocklist = nil)
+ return data if blocklist.nil?
+
+ blocklist.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 blocklisted 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 blocklist attribute #{item}.")
+ return nil
+ end
+
+ item_ref = item_ref[part]
+ end
+
+ unless item_ref.key?(parts[-1])
+ Chef::Log.warn("Could not find blocklist 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.is_a? 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/audit/audit_event_proxy.rb b/lib/chef/audit/audit_event_proxy.rb
deleted file mode 100644
index c4d67fa8f4..0000000000
--- a/lib/chef/audit/audit_event_proxy.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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.
-#
-
-RSpec::Support.require_rspec_core "formatters/base_text_formatter"
-
-class Chef
- class Audit
- class AuditEventProxy < ::RSpec::Core::Formatters::BaseFormatter
- ::RSpec::Core::Formatters.register self, :stop, :example_group_started
-
- # TODO I don't like this, but I don't see another way to pass this in
- # see rspec files configuration.rb#L671 and formatters.rb#L129
- def self.events=(events)
- @@events = events
- end
-
- def events
- @@events
- end
-
- def example_group_started(notification)
- 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}")
- events.control_group_started(desc)
- end
- end
-
- def stop(notification)
- Chef::Log.info("Successfully executed all `control_group` blocks and contained examples")
- notification.examples.each do |example|
- control_group_name, control_data = build_control_from(example)
- e = example.exception
- if e
- events.control_example_failure(control_group_name, control_data, e)
- else
- events.control_example_success(control_group_name, control_data)
- end
- end
- end
-
- private
-
- def build_control_from(example)
- described_class = example.metadata[:described_class]
- if described_class
- resource_type = described_class.class.name.split(":")[-1]
- resource_name = described_class.name
- end
-
- # The following code builds up the context - the list of wrapping `describe` or `control` blocks
- describe_groups = []
- group = example.metadata[:example_group]
- # If the innermost block has a resource instead of a string, don't include it in context
- describe_groups.unshift(group[:description]) if described_class.nil?
- group = group[:parent_example_group]
- until group.nil?
- describe_groups.unshift(group[:description])
- group = group[:parent_example_group]
- end
-
- # 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, {
- :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
- end
-end
diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb
deleted file mode 100644
index 8546a21bb4..0000000000
--- a/lib/chef/audit/audit_reporter.rb
+++ /dev/null
@@ -1,176 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "chef/event_dispatch/base"
-require "chef/audit/control_group_data"
-require "time"
-
-class Chef
- class Audit
- class AuditReporter < EventDispatch::Base
-
- attr_reader :rest_client, :audit_data, :ordered_control_groups, :run_status
- private :rest_client, :audit_data, :ordered_control_groups, :run_status
-
- PROTOCOL_VERSION = "0.1.1"
-
- def initialize(rest_client)
- @rest_client = rest_client
- # Ruby 1.9.3 and above "enumerate their values in the order that the corresponding keys were inserted."
- @ordered_control_groups = Hash.new
- @audit_phase_error = nil
- end
-
- def run_context
- run_status.run_context
- end
-
- def audit_phase_start(run_status)
- Chef::Log.debug("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|
- audit_data.add_control_group(control_group)
- end
- end
-
- # If the audit phase failed, its because there was some kind of error in the framework
- # that runs tests - normal errors are interpreted as EXAMPLE failures and captured.
- # We still want to send available audit information to the server so we process the
- # known control groups.
- 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|
- audit_data.add_control_group(control_group)
- end
- end
-
- def run_completed(node)
- post_auditing_data
- end
-
- def run_failed(error)
- # Audit phase errors are captured when audit_phase_failed gets called.
- # The error passed here isn't relevant to auditing, so we ignore it.
- post_auditing_data
- end
-
- def control_group_started(name)
- if ordered_control_groups.has_key?(name)
- raise Chef::Exceptions::AuditControlGroupDuplicate.new(name)
- end
- metadata = run_context.audits[name].metadata
- ordered_control_groups.store(name, ControlGroupData.new(name, metadata))
- end
-
- def control_example_success(control_group_name, example_data)
- control_group = ordered_control_groups[control_group_name]
- control_group.example_success(example_data)
- end
-
- def control_example_failure(control_group_name, example_data, error)
- control_group = ordered_control_groups[control_group_name]
- control_group.example_failure(example_data, error.message)
- end
-
- # If @audit_enabled is nil or true, we want to run audits
- def auditing_enabled?
- Chef::Config[:audit_mode] != :disabled
- end
-
- private
-
- def post_auditing_data
- unless auditing_enabled?
- Chef::Log.debug("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")
- return
- end
-
- audit_data.start_time = iso8601ify(run_status.start_time)
- 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})")
- run_data = audit_data.to_hash
-
- if @audit_phase_error
- error_info = "#{@audit_phase_error.class}: #{@audit_phase_error.message}"
- error_info << "\n#{@audit_phase_error.backtrace.join("\n")}" if @audit_phase_error.backtrace
- run_data[:error] = error_info
- end
-
- Chef::Log.debug "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}"
- begin
- rest_client.post(audit_history_url, run_data, headers)
- rescue StandardError => e
- if e.respond_to? :response
- # 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.")
- 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)
- if Chef::Config.chef_zero.enabled
- Chef::Log.debug("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})")
- end
-
- if Chef::Config[:enable_reporting_url_fatals]
- Chef::Log.error("Reporting fatals enabled. Aborting run.")
- raise
- end
- end
- end
-
- def headers(additional_headers = {})
- options = { "X-Ops-Audit-Report-Protocol-Version" => PROTOCOL_VERSION }
- options.merge(additional_headers)
- end
-
- def encode_gzip(data)
- "".tap do |out|
- Zlib::GzipWriter.wrap(StringIO.new(out)) { |gz| gz << data }
- end
- end
-
- def iso8601ify(time)
- time.utc.iso8601.to_s
- end
- end
- end
-end
diff --git a/lib/chef/audit/control_group_data.rb b/lib/chef/audit/control_group_data.rb
deleted file mode 100644
index 4dffbdf3dd..0000000000
--- a/lib/chef/audit/control_group_data.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "securerandom"
-
-class Chef
- class Audit
- class AuditData
- attr_reader :node_name, :run_id, :control_groups
- attr_accessor :start_time, :end_time
-
- def initialize(node_name, run_id)
- @node_name = node_name
- @run_id = run_id
- @control_groups = []
- end
-
- def add_control_group(control_group)
- control_groups << control_group
- end
-
- def to_hash
- {
- :node_name => node_name,
- :run_id => run_id,
- :start_time => start_time,
- :end_time => end_time,
- :control_groups => control_groups.collect { |c| c.to_hash },
- }
- end
- end
-
- class ControlGroupData
- attr_reader :name, :status, :number_succeeded, :number_failed, :controls, :metadata
-
- def initialize(name, metadata = {})
- @status = "success"
- @controls = []
- @number_succeeded = 0
- @number_failed = 0
- @name = name
- @metadata = metadata
- end
-
- def example_success(control_data)
- @number_succeeded += 1
- control = create_control(control_data)
- control.status = "success"
- controls << control
- control
- end
-
- def example_failure(control_data, details)
- @number_failed += 1
- @status = "failure"
- control = create_control(control_data)
- control.details = details if details
- control.status = "failure"
- controls << control
- control
- end
-
- def to_hash
- # We sort it so the examples appear in the output in the same order
- # they appeared in the recipe
- controls.sort! { |x, y| x.line_number <=> y.line_number }
- h = {
- :name => name,
- :status => status,
- :number_succeeded => number_succeeded,
- :number_failed => number_failed,
- :controls => controls.collect { |c| c.to_hash },
- }
- # If there is a duplicate key, metadata will overwrite it
- add_display_only_data(h).merge(metadata)
- end
-
- private
-
- def create_control(control_data)
- ControlData.new(control_data)
- end
-
- # The id and control sequence number are ephemeral data - they are not needed
- # to be persisted and can be regenerated at will. They are only needed
- # for display purposes.
- def add_display_only_data(group)
- group[:id] = SecureRandom.uuid
- group[:controls].collect!.with_index do |c, i|
- # i is zero-indexed, and we want the display one-indexed
- c[:sequence_number] = i + 1
- c
- end
- group
- end
-
- end
-
- class ControlData
- attr_reader :name, :resource_type, :resource_name, :context, :line_number
- attr_accessor :status, :details
-
- def initialize(control_data = {})
- control_data.each do |k, v|
- self.instance_variable_set("@#{k}", v)
- end
- end
-
- def to_hash
- h = {
- :name => name,
- :status => status,
- :details => details,
- :resource_type => resource_type,
- :resource_name => resource_name,
- }
- h[:context] = context || []
- h
- end
- end
-
- end
-end
diff --git a/lib/chef/audit/logger.rb b/lib/chef/audit/logger.rb
deleted file mode 100644
index 759683ccc8..0000000000
--- a/lib/chef/audit/logger.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# 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 "stringio"
-
-class Chef
- class Audit
- class Logger
- def self.puts(message = "")
- @buffer ||= StringIO.new
- @buffer.puts(message)
-
- Chef::Log.info(message)
- end
-
- def self.read_buffer
- return "" if @buffer.nil?
- @buffer.string
- end
- end
- end
-end
diff --git a/lib/chef/audit/rspec_formatter.rb b/lib/chef/audit/rspec_formatter.rb
deleted file mode 100644
index 234202b684..0000000000
--- a/lib/chef/audit/rspec_formatter.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@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 "rspec/core"
-
-class Chef
- class Audit
- class RspecFormatter < RSpec::Core::Formatters::DocumentationFormatter
- RSpec::Core::Formatters.register self, :close
-
- # @api public
- #
- # Invoked at the very end, `close` allows the formatter to clean
- # up resources, e.g. open streams, etc.
- #
- # @param _notification [NullNotification] (Ignored)
- def close(_notification)
- # Normally Rspec closes the streams it's given. We don't want it for Chef.
- end
- end
- end
-end
diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb
deleted file mode 100644
index 837346381c..0000000000
--- a/lib/chef/audit/runner.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# Author:: Claire McQuin (<claire@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 "chef/audit/logger"
-
-class Chef
- class Audit
- class Runner
-
- attr_reader :run_context
- private :run_context
-
- def initialize(run_context)
- @run_context = run_context
- end
-
- def run
- setup
- register_control_groups
- do_run
- end
-
- def failed?
- RSpec.world.reporter.failed_examples.size > 0
- end
-
- def num_failed
- RSpec.world.reporter.failed_examples.size
- end
-
- def num_total
- RSpec.world.reporter.examples.size
- end
-
- def exclusion_pattern
- Regexp.new(".+[\\\/]lib[\\\/]chef[\\\/]")
- end
-
- private
-
- # Prepare to run audits:
- # - Require files
- # - Configure RSpec
- # - Configure Specinfra/Serverspec
- def setup
- require_deps
- configure_rspec
- configure_specinfra
- end
-
- # RSpec uses a global configuration object, RSpec.configuration. We found
- # there was interference between the configuration for audit-mode and
- # the configuration for our own spec tests in these cases:
- # 1. Specinfra and Serverspec modify RSpec.configuration when loading.
- # 2. Setting output/error streams.
- # 3. Adding formatters.
- # 4. Defining example group aliases.
- #
- # Moreover, Serverspec loads its DSL methods into the global namespace,
- # which causes conflicts with the Chef namespace for resources and packages.
- #
- # We wait until we're in the audit-phase of the chef-client run to load
- # these files. This helps with the namespacing problems we saw, and
- # prevents Specinfra and Serverspec from modifying the RSpec configuration
- # used by our spec tests.
- def require_deps
- require "rspec"
- require "rspec/its"
- require "specinfra"
- require "specinfra/helper"
- require "specinfra/helper/set"
- require "serverspec/helper"
- require "serverspec/matcher"
- require "serverspec/subject"
- require "chef/audit/audit_event_proxy"
- require "chef/audit/rspec_formatter"
-
- Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set)
- end
-
- # Configure RSpec just the way we like it:
- # - Set location of error and output streams
- # - Add custom audit-mode formatters
- # - Explicitly disable :should syntax
- # - Set :color option according to chef config
- # - Disable exposure of global DSL
- def configure_rspec
- set_streams
- add_formatters
- disable_should_syntax
-
- RSpec.configure do |c|
- c.color = Chef::Config[:color]
- c.expose_dsl_globally = false
- c.project_source_dirs = Array(Chef::Config[:cookbook_path])
- c.backtrace_exclusion_patterns << exclusion_pattern
- end
- end
-
- # Set the error and output streams which audit-mode will use to report
- # human-readable audit information.
- #
- # This should always be called before #add_formatters. RSpec won't allow
- # the output stream to be changed for a formatter once the formatter has
- # been added.
- def set_streams
- RSpec.configuration.output_stream = Chef::Audit::Logger
- RSpec.configuration.error_stream = Chef::Audit::Logger
- end
-
- # Add formatters which we use to
- # 1. Output human-readable data to the output stream,
- # 2. Collect JSON data to send back to the analytics server.
- def add_formatters
- RSpec.configuration.add_formatter(Chef::Audit::AuditEventProxy)
- RSpec.configuration.add_formatter(Chef::Audit::RspecFormatter)
- Chef::Audit::AuditEventProxy.events = run_context.events
- end
-
- # Audit-mode uses RSpec 3. :should syntax is deprecated by default in
- # RSpec 3, so we explicitly disable it here.
- #
- # This can be removed once :should is removed from RSpec.
- def disable_should_syntax
- RSpec.configure do |config|
- config.expect_with :rspec do |c|
- c.syntax = :expect
- end
- end
- end
-
- # Set up the backend for Specinfra/Serverspec. :exec is the local system; on Windows, it is :cmd
- def configure_specinfra
- if Chef::Platform.windows?
- Specinfra.configuration.backend = :cmd
- Specinfra.configuration.os = { :family => "windows" }
- else
- Specinfra.configuration.backend = :exec
- end
- end
-
- # Iterates through the control groups registered to this run_context, builds an
- # example group (RSpec::Core::ExampleGroup) object per control group, and
- # registers the group with the RSpec.world.
- #
- # We could just store an array of example groups and not use RSpec.world,
- # but it may be useful later if we decide to apply our own ordering scheme
- # or use example group filters.
- def register_control_groups
- add_example_group_methods
- run_context.audits.each do |name, group|
- ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block)
- RSpec.world.record(ctl_grp)
- end
- end
-
- # Add example group method aliases to RSpec.
- #
- # __control_group__: Used internally to create example groups from the control
- # groups saved in the run_context.
- # control: Used within the context of a control group block, like RSpec's
- # describe or context.
- def add_example_group_methods
- RSpec::Core::ExampleGroup.define_example_group_method :__control_group__
- RSpec::Core::ExampleGroup.define_example_group_method :control
- end
-
- # Run the audits!
- def do_run
- # RSpec::Core::Runner wants to be initialized with an
- # RSpec::Core::ConfigurationOptions object, which is used to process
- # command line configuration arguments. We directly fiddle with the
- # internal RSpec configuration object, so we give nil here and let
- # RSpec pick up its own configuration and world.
- runner = RSpec::Core::Runner.new(nil)
- runner.run_specs(RSpec.world.ordered_example_groups)
- end
-
- end
- end
-end
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index f019448bd8..bcb5eec4e4 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,10 +26,12 @@
# injected" into this class by other objects and do not reference the class symbols in those files
# directly and we do not need to require those files here.
-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_relative "platform/provider_priority_map"
+require_relative "platform/resource_priority_map"
+require_relative "platform/provider_handler_map"
+require_relative "platform/resource_handler_map"
+require_relative "deprecated"
+require_relative "event_dispatch/dsl"
class Chef
class << self
@@ -94,8 +96,8 @@ class Chef
#
# @return [Array<Class>] Modified Priority Array of Provider Classes to use for the resource_name on the node
#
- def set_provider_priority_array(resource_name, priority_array, *filter, &block)
- result = provider_priority_map.set_priority_array(resource_name.to_sym, priority_array, *filter, &block)
+ def set_provider_priority_array(resource_name, priority_array, **filter, &block)
+ result = provider_priority_map.set_priority_array(resource_name.to_sym, priority_array, **filter, &block)
result = result.dup if result
result
end
@@ -109,8 +111,8 @@ class Chef
#
# @return [Array<Class>] Modified Priority Array of Resource Classes to use for the resource_name on the node
#
- def set_resource_priority_array(resource_name, priority_array, *filter, &block)
- result = resource_priority_map.set_priority_array(resource_name.to_sym, priority_array, *filter, &block)
+ def set_resource_priority_array(resource_name, priority_array, **filter, &block)
+ result = resource_priority_map.set_priority_array(resource_name.to_sym, priority_array, **filter, &block)
result = result.dup if result
result
end
@@ -197,31 +199,43 @@ class Chef
#
# Emit a deprecation message.
#
- # @param message The message to send.
- # @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).
+ # @param type [Symbol] The message to send. This should refer to a class
+ # defined in Chef::Deprecated
+ # @param message [String, nil] An explicit message to display, rather than
+ # the generic one associated with the deprecation.
+ # @param location [String, nil] 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).
+ # @return [void]
#
# @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)
- else
- Chef::Log.deprecation(message, location)
+ run_context.events.deprecation(deprecation, location)
+ elsif !deprecation.silenced?
+ Chef::Log.deprecation(deprecation.to_s)
end
end
- end
- # @api private Only for test dependency injection; not evenly implemented as yet.
- def self.path_to(path)
- path
+ # Log a generic deprecation warning that doesn't have a specific class in
+ # Chef::Deprecated.
+ #
+ # This should generally not be used, as the user will not be given a link
+ # to get more information on fixing the deprecation warning.
+ #
+ # @see #deprecated
+ def log_deprecation(message, location = nil)
+ location ||= Chef::Log.caller_location
+ Chef.deprecated(:generic, message, location)
+ end
end
reset!
diff --git a/lib/chef/chef_fs.rb b/lib/chef/chef_fs.rb
index 43a9efd5e2..bb3408b781 100644
--- a/lib/chef/chef_fs.rb
+++ b/lib/chef/chef_fs.rb
@@ -1,4 +1,4 @@
-require "chef/platform"
+require_relative "platform"
#
# ChefFS was designed to be a near-1:1 translation between Chef server endpoints
@@ -53,7 +53,7 @@ require "chef/platform"
class Chef
module ChefFS
def self.windows?
- Chef::Platform.windows?
+ ChefUtils.windows?
end
end
end
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index 6b3e830f8d..eeeb96e38b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/cookbook_manifest"
+require_relative "../cookbook_manifest"
require "chef_zero/data_store/memory_store"
require "chef_zero/data_store/data_already_exists_error"
require "chef_zero/data_store/data_not_found_error"
-require "chef/chef_fs/file_pattern"
-require "chef/chef_fs/file_system"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/file_system/memory/memory_root"
-require "fileutils"
+require_relative "file_pattern"
+require_relative "file_system"
+require_relative "file_system/exceptions"
+require_relative "file_system/memory/memory_root"
+require "fileutils" unless defined?(FileUtils)
class Chef
module ChefFS
@@ -172,11 +172,11 @@ class Chef
@memory_store.create_dir(path, name, *options)
else
with_parent_dir(path + [name], *options) do |parent, name|
- begin
- parent.create_child(name, nil)
- rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e
- raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e)
- end
+
+ parent.create_child(name, nil)
+ rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e
+ raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e)
+
end
end
end
@@ -204,13 +204,13 @@ class Chef
@memory_store.create(path, name, data, *options)
elsif path[0] == "cookbooks" && path.length == 2
- # Do nothing. The entry gets created when the cookbook is created.
+ # Do nothing. The entry gets created when the cookbook is created.
# /policy_groups/GROUP/policies/NAME
elsif path[0] == "policy_groups" && path[2] == "policies"
# Just set or create the proper entry in the hash
update_json(to_chef_fs_path(path[0..1]), {}, *options) do |group|
- if policies.has_key?(path[3])
+ if policies.key?(path[3])
raise ChefZero::DataStore::DataAlreadyExistsError.new(path, group)
end
@@ -246,16 +246,16 @@ class Chef
end
else
- if !data.is_a?(String)
+ unless data.is_a?(String)
raise "set only works with strings"
end
with_parent_dir(path + [name], *options) do |parent, name|
- begin
- parent.create_child(name, data)
- rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e
- raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e)
- end
+
+ parent.create_child(name, data)
+ rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e
+ raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e)
+
end
end
end
@@ -265,7 +265,7 @@ class Chef
@memory_store.get(path)
elsif path[0] == "file_store" && path[1] == "repo"
- entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join("/"))
+ entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[2..].join("/"))
begin
entry.read
rescue Chef::ChefFS::FileSystem::NotFoundError => e
@@ -279,6 +279,7 @@ class Chef
if !policy_group["policies"] || !policy_group["policies"][path[3]]
raise ChefZero::DataStore::DataNotFoundError.new(path, entry)
end
+
# The policy group looks like:
# {
# "policies": {
@@ -311,7 +312,7 @@ class Chef
cookbook_type = path[0]
result = nil
begin
- result = Chef::CookbookManifest.new(entry.chef_object, policy_mode: cookbook_type == "cookbook_artifacts").to_hash
+ result = Chef::CookbookManifest.new(entry.chef_object, policy_mode: cookbook_type == "cookbook_artifacts").to_h
rescue Chef::ChefFS::FileSystem::NotFoundError => e
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
end
@@ -319,14 +320,14 @@ class Chef
result.each_pair do |key, value|
if value.is_a?(Array)
value.each do |file|
- if file.is_a?(Hash) && file.has_key?("checksum")
+ if file.is_a?(Hash) && file.key?("checksum")
relative = ["file_store", "repo", cookbook_type]
if chef_fs.versioned_cookbooks || cookbook_type == "cookbook_artifacts"
relative << "#{path[1]}-#{path[2]}"
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
@@ -334,7 +335,7 @@ class Chef
end
if cookbook_type == "cookbook_artifacts"
- result["metadata"] = result["metadata"].to_hash
+ result["metadata"] = result["metadata"].to_h
result["metadata"].delete_if do |key, value|
value == [] ||
(value == {} && !%w{dependencies attributes recipes}.include?(key)) ||
@@ -348,11 +349,11 @@ class Chef
else
with_entry(path) do |entry|
- begin
- entry.read
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
- end
+
+ entry.read
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+
end
end
end
@@ -361,7 +362,7 @@ class Chef
if use_memory_store?(path)
@memory_store.set(path, data, *options)
else
- if !data.is_a?(String)
+ unless data.is_a?(String)
raise "set only works with strings: #{path} = #{data.inspect}"
end
@@ -398,9 +399,10 @@ class Chef
# DELETE /policy_groups/GROUP/policies/POLICY
elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4
update_json(to_chef_fs_path(path[0..1]), {}) do |group|
- unless group["policies"] && group["policies"].has_key?(path[3])
+ unless group["policies"] && group["policies"].key?(path[3])
raise ChefZero::DataStore::DataNotFoundError.new(path)
end
+
group["policies"].delete(path[3])
group
end
@@ -413,6 +415,7 @@ class Chef
if result.size == members.size
raise ChefZero::DataStore::DataNotFoundError.new(path)
end
+
result
end
@@ -424,20 +427,21 @@ class Chef
if result.size == invitations.size
raise ChefZero::DataStore::DataNotFoundError.new(path)
end
+
result
end
else
with_entry(path) do |entry|
- begin
- if %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length >= 3
- entry.delete(true)
- else
- entry.delete(false)
- end
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+
+ if %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length >= 3
+ entry.delete(true)
+ else
+ entry.delete(false)
end
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+
end
end
end
@@ -457,20 +461,21 @@ class Chef
policies.children.each do |policy|
# 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
+ raise ChefZero::DataStore::DataNotFoundError.new(path) unless found_policy
end
else
with_entry(path) do |entry|
- begin
- entry.delete(options.include?(:recursive))
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
- end
+
+ entry.delete(options.include?(:recursive))
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+
end
end
end
@@ -482,11 +487,11 @@ class Chef
# LIST /policies
elsif path == [ "policies" ]
with_entry([ path[0] ]) do |policies|
- begin
- policies.children.map { |policy| policy.name[0..-6].rpartition("-")[0] }.uniq
- rescue Chef::ChefFS::FileSystem::NotFoundError
- []
- end
+
+ policies.children.map { |policy| policy.name[0..-6].rpartition("-")[0] }.uniq
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ []
+
end
# LIST /policies/POLICY/revisions
@@ -502,6 +507,7 @@ class Chef
revisions << revision if name == path[1]
end
raise ChefZero::DataStore::DataNotFoundError.new(path) if revisions.empty?
+
revisions
end
@@ -518,32 +524,33 @@ class Chef
elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 1
with_entry(path) do |entry|
- begin
- if path[0] == "cookbook_artifacts"
- entry.children.map { |child| child.name.rpartition("-")[0] }.uniq
- elsif chef_fs.versioned_cookbooks
- # /cookbooks/name-version -> /cookbooks/name
- entry.children.map { |child| split_name_version(child.name)[0] }.uniq
- else
- entry.children.map { |child| child.name }
- end
- rescue Chef::ChefFS::FileSystem::NotFoundError
- # If the cookbooks dir doesn't exist, we have no cookbooks (not 404)
- []
+
+ if path[0] == "cookbook_artifacts"
+ entry.children.map { |child| child.name.rpartition("-")[0] }.uniq
+ elsif chef_fs.versioned_cookbooks
+ # /cookbooks/name-version -> /cookbooks/name
+ entry.children.map { |child| split_name_version(child.name)[0] }.uniq
+ else
+ entry.children.map(&:name)
end
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ # If the cookbooks dir doesn't exist, we have no cookbooks (not 404)
+ []
+
end
elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 2
if chef_fs.versioned_cookbooks || path[0] == "cookbook_artifacts"
result = with_entry([ path[0] ]) do |entry|
# list /cookbooks/name = filter /cookbooks/name-version down to name
- entry.children.map { |child| split_name_version(child.name) }.
- select { |name, version| name == path[1] }.
- map { |name, version| version }
+ entry.children.map { |child| split_name_version(child.name) }
+ .select { |name, version| name == path[1] }
+ .map { |name, version| version }
end
if result.empty?
raise ChefZero::DataStore::DataNotFoundError.new(path)
end
+
result
else
# list /cookbooks/name = <single version>
@@ -553,16 +560,16 @@ class Chef
else
result = with_entry(path) do |entry|
- begin
- entry.children.map { |c| zero_filename(c) }.sort
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- # /cookbooks, /data, etc. never return 404
- if path_always_exists?(path)
- []
- else
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
- end
+
+ entry.children.map { |c| zero_filename(c) }.sort
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ # /cookbooks, /data, etc. never return 404
+ if path_always_exists?(path)
+ []
+ else
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
end
+
end
# Older versions of chef-zero do not understand policies and cookbook_artifacts,
@@ -581,7 +588,7 @@ class Chef
# /policy_groups/NAME/policies/POLICYNAME
elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4
group = get_json(to_chef_fs_path(path[0..1]), {})
- group["policies"] && group["policies"].has_key?(path[3])
+ group["policies"] && group["policies"].key?(path[3])
else
path_always_exists?(path) || Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
@@ -611,7 +618,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)
@@ -628,7 +635,7 @@ class Chef
cookbook.each_pair do |key, value|
if value.is_a?(Array)
value.each do |file|
- if file.is_a?(Hash) && file.has_key?("checksum")
+ if file.is_a?(Hash) && file.key?("checksum")
file_data = @memory_store.get(["file_store", "checksums", file["checksum"]])
cookbook_fs.add_file(File.join(cookbook_path, file["path"]), file_data)
end
@@ -638,7 +645,7 @@ class Chef
# Create the .uploaded-cookbook-version.json
cookbooks = chef_fs.child(cookbook_type)
- if !cookbooks.exists?
+ unless cookbooks.exists?
cookbooks = chef_fs.create_child(cookbook_type)
end
# We are calling a cookbooks-specific API, so get multiplexed_dirs out of the way if it is there
@@ -717,8 +724,8 @@ class Chef
path[-1] = "#{path[-1]}.json"
end
- # /acls/containers|nodes|... do NOT drop into the next elsif, and do
- # not get .json appended
+ # /acls/containers|nodes|... do NOT drop into the next elsif, and do
+ # not get .json appended
# /nodes|clients|.../x.json
elsif path.length == 2
@@ -768,7 +775,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
@@ -779,15 +786,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)
@@ -848,6 +853,7 @@ class Chef
def ensure_dir(entry)
return entry if entry.exists?
+
parent = entry.parent
if parent
ensure_dir(parent)
diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb
index 2d887f4780..1e3aa137cd 100644
--- a/lib/chef/chef_fs/command_line.rb
+++ b/lib/chef/chef_fs/command_line.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/util/diff"
+require_relative "file_system"
+require_relative "file_system/exceptions"
+require_relative "../util/diff"
class Chef
module ChefFS
@@ -44,6 +44,7 @@ class Chef
when :directory_to_file
next if diff_filter && diff_filter !~ /T/
+
if output_mode == :name_only
yield "#{new_path}\n"
elsif output_mode == :name_status
@@ -54,6 +55,7 @@ class Chef
when :file_to_directory
next if diff_filter && diff_filter !~ /T/
+
if output_mode == :name_only
yield "#{new_path}\n"
elsif output_mode == :name_status
@@ -71,6 +73,7 @@ class Chef
new_path += File.extname(old_path)
end
next if diff_filter && diff_filter !~ /D/
+
if output_mode == :name_only
yield "#{new_path}\n"
elsif output_mode == :name_status
@@ -86,6 +89,7 @@ class Chef
when :added
next if diff_filter && diff_filter !~ /A/
+
if output_mode == :name_only
yield "#{new_path}\n"
elsif output_mode == :name_status
@@ -101,6 +105,7 @@ class Chef
when :modified
next if diff_filter && diff_filter !~ /M/
+
if output_mode == :name_only
yield "#{new_path}\n"
elsif output_mode == :name_status
@@ -127,7 +132,7 @@ class Chef
end
end
end
- if !found_match
+ unless found_match
ui.error "#{pattern}: No such file or directory on remote or local" if ui
error = true
end
@@ -146,38 +151,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
@@ -185,9 +190,9 @@ class Chef
are_same, old_value, new_value = Chef::ChefFS::FileSystem.compare(old_entry, new_entry)
if are_same
if old_value == :none
- return [ [ :both_nonexistent, old_entry, new_entry ] ]
+ [ [ :both_nonexistent, old_entry, new_entry ] ]
else
- return [ [ :same, old_entry, new_entry ] ]
+ [ [ :same, old_entry, new_entry ] ]
end
else
if old_value == :none
@@ -229,17 +234,17 @@ class Chef
end
end
- if old_value == :none || (old_value == nil && !old_entry.exists?)
- return [ [ :added, old_entry, new_entry, old_value, new_value ] ]
+ if old_value == :none || (old_value.nil? && !old_entry.exists?)
+ [ [ :added, old_entry, new_entry, old_value, new_value ] ]
elsif new_value == :none
- return [ [ :deleted, old_entry, new_entry, old_value, new_value ] ]
+ [ [ :deleted, old_entry, new_entry, old_value, new_value ] ]
else
- return [ [ :modified, old_entry, new_entry, old_value, new_value ] ]
+ [ [ :modified, old_entry, new_entry, old_value, new_value ] ]
end
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
class << self
@@ -266,26 +271,24 @@ class Chef
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
- begin
- new_tempfile = Tempfile.new("new")
- new_tempfile.write(new_value)
- new_tempfile.close
-
- begin
- old_tempfile = Tempfile.new("old")
- old_tempfile.write(old_value)
- old_tempfile.close
-
- result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path)
- result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}")
- result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}")
- result
- ensure
- old_tempfile.close!
- end
- ensure
- new_tempfile.close!
- end
+
+ new_tempfile = Tempfile.new("new")
+ new_tempfile.write(new_value)
+ new_tempfile.close
+
+ old_tempfile = Tempfile.new("old")
+ old_tempfile.write(old_value)
+ old_tempfile.close
+
+ result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path)
+ result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}")
+ result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}")
+ result
+ rescue => e
+ "!!! Unable to diff #{old_path} and #{new_path} due to #{e}"
+ ensure
+ old_tempfile.close!
+ new_tempfile.close!
end
end
end
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index 63a1363724..6f9183f40b 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/chef_fs/path_utils"
+require_relative "../log"
+require_relative "path_utils"
class Chef
module ChefFS
@@ -44,7 +44,7 @@ class Chef
"users" => "user",
"policies" => "policy",
"policy_groups" => "policy_group",
- }
+ }.freeze
INFLECTIONS.each { |k, v| k.freeze; v.freeze }
INFLECTIONS.freeze
@@ -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
@@ -150,7 +150,7 @@ class Chef
hosted_everything or allow repo_mode to default}
end
# Default to getting *everything* from the server.
- if !@chef_config[:repo_mode]
+ unless @chef_config[:repo_mode]
if is_hosted?
@chef_config[:repo_mode] = "hosted_everything"
else
@@ -164,7 +164,7 @@ class Chef
attr_reader :cookbook_version
def is_hosted?
- @chef_config[:chef_server_url] =~ /\/+organizations\/.+/
+ @chef_config[:chef_server_url] =~ %r{/+organizations/.+}
end
def chef_fs
@@ -172,8 +172,8 @@ class Chef
end
def create_chef_fs
- require "chef/chef_fs/file_system/chef_server/chef_server_root_dir"
- Chef::ChefFS::FileSystem::ChefServer::ChefServerRootDir.new("remote", @chef_config, :cookbook_version => @cookbook_version)
+ require_relative "file_system/chef_server/chef_server_root_dir"
+ Chef::ChefFS::FileSystem::ChefServer::ChefServerRootDir.new("remote", @chef_config, cookbook_version: @cookbook_version)
end
def local_fs
@@ -181,7 +181,7 @@ class Chef
end
def create_local_fs
- require "chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir"
+ require_relative "file_system/repository/chef_repository_file_system_root_dir"
Chef::ChefFS::FileSystem::Repository::ChefRepositoryFileSystemRootDir.new(object_paths, Array(chef_config[:chef_repo_path]).flatten, @chef_config)
end
@@ -272,6 +272,7 @@ class Chef
# cookbooks -> cookbook_path
singular_name = INFLECTIONS[object_name]
raise "Unknown object name #{object_name}" unless singular_name
+
variable_name = "#{singular_name}_path"
paths = Array(@chef_config[variable_name]).flatten
result[object_name] = paths.map { |path| File.expand_path(path) }
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..e64f3d245c 100644
--- a/lib/chef/chef_fs/data_handler/acl_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/acl_data_handler.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
@@ -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..3e5c4f1b84 100644
--- a/lib/chef/chef_fs/data_handler/client_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/client_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/api_client"
+require_relative "data_handler_base"
+require_relative "../../api_client"
class Chef
module ChefFS
@@ -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/client_key_data_handler.rb b/lib/chef/chef_fs/data_handler/client_key_data_handler.rb
index 6276413bcf..fc81a658cf 100644
--- a/lib/chef/chef_fs/data_handler/client_key_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/client_key_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/api_client"
+require_relative "data_handler_base"
+require_relative "../../api_client"
class Chef
module ChefFS
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..25bdf73b04 100644
--- a/lib/chef/chef_fs/data_handler/container_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/container_data_handler.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
@@ -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.
@@ -24,7 +24,7 @@ class Chef
def verify_integrity(object, entry)
base_name = remove_dot_json(entry.name)
if object["containername"] != base_name
- yield("Name in #{entry.path_for_printing} must be '#{base_name}' (is '#{object['containername']}')")
+ yield("Name in #{entry.path_for_printing} must be '#{base_name}' (is '#{object["containername"]}')")
end
end
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..ee1480c62e 100644
--- a/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/cookbook/metadata"
+require_relative "data_handler_base"
+require_relative "../../cookbook/metadata"
class Chef
module ChefFS
@@ -24,7 +24,7 @@ class Chef
end
def preserve_key?(key)
- return key == "cookbook_name" || key == "version"
+ %w{cookbook_name version}.include?(key)
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..4f9a7ae151 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
@@ -1,10 +1,12 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/data_bag_item"
+require_relative "data_handler_base"
+require_relative "../../data_bag_item"
class Chef
module ChefFS
module DataHandler
class DataBagItemDataHandler < DataHandlerBase
+ RESERVED_NAMES = /^(node|role|environment|client)$/.freeze
+
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
@@ -51,7 +54,9 @@ class Chef
def verify_integrity(object, entry)
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']}')")
+ yield("ID in #{entry.path_for_printing} must be '#{base_name}' (is '#{object["raw_data"]["id"]}')")
+ elsif RESERVED_NAMES.match?(entry.parent.name)
+ 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 3668f77dd5..d51e54e8ab 100644
--- a/lib/chef/chef_fs/data_handler/data_handler_base.rb
+++ b/lib/chef/chef_fs/data_handler/data_handler_base.rb
@@ -56,19 +56,22 @@ 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}
#
def normalize_hash(object, defaults)
# Make a normalized result in the specified order for diffing
result = {}
- defaults.each_pair do |key, default|
- result[key] = object.has_key?(key) ? object[key] : default
+ defaults.each_pair do |key, value|
+ result[key] = object.is_a?(Hash) && object.key?(key) ? object[key] : value
end
- object.each_pair do |key, value|
- result[key] = value if !result.has_key?(key)
+ if object.is_a?(Hash)
+ object.each_pair do |key, value|
+ result[key] = value unless result.key?(key)
+ end
+ else
+ Chef::Log.warn "Encountered invalid object during normalization. Using these defaults #{defaults}"
end
result
end
@@ -111,7 +114,7 @@ class Chef
def from_ruby(path)
r = chef_class.new
r.from_file(path)
- r.to_hash
+ r.to_h
end
#
@@ -140,8 +143,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"
@@ -195,7 +197,7 @@ class Chef
def verify_integrity(object, entry)
base_name = remove_file_extension(entry.name)
if object["name"] != base_name
- yield("Name must be '#{base_name}' (is '#{object['name']}')")
+ yield("Name must be '#{base_name}' (is '#{object["name"]}')")
end
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 68f6daee9a..e3aa242fc9 100644
--- a/lib/chef/chef_fs/data_handler/environment_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/environment_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/environment"
+require_relative "data_handler_base"
+require_relative "../../environment"
class Chef
module ChefFS
@@ -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..3fffec5d11 100644
--- a/lib/chef/chef_fs/data_handler/group_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/group_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/api_client"
+require_relative "data_handler_base"
+require_relative "../../api_client"
class Chef
module ChefFS
@@ -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..c4698afd2b 100644
--- a/lib/chef/chef_fs/data_handler/node_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/node_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/node"
+require_relative "data_handler_base"
+require_relative "../../node"
class Chef
module ChefFS
@@ -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..01d227ffaf 100644
--- a/lib/chef/chef_fs/data_handler/organization_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/organization_data_handler.rb
@@ -1,22 +1,21 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
module DataHandler
class OrganizationDataHandler < DataHandlerBase
def normalize(organization, entry)
- result = normalize_hash(organization, {
+ normalize_hash(organization, {
"name" => entry.org,
"full_name" => entry.org,
"org_type" => "Business",
"clientname" => "#{entry.org}-validator",
"billing_plan" => "platform-free",
})
- result
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.
@@ -27,7 +26,7 @@ class Chef
# @yieldparam [s<string>] error message
def verify_integrity(object, entry)
if entry.org != object["name"]
- yield("Name must be '#{entry.org}' (is '#{object['name']}')")
+ yield("Name must be '#{entry.org}' (is '#{object["name"]}')")
end
end
end
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..b3be2a962f 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
@@ -1,11 +1,11 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
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/organization_members_data_handler.rb b/lib/chef/chef_fs/data_handler/organization_members_data_handler.rb
index 8e452a413c..944d3fa0a5 100644
--- a/lib/chef/chef_fs/data_handler/organization_members_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/organization_members_data_handler.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
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..8ae749af73 100644
--- a/lib/chef/chef_fs/data_handler/policy_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/policy_data_handler.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
@@ -28,18 +28,18 @@ 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
def verify_integrity(object_data, entry)
name, revision = name_and_revision(entry.name)
if object_data["name"] != name
- yield("Object name '#{object_data['name']}' doesn't match entry '#{name}'.")
+ yield("Object name '#{object_data["name"]}' doesn't match entry '#{name}'.")
end
if object_data["revision_id"] != revision
- yield("Object revision ID '#{object_data['revision_id']}' doesn't match entry '#{revision}'.")
+ yield("Object revision ID '#{object_data["revision_id"]}' doesn't match entry '#{revision}'.")
end
end
end
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..bb0d4efb1c 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
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
@@ -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 b09c146a5d..1007a29c8a 100644
--- a/lib/chef/chef_fs/data_handler/role_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/role_data_handler.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/data_handler/data_handler_base"
-require "chef/role"
+require_relative "data_handler_base"
+require_relative "../../role"
class Chef
module ChefFS
@@ -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..50a0c0682d 100644
--- a/lib/chef/chef_fs/data_handler/user_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/user_data_handler.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/data_handler/data_handler_base"
+require_relative "data_handler_base"
class Chef
module ChefFS
@@ -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 9c12bd4b96..37e72f379b 100644
--- a/lib/chef/chef_fs/file_pattern.rb
+++ b/lib/chef/chef_fs/file_pattern.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs"
-require "chef/chef_fs/path_utils"
+require_relative "../chef_fs"
+require_relative "path_utils"
class Chef
module ChefFS
@@ -74,6 +74,7 @@ class Chef
argument_is_absolute = Chef::ChefFS::PathUtils.is_absolute?(path)
return false if is_absolute != argument_is_absolute
+
path = path[1, path.length - 1] if argument_is_absolute
path_parts = Chef::ChefFS::PathUtils.split(path)
@@ -81,10 +82,11 @@ class Chef
return false if regexp_parts.length <= path_parts.length && !has_double_star
# If the path doesn't match up to this point, children won't match either.
return false if path_parts.zip(regexp_parts).any? { |part, regexp| !regexp.nil? && !regexp.match(part) }
+
# 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 +116,8 @@ 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.
@@ -123,7 +126,8 @@ class Chef
# abc/*def.exact_path == 'abc/def'
# abc/x\\yz.exact_path == 'abc/xyz'
def exact_path
- return nil if has_double_star || exact_parts.any? { |part| part.nil? }
+ return nil if has_double_star || exact_parts.any?(&:nil?)
+
result = Chef::ChefFS::PathUtils.join(*exact_parts)
is_absolute ? Chef::ChefFS::PathUtils.join("", result) : result
end
@@ -151,6 +155,7 @@ class Chef
def match?(path)
argument_is_absolute = Chef::ChefFS::PathUtils.is_absolute?(path)
return false if is_absolute != argument_is_absolute
+
path = path[1, path.length - 1] if argument_is_absolute
!!regexp.match(path)
end
@@ -183,7 +188,7 @@ class Chef
end
def calculate
- if !@regexp
+ unless @regexp
@is_absolute = Chef::ChefFS::PathUtils.is_absolute?(@pattern)
full_regexp_parts = []
@@ -199,7 +204,7 @@ class Chef
end
# Skip // and /./ (pretend it's not there)
- if exact == "" || exact == "."
+ if ["", "."].include?(exact)
next
end
@@ -213,9 +218,10 @@ class Chef
if has_double_star_prev
raise ArgumentError, ".. overlapping a ** is unsupported"
end
+
full_regexp_parts.pop
normalized_parts.pop
- if !@has_double_star
+ unless @has_double_star
@regexp_parts.pop
@exact_parts.pop
end
@@ -226,7 +232,7 @@ class Chef
# Build up the regexp
full_regexp_parts << regexp
normalized_parts << part
- if !@has_double_star
+ unless @has_double_star
@regexp_parts << Regexp.new("^#{regexp}$")
@exact_parts << exact
end
@@ -239,7 +245,7 @@ class Chef
end
def self.pattern_special_characters
- if Chef::ChefFS.windows?
+ if ChefUtils.windows?
@pattern_special_characters ||= /(\*\*|\*|\?|[\*\?\.\|\(\)\[\]\{\}\+\\\\\^\$])/
else
# Unix also supports character regexes and backslashes
@@ -259,7 +265,7 @@ class Chef
pattern.split(pattern_special_characters).each_with_index do |part, index|
# Odd indexes from the split are symbols. Even are normal bits.
if index.even?
- exact << part if !exact.nil?
+ exact << part unless exact.nil?
regexp << part
else
case part
@@ -277,7 +283,7 @@ class Chef
else
if part[0, 1] == '\\' && part.length == 2
# backslash escapes are only supported on Unix, and are handled here by leaving the escape on (it means the same thing in a regex)
- exact << part[1, 1] if !exact.nil?
+ exact << part[1, 1] unless exact.nil?
if regexp_escape_characters.include?(part[1, 1])
regexp << part
else
@@ -288,7 +294,7 @@ class Chef
exact = nil
regexp << part
else
- exact += part if !exact.nil?
+ exact += part unless exact.nil?
regexp << "\\#{part}"
end
end
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index 1a8da2fd6b..73c3f0090a 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/path_utils"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/parallelizer"
+require_relative "path_utils"
+require_relative "file_system/exceptions"
+require_relative "parallelizer"
class Chef
module ChefFS
@@ -94,6 +94,7 @@ class Chef
def self.resolve_path(entry, path)
return entry if path.length == 0
return resolve_path(entry.root, path) if path[0, 1] == "/" && entry.root != entry
+
if path[0, 1] == "/"
path = path[1, path.length - 1]
end
@@ -194,7 +195,7 @@ class Chef
# 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.display_path)
+ unless found_paths.include?(b.display_path)
a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.display_path)
yield [ a, b ]
end
@@ -228,7 +229,7 @@ class Chef
# Check b for children that aren't in a
b.children.each do |b_child|
- if !a_children_names.include?(b_child.bare_name)
+ unless a_children_names.include?(b_child.bare_name)
result << [ a.child(b_child.bare_name), b_child ]
end
end
@@ -291,7 +292,7 @@ class Chef
end
end
else
- ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui
+ ui.output("Not deleting extra entry #{dest_path} (purge is off)") if ui
end
end
@@ -419,7 +420,7 @@ class Chef
ui.output "Created #{parent_path}" if ui
end
end
- return parent
+ parent
end
def parallel_do(enum, options = {}, &block)
diff --git a/lib/chef/chef_fs/file_system/base_fs_dir.rb b/lib/chef/chef_fs/file_system/base_fs_dir.rb
index 2827e5b384..3805fa2bb0 100644
--- a/lib/chef/chef_fs/file_system/base_fs_dir.rb
+++ b/lib/chef/chef_fs/file_system/base_fs_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_object"
-require "chef/chef_fs/file_system/nonexistent_fs_object"
+require_relative "base_fs_object"
+require_relative "nonexistent_fs_object"
class Chef
module ChefFS
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 9767b5b1ba..a0bcf3ff65 100644
--- a/lib/chef/chef_fs/file_system/base_fs_object.rb
+++ b/lib/chef/chef_fs/file_system/base_fs_object.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/path_utils"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "../path_utils"
+require_relative "exceptions"
class Chef
module ChefFS
@@ -32,6 +32,7 @@ class Chef
if name != ""
raise ArgumentError, "Name of root object must be empty string: was '#{name}' instead"
end
+
@path = "/"
end
end
@@ -107,13 +108,15 @@ class Chef
# Override children to report your *actual* list of children as an array.
def children
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
[]
end
# Expand this entry into a chef object (Chef::Role, ::Node, etc.)
def chef_object
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
nil
end
@@ -124,14 +127,16 @@ class Chef
# your entry class, and will be called without actually reading the
# file_contents. This is used for knife upload /cookbooks/cookbookname.
def create_child(name, file_contents)
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise OperationNotAllowedError.new(:create_child, self)
end
# Delete this item, possibly recursively. Entries MUST NOT delete a
# directory unless recurse is true.
def delete(recurse)
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise OperationNotAllowedError.new(:delete, self)
end
@@ -166,13 +171,15 @@ class Chef
# Read the contents of this file entry.
def read
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise OperationNotAllowedError.new(:read, self)
end
# Write the contents of this file entry.
def write(file_contents)
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise OperationNotAllowedError.new(:write, self)
end
@@ -184,4 +191,4 @@ class Chef
end
end
-require "chef/chef_fs/file_system/nonexistent_fs_object"
+require_relative "nonexistent_fs_object"
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 006098a0c9..f9448c2fc1 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/acl_entry"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "../base_fs_dir"
+require_relative "acl_entry"
+require_relative "../exceptions"
class Chef
module ChefFS
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 f4655412fa..db29ce7ba7 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "rest_list_entry"
+require_relative "../exceptions"
class Chef
module ChefFS
module FileSystem
module ChefServer
class AclEntry < RestListEntry
- PERMISSIONS = %w{create read update delete grant}
+ PERMISSIONS = %w{create read update delete grant}.freeze
def api_path
"#{super}/_acl"
@@ -47,17 +47,17 @@ class Chef
# ACL writes are fun.
acls = data_handler.normalize(Chef::JSONCompat.parse(file_contents), self)
PERMISSIONS.each do |permission|
- begin
- rest.put("#{api_path}/#{permission}", { permission => acls[permission] })
- rescue Timeout::Error => e
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e, "Timeout writing: #{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(:write, self, e, "HTTP error writing: #{e}")
- end
+
+ rest.put("#{api_path}/#{permission}", { permission => acls[permission] })
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e, "Timeout writing: #{e}")
+ rescue Net::HTTPClientException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e, "HTTP error writing: #{e}")
end
+
end
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/acls_dir.rb b/lib/chef/chef_fs/file_system/chef_server/acls_dir.rb
index b9af486203..9bc348482b 100644
--- a/lib/chef/chef_fs/file_system/chef_server/acls_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/acls_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,19 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/acl_dir"
-require "chef/chef_fs/file_system/chef_server/cookbooks_acl_dir"
-require "chef/chef_fs/file_system/chef_server/policies_acl_dir"
-require "chef/chef_fs/file_system/chef_server/acl_entry"
-require "chef/chef_fs/data_handler/acl_data_handler"
+require_relative "../base_fs_dir"
+require_relative "acl_dir"
+require_relative "cookbooks_acl_dir"
+require_relative "policies_acl_dir"
+require_relative "acl_entry"
+require_relative "../../data_handler/acl_data_handler"
class Chef
module ChefFS
module FileSystem
module ChefServer
class AclsDir < BaseFSDir
- ENTITY_TYPES = %w{clients containers cookbook_artifacts cookbooks data_bags environments groups nodes policies policy_groups roles} # we don't read sandboxes, so we don't read their acls
+ ENTITY_TYPES = %w{clients containers cookbook_artifacts cookbooks data_bags environments groups nodes policies policy_groups roles}.freeze # we don't read sandboxes, so we don't read their acls
def data_handler
@data_handler ||= Chef::ChefFS::DataHandler::AclDataHandler.new
@@ -60,7 +60,7 @@ class Chef
AclDir.new(entity_type, self)
end
end
- @children << AclEntry.new("organization.json", self, true) # the org acl is retrieved as GET /organizations/ORGNAME/ANYTHINGATALL/_acl
+ @children << AclEntry.new("organization.json", self, true) # the org acl is retrieved as GET /organizations/ORGNAME/ANYTHING/_acl
end
@children
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..3477278c96 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,30 +16,30 @@
# limitations under the License.
#
-require "chef/server_api"
-require "chef/chef_fs/file_system/chef_server/acls_dir"
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/chef_server/cookbooks_dir"
-require "chef/chef_fs/file_system/chef_server/cookbook_artifacts_dir"
-require "chef/chef_fs/file_system/chef_server/versioned_cookbooks_dir"
-require "chef/chef_fs/file_system/chef_server/data_bags_dir"
-require "chef/chef_fs/file_system/chef_server/nodes_dir"
-require "chef/chef_fs/file_system/chef_server/org_entry"
-require "chef/chef_fs/file_system/chef_server/organization_invites_entry"
-require "chef/chef_fs/file_system/chef_server/organization_members_entry"
-require "chef/chef_fs/file_system/chef_server/policies_dir"
-require "chef/chef_fs/file_system/chef_server/policy_groups_dir"
-require "chef/chef_fs/file_system/chef_server/environments_dir"
-require "chef/chef_fs/data_handler/acl_data_handler"
-require "chef/chef_fs/data_handler/client_data_handler"
-require "chef/chef_fs/data_handler/environment_data_handler"
-require "chef/chef_fs/data_handler/node_data_handler"
-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/chef_fs/data_handler/policy_group_data_handler"
+require_relative "../../../server_api"
+require_relative "acls_dir"
+require_relative "../base_fs_dir"
+require_relative "rest_list_dir"
+require_relative "cookbooks_dir"
+require_relative "cookbook_artifacts_dir"
+require_relative "versioned_cookbooks_dir"
+require_relative "data_bags_dir"
+require_relative "nodes_dir"
+require_relative "org_entry"
+require_relative "organization_invites_entry"
+require_relative "organization_members_entry"
+require_relative "policies_dir"
+require_relative "policy_groups_dir"
+require_relative "environments_dir"
+require_relative "../../data_handler/acl_data_handler"
+require_relative "../../data_handler/client_data_handler"
+require_relative "../../data_handler/environment_data_handler"
+require_relative "../../data_handler/node_data_handler"
+require_relative "../../data_handler/role_data_handler"
+require_relative "../../data_handler/user_data_handler"
+require_relative "../../data_handler/group_data_handler"
+require_relative "../../data_handler/container_data_handler"
+require_relative "../../data_handler/policy_group_data_handler"
class Chef
module ChefFS
@@ -99,15 +99,15 @@ class Chef
end
def rest
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true, :api_version => "0")
+ Chef::ServerAPI.new(chef_server_url, client_name: chef_username, signing_key_filename: chef_private_key, raw_output: true, api_version: "0")
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
@@ -186,7 +186,7 @@ class Chef
RestListDir.new("users", self, nil, Chef::ChefFS::DataHandler::UserDataHandler.new),
]
end
- result.sort_by { |child| child.name }
+ result.sort_by(&:name)
end
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_artifact_dir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_artifact_dir.rb
index faea96e944..936eedccfa 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbook_artifact_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_artifact_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/chef_server/cookbook_dir"
+require_relative "cookbook_dir"
class Chef
module ChefFS
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..feda934b54 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/cookbooks_dir"
-require "chef/chef_fs/file_system/chef_server/cookbook_artifact_dir"
+require_relative "cookbooks_dir"
+require_relative "cookbook_artifact_dir"
class Chef
module ChefFS
@@ -44,7 +44,7 @@ class Chef
result = []
root.get_json("#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks|
cookbooks["versions"].each do |cookbook_version|
- result << CookbookArtifactDir.new("#{cookbook_name}-#{cookbook_version['identifier']}", self)
+ result << CookbookArtifactDir.new("#{cookbook_name}-#{cookbook_version["identifier"]}", self)
end
end
result.sort_by(&:name)
@@ -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}"
@@ -65,15 +65,15 @@ class Chef
file_class.symlink other.file_path, proxy_cookbook_path
# Instantiate a proxy loader using the temporary symlink
- proxy_loader = Chef::Cookbook::CookbookVersionLoader.new(proxy_cookbook_path, other.parent.chefignore)
- proxy_loader.load_cookbooks
+ proxy_loader = Chef::Cookbook::CookbookVersionLoader.new(proxy_cookbook_path, other.chefignore)
+ proxy_loader.load!
cookbook_to_upload = proxy_loader.cookbook_version
cookbook_to_upload.identifier = identifier
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
@@ -86,12 +86,16 @@ class Chef
# the symlink without removing the original contents if we
# are running on windows
#
- if Chef::Platform.windows?
+ if ChefUtils.windows?
Dir.rmdir proxy_cookbook_path
end
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..f724b8ab21 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/chef_fs/command_line"
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/chef_server/cookbook_subdir"
-require "chef/chef_fs/file_system/chef_server/cookbook_file"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/cookbook_version"
-require "chef/cookbook_uploader"
+require_relative "../../command_line"
+require_relative "rest_list_dir"
+require_relative "cookbook_subdir"
+require_relative "cookbook_file"
+require_relative "../exceptions"
+require_relative "../../../cookbook_version"
+require_relative "../../../cookbook_uploader"
class Chef
module ChefFS
@@ -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,45 +61,40 @@ 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])
+ unless container
+ 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 }
+ @children = @children.sort_by(&:name)
end
@children
end
@@ -126,7 +109,7 @@ class Chef
rest.delete(api_path)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e, "Timeout deleting: #{e}")
- rescue Net::HTTPServerException
+ rescue Net::HTTPClientException
if $!.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
else
@@ -134,7 +117,8 @@ class Chef
end
end
else
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise MustDeleteRecursivelyError.new(self, "#{path_for_printing} must be deleted recursively")
end
end
@@ -149,12 +133,13 @@ class Chef
end
def compare_to(other)
- if !other.dir?
+ unless other.dir?
return [ !exists?, nil, nil ]
end
+
are_same = true
Chef::ChefFS::CommandLine.diff_entries(self, other, nil, :name_only).each do |type, old_entry, new_entry|
- if [ :directory_to_file, :file_to_directory, :deleted, :added, :modified ].include?(type)
+ if %i{directory_to_file file_to_directory deleted added modified}.include?(type)
are_same = false
end
end
@@ -166,7 +151,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 +177,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
@@ -196,7 +185,7 @@ class Chef
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading: #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "404"
@could_not_get_chef_object = e
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, @could_not_get_chef_object)
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..c49ed51d81 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_object"
-require "chef/http/simple"
-require "openssl"
+require_relative "../base_fs_object"
+require_relative "../../../http/simple"
+require "digest" unless defined?(Digest)
class Chef
module ChefFS
@@ -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", &:read)
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e, "Timeout reading #{file[:url]}: #{e}")
+ rescue Net::HTTPClientException => 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
@@ -75,7 +69,7 @@ class Chef
private
def calc_checksum(value)
- OpenSSL::Digest::MD5.hexdigest(value)
+ ::Digest::MD5.hexdigest(value)
end
end
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbook_subdir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbook_subdir.rb
index 01297a39ba..91b5e47d66 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbook_subdir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbook_subdir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/base_fs_dir"
+require_relative "../base_fs_dir"
class Chef
module ChefFS
@@ -39,7 +39,7 @@ class Chef
def can_have_child?(name, is_dir)
if is_dir
- return false if !@recursive
+ return false unless @recursive
else
return false if @ruby_only && name !~ /\.rb$/
end
diff --git a/lib/chef/chef_fs/file_system/chef_server/cookbooks_acl_dir.rb b/lib/chef/chef_fs/file_system/chef_server/cookbooks_acl_dir.rb
index e020d0fb34..c2430c6ba5 100644
--- a/lib/chef/chef_fs/file_system/chef_server/cookbooks_acl_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/cookbooks_acl_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/chef_server/acl_dir"
+require_relative "acl_dir"
class Chef
module ChefFS
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..e9f973ef6f 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/chef_server/cookbook_dir"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir"
-require "chef/mixin/file_class"
+require_relative "rest_list_dir"
+require_relative "cookbook_dir"
+require_relative "../exceptions"
+require_relative "../repository/chef_repository_file_system_cookbook_dir"
+require_relative "../../../mixin/file_class"
-require "tmpdir"
+require "tmpdir" unless defined?(Dir.mktmpdir)
class Chef
module ChefFS
@@ -60,7 +60,7 @@ class Chef
upload_cookbook(other, options)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e, "Timeout writing: #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
case e.response.code
when "409"
raise Chef::ChefFS::FileSystem::CookbookFrozenError.new(:write, self, e, "Cookbook #{other.name} is frozen")
@@ -72,19 +72,36 @@ class Chef
end
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)
+ cookbook = other.chef_object if other.chef_object
+ raise Chef::Exceptions::MetadataNotFound.new(cookbook.root_paths[0], cookbook.name) unless cookbook.has_metadata_file?
- with_actual_cookbooks_dir(other.parent.file_path) do
- uploader.upload_cookbooks
+ if cookbook
+ Chef::CookbookLoader.copy_to_tmp_dir_from_array([cookbook]) do |tmp_cl|
+ tmp_cl.load_cookbooks
+ tmp_cl.compile_metadata
+ tmp_cl.freeze_versions if options[:freeze]
+ cookbook_for_upload = []
+ tmp_cl.each do |cookbook_name, cookbook|
+ cookbook_for_upload << cookbook
+ end
+
+ uploader = Chef::CookbookUploader.new(cookbook_for_upload, force: options[:force], rest: chef_rest)
+
+ with_actual_cookbooks_dir(other.parent.file_path) do
+ uploader.upload_cookbooks
+ end
+ end
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
- Chef::Config.cookbook_path = actual_cookbook_path if !Chef::Config.cookbook_path
+ Chef::Config.cookbook_path = actual_cookbook_path unless Chef::Config.cookbook_path
yield
ensure
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 ee0ecd3b40..399d5af738 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-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"
+require_relative "rest_list_dir"
+require_relative "data_bag_entry"
+require_relative "../exceptions"
+require_relative "../../data_handler/data_bag_item_data_handler"
class Chef
module ChefFS
@@ -48,15 +48,16 @@ class Chef
end
def delete(recurse)
- if !recurse
- raise NotFoundError.new(self) if !exists?
+ unless recurse
+ raise NotFoundError.new(self) unless exists?
+
raise MustDeleteRecursivelyError.new(self, "#{path_for_printing} must be deleted recursively")
end
begin
rest.delete(api_path)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e, "Timeout deleting: #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
else
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
index c0093058b7..19e0fb409e 100644
--- 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
@@ -1,5 +1,5 @@
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/data_handler/data_bag_item_data_handler"
+require_relative "rest_list_entry"
+require_relative "../../data_handler/data_bag_item_data_handler"
class Chef
module ChefFS
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..1e824c155c 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/chef_server/data_bag_dir"
+require_relative "rest_list_dir"
+require_relative "data_bag_dir"
class Chef
module ChefFS
@@ -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::HTTPClientException => 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
@@ -52,7 +50,7 @@ class Chef
rest.post(api_path, { "name" => name })
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating child '#{name}': #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "409"
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self, e, "Cannot create #{name} under #{path}: already exists")
else
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 e4714cf089..8366d9d326 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "../base_fs_dir"
+require_relative "rest_list_entry"
+require_relative "../exceptions"
class Chef
module ChefFS
@@ -40,12 +40,14 @@ class Chef
end
def delete(recurse)
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise DefaultEnvironmentCannotBeModifiedError.new(:delete, self)
end
def write(file_contents)
- raise NotFoundError.new(self) if !exists?
+ raise NotFoundError.new(self) unless exists?
+
raise DefaultEnvironmentCannotBeModifiedError.new(:write, self)
end
end
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 df2388f1df..81d6f91f33 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/data_handler/node_data_handler"
+require_relative "../base_fs_dir"
+require_relative "rest_list_entry"
+require_relative "../exceptions"
+require_relative "../../data_handler/node_data_handler"
class Chef
module ChefFS
@@ -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, 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::HTTPClientException => 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 7253de3449..f2dfc944cd 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
@@ -1,5 +1,5 @@
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/data_handler/organization_data_handler"
+require_relative "rest_list_entry"
+require_relative "../../data_handler/organization_data_handler"
class Chef
module ChefFS
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 adaffb99a7..d852a3bc2e 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
@@ -1,6 +1,6 @@
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/data_handler/organization_invites_data_handler"
-require "chef/json_compat"
+require_relative "rest_list_entry"
+require_relative "../../data_handler/organization_invites_data_handler"
+require_relative "../../../json_compat"
class Chef
module ChefFS
@@ -40,19 +40,19 @@ class Chef
end
def write(contents)
- desired_invites = minimize_value(Chef::JSONCompat.parse(contents, :create_additions => false))
+ desired_invites = minimize_value(Chef::JSONCompat.parse(contents, create_additions: false))
actual_invites = _read_json.inject({}) { |h, val| h[val["username"]] = val["id"]; h }
invites = actual_invites.keys
(desired_invites - invites).each do |invite|
- begin
- rest.post(api_path, { "user" => invite })
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- Chef::Log.warn("Could not invite #{invite} to organization #{org}: #{api_error_text(e.response)}")
- else
- raise
- end
+
+ rest.post(api_path, { "user" => invite })
+ rescue Net::HTTPClientException => e
+ if e.response.code == "409"
+ Chef::Log.warn("Could not invite #{invite} to organization #{org}: #{api_error_text(e.response)}")
+ else
+ raise
end
+
end
(invites - desired_invites).each do |invite|
rest.delete(File.join(api_path, actual_invites[invite]))
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 7e9c7141c4..ded890cc32 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
@@ -1,6 +1,6 @@
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/data_handler/organization_members_data_handler"
-require "chef/json_compat"
+require_relative "rest_list_entry"
+require_relative "../../data_handler/organization_members_data_handler"
+require_relative "../../../json_compat"
class Chef
module ChefFS
@@ -40,18 +40,18 @@ class Chef
end
def write(contents)
- desired_members = minimize_value(Chef::JSONCompat.parse(contents, :create_additions => false))
+ desired_members = minimize_value(Chef::JSONCompat.parse(contents, create_additions: false))
members = minimize_value(_read_json)
(desired_members - members).each do |member|
- begin
- rest.post(api_path, "username" => member)
- rescue Net::HTTPServerException => e
- if %w{404 405}.include?(e.response.code)
- raise "Chef server at #{api_path} does not allow you to directly add members. Please either upgrade your Chef server or move the users you want into invitations.json instead of members.json."
- else
- raise
- end
+
+ rest.post(api_path, "username" => member)
+ rescue Net::HTTPClientException => e
+ if %w{404 405}.include?(e.response.code)
+ raise "Chef server at #{api_path} does not allow you to directly add members. Please either upgrade your Chef server or move the users you want into invitations.json instead of members.json."
+ else
+ raise
end
+
end
(members - desired_members).each do |member|
rest.delete(File.join(api_path, member))
diff --git a/lib/chef/chef_fs/file_system/chef_server/policies_acl_dir.rb b/lib/chef/chef_fs/file_system/chef_server/policies_acl_dir.rb
index fa1d184b7d..ba036c37d6 100644
--- a/lib/chef/chef_fs/file_system/chef_server/policies_acl_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/policies_acl_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/chef_server/acl_dir"
+require_relative "acl_dir"
class Chef
module ChefFS
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..aa6981f23c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/chef_server/policy_revision_entry"
+require_relative "rest_list_dir"
+require_relative "policy_revision_entry"
class Chef
module ChefFS
@@ -66,42 +66,41 @@ 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
+ # 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"].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::HTTPClientException => 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 /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::HTTPClientException => 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
@@ -133,7 +132,7 @@ class Chef
rest.post("#{api_path}/#{policy_name}/revisions", object)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating '#{name}': #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# 404 = NotFoundError
if e.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
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..2c2707b239 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/exceptions"
+require_relative "../exceptions"
class Chef
module ChefFS
@@ -77,11 +77,12 @@ class Chef
object["policies"].each do |policy_name, policy_data|
policy_path = "/policies/#{policy_name}/revisions/#{policy_data["revision_id"]}"
- get_data = begin
- rest.get(policy_path)
- rescue Net::HTTPServerException => e
- raise "Could not find policy '#{policy_name}'' with revision '#{policy_data["revision_id"]}'' on the server"
- end
+ get_data =
+ begin
+ rest.get(policy_path)
+ rescue Net::HTTPClientException => e
+ raise "Could not find policy '#{policy_name}'' with revision '#{policy_data["revision_id"]}'' on the server"
+ end
# GET policy data
server_policy_data = Chef::JSONCompat.parse(get_data)
@@ -93,7 +94,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
@@ -113,7 +114,7 @@ class Chef
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating '#{name}': #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# 404 = NotFoundError
if e.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
diff --git a/lib/chef/chef_fs/file_system/chef_server/policy_groups_dir.rb b/lib/chef/chef_fs/file_system/chef_server/policy_groups_dir.rb
index 0452fa4573..37e5080e6a 100644
--- a/lib/chef/chef_fs/file_system/chef_server/policy_groups_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server/policy_groups_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/file_system/chef_server/policy_group_entry"
+require_relative "../base_fs_dir"
+require_relative "rest_list_entry"
+require_relative "../exceptions"
+require_relative "policy_group_entry"
class Chef
module ChefFS
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 325b18e429..9b99fea730 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
@@ -1,5 +1,5 @@
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/data_handler/policy_data_handler"
+require_relative "rest_list_entry"
+require_relative "../../data_handler/policy_data_handler"
class Chef
module ChefFS
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 dfd26a0241..0a1ee83c36 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_entry"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "../base_fs_dir"
+require_relative "rest_list_entry"
+require_relative "../exceptions"
class Chef
module ChefFS
@@ -71,40 +71,39 @@ 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, 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
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
+ # 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, true)
+ end
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}")
+ rescue Net::HTTPClientException => 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::HTTPClientException => e
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
- else
- raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
- end
- # Anything else is unexpected (OperationFailedError)
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "HTTP error retrieving children: #{e}")
+ end
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
@@ -135,7 +134,7 @@ class Chef
rest.post(api_path, object)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating '#{name}': #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# 404 = NotFoundError
if e.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
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 b8ec5f8524..bd0d400723 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_object"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/role"
-require "chef/node"
-require "chef/json_compat"
+require_relative "../base_fs_object"
+require_relative "../exceptions"
+require_relative "../../../role"
+require_relative "../../../node"
+require_relative "../../../json_compat"
class Chef
module ChefFS
@@ -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
@@ -69,7 +70,14 @@ class Chef
def exists?
if @exists.nil?
begin
- @exists = parent.children.any? { |child| child.api_child_name == api_child_name }
+ @this_object_cache = rest.get(api_path)
+ @exists = true
+ rescue Net::HTTPClientException => e
+ if e.response.code == "404"
+ @exists = false
+ else
+ raise
+ end
rescue Chef::ChefFS::FileSystem::NotFoundError
@exists = false
end
@@ -78,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::HTTPClientException => 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
- Chef::JSONCompat.to_json_pretty(minimize_value(_read_json))
+ # Minimize the value (get rid of defaults) so the results don't look terrible
+ Chef::JSONCompat.to_json_pretty(normalize_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::HTTPClientException => 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
@@ -116,7 +122,11 @@ class Chef
end
def minimize_value(value)
- data_handler.minimize(data_handler.normalize(value, self), self)
+ data_handler.minimize(normalize_value(value), self)
+ end
+
+ def normalize_value(value)
+ data_handler.normalize(value, self)
end
def compare_to(other)
@@ -148,6 +158,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
@@ -156,6 +169,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
@@ -165,7 +181,7 @@ class Chef
if data_handler
object = data_handler.normalize_for_put(object, self)
data_handler.verify_integrity(object, self) do |error|
- raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, nil, "#{error}")
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, nil, error.to_s)
end
end
@@ -173,7 +189,7 @@ class Chef
rest.put(api_path, object)
rescue Timeout::Error => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e, "Timeout writing: #{e}")
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "404"
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
else
@@ -183,11 +199,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..84a331f2a0 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/chef_server/cookbook_dir"
+require_relative "cookbook_dir"
class Chef
module ChefFS
@@ -24,8 +24,8 @@ class Chef
module ChefServer
class VersionedCookbookDir < CookbookDir
# See Erchef code
- # https://github.com/opscode/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94
- VALID_VERSIONED_COOKBOOK_NAME = /^([.a-zA-Z0-9_-]+)-(\d+\.\d+\.\d+)$/
+ # https://github.com/chef/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94
+ VALID_VERSIONED_COOKBOOK_NAME = /^([.a-zA-Z0-9_-]+)-(\d+\.\d+\.\d+)$/.freeze
def initialize(name, parent, options = {})
super(name, parent)
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..4f8aca0d23 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/chef_server/cookbooks_dir"
-require "chef/chef_fs/file_system/chef_server/versioned_cookbook_dir"
+require_relative "cookbooks_dir"
+require_relative "versioned_cookbook_dir"
class Chef
module ChefFS
@@ -50,7 +50,7 @@ class Chef
result = []
root.get_json("#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks|
cookbooks["versions"].each do |cookbook_version|
- result << VersionedCookbookDir.new("#{cookbook_name}-#{cookbook_version['version']}", self)
+ result << VersionedCookbookDir.new("#{cookbook_name}-#{cookbook_version["version"]}", self)
end
end
result.sort_by(&:name)
@@ -71,14 +71,14 @@ class Chef
file_class.symlink other.file_path, proxy_cookbook_path
# Instantiate a proxy loader using the temporary symlink
- proxy_loader = Chef::Cookbook::CookbookVersionLoader.new(proxy_cookbook_path, other.parent.chefignore)
- proxy_loader.load_cookbooks
+ proxy_loader = Chef::Cookbook::CookbookVersionLoader.new(proxy_cookbook_path, other.chefignore)
+ proxy_loader.load!
cookbook_to_upload = proxy_loader.cookbook_version
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
@@ -91,12 +91,16 @@ class Chef
# the symlink without removing the original contents if we
# are running on windows
#
- if Chef::Platform.windows?
+ if ChefUtils.windows?
Dir.rmdir proxy_cookbook_path
end
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 2a1baba8f5..8f6ce64f02 100644
--- a/lib/chef/chef_fs/file_system/exceptions.rb
+++ b/lib/chef/chef_fs/file_system/exceptions.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/chef_fs/file_system/memory/memory_dir.rb b/lib/chef/chef_fs/file_system/memory/memory_dir.rb
index 6049f404b1..e52a4b7c0f 100644
--- a/lib/chef/chef_fs/file_system/memory/memory_dir.rb
+++ b/lib/chef/chef_fs/file_system/memory/memory_dir.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/memory/memory_file"
+require_relative "../base_fs_dir"
+require_relative "memory_file"
class Chef
module ChefFS
@@ -38,7 +38,7 @@ class Chef
dir = self
path_parts.each do |path_part|
subdir = dir.child(path_part)
- if !subdir.exists?
+ unless subdir.exists?
subdir = MemoryDir.new(path_part, dir)
dir.add_child(subdir)
end
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..9201893dda 100644
--- a/lib/chef/chef_fs/file_system/memory/memory_file.rb
+++ b/lib/chef/chef_fs/file_system/memory/memory_file.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/file_system/base_fs_object"
+require_relative "../base_fs_object"
class Chef
module ChefFS
@@ -11,7 +11,7 @@ class Chef
end
def read
- return @value
+ @value
end
end
end
diff --git a/lib/chef/chef_fs/file_system/memory/memory_root.rb b/lib/chef/chef_fs/file_system/memory/memory_root.rb
index 4881b3d951..4a8b281389 100644
--- a/lib/chef/chef_fs/file_system/memory/memory_root.rb
+++ b/lib/chef/chef_fs/file_system/memory/memory_root.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/file_system/memory/memory_dir"
+require_relative "memory_dir"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
index 21abc012f8..82fa146264 100644
--- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb
+++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
@@ -1,5 +1,5 @@
-require "chef/chef_fs/file_system/base_fs_object"
-require "chef/chef_fs/file_system/nonexistent_fs_object"
+require_relative "base_fs_object"
+require_relative "nonexistent_fs_object"
class Chef
module ChefFS
@@ -17,22 +17,20 @@ class Chef
end
def children
- begin
- 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}") unless seen[child.name].path_for_printing == child.path_for_printing
- else
- result << child
- seen[child.name] = child
- end
+ 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}") 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.debug("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 1a48d23047..3a30f68665 100644
--- a/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb
+++ b/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_object"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "base_fs_object"
+require_relative "exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/acl.rb b/lib/chef/chef_fs/file_system/repository/acl.rb
index 023ae11379..cfeb5fd3c9 100644
--- a/lib/chef/chef_fs/file_system/repository/acl.rb
+++ b/lib/chef/chef_fs/file_system/repository/acl.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/acl_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/acl_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
@@ -32,7 +32,7 @@ class Chef
end
def bare_name
- if name == "organization" && parent.kind_of?(AclDir)
+ if name == "organization" && parent.is_a?(AclDir)
"organization.json"
else
name
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 110befdf22..4a5bc03f15 100644
--- a/lib/chef/chef_fs/file_system/repository/acls_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/acls_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/acl"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/repository/acls_sub_dir"
-require "chef/chef_fs/file_system/chef_server/acls_dir"
-require "chef/chef_fs/data_handler/acl_data_handler"
+require_relative "acl"
+require_relative "directory"
+require_relative "acls_sub_dir"
+require_relative "../chef_server/acls_dir"
+require_relative "../../data_handler/acl_data_handler"
class Chef
module ChefFS
@@ -28,7 +28,7 @@ class Chef
module Repository
class AclsDir < Repository::Directory
- BARE_FILES = %w{ organization.json root }
+ BARE_FILES = %w{ organization.json root }.freeze
def can_have_child?(name, is_dir)
is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : BARE_FILES.include?(name)
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 70c7d77480..c239b588d1 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
@@ -1,6 +1,6 @@
#
# Author:: Jordan Running (<jr@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/acl"
-require "chef/chef_fs/data_handler/acl_data_handler"
-require "chef/chef_fs/file_system/repository/directory"
+require_relative "acl"
+require_relative "../../data_handler/acl_data_handler"
+require_relative "directory"
class Chef
module ChefFS
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 3e1edc8d62..94b52b9201 100644
--- a/lib/chef/chef_fs/file_system/repository/base_file.rb
+++ b/lib/chef/chef_fs/file_system/repository/base_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system_cache"
+require_relative "../../file_system_cache"
class Chef
module ChefFS
@@ -43,12 +43,12 @@ class Chef
file_path = "#{parent.file_path}/#{name}"
- Chef::Log.debug "BaseFile: Detecting file extension for #{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.debug "BaseFile: got a file path of #{file_path} for #{name}"
+ 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 = file_path
@@ -92,6 +92,7 @@ class Chef
end
attr_writer :write_pretty_json
+
def write_pretty_json
@write_pretty_json.nil? ? root.write_pretty_json : @write_pretty_json
end
@@ -122,7 +123,7 @@ class Chef
if is_ruby_file?
data_handler.from_ruby(file_path).to_json
else
- File.open(file_path, "rb") { |f| f.read }
+ File.open(file_path, "rb", &:read)
end
rescue Errno::ENOENT
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
@@ -132,6 +133,7 @@ class Chef
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_artifact_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_artifact_dir.rb
index 83c13e5e20..2cbbd466c3 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_artifact_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_artifact_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir"
+require_relative "chef_repository_file_system_cookbook_dir"
class Chef
module ChefFS
@@ -25,11 +25,11 @@ class Chef
class ChefRepositoryFileSystemCookbookArtifactDir < ChefRepositoryFileSystemCookbookDir
# Override from parent
def cookbook_version
- loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore)
+ loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, chefignore)
cookbook_name, _dash, identifier = name.rpartition("-")
# KLUDGE: We shouldn't have to use instance_variable_set
loader.instance_variable_set(:@cookbook_name, cookbook_name)
- loader.load_cookbooks
+ loader.load!
cookbook_version = loader.cookbook_version
cookbook_version.identifier = identifier
cookbook_version
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..0bb1ba1cac 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,12 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry"
-require "chef/chef_fs/file_system/chef_server/cookbook_dir"
-require "chef/chef_fs/file_system/chef_server/versioned_cookbook_dir"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/cookbook/cookbook_version_loader"
+require_relative "chef_repository_file_system_cookbook_entry"
+require_relative "../chef_server/cookbook_dir"
+require_relative "../chef_server/versioned_cookbook_dir"
+require_relative "../exceptions"
+require_relative "../../../cookbook/cookbook_version_loader"
+require_relative "../../../cookbook/chefignore"
class Chef
module ChefFS
@@ -30,10 +31,16 @@ class Chef
# Represents ROOT/cookbooks/:cookbook
class ChefRepositoryFileSystemCookbookDir < ChefRepositoryFileSystemCookbookEntry
- # API Required by Respository::Directory
+ # API Required by Repository::Directory
+ def chefignore
+ @chefignore ||= Chef::Cookbook::Chefignore.new(file_path)
+ rescue Errno::EISDIR, Errno::EACCES
+ # Work around a bug in Chefignore when chefignore is a directory
+ end
def fs_entry_valid?
return false unless File.directory?(file_path) && name_valid?
+
if can_upload?
true
else
@@ -54,6 +61,7 @@ class Chef
if exists?
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
end
+
begin
Dir.mkdir(file_path)
rescue Errno::EEXIST
@@ -64,11 +72,11 @@ class Chef
def write(cookbook_path, cookbook_version_json, from_fs)
# Use the copy/diff algorithm to copy it down so we don't destroy
# chefignored data. This is terribly un-thread-safe.
- Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new("/#{cookbook_path}"), from_fs, self, nil, { :purge => true })
+ Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new("/#{cookbook_path}"), from_fs, self, nil, { purge: true })
# Write out .uploaded-cookbook-version.json
# cookbook_file_path = File.join(file_path, cookbook_name) <- this should be the same as self.file_path
- if !File.exists?(file_path)
+ unless File.exist?(file_path)
FileUtils.mkdir_p(file_path)
end
uploaded_cookbook_version_path = File.join(file_path, Chef::Cookbook::CookbookVersionLoader::UPLOADED_COOKBOOK_VERSION_FILE)
@@ -80,18 +88,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
+ unless 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,12 +105,13 @@ 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
+
super(name, is_dir)
end
@@ -112,7 +119,8 @@ 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)
@@ -124,19 +132,18 @@ class Chef
end
def can_upload?
- File.exists?(uploaded_cookbook_version_path) || children.size > 0
+ File.exist?(uploaded_cookbook_version_path) || children.size > 0
end
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
- loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore)
- loader.load_cookbooks
+ loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, chefignore)
+ loader.load!
loader.cookbook_version
end
end
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 4019c6985b..d28bb65db6 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/file_system_entry"
-require "chef/chef_fs/file_system/repository/cookbooks_dir"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "file_system_entry"
+require_relative "cookbooks_dir"
+require_relative "../exceptions"
class Chef
module ChefFS
@@ -52,31 +52,30 @@ 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)
if is_dir
return recursive && name != "." && name != ".."
elsif ruby_only
- return false if name[-3..-1] != ".rb"
+ return false if name[-3..] != ".rb"
end
# Check chefignore
- ignorer = parent
+ ignorer = self
+
loop do
- if ignorer.is_a?(CookbooksDir)
+ if ignorer.is_a?(ChefRepositoryFileSystemCookbookDir)
# Grab the path from entry to child
path_to_child = name
child = self
- while child.parent != ignorer
+ while child != ignorer
path_to_child = PathUtils.join(child.name, path_to_child)
child = child.parent
end
@@ -103,6 +102,7 @@ class Chef
if child.exists?
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child)
end
+
if file_contents
child.write(file_contents)
else
@@ -123,9 +123,10 @@ class Chef
FileSystemCache.instance.delete!(file_path)
begin
if dir?
- if !recurse
+ unless recurse
raise MustDeleteRecursivelyError.new(self, $!)
end
+
FileUtils.rm_r(file_path)
else
File.delete(file_path)
@@ -136,15 +137,13 @@ class Chef
end
def exists?
- File.exists?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
+ File.exist?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
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", &: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..f7c00e7754 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,34 +16,35 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/repository/acls_dir"
-require "chef/chef_fs/file_system/repository/clients_dir"
-require "chef/chef_fs/file_system/repository/cookbooks_dir"
-require "chef/chef_fs/file_system/repository/cookbook_artifacts_dir"
-require "chef/chef_fs/file_system/repository/containers_dir"
-require "chef/chef_fs/file_system/repository/data_bags_dir"
-require "chef/chef_fs/file_system/repository/environments_dir"
-require "chef/chef_fs/file_system/repository/groups_dir"
-require "chef/chef_fs/file_system/repository/nodes_dir"
-require "chef/chef_fs/file_system/repository/policy_groups_dir"
-require "chef/chef_fs/file_system/repository/roles_dir"
-require "chef/chef_fs/file_system/repository/users_dir"
-require "chef/chef_fs/file_system/repository/client_keys_dir"
-require "chef/chef_fs/file_system/repository/file_system_entry"
-require "chef/chef_fs/file_system/repository/policies_dir"
-require "chef/chef_fs/file_system/repository/versioned_cookbooks_dir"
-require "chef/chef_fs/file_system/multiplexed_dir"
-require "chef/chef_fs/data_handler/client_data_handler"
-require "chef/chef_fs/data_handler/client_key_data_handler"
-require "chef/chef_fs/data_handler/environment_data_handler"
-require "chef/chef_fs/data_handler/node_data_handler"
-require "chef/chef_fs/data_handler/policy_data_handler"
-require "chef/chef_fs/data_handler/policy_group_data_handler"
-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_relative "../base_fs_dir"
+require_relative "acls_dir"
+require_relative "clients_dir"
+require_relative "cookbooks_dir"
+require_relative "cookbook_artifacts_dir"
+require_relative "containers_dir"
+require_relative "data_bags_dir"
+require_relative "environments_dir"
+require_relative "groups_dir"
+require_relative "nodes_dir"
+require_relative "policy_groups_dir"
+require_relative "roles_dir"
+require_relative "users_dir"
+require_relative "client_keys_dir"
+require_relative "file_system_entry"
+require_relative "policies_dir"
+require_relative "versioned_cookbooks_dir"
+require_relative "../multiplexed_dir"
+require_relative "../../data_handler/client_data_handler"
+require_relative "../../data_handler/client_key_data_handler"
+require_relative "../../data_handler/environment_data_handler"
+require_relative "../../data_handler/node_data_handler"
+require_relative "../../data_handler/policy_data_handler"
+require_relative "../../data_handler/policy_group_data_handler"
+require_relative "../../data_handler/role_data_handler"
+require_relative "../../data_handler/user_data_handler"
+require_relative "../../data_handler/group_data_handler"
+require_relative "../../data_handler/container_data_handler"
+require_relative "../../../win32/security" if ChefUtils.windows?
class Chef
module ChefFS
@@ -83,19 +84,19 @@ class Chef
attr_reader :child_paths
attr_reader :versioned_cookbooks
- CHILDREN = %w{org.json invitations.json members.json}
+ CHILDREN = %w{org.json invitations.json members.json}.freeze
def children
@children ||= begin
result = child_paths.keys.sort.map { |name| make_child_entry(name) }
result += CHILDREN.map { |name| make_child_entry(name) }
- result.select { |c| c && c.exists? }.sort_by { |c| c.name }
+ result.select { |c| c && c.exists? }.sort_by(&:name)
end
end
def can_have_child?(name, is_dir)
if is_dir
- child_paths.has_key?(name)
+ child_paths.key?(name)
elsif root_dir
CHILDREN.include?(name)
else
@@ -108,10 +109,23 @@ class Chef
child = root_dir.create_child(name, file_contents)
else
child_paths[name].each do |path|
- begin
- Dir.mkdir(path)
- rescue Errno::EEXIST
+
+ ::FileUtils.mkdir_p(path)
+ ::FileUtils.chmod(0700, path)
+ if ChefUtils.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
child = make_child_entry(name)
end
@@ -126,15 +140,15 @@ class Chef
# Used to print out a human-readable file system description
def fs_description
repo_paths = root_paths || [ File.dirname(child_paths["cookbooks"][0]) ]
- result = "repository at #{repo_paths.join(', ')}\n"
+ result = "repository at #{repo_paths.join(", ")}"
if versioned_cookbooks
- result << " Multiple versions per cookbook\n"
+ result << " (Multiple versions per cookbook)"
else
- result << " One version per cookbook\n"
+ result << " (One version per cookbook)"
end
child_paths.each_pair do |name, paths|
if paths.any? { |path| !repo_paths.include?(File.dirname(path)) }
- result << " #{name} at #{paths.join(', ')}\n"
+ result << " #{name} at #{paths.join(", ")}\n"
end
end
result
@@ -147,7 +161,7 @@ class Chef
# members.json and org.json may be found.
#
def root_dir
- existing_paths = root_paths.select { |path| File.exists?(path) }
+ existing_paths = root_paths.select { |path| File.exist?(path) }
if existing_paths.size > 0
MultiplexedDir.new(existing_paths.map do |path|
dir = FileSystemEntry.new(name, parent, path)
@@ -165,14 +179,16 @@ class Chef
#
def make_child_entry(name)
if CHILDREN.include?(name)
- return nil if !root_dir
+ return nil unless root_dir
+
return root_dir.child(name)
end
- paths = (child_paths[name] || []).select { |path| File.exists?(path) }
+ paths = (child_paths[name] || []).select { |path| File.exist?(path) }
if paths.size == 0
return NonexistentFSObject.new(name, self)
end
+
case name
when "acls"
dirs = paths.map { |path| AclsDir.new(name, self, path) }
diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir.rb
index 5dc74d85da..353f4536bb 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir"
+require_relative "chef_repository_file_system_cookbook_dir"
class Chef
module ChefFS
@@ -25,14 +25,15 @@ class Chef
class ChefRepositoryFileSystemVersionedCookbookDir < ChefRepositoryFileSystemCookbookDir
# Override from parent
def cookbook_version
- loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore)
+ loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, chefignore)
# We need the canonical cookbook name if we are using versioned cookbooks, but we don't
# want to spend a lot of time adding code to the main Chef libraries
canonical_name = canonical_cookbook_name(File.basename(file_path))
raise "When versioned_cookbooks mode is on, cookbook #{file_path} must match format <cookbook_name>-x.y.z" unless canonical_name
+
# KLUDGE: We shouldn't have to use instance_variable_set
loader.instance_variable_set(:@cookbook_name, canonical_name)
- loader.load_cookbooks
+ loader.load!
loader.cookbook_version
end
end
diff --git a/lib/chef/chef_fs/file_system/repository/client.rb b/lib/chef/chef_fs/file_system/repository/client.rb
index 6a99b7f005..cc5bc9ec7a 100644
--- a/lib/chef/chef_fs/file_system/repository/client.rb
+++ b/lib/chef/chef_fs/file_system/repository/client.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/client_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/client_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/client_key.rb b/lib/chef/chef_fs/file_system/repository/client_key.rb
index 8ca4f85d2f..c6fa1399d3 100644
--- a/lib/chef/chef_fs/file_system/repository/client_key.rb
+++ b/lib/chef/chef_fs/file_system/repository/client_key.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/client_key_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/client_key_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/client_keys_dir.rb b/lib/chef/chef_fs/file_system/repository/client_keys_dir.rb
index 9e7e7b3d5c..28d2f9dc05 100644
--- a/lib/chef/chef_fs/file_system/repository/client_keys_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/client_keys_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: Jordan Running (<jr@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/client_keys_sub_dir"
-require "chef/chef_fs/data_handler/client_key_data_handler"
-require "chef/chef_fs/file_system/repository/directory"
+require_relative "client_keys_sub_dir"
+require_relative "../../data_handler/client_key_data_handler"
+require_relative "directory"
class Chef
module ChefFS
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 6aafcfe294..ff0a4602af 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
@@ -1,6 +1,6 @@
#
# Author:: Jordan Running (<jr@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/client_key"
-require "chef/chef_fs/data_handler/client_key_data_handler"
-require "chef/chef_fs/file_system/repository/directory"
+require_relative "client_key"
+require_relative "../../data_handler/client_key_data_handler"
+require_relative "directory"
class Chef
module ChefFS
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 01027f83ac..c466a205c3 100644
--- a/lib/chef/chef_fs/file_system/repository/clients_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/clients_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/client"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "client"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/container.rb b/lib/chef/chef_fs/file_system/repository/container.rb
index e14a2ded73..1ea64643e0 100644
--- a/lib/chef/chef_fs/file_system/repository/container.rb
+++ b/lib/chef/chef_fs/file_system/repository/container.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/container_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/container_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 2af496f418..ca01b1df00 100644
--- a/lib/chef/chef_fs/file_system/repository/containers_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/containers_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/container"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "container"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/cookbook_artifacts_dir.rb b/lib/chef/chef_fs/file_system/repository/cookbook_artifacts_dir.rb
index 773d9a5e5a..bac9de3729 100644
--- a/lib/chef/chef_fs/file_system/repository/cookbook_artifacts_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/cookbook_artifacts_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/cookbooks_dir"
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_artifact_dir"
+require_relative "cookbooks_dir"
+require_relative "chef_repository_file_system_cookbook_artifact_dir"
class Chef
module ChefFS
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..c0751e8fe2 100644
--- a/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/cookbooks_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_dir"
-require "chef/cookbook/chefignore"
+require_relative "directory"
+require_relative "chef_repository_file_system_cookbook_dir"
+require_relative "../../../cookbook/chefignore"
class Chef
module ChefFS
@@ -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/data_bag.rb b/lib/chef/chef_fs/file_system/repository/data_bag.rb
index fb3ad9da77..759c09af8c 100644
--- a/lib/chef/chef_fs/file_system/repository/data_bag.rb
+++ b/lib/chef/chef_fs/file_system/repository/data_bag.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/repository/data_bag_item"
+require_relative "directory"
+require_relative "data_bag_item"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/data_bag_item.rb b/lib/chef/chef_fs/file_system/repository/data_bag_item.rb
index 2e3fb39606..31a6777508 100644
--- a/lib/chef/chef_fs/file_system/repository/data_bag_item.rb
+++ b/lib/chef/chef_fs/file_system/repository/data_bag_item.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/data_bag_item_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/data_bag_item_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/data_bags_dir.rb b/lib/chef/chef_fs/file_system/repository/data_bags_dir.rb
index 666fede62b..0ce6cb753e 100644
--- a/lib/chef/chef_fs/file_system/repository/data_bags_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/data_bags_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/repository/data_bag"
+require_relative "directory"
+require_relative "data_bag"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb
index 328cf92b03..72a12d665f 100644
--- a/lib/chef/chef_fs/file_system/repository/directory.rb
+++ b/lib/chef/chef_fs/file_system/repository/directory.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/chef_fs/file_system_cache"
+require_relative "../../file_system_cache"
class Chef
module ChefFS
@@ -71,9 +71,10 @@ class Chef
def children
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?) }
+
+ 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)
@@ -84,6 +85,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)
@@ -102,7 +104,7 @@ class Chef
children.empty?
end
- # Public API callied by chef_fs/file_system
+ # Public API called by chef_fs/file_system
def child(name)
possible_child = make_child_entry(name)
if possible_child.name_valid?
@@ -122,6 +124,7 @@ class Chef
if exists?
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
end
+
begin
FileSystemCache.instance.delete!(file_path)
Dir.mkdir(file_path)
@@ -136,9 +139,10 @@ class Chef
def delete(recurse)
if exists?
- if !recurse
+ unless recurse
raise MustDeleteRecursivelyError.new(self, $!)
end
+
FileUtils.rm_r(file_path)
FileSystemCache.instance.delete!(file_path)
else
@@ -147,7 +151,7 @@ class Chef
end
def exists?
- File.exists?(file_path)
+ File.exist?(file_path)
end
protected
diff --git a/lib/chef/chef_fs/file_system/repository/environment.rb b/lib/chef/chef_fs/file_system/repository/environment.rb
index 9ef9741308..759807c869 100644
--- a/lib/chef/chef_fs/file_system/repository/environment.rb
+++ b/lib/chef/chef_fs/file_system/repository/environment.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/environment_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/environment_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 4d04348d6e..3c5c69fdfb 100644
--- a/lib/chef/chef_fs/file_system/repository/environments_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/environments_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/environment"
-require "chef/chef_fs/data_handler/environment_data_handler"
-require "chef/chef_fs/file_system/repository/directory"
+require_relative "environment"
+require_relative "../../data_handler/environment_data_handler"
+require_relative "directory"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/file_system_entry.rb b/lib/chef/chef_fs/file_system/repository/file_system_entry.rb
index 45ae002521..902bc63038 100644
--- a/lib/chef/chef_fs/file_system/repository/file_system_entry.rb
+++ b/lib/chef/chef_fs/file_system/repository/file_system_entry.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/base_fs_dir"
-require "chef/chef_fs/file_system/chef_server/rest_list_dir"
-require "chef/chef_fs/file_system/exceptions"
-require "chef/chef_fs/path_utils"
-require "fileutils"
+require_relative "../base_fs_dir"
+require_relative "../chef_server/rest_list_dir"
+require_relative "../exceptions"
+require_relative "../../path_utils"
+require "fileutils" unless defined?(FileUtils)
class Chef
module ChefFS
@@ -80,9 +80,9 @@ class Chef
def children
# Except cookbooks and data bag dirs, all things must be json files
- Dir.entries(file_path).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?) }
+ Dir.entries(file_path).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?) }
rescue Errno::ENOENT
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
@@ -92,6 +92,7 @@ class Chef
if child.exists?
raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child)
end
+
if file_contents
child.write(file_contents)
else
@@ -108,9 +109,10 @@ class Chef
def delete(recurse)
if dir?
- if !recurse
+ unless recurse
raise MustDeleteRecursivelyError.new(self, $!)
end
+
FileUtils.rm_r(file_path)
else
File.delete(file_path)
@@ -120,11 +122,11 @@ class Chef
end
def exists?
- File.exists?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
+ File.exist?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
end
def read
- File.open(file_path, "rb") { |f| f.read }
+ File.open(file_path, "rb", &:read)
rescue Errno::ENOENT
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
end
diff --git a/lib/chef/chef_fs/file_system/repository/group.rb b/lib/chef/chef_fs/file_system/repository/group.rb
index 302e72739b..42c54c5daa 100644
--- a/lib/chef/chef_fs/file_system/repository/group.rb
+++ b/lib/chef/chef_fs/file_system/repository/group.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/group_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/group_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 20728d1248..b0e9defa7b 100644
--- a/lib/chef/chef_fs/file_system/repository/groups_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/groups_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/group"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "group"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/node.rb b/lib/chef/chef_fs/file_system/repository/node.rb
index d8f0dec7c4..d7bbe9beef 100644
--- a/lib/chef/chef_fs/file_system/repository/node.rb
+++ b/lib/chef/chef_fs/file_system/repository/node.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/node_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/node_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 33ca7ca709..3c45df4863 100644
--- a/lib/chef/chef_fs/file_system/repository/nodes_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/nodes_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,10 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/node"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "node"
+require_relative "directory"
+require_relative "../exceptions"
+require_relative "../../../win32/security" if ChefUtils.windows?
class Chef
module ChefFS
@@ -30,6 +31,27 @@ class Chef
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 ChefUtils.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/policies_dir.rb b/lib/chef/chef_fs/file_system/repository/policies_dir.rb
index c74ea5469b..612bcbf3d8 100644
--- a/lib/chef/chef_fs/file_system/repository/policies_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/policies_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/policy"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/data_handler/policy_data_handler"
+require_relative "policy"
+require_relative "directory"
+require_relative "../../data_handler/policy_data_handler"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/policy.rb b/lib/chef/chef_fs/file_system/repository/policy.rb
index 695bf17e83..67823445f8 100644
--- a/lib/chef/chef_fs/file_system/repository/policy.rb
+++ b/lib/chef/chef_fs/file_system/repository/policy.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/policy_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/policy_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/policy_group.rb b/lib/chef/chef_fs/file_system/repository/policy_group.rb
index e4182847b6..187ce21ef2 100644
--- a/lib/chef/chef_fs/file_system/repository/policy_group.rb
+++ b/lib/chef/chef_fs/file_system/repository/policy_group.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/policy_group_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/policy_group_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 4db85a93f7..c7388c8616 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
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/policy_group"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "policy_group"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/role.rb b/lib/chef/chef_fs/file_system/repository/role.rb
index d97ae0e7a1..512b917d20 100644
--- a/lib/chef/chef_fs/file_system/repository/role.rb
+++ b/lib/chef/chef_fs/file_system/repository/role.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/role_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/role_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 42f4376e71..86dbb2bafd 100644
--- a/lib/chef/chef_fs/file_system/repository/roles_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/roles_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/role"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "role"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/user.rb b/lib/chef/chef_fs/file_system/repository/user.rb
index 59355cc303..f3b8f1da38 100644
--- a/lib/chef/chef_fs/file_system/repository/user.rb
+++ b/lib/chef/chef_fs/file_system/repository/user.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/data_handler/user_data_handler"
-require "chef/chef_fs/file_system/repository/base_file"
+require_relative "../../data_handler/user_data_handler"
+require_relative "base_file"
class Chef
module ChefFS
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 9e8621575b..36ef0ba80d 100644
--- a/lib/chef/chef_fs/file_system/repository/users_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/users_dir.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/user"
-require "chef/chef_fs/file_system/repository/directory"
-require "chef/chef_fs/file_system/exceptions"
+require_relative "user"
+require_relative "directory"
+require_relative "../exceptions"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system/repository/versioned_cookbooks_dir.rb b/lib/chef/chef_fs/file_system/repository/versioned_cookbooks_dir.rb
index 80f77d02be..7989d14a2b 100644
--- a/lib/chef/chef_fs/file_system/repository/versioned_cookbooks_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/versioned_cookbooks_dir.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs/file_system/repository/cookbooks_dir"
-require "chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbook_dir"
+require_relative "cookbooks_dir"
+require_relative "chef_repository_file_system_versioned_cookbook_dir"
class Chef
module ChefFS
diff --git a/lib/chef/chef_fs/file_system_cache.rb b/lib/chef/chef_fs/file_system_cache.rb
index a9d8d8bfe4..7944178f99 100644
--- a/lib/chef/chef_fs/file_system_cache.rb
+++ b/lib/chef/chef_fs/file_system_cache.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +15,8 @@
# limitations under the License.
#
-require "singleton"
-require "chef/client"
+require "singleton" unless defined?(Singleton)
+require_relative "../client"
class Chef
module ChefFS
@@ -51,7 +51,7 @@ class Chef
def delete!(path)
parent = _get_parent(path)
- Chef::Log.debug("Deleting parent #{parent} and #{path} from FileSystemCache")
+ Chef::Log.trace("Deleting parent #{parent} and #{path} from FileSystemCache")
if @cache.key?(path)
@cache.delete(path)
end
@@ -73,6 +73,7 @@ class Chef
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
diff --git a/lib/chef/chef_fs/knife.rb b/lib/chef/chef_fs/knife.rb
index 1731e19ce1..ba993beee4 100644
--- a/lib/chef/chef_fs/knife.rb
+++ b/lib/chef/chef_fs/knife.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,9 @@
# limitations under the License.
#
-require "chef/knife"
-require "pathname"
+require_relative "../knife"
+require "pathname" unless defined?(Pathname)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
module ChefFS
@@ -25,11 +26,11 @@ class Chef
# Workaround for CHEF-3932
def self.deps
super do
- require "chef/config"
- require "chef/chef_fs/parallelizer"
- require "chef/chef_fs/config"
- require "chef/chef_fs/file_pattern"
- require "chef/chef_fs/path_utils"
+ require_relative "../config"
+ require_relative "parallelizer"
+ require_relative "config"
+ require_relative "file_pattern"
+ require_relative "path_utils"
yield
end
end
@@ -40,21 +41,19 @@ class Chef
# Ensure we always get to do our includes, whether subclass calls deps or not
c.deps do
end
-
- c.options.merge!(options)
end
option :repo_mode,
- :long => "--repo-mode MODE",
- :description => "Specifies the local repository layout. Values: static, everything, hosted_everything. Default: everything/hosted_everything"
+ long: "--repo-mode MODE",
+ description: "Specifies the local repository layout. Values: static, everything, hosted_everything. Default: everything/hosted_everything"
option :chef_repo_path,
- :long => "--chef-repo-path PATH",
- :description => "Overrides the location of chef repo. Default is specified by chef_repo_path in the config"
+ long: "--chef-repo-path PATH",
+ description: "Overrides the location of #{ChefUtils::Dist::Infra::PRODUCT} repo. Default is specified by chef_repo_path in the config"
option :concurrency,
- :long => "--concurrency THREADS",
- :description => "Maximum number of simultaneous requests to send (default: 10)"
+ long: "--concurrency THREADS",
+ description: "Maximum number of simultaneous requests to send (default: 10)"
def configure_chef
super
diff --git a/lib/chef/chef_fs/parallelizer.rb b/lib/chef/chef_fs/parallelizer.rb
index ccbf7ad96e..c4d17a842d 100644
--- a/lib/chef/chef_fs/parallelizer.rb
+++ b/lib/chef/chef_fs/parallelizer.rb
@@ -1,5 +1,4 @@
-require "thread"
-require "chef/chef_fs/parallelizer/parallel_enumerable"
+require_relative "parallelizer/parallel_enumerable"
class Chef
module ChefFS
@@ -45,7 +44,7 @@ class Chef
end
def parallel_do(enumerable, options = {}, &block)
- ParallelEnumerable.new(@tasks, enumerable, options.merge(:ordered => false), &block).wait
+ ParallelEnumerable.new(@tasks, enumerable, options.merge(ordered: false), &block).wait
end
def stop(wait = true, timeout = nil)
@@ -86,19 +85,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 ab578bdb7f..5fafc5c88f 100644
--- a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
+++ b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
@@ -1,4 +1,4 @@
-require "chef/chef_fs/parallelizer/flatten_enumerable"
+require_relative "flatten_enumerable"
class Chef
module ChefFS
@@ -145,8 +145,9 @@ class Chef
def each_with_exceptions_unordered
if @each_running
- raise "each() called on parallel enumerable twice simultaneously! Bad mojo"
+ raise "each() called on parallel enumerable twice simultaneously! Bad mojo"
end
+
@each_running = true
begin
# Grab all the inputs, yielding any responses during enumeration
@@ -197,7 +198,7 @@ class Chef
# If we exited early, perhaps due to any? finding a result, we want
# to make sure and throw away any extra results (gracefully) so that
# the next enumerator can start over.
- if !finished?
+ unless finished?
stop
end
raise
@@ -234,7 +235,7 @@ class Chef
# The order of these checks is important, as well, to be thread safe.
# 1. If @unconsumed_input.empty? is true, then we will never have any more
# work legitimately picked up.
- # 2. If @in_process == 0, then there is no work in process, and because ofwhen unconsumed_input is empty, it will never go back up, because
+ # 2. If @in_process == 0, then there is no work in process, and because of when unconsumed_input is empty, it will never go back up, because
# this is called after the input enumerator is finished. Note that switching #2 and #1
# could cause a race, because in_process is incremented *before* consuming input.
# 3. If @unconsumed_output.empty? is true, then we are done with outputs.
diff --git a/lib/chef/chef_fs/path_utils.rb b/lib/chef/chef_fs/path_utils.rb
index 7b2de5e3e0..1682120c86 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/chef_fs"
-require "pathname"
+require_relative "../chef_fs"
+require "pathname" unless defined?(Pathname)
class Chef
module ChefFS
@@ -42,6 +42,7 @@ class Chef
def self.join(*parts)
return "" if parts.length == 0
+
# Determine if it started with a slash
absolute = parts[0].length == 0 || parts[0].length > 0 && parts[0] =~ /^#{regexp_path_separator}/
# Remove leading and trailing slashes from each part so that the join will work (and the slash at the end will go away)
@@ -57,7 +58,7 @@ class Chef
end
def self.regexp_path_separator
- Chef::ChefFS.windows? ? '[\/\\\\]' : "/"
+ ChefUtils.windows? ? '[\/\\\\]' : "/"
end
# Given a server path, determines if it is absolute.
@@ -70,9 +71,9 @@ class Chef
# part that actually exists. The paths operated on here are not Chef-FS paths.
# These are OS paths that may contain symlinks but may not also fully exist.
#
- # If /x is a symlink to /blarghle, and has no subdirectories, then:
- # PathUtils.realest_path('/x/y/z') == '/blarghle/y/z'
- # PathUtils.realest_path('/x/*/z') == '/blarghle/*/z'
+ # If /x is a symlink to /foo_bar, and has no subdirectories, then:
+ # PathUtils.realest_path('/x/y/z') == '/foo_bar/y/z'
+ # PathUtils.realest_path('/x/*/z') == '/foo_bar/*/z'
# PathUtils.realest_path('/*/y/z') == '/*/y/z'
#
# TODO: Move this to wherever util/path_helper is these days.
@@ -87,10 +88,11 @@ class Chef
# This can occur if a path such as "C:" is given. Ruby gives the parent as "C:."
# for reasons only it knows.
raise ArgumentError "Invalid path segment #{path}" if parent_path.length > path.length
+
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)
@@ -99,9 +101,9 @@ class Chef
File.join(path, *suffix.reverse)
end
- # Compares two path fragments according to the case-sentitivity of the host platform.
+ # Compares two path fragments according to the case-sensitivity of the host platform.
def self.os_path_eq?(left, right)
- Chef::ChefFS.windows? ? left.casecmp(right) == 0 : left == right
+ ChefUtils.windows? ? left.casecmp(right) == 0 : left == right
end
# Given two general OS-dependent file paths, determines the relative path of the
@@ -113,9 +115,10 @@ class Chef
def self.descendant_path(path, ancestor)
candidate_fragment = path[0, ancestor.length]
return nil unless PathUtils.os_path_eq?(candidate_fragment, ancestor)
+
if ancestor.length == path.length
""
- elsif path[ancestor.length, 1] =~ /#{PathUtils.regexp_path_separator}/
+ elsif /#{PathUtils.regexp_path_separator}/.match?(path[ancestor.length, 1])
path[ancestor.length + 1..-1]
else
nil
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index c857da1b93..094b59fc35 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,52 +18,55 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/path_sanity"
-require "chef/log"
-require "chef/server_api"
-require "chef/api_client"
-require "chef/api_client/registration"
-require "chef/audit/runner"
-require "chef/node"
-require "chef/role"
-require "chef/file_cache"
-require "chef/run_context"
-require "chef/runner"
-require "chef/run_status"
-require "chef/cookbook/cookbook_collection"
-require "chef/cookbook/file_vendor"
-require "chef/cookbook/file_system_file_vendor"
-require "chef/cookbook/remote_file_vendor"
-require "chef/event_dispatch/dispatcher"
-require "chef/event_loggers/base"
-require "chef/event_loggers/windows_eventlog"
-require "chef/exceptions"
-require "chef/formatters/base"
-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"
-require "chef/request_id"
-require "chef/platform/rebooter"
-require "chef/mixin/deprecation"
-require "ohai"
-require "rbconfig"
+require_relative "config"
+require_relative "mixin/params_validate"
+require "chef-utils/dsl/default_paths" unless defined?(ChefUtils::DSL::DefaultPaths)
+require_relative "log"
+require_relative "deprecated"
+require_relative "server_api"
+require_relative "api_client"
+require_relative "api_client/registration"
+require_relative "node"
+require_relative "role"
+require_relative "file_cache"
+Chef.autoload :RunContext, File.expand_path("run_context", __dir__)
+require_relative "runner"
+require_relative "run_status"
+require_relative "cookbook/cookbook_collection"
+require_relative "cookbook/file_vendor"
+require_relative "cookbook/file_system_file_vendor"
+require_relative "cookbook/remote_file_vendor"
+require_relative "event_dispatch/dispatcher"
+require_relative "event_loggers/base"
+require_relative "event_loggers/windows_eventlog"
+require_relative "exceptions"
+require_relative "formatters/base"
+require_relative "formatters/doc"
+require_relative "formatters/minimal"
+require_relative "version"
+require_relative "action_collection"
+require_relative "resource_reporter"
+require_relative "data_collector"
+require_relative "run_lock"
+Chef.autoload :PolicyBuilder, File.expand_path("policy_builder", __dir__)
+require_relative "request_id"
+require_relative "platform/rebooter"
+require_relative "mixin/deprecation"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require "ohai" unless defined?(Ohai::System)
+require "rbconfig" unless defined?(RbConfig)
+require "forwardable" unless defined?(Forwardable)
+
+require_relative "compliance/runner"
class Chef
# == Chef::Client
# The main object in a Chef run. Preps a Chef::Node and Chef::RunContext,
# syncs cookbooks if necessary, and triggers convergence.
class Client
- include Chef::Mixin::PathSanity
-
extend Chef::Mixin::Deprecation
+ extend Forwardable
#
# The status of the Chef run.
#
@@ -72,6 +75,13 @@ class Chef
attr_reader :run_status
#
+ # The run context of the Chef run.
+ #
+ # @return [Chef::RunContext]
+ #
+ attr_reader :run_context
+
+ #
# The node represented by this client.
#
# @return [Chef::Node]
@@ -92,21 +102,6 @@ class Chef
attr_reader :ohai
#
- # The rest object used to communicate with the Chef server.
- #
- # @return [Chef::ServerAPI]
- #
- 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]
@@ -142,6 +137,10 @@ class Chef
#
attr_reader :events
+ attr_reader :logger
+
+ def_delegator :@run_context, :transport_connection
+
#
# Creates a new Chef::Client.
#
@@ -155,13 +154,15 @@ 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])
@events = EventDispatch::Dispatcher.new(*event_handlers)
- # TODO it seems like a bad idea to be deletin' other peoples' hashes.
+ # @todo it seems like a bad idea to be deletin' other peoples' hashes.
@override_runlist = args.delete(:override_runlist)
@specific_recipes = args.delete(:specific_recipes)
@run_status = Chef::RunStatus.new(nil, events)
@@ -222,30 +223,11 @@ class Chef
# @see #converge_and_save
# @see Chef::Runner
#
- # Phase 4: Audit
- # --------------
- # Runs 'control_group' audits in recipes. This entire section can be enabled or disabled with config.
- #
- # 1. 'control_group' DSL collects audits during Phase 2
- # 2. Audits are run using RSpec
- # 3. Errors are collected and reported using the formatters
- #
- # @see #run_audits
- # @see Chef::Audit::Runner#run
- #
- # @raise [Chef::Exceptions::RunFailedWrappingError] If converge or audit failed.
- #
- # @see Chef::Config#enforce_path_sanity
- # @see Chef::Config#solo
- # @see Chef::Config#audit_mode
- #
# @return Always returns true.
#
def run
start_profiling
- run_error = nil
-
runlock = RunLock.new(Chef::Config.lockfile)
# TODO feels like acquire should have its own block arg for this
runlock.acquire
@@ -253,106 +235,113 @@ class Chef
begin
runlock.save_pid
- 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}")
- enforce_path_sanity
+ events.register(Chef::DataCollector::Reporter.new(events))
+ events.register(Chef::ActionCollection.new(events))
+ events.register(Chef::Compliance::Runner.new)
+
+ run_status.run_id = request_id = Chef::RequestID.instance.request_id
+
+ @run_context = Chef::RunContext.new
+ run_context.events = events
+ run_status.run_context = run_context
+
+ events.run_start(Chef::VERSION, run_status)
+ logger.info("*** #{ChefUtils::Dist::Infra::PRODUCT} #{Chef::VERSION} ***")
+ logger.info("Platform: #{RUBY_PLATFORM}")
+ logger.info "#{ChefUtils::Dist::Infra::CLIENT.capitalize} pid: #{Process.pid}"
+ logger.info "Targeting node: #{Chef::Config.target_mode.host}" if Chef::Config.target_mode?
+ logger.debug("#{ChefUtils::Dist::Infra::CLIENT.capitalize} request_id: #{request_id}")
+ logger.warn("`enforce_path_sanity` is deprecated, please use `enforce_default_paths` instead!") if Chef::Config[:enforce_path_sanity]
+ ENV["PATH"] = ChefUtils::DSL::DefaultPaths.default_paths if Chef::Config[:enforce_default_paths] || Chef::Config[:enforce_path_sanity]
+
run_ohai
- register unless Chef::Config[:solo_legacy_mode]
- register_data_collector_reporter
+ unless Chef::Config[:solo_legacy_mode]
+ register
+
+ # create and save the rest objects in the run_context
+ run_context.rest = rest
+ run_context.rest_clean = rest_clean
+
+ events.register(Chef::ResourceReporter.new(rest_clean))
+ end
load_node
build_node
- run_status.run_id = request_id
run_status.start_clock
- Chef::Log.info("Starting Chef Run for #{node.name}")
+ logger.info("Starting #{ChefUtils::Dist::Infra::PRODUCT} Run for #{node.name}")
run_started
do_windows_admin_check
- run_context = setup_run_context
+ Chef.resource_handler_map.lock!
+ Chef.provider_handler_map.lock!
- if Chef::Config[:audit_mode] != :audit_only
- converge_error = converge_and_save(run_context)
- end
+ setup_run_context
- 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")
- elsif Chef::Config[:audit_mode] != :disabled
- audit_error = run_audits(run_context)
- end
+ load_required_recipe(@rest, run_context) unless Chef::Config[:solo_legacy_mode]
- # Raise converge_error so run_failed reporters/events are processed.
- raise converge_error if converge_error
+ converge_and_save(run_context)
run_status.stop_clock
- Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds")
+ logger.info("#{ChefUtils::Dist::Infra::PRODUCT} 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
+ warn_if_eol
+
# rebooting has to be the last thing we do, no exceptions.
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)
+ Chef::Application.debug_stacktrace(run_error)
+ raise run_error
ensure
Chef::RequestID.instance.reset_request_id
@run_status = nil
runlock.release
end
- # Raise audit, converge, and other errors here so that we exit
- # with the proper exit status code and everything gets raised
- # as a RunFailedWrappingError
- if run_error || converge_error || audit_error
- error = if Chef::Config[:audit_mode] == :disabled
- run_error || converge_error
- else
- e = if run_error == converge_error
- Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error)
- else
- Chef::Exceptions::RunFailedWrappingError.new(run_error, converge_error, audit_error)
- end
- e.fill_backtrace
- e
- end
-
- Chef::Application.debug_stacktrace(error)
- raise error
- end
-
true
end
#
# Private API
- # TODO make this stuff protected or private
+ # @todo make this stuff protected or private
#
# @api private
+ def warn_if_eol
+ require_relative "version"
+
+ # We make a release every year so take the version you're on + 2006 and you get
+ # the year it goes EOL
+ eol_year = 2006 + Gem::Version.new(Chef::VERSION).segments.first
+
+ if Time.now > Time.new(eol_year, 5, 01)
+ logger.warn("This release of #{ChefUtils::Dist::Infra::PRODUCT} became end of life (EOL) on May 1st #{eol_year}. Please update to a supported release to receive new features, bug fixes, and security updates.")
+ end
+ end
+
+ # @api private
def configure_formatters
formatters_for_run.map do |formatter_name, output_path|
if output_path.nil?
Chef::Formatters.new(formatter_name, STDOUT_FD, STDERR_FD)
- else
+ elsif output_path.is_a?(String)
io = File.open(output_path, "a+")
io.sync = true
Chef::Formatters.new(formatter_name, io, io)
@@ -362,19 +351,15 @@ class Chef
# @api private
def formatters_for_run
- if Chef::Config.formatters.empty?
- [default_formatter]
- else
- Chef::Config.formatters
- end
- end
+ return Chef::Config.formatters unless Chef::Config.formatters.empty?
- # @api private
- def default_formatter
- if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter]
- [:doc]
- else
- [:null]
+ [ Chef::Config[:log_location] ].flatten.map do |log_location|
+ log_location = nil if log_location == STDOUT
+ if !Chef::Config[:force_logger] || Chef::Config[:force_formatter]
+ [:doc, log_location]
+ else
+ [:null]
+ end
end
end
@@ -395,26 +380,27 @@ 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.
+ # Standard rest object for talking to the Chef Server
+ #
+ # FIXME: Can we drop this and only use the rest_clean object? Did I add rest_clean
+ # only out of some cant-break-a-minor-version paranoia?
#
# @api private
- def rest_clean(client_name = node_name, config = Chef::Config)
- @rest_clean ||=
- Chef::ServerAPI.new(config[:chef_server_url], client_name: client_name,
- signing_key_filename: config[:client_key], validate_utf8: false)
+ def rest
+ @rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: node_name,
+ signing_key_filename: Chef::Config[:client_key])
end
- # Resource reporters send event information back to the chef server for
- # processing. Can only be called after we have a @rest object
+ # 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 register_reporters
- [
- Chef::ResourceReporter.new(rest_clean),
- Chef::Audit::AuditReporter.new(rest_clean),
- ].each do |r|
- events.register(r)
- end
+ def rest_clean
+ @rest_clean ||=
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: node_name,
+ signing_key_filename: Chef::Config[:client_key], validate_utf8: false)
end
#
@@ -507,10 +493,53 @@ class Chef
#
# @api private
def setup_run_context
- run_context = policy_builder.setup_run_context(specific_recipes)
+ @run_context = policy_builder.setup_run_context(specific_recipes, run_context)
assert_cookbook_path_not_empty(run_context)
- run_status.run_context = run_context
+ run_status.run_context = run_context # backcompat for chefspec
+ run_context
+ 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::HTTPClientException => e
+ case e.response
+ when Net::HTTPNotFound
+ logger.trace("Required Recipe not configured on the server, skipping it")
+ else
+ raise
+ end
end
#
@@ -538,9 +567,9 @@ class Chef
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
@@ -556,7 +585,8 @@ 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 init_package} : nil
+ ohai.transport_connection = transport_connection if Chef::Config.target_mode?
ohai.all_plugins(filter)
events.ohai_completed(node)
end
@@ -610,22 +640,16 @@ 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
- # We now have the client key, and should use it from now on.
- @rest = Chef::ServerAPI.new(config[:chef_server_url], client_name: client_name,
- signing_key_filename: config[:client_key])
- # force initialization of the rest_clean API object
- rest_clean(client_name, config)
- register_reporters
rescue Exception => e
# TODO this should probably only ever fire if we *started* registration.
# Move it to the block above.
@@ -645,14 +669,9 @@ class Chef
#
# @param run_context The run context.
#
- # @return The thrown exception, if we are in audit mode. `nil` means the
- # converge was successful or ended early.
- #
- # @raise Any converge exception, unless we are in audit mode, in which case
- # we *return* the exception.
+ # @raise Any converge exception
#
# @see Chef::Runner#converge
- # @see Chef::Config#audit_mode
# @see Chef::EventDispatch#converge_start
# @see Chef::EventDispatch#converge_complete
# @see Chef::EventDispatch#converge_failed
@@ -660,93 +679,32 @@ class Chef
# @api private
#
def converge(run_context)
- converge_exception = nil
catch(:end_client_run_early) do
- begin
- events.converge_start(run_context)
- Chef::Log.debug("Converging node #{node_name}")
- @runner = Chef::Runner.new(run_context)
- @runner.converge
- events.converge_complete
- rescue Exception => e
- events.converge_failed(e)
- raise e if Chef::Config[:audit_mode] == :disabled
- converge_exception = e
- end
+
+ events.converge_start(run_context)
+ logger.debug("Converging node #{node_name}")
+ @runner = Chef::Runner.new(run_context)
+ @runner.converge
+ events.converge_complete
+ rescue Exception => e
+ events.converge_failed(e)
+ raise e
+
end
- converge_exception
end
- #
# Converge the node via and then save it if successful.
#
- # @param run_context The run context.
- #
- # @return The thrown exception, if we are in audit mode. `nil` means the
- # converge was successful or ended early.
- #
- # @raise Any converge or node save exception, unless we are in audit mode,
- # in which case we *return* the exception.
+ # If converge() raises it is important that save_updated_node is bypassed.
#
- # @see #converge
- # @see #save_updated_mode
- # @see Chef::Config#audit_mode
+ # @param run_context [Chef::RunContext] The run context.
+ # @raise Any converge or node save exception
#
# @api private
#
- # We don't want to change the old API on the `converge` method to have it perform
- # saving. So we wrap it in this method.
- # TODO given this seems to be pretty internal stuff, how badly do we need to
- # split this stuff up?
- #
def converge_and_save(run_context)
- converge_exception = converge(run_context)
- unless converge_exception
- begin
- save_updated_node
- rescue Exception => e
- raise e if Chef::Config[:audit_mode] == :disabled
- converge_exception = e
- end
- end
- converge_exception
- end
-
- #
- # Run the audit phase.
- #
- # Triggers the audit_phase_start, audit_phase_complete and
- # audit_phase_failed events.
- #
- # @param run_context The run context.
- #
- # @return Any thrown exceptions. `nil` if successful.
- #
- # @see Chef::Audit::Runner#run
- # @see Chef::EventDispatch#audit_phase_start
- # @see Chef::EventDispatch#audit_phase_complete
- # @see Chef::EventDispatch#audit_phase_failed
- #
- # @api private
- #
- def run_audits(run_context)
- begin
- events.audit_phase_start(run_status)
- Chef::Log.info("Starting audit phase")
- auditor = Chef::Audit::Runner.new(run_context)
- auditor.run
- if auditor.failed?
- audit_exception = Chef::Exceptions::AuditsFailed.new(auditor.num_failed, auditor.num_total)
- @events.audit_phase_failed(audit_exception, Chef::Audit::Logger.read_buffer)
- else
- @events.audit_phase_complete(Chef::Audit::Logger.read_buffer)
- end
- rescue Exception => e
- Chef::Log.error("Audit phase failed with error message: #{e.message}")
- @events.audit_phase_failed(e, Chef::Audit::Logger.read_buffer)
- audit_exception = e
- end
- audit_exception
+ converge(run_context)
+ save_updated_node
end
#
@@ -774,20 +732,20 @@ class Chef
# @api private
#
def do_windows_admin_check
- if Chef::Platform.windows?
- Chef::Log.debug("Checking for administrator privileges....")
+ if ChefUtils.windows?
+ logger.trace("Checking for administrator privileges....")
if !has_admin_privileges?
- message = "chef-client doesn't have administrator privileges on node #{node_name}."
+ message = "#{ChefUtils::Dist::Infra::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("#{ChefUtils::Dist::Infra::CLIENT} has administrator privileges on node #{node_name}.")
end
end
end
@@ -893,15 +851,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
@@ -915,18 +864,20 @@ class Chef
def start_profiling
return unless Chef::Config[:profile_ruby]
+
profiling_prereqs!
RubyProf.start
end
def end_profiling
return unless Chef::Config[:profile_ruby]
+
profiling_prereqs!
path = Chef::FileCache.create_cache_path("graph_profile.out", false)
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)
@@ -934,7 +885,7 @@ class Chef
end
def is_last_element?(index, object)
- object.kind_of?(Array) ? index == object.size - 1 : true
+ object.is_a?(Array) ? index == object.size - 1 : true
end
def assert_cookbook_path_not_empty(run_context)
@@ -943,32 +894,26 @@ class Chef
# 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
def has_admin_privileges?
- require "chef/win32/security"
+ require_relative "win32/security"
Chef::ReservedNames::Win32::Security.has_admin_privileges?
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
# HACK cannot load this first, but it must be loaded.
-require "chef/cookbook_loader"
-require "chef/cookbook_version"
-require "chef/cookbook/synchronizer"
+require_relative "cookbook_loader"
+require_relative "cookbook_version"
+require_relative "cookbook/synchronizer"
diff --git a/lib/chef/compliance/default_attributes.rb b/lib/chef/compliance/default_attributes.rb
new file mode 100644
index 0000000000..9b368d4f64
--- /dev/null
+++ b/lib/chef/compliance/default_attributes.rb
@@ -0,0 +1,93 @@
+# Author:: Stephan Renatus <srenatus@chef.io>
+# Copyright:: (c) 2016-2019, 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 "chef/node/attribute_collections" # for VividMash
+require "chef/util/path_helper"
+
+class Chef
+ module Compliance
+ DEFAULT_ATTRIBUTES = Chef::Node::VividMash.new(
+ # If enabled, a cache is built for all backend calls. This should only be
+ # disabled if you are expecting unique results from the same backend call.
+ # Under the covers, this controls :command and :file caching on Chef InSpec's
+ # Train connection.
+ "inspec_backend_cache" => true,
+
+ # Controls what is done with the resulting report after the Chef InSpec run.
+ # Accepts a single string value or an array of multiple values.
+ # Accepted values: 'chef-server-automate', 'chef-automate', 'json-file', 'audit-enforcer'
+ "reporter" => "json-file",
+
+ # Controls if Chef InSpec profiles should be fetched from Chef Automate or Chef Infra Server
+ # in addition to the default fetch locations provided by Chef Inspec.
+ # Accepted values: nil, 'chef-server', 'chef-automate'
+ "fetcher" => nil,
+
+ # Allow for connections to HTTPS endpoints using self-signed ssl certificates.
+ "insecure" => nil,
+
+ # Controls verbosity of Chef InSpec runner.
+ "quiet" => true,
+
+ # Chef Inspec Compliance profiles to be used for scan of node.
+ # See README.md for details
+ "profiles" => {},
+
+ # Extra inputs passed to Chef InSpec to allow finer-grained control over behavior.
+ # These are mapped to Chef InSpec's inputs, but are named attributes here for legacy reasons.
+ # See Chef Inspec's documentation for more information: https://docs.chef.io/inspec/inputs/
+ "attributes" => {},
+
+ # A string path or an array of paths to Chef InSpec waiver files.
+ # See Chef Inspec's documentation for more information: https://docs.chef.io/inspec/waivers/
+ "waiver_file" => nil,
+
+ "json_file" => {
+ # The location on disk that Chef InSpec's json reports are saved to when using the
+ # 'json-file' reporter. Defaults to:
+ # <chef_cache_path>/compliance_reports/compliance-<timestamp>.json
+ "location" => Chef::Util::PathHelper.join(
+ Chef::Config[:cache_path],
+ "compliance_reports",
+ Time.now.utc.strftime("compliance-%Y%m%d%H%M%S.json")
+ ),
+ },
+
+ # Control results that have a `run_time` below this limit will
+ # be stripped of the `start_time` and `run_time` fields to
+ # reduce the size of the reports being sent to Chef Automate.
+ "run_time_limit" => 1.0,
+
+ # A control result message that exceeds this character limit will be truncated.
+ # This helps keep reports to a reasonable size. On rare occasions, we've seen messages exceeding 9 MB in size,
+ # causing the report to not be ingested in the backend because of the 4 MB report size rpc limitation.
+ # Chef InSpec will append this text at the end of any truncated messages: `[Truncated to 10000 characters]`
+ "result_message_limit" => 10000,
+
+ # When a Chef InSpec resource throws an exception, results will contain a short error message and a
+ # detailed ruby stacktrace of the error. This attribute instructs Chef InSpec not to include the detailed stacktrace in order
+ # to keep the overall report to a manageable size.
+ "result_include_backtrace" => false,
+
+ # The array of results per control will be truncated at this limit to avoid large reports that cannot be
+ # processed by Chef Automate. A summary of removed results will be sent with each impacted control.
+ "control_results_limit" => 50,
+
+ # If enabled, a hash representation of the Chef Infra node object will be sent to Chef InSpec in an input
+ # named `chef_node`.
+ "chef_node_attribute_enabled" => false
+ )
+ end
+end
diff --git a/lib/chef/compliance/fetcher/automate.rb b/lib/chef/compliance/fetcher/automate.rb
new file mode 100644
index 0000000000..b254684280
--- /dev/null
+++ b/lib/chef/compliance/fetcher/automate.rb
@@ -0,0 +1,69 @@
+require "uri" unless defined?(URI)
+require "plugins/inspec-compliance/lib/inspec-compliance"
+
+class Chef
+ module Compliance
+ module Fetcher
+ class Automate < ::InspecPlugins::Compliance::Fetcher
+ name "chef-automate"
+
+ # Positions this fetcher before Chef InSpec's `compliance` fetcher.
+ # Only load this file if you want to use Compliance Phase in Chef Solo with Chef Automate.
+ priority 502
+
+ CONFIG = {
+ "insecure" => true,
+ "token" => nil,
+ "server_type" => "automate",
+ "automate" => {
+ "ent" => "default",
+ "token_type" => "dctoken",
+ },
+ }.freeze
+
+ def self.resolve(target)
+ uri = get_target_uri(target)
+ return nil if uri.nil?
+
+ config = CONFIG.dup
+
+ # we have detailed information available in our lockfile, no need to ask the server
+ if target.respond_to?(:key?) && target.key?(:url)
+ profile_fetch_url = target[:url]
+ else
+ # verifies that the target e.g base/ssh exists
+ base_path = "/compliance/profiles/#{uri.host}#{uri.path}"
+
+ profile_path = if target.respond_to?(:key?) && target.key?(:version)
+ "#{base_path}/version/#{target[:version]}/tar"
+ else
+ "#{base_path}/tar"
+ end
+
+ url = URI(Chef::Config[:data_collector][:server_url])
+ url.path = profile_path
+ profile_fetch_url = url.to_s
+
+ config["token"] = Chef::Config[:data_collector][:token]
+
+ if config["token"].nil?
+ raise Inspec::FetcherFailure,
+ "No data-collector token set, which is required by the chef-automate fetcher. " \
+ "Set the `data_collector.token` configuration parameter in your client.rb " \
+ 'or use the "chef-server-automate" reporter which does not require any ' \
+ "data-collector settings and uses #{ChefUtils::Dist::Server::PRODUCT} to fetch profiles."
+ end
+ end
+
+ new(profile_fetch_url, config)
+ rescue URI::Error => _e
+ nil
+ end
+
+ def to_s
+ "#{ChefUtils::Dist::Automate::PRODUCT} for #{ChefUtils::Dist::Solo::PRODUCT} Fetcher"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/fetcher/chef_server.rb b/lib/chef/compliance/fetcher/chef_server.rb
new file mode 100644
index 0000000000..96a2213b69
--- /dev/null
+++ b/lib/chef/compliance/fetcher/chef_server.rb
@@ -0,0 +1,134 @@
+require "uri" unless defined?(URI)
+require "plugins/inspec-compliance/lib/inspec-compliance"
+
+# This class implements an InSpec fetcher for Chef Server. The implementation
+# is based on the Chef Compliance fetcher and only adapts the calls to redirect
+# the requests via Chef Server.
+#
+# This implementation depends on chef-client runtime, therefore it is only executable
+# inside of a chef-client run
+
+class Chef
+ module Compliance
+ module Fetcher
+ class ChefServer < ::InspecPlugins::Compliance::Fetcher
+ name "chef-server"
+
+ # it positions itself before `compliance` fetcher
+ # only load it, if the Chef Server is integrated with Chef Compliance
+ priority 501
+
+ CONFIG = { "insecure" => true }.freeze
+
+ # Accepts URLs to compliance profiles in one of two forms:
+ # * a String URL with a compliance scheme, like "compliance://namespace/profile_name"
+ # * a Hash with a key of `compliance` and a value like "compliance/profile_name" and optionally a `version` key with a String value
+ def self.resolve(target)
+ profile_uri = get_target_uri(target)
+ return nil if profile_uri.nil?
+
+ organization = Chef::Config[:chef_server_url].split("/").last
+ owner = profile_uri.user ? "#{profile_uri.user}@#{profile_uri.host}" : profile_uri.host
+ version = target[:version] if target.respond_to?(:key?)
+
+ path_parts = [""]
+ path_parts << "compliance" if chef_server_reporter? || chef_server_fetcher?
+ path_parts << "organizations"
+ path_parts << organization
+ path_parts << "owners"
+ path_parts << owner
+ path_parts << "compliance"
+ path_parts << profile_uri.path
+ path_parts << "version/#{version}" if version
+ path_parts << "tar"
+
+ target_url = URI(Chef::Config[:chef_server_url])
+ target_url.path = File.join(path_parts)
+ Chef::Log.info("Fetching profile from: #{target_url}")
+
+ new(target_url, CONFIG)
+ rescue URI::Error => _e
+ nil
+ end
+
+ #
+ # We want to save compliance: in the lockfile rather than url: to
+ # make sure we go back through the ComplianceAPI handling.
+ #
+ def resolved_source
+ { compliance: chef_server_url }
+ end
+
+ # Downloads archive to temporary file using a Chef::ServerAPI
+ # client so that Chef Server's header-based authentication can be
+ # used.
+ def download_archive_to_temp
+ return @temp_archive_path unless @temp_archive_path.nil?
+
+ rest = Chef::ServerAPI.new(@target, Chef::Config.merge(ssl_verify_mode: :verify_none))
+ archive = with_http_rescue do
+ rest.streaming_request(@target)
+ end
+ @archive_type = ".tar.gz"
+
+ if archive.nil?
+ path = @target.respond_to?(:path) ? @target.path : path
+ raise Inspec::FetcherFailure, "Unable to find requested profile on path: '#{path}' on the #{ChefUtils::Dist::Automate::PRODUCT} system."
+ end
+
+ Inspec::Log.debug("Archive stored at temporary location: #{archive.path}")
+ @temp_archive_path = archive.path
+ end
+
+ def with_http_rescue
+ response = yield
+ if response.respond_to?(:code)
+ # handle non 200 error codes, they are not raised as Net::HTTPClientException
+ handle_http_error_code(response.code) if response.code.to_i >= 300
+ end
+ response
+ rescue Net::HTTPClientException => e
+ Chef::Log.error e
+ handle_http_error_code(e.response.code)
+ end
+
+ def handle_http_error_code(code)
+ case code
+ when /401|403/
+ Chef::Log.error "Auth issue: see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting)."
+ when /404/
+ Chef::Log.error "Object does not exist on remote server."
+ when /413/
+ Chef::Log.error "You most likely hit the erchef request size in #{ChefUtils::Dist::Server::PRODUCT} that defaults to ~2MB. To increase this limit see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting) or the Chef Infra Server configuration documentation (https://docs.chef.io/server/config_rb_server/)"
+ when /429/
+ Chef::Log.error "This error typically means the data sent was larger than #{ChefUtils::Dist::Automate::PRODUCT}'s limit (4 MB). Run InSpec locally to identify any controls producing large diffs."
+ end
+ msg = "Received HTTP error #{code}"
+ Chef::Log.error msg
+ raise Inspec::FetcherFailure, msg
+ end
+
+ def to_s
+ "#{ChefUtils::Dist::Server::PRODUCT}/Compliance Profile Loader"
+ end
+
+ CHEF_SERVER_REPORTERS = %w{chef-server chef-server-compliance chef-server-visibility chef-server-automate}.freeze
+ def self.chef_server_reporter?
+ (Array(Chef.node.attributes["audit"]["reporter"]) & CHEF_SERVER_REPORTERS).any?
+ end
+
+ CHEF_SERVER_FETCHERS = %w{chef-server chef-server-compliance chef-server-visibility chef-server-automate}.freeze
+ def self.chef_server_fetcher?
+ CHEF_SERVER_FETCHERS.include?(Chef.node.attributes["audit"]["fetcher"])
+ end
+
+ private
+
+ def chef_server_url
+ m = %r{^#{@config['server']}/owners/(?<owner>[^/]+)/compliance/(?<id>[^/]+)/tar$}.match(@target)
+ "#{m[:owner]}/#{m[:id]}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/reporter/automate.rb b/lib/chef/compliance/reporter/automate.rb
new file mode 100644
index 0000000000..cae0256085
--- /dev/null
+++ b/lib/chef/compliance/reporter/automate.rb
@@ -0,0 +1,201 @@
+class Chef
+ module Compliance
+ module Reporter
+ #
+ # Used to send inspec reports to Chef Automate via the data_collector service
+ #
+ class Automate
+ def initialize(opts)
+ @entity_uuid = opts[:entity_uuid]
+ @run_id = opts[:run_id]
+ @node_name = opts[:node_info][:node]
+ @environment = opts[:node_info][:environment]
+ @roles = opts[:node_info][:roles]
+ @recipes = opts[:node_info][:recipes]
+ @insecure = opts[:insecure]
+ @chef_tags = opts[:node_info][:chef_tags]
+ @policy_group = opts[:node_info][:policy_group]
+ @policy_name = opts[:node_info][:policy_name]
+ @source_fqdn = opts[:node_info][:source_fqdn]
+ @organization_name = opts[:node_info][:organization_name]
+ @ipaddress = opts[:node_info][:ipaddress]
+ @fqdn = opts[:node_info][:fqdn]
+ @run_time_limit = opts[:run_time_limit]
+ @control_results_limit = opts[:control_results_limit]
+ @timestamp = opts.fetch(:timestamp) { Time.now }
+
+ @url = Chef::Config[:data_collector][:server_url]
+ @token = Chef::Config[:data_collector][:token]
+ end
+
+ # Method used in order to send the inspec report to the data_collector server
+ def send_report(report)
+ unless @entity_uuid && @run_id
+ Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
+ return false
+ end
+
+ unless @url && @token
+ Chef::Log.warn "data_collector.token and data_collector.server_url must be defined in client.rb! Further information: https://docs.chef.io/chef_compliance_phase/#direct-reporting-to-chef-automate"
+ return false
+ end
+
+ headers = {
+ "Content-Type" => "application/json",
+ "x-data-collector-auth" => "version=1.0",
+ "x-data-collector-token" => @token,
+ }
+
+ all_report_shas = report[:profiles].map { |p| p[:sha256] }
+ missing_report_shas = missing_automate_profiles(headers, all_report_shas)
+
+ full_report = truncate_controls_results(enriched_report(report), @control_results_limit)
+ full_report = strip_profiles_meta(full_report, missing_report_shas, @run_time_limit)
+ json_report = Chef::JSONCompat.to_json(full_report, validate_utf8: false)
+
+ # Automate GRPC currently has a message limit of ~4MB
+ # https://github.com/chef/automate/issues/1417#issuecomment-541908157
+ if json_report.bytesize > 4 * 1024 * 1024
+ Chef::Log.warn "Generated report size is #{(json_report.bytesize / (1024 * 1024.0)).round(2)} MB. #{ChefUtils::Dist::Automate::PRODUCT} has an internal 4MB limit that is not currently configurable."
+ end
+
+ unless json_report
+ Chef::Log.warn "Something went wrong, report can't be nil"
+ return false
+ end
+
+ begin
+ Chef::Log.info "Report to #{ChefUtils::Dist::Automate::PRODUCT}: #{@url}"
+ Chef::Log.debug "Compliance Report: #{json_report}"
+ http_client.post(nil, json_report, headers)
+ true
+ rescue => e
+ Chef::Log.error "send_report: POST to #{@url} returned: #{e.message}"
+ false
+ end
+ end
+
+ def http_client(url = @url)
+ if @insecure
+ Chef::HTTP.new(url, ssl_verify_mode: :verify_none)
+ else
+ Chef::HTTP.new(url)
+ end
+ end
+
+ def enriched_report(final_report)
+ final_report[:profiles].compact!
+
+ # Label this content as an inspec_report
+ final_report[:type] = "inspec_report"
+
+ final_report[:node_name] = @node_name
+ final_report[:end_time] = @timestamp.utc.strftime("%FT%TZ")
+ final_report[:node_uuid] = @entity_uuid
+ final_report[:environment] = @environment
+ final_report[:roles] = @roles
+ final_report[:recipes] = @recipes
+ final_report[:report_uuid] = @run_id
+ final_report[:source_fqdn] = @source_fqdn
+ final_report[:organization_name] = @organization_name
+ final_report[:policy_group] = @policy_group
+ final_report[:policy_name] = @policy_name
+ final_report[:chef_tags] = @chef_tags
+ final_report[:ipaddress] = @ipaddress
+ final_report[:fqdn] = @fqdn
+
+ final_report
+ end
+
+ CONTROL_RESULT_SORT_ORDER = %w{ failed skipped passed }.freeze
+
+ # Truncates the number of results per control in the report when they exceed max_results.
+ # The truncation prioritizes failed and skipped results over passed ones.
+ # Controls where results have been truncated will get a new object 'removed_results_counts'
+ # with the status counts of the truncated results
+ def truncate_controls_results(report, max_results)
+ return report unless max_results.is_a?(Integer) && max_results > 0
+
+ report.fetch(:profiles, []).each do |profile|
+ profile.fetch(:controls, []).each do |control|
+ # Only bother with truncation if the number of results exceed max_results
+ next unless control[:results].length > max_results
+
+ res = control[:results]
+ res.sort_by! { |r| CONTROL_RESULT_SORT_ORDER.index(r[:status]) }
+
+ # Count the results that will be truncated
+ truncated = { failed: 0, skipped: 0, passed: 0 }
+ (max_results..res.length - 1).each do |i|
+ case res[i][:status]
+ when "failed"
+ truncated[:failed] += 1
+ when "skipped"
+ truncated[:skipped] += 1
+ when "passed"
+ truncated[:passed] += 1
+ end
+ end
+ # Truncate the results array now
+ control[:results] = res[0..max_results - 1]
+ control[:removed_results_counts] = truncated
+ end
+ end
+ report
+ end
+
+ # Contacts the metasearch Automate API to check which of the inspec profile sha256 ids
+ # passed in via `report_shas` are missing from the Automate profiles metadata database.
+ def missing_automate_profiles(headers, report_shas)
+ Chef::Log.debug "Checking the #{ChefUtils::Dist::Automate::PRODUCT} profiles metadata for: #{report_shas}"
+ meta_url = URI(@url)
+ meta_url.path = "/compliance/profiles/metasearch"
+ response_str = http_client(meta_url.to_s).post(nil, "{\"sha256\": #{report_shas}}", headers)
+ missing_shas = Chef::JSONCompat.parse(response_str)["missing_sha256"]
+ unless missing_shas.empty?
+ Chef::Log.info "#{ChefUtils::Dist::Automate::PRODUCT} is missing metadata for the following profile ids: #{missing_shas}"
+ end
+ missing_shas
+ rescue => e
+ Chef::Log.error "missing_automate_profiles error: #{e.message}"
+ # If we get an error it's safer to assume none of the profile shas exist in Automate
+ report_shas
+ end
+
+ # Profile 'name' is a required property.
+ # By not sending it in the report, we make it clear to the ingestion backend that the profile metadata has been stripped from this profile in the report.
+ # Profile 'title' and 'version' are still kept for troubleshooting purposes in the backend.
+ SEEN_PROFILE_UNNECESSARY_FIELDS = %i{ copyright copyright_email groups license maintainer name summary supports}.freeze
+
+ SEEN_PROFILE_UNNECESSARY_CONTROL_FIELDS = %i{ code desc descriptions impact refs source_location tags title }.freeze
+
+ # TODO: This mutates the report and probably doesn't need to.
+ def strip_profiles_meta(report, missing_report_shas, run_time_limit)
+ report[:profiles].each do |p|
+ next if missing_report_shas.include?(p[:sha256])
+
+ p.delete_if { |f| SEEN_PROFILE_UNNECESSARY_FIELDS.include?(f) }
+
+ next unless p[:controls].is_a?(Array)
+
+ p[:controls].each do |c|
+ c.delete_if { |f| SEEN_PROFILE_UNNECESSARY_CONTROL_FIELDS.include?(f) }
+ c.delete(:waiver_data) if c[:waiver_data] == {}
+
+ next unless c[:results].is_a?(Array)
+
+ c[:results].each do |r|
+ if r[:run_time].is_a?(Float) && r[:run_time] < run_time_limit
+ r.delete(:start_time)
+ r.delete(:run_time)
+ end
+ end
+ end
+ end
+ report[:run_time_limit] = run_time_limit
+ report
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/reporter/chef_server_automate.rb b/lib/chef/compliance/reporter/chef_server_automate.rb
new file mode 100644
index 0000000000..46ca7dc7ba
--- /dev/null
+++ b/lib/chef/compliance/reporter/chef_server_automate.rb
@@ -0,0 +1,94 @@
+require_relative "automate"
+
+class Chef
+ module Compliance
+ module Reporter
+ #
+ # Used to send inspec reports to Chef Automate server via Chef Server
+ #
+ class ChefServerAutomate < Chef::Compliance::Reporter::Automate
+ attr_reader :url
+
+ def initialize(opts)
+ @entity_uuid = opts[:entity_uuid]
+ @run_id = opts[:run_id]
+ @node_name = opts[:node_info][:node]
+ @insecure = opts[:insecure]
+ @environment = opts[:node_info][:environment]
+ @roles = opts[:node_info][:roles]
+ @recipes = opts[:node_info][:recipes]
+ @url = opts[:url]
+ @chef_tags = opts[:node_info][:chef_tags]
+ @policy_group = opts[:node_info][:policy_group]
+ @policy_name = opts[:node_info][:policy_name]
+ @source_fqdn = opts[:node_info][:source_fqdn]
+ @organization_name = opts[:node_info][:organization_name]
+ @ipaddress = opts[:node_info][:ipaddress]
+ @fqdn = opts[:node_info][:fqdn]
+ @control_results_limit = opts[:control_results_limit]
+ @timestamp = opts.fetch(:timestamp) { Time.now }
+ end
+
+ def send_report(report)
+ unless @entity_uuid && @run_id
+ Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
+ return false
+ end
+
+ automate_report = truncate_controls_results(enriched_report(report), @control_results_limit)
+
+ report_size = Chef::JSONCompat.to_json(automate_report, validate_utf8: false).bytesize
+ # this is set to slightly less than the oc_erchef limit
+ if report_size > 900 * 1024
+ Chef::Log.warn "Generated report size is #{(report_size / (1024 * 1024.0)).round(2)} MB. #{ChefUtils::Dist::Server::PRODUCT} < 13.0 defaults to a limit of ~1MB, 13.0+ defaults to a limit of ~2MB."
+ end
+
+ Chef::Log.info "Report to #{ChefUtils::Dist::Automate::PRODUCT} via #{ChefUtils::Dist::Server::PRODUCT}: #{@url}"
+ with_http_rescue do
+ http_client.post(@url, automate_report)
+ return true
+ end
+ false
+ end
+
+ def http_client
+ config = if @insecure
+ Chef::Config.merge(ssl_verify_mode: :verify_none)
+ else
+ Chef::Config
+ end
+
+ Chef::ServerAPI.new(@url, config)
+ end
+
+ def with_http_rescue
+ response = yield
+ if response.respond_to?(:code)
+ # handle non 200 error codes, they are not raised as Net::HTTPClientException
+ handle_http_error_code(response.code) if response.code.to_i >= 300
+ end
+ response
+ rescue Net::HTTPClientException => e
+ Chef::Log.error e
+ handle_http_error_code(e.response.code)
+ end
+
+ def handle_http_error_code(code)
+ case code
+ when /401|403/
+ Chef::Log.error "Auth issue: see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting)."
+ when /404/
+ Chef::Log.error "Object does not exist on remote server."
+ when /413/
+ Chef::Log.error "You most likely hit the erchef request size in #{ChefUtils::Dist::Server::PRODUCT} that defaults to ~2MB. To increase this limit see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting) or the Chef Infra Server configuration documentation (https://docs.chef.io/server/config_rb_server/)"
+ when /429/
+ Chef::Log.error "This error typically means the data sent was larger than #{ChefUtils::Dist::Automate::PRODUCT}'s limit (4 MB). Run InSpec locally to identify any controls producing large diffs."
+ end
+ msg = "Received HTTP error #{code}"
+ Chef::Log.error msg
+ raise msg
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/reporter/compliance_enforcer.rb b/lib/chef/compliance/reporter/compliance_enforcer.rb
new file mode 100644
index 0000000000..1c63e43b28
--- /dev/null
+++ b/lib/chef/compliance/reporter/compliance_enforcer.rb
@@ -0,0 +1,20 @@
+class Chef
+ module Compliance
+ module Reporter
+ class AuditEnforcer
+ class ControlFailure < StandardError; end
+
+ def send_report(report)
+ report.fetch(:profiles, []).each do |profile|
+ profile.fetch(:controls, []).each do |control|
+ control.fetch(:results, []).each do |result|
+ raise ControlFailure, "Audit #{control[:id]} has failed. Aborting #{ChefUtils::Dist::Infra::CLIENT} run." if result[:status] == "failed"
+ end
+ end
+ end
+ true
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/reporter/json_file.rb b/lib/chef/compliance/reporter/json_file.rb
new file mode 100644
index 0000000000..471d9f64b1
--- /dev/null
+++ b/lib/chef/compliance/reporter/json_file.rb
@@ -0,0 +1,19 @@
+require_relative "../../json_compat"
+
+class Chef
+ module Compliance
+ module Reporter
+ class JsonFile
+ def initialize(opts)
+ @path = opts.fetch(:file)
+ end
+
+ def send_report(report)
+ FileUtils.mkdir_p(File.dirname(@path), mode: 0700)
+
+ File.write(@path, Chef::JSONCompat.to_json(report))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/compliance/runner.rb b/lib/chef/compliance/runner.rb
new file mode 100644
index 0000000000..86344367c2
--- /dev/null
+++ b/lib/chef/compliance/runner.rb
@@ -0,0 +1,266 @@
+autoload :Inspec, "inspec"
+
+require_relative "default_attributes"
+require_relative "reporter/automate"
+require_relative "reporter/chef_server_automate"
+require_relative "reporter/compliance_enforcer"
+require_relative "reporter/json_file"
+
+class Chef
+ module Compliance
+ class Runner < EventDispatch::Base
+ extend Forwardable
+
+ attr_accessor :run_id, :recipes
+ attr_reader :node
+ def_delegators :node, :logger
+
+ def enabled?
+ audit_cookbook_present = recipes.include?("audit::default")
+
+ logger.info("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
+ logger.info("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
+
+ inspec_profiles.any? && !audit_cookbook_present
+ end
+
+ def node=(node)
+ @node = node
+ node.default["audit"] = Chef::Compliance::DEFAULT_ATTRIBUTES.merge(node.default["audit"])
+ end
+
+ def node_load_completed(node, _expanded_run_list, _config)
+ self.node = node
+ end
+
+ def run_started(run_status)
+ self.run_id = run_status.run_id
+ end
+
+ def run_list_expanded(run_list_expansion)
+ self.recipes = run_list_expansion.recipes
+ end
+
+ def run_completed(_node, _run_status)
+ return unless enabled?
+
+ logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
+
+ report
+ end
+
+ def run_failed(_exception, _run_status)
+ return unless enabled?
+
+ logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
+
+ report
+ end
+
+ ### Below code adapted from audit cookbook's files/default/handler/audit_report.rb
+
+ DEPRECATED_CONFIG_VALUES = %w{
+ attributes_save
+ fail_if_not_present
+ inspec_gem_source
+ inspec_version
+ interval
+ owner
+ raise_if_unreachable
+ }.freeze
+
+ def warn_for_deprecated_config_values!
+ deprecated_config_values = (node["audit"].keys & DEPRECATED_CONFIG_VALUES)
+
+ if deprecated_config_values.any?
+ values = deprecated_config_values.sort.map { |v| "'#{v}'" }.join(", ")
+ logger.warn "audit cookbook config values #{values} are not supported in #{ChefUtils::Dist::Infra::PRODUCT}'s Compliance Phase."
+ end
+ end
+
+ def report(report = generate_report)
+ warn_for_deprecated_config_values!
+
+ if report.empty?
+ logger.error "Compliance report was not generated properly, skipped reporting"
+ return
+ end
+
+ Array(node["audit"]["reporter"]).each do |reporter|
+ send_report(reporter, report)
+ end
+ end
+
+ def inspec_opts
+ inputs = node["audit"]["attributes"].to_h
+ if node["audit"]["chef_node_attribute_enabled"]
+ inputs["chef_node"] = node.to_h
+ inputs["chef_node"]["chef_environment"] = node.chef_environment
+ end
+
+ {
+ backend_cache: node["audit"]["inspec_backend_cache"],
+ inputs: inputs,
+ logger: logger,
+ output: node["audit"]["quiet"] ? ::File::NULL : STDOUT,
+ report: true,
+ reporter: ["json-automate"],
+ reporter_backtrace_inclusion: node["audit"]["result_include_backtrace"],
+ reporter_message_truncation: node["audit"]["result_message_limit"],
+ waiver_file: Array(node["audit"]["waiver_file"]),
+ }
+ end
+
+ def inspec_profiles
+ profiles = node["audit"]["profiles"]
+
+ # TODO: Custom exception class here?
+ unless profiles.respond_to?(:map) && profiles.all? { |_, p| p.respond_to?(:transform_keys) && p.respond_to?(:update) }
+ raise "#{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
+ end
+
+ profiles.map do |name, profile|
+ profile.transform_keys(&:to_sym).update(name: name)
+ end
+ end
+
+ def load_fetchers!
+ case node["audit"]["fetcher"]
+ when "chef-automate"
+ require_relative "fetcher/automate"
+ when "chef-server"
+ require_relative "fetcher/chef_server"
+ when nil
+ # intentionally blank
+ else
+ raise "Invalid value specified for Compliance Phase's fetcher: '#{node["audit"]["fetcher"]}'. Valid values are 'chef-automate', 'chef-server', or nil."
+ end
+ end
+
+ def generate_report(opts: inspec_opts, profiles: inspec_profiles)
+ load_fetchers!
+
+ logger.debug "Options are set to: #{opts}"
+ runner = ::Inspec::Runner.new(opts)
+
+ if profiles.empty?
+ failed_report("No #{Inspec::Dist::PRODUCT_NAME} profiles are defined.")
+ return
+ end
+
+ profiles.each { |target| runner.add_target(target) }
+
+ logger.info "Running profiles from: #{profiles.inspect}"
+ runner.run
+ runner.report.tap do |r|
+ logger.debug "Compliance Report #{r}"
+ end
+ rescue Inspec::FetcherFailure => e
+ failed_report("Cannot fetch all profiles: #{profiles}. Please make sure you're authenticated and the server is reachable. #{e.message}")
+ rescue => e
+ failed_report(e.message)
+ end
+
+ # In case InSpec raises a runtime exception without providing a valid report,
+ # we make one up and add two new fields to it: `status` and `status_message`
+ def failed_report(err)
+ logger.error "#{Inspec::Dist::PRODUCT_NAME} has raised a runtime exception. Generating a minimal failed report."
+ logger.error err
+ {
+ "platform": {
+ "name": "unknown",
+ "release": "unknown",
+ },
+ "profiles": [],
+ "statistics": {
+ "duration": 0.0000001,
+ },
+ "version": Inspec::VERSION,
+ "status": "failed",
+ "status_message": err,
+ }
+ end
+
+ # extracts relevant node data
+ def node_info
+ chef_server_uri = URI(Chef::Config[:chef_server_url])
+
+ runlist_roles = node.run_list.select { |item| item.type == :role }.map(&:name)
+ runlist_recipes = node.run_list.select { |item| item.type == :recipe }.map(&:name)
+ {
+ node: node.name,
+ os: {
+ release: node["platform_version"],
+ family: node["platform"],
+ },
+ environment: node.environment,
+ roles: runlist_roles,
+ recipes: runlist_recipes,
+ policy_name: node.policy_name || "",
+ policy_group: node.policy_group || "",
+ chef_tags: node.tags,
+ organization_name: chef_server_uri.path.split("/").last || "",
+ source_fqdn: chef_server_uri.host || "",
+ ipaddress: node["ipaddress"],
+ fqdn: node["fqdn"],
+ }
+ end
+
+ def send_report(reporter_type, report)
+ logger.info "Reporting to #{reporter_type}"
+
+ reporter = reporter(reporter_type)
+
+ reporter.send_report(report) if reporter
+ end
+
+ def reporter(reporter_type)
+ case reporter_type
+ when "chef-automate"
+ opts = {
+ control_results_limit: node["audit"]["control_results_limit"],
+ entity_uuid: node["chef_guid"],
+ insecure: node["audit"]["insecure"],
+ node_info: node_info,
+ run_id: run_id,
+ run_time_limit: node["audit"]["run_time_limit"],
+ }
+ Chef::Compliance::Reporter::Automate.new(opts)
+ when "chef-server-automate"
+ opts = {
+ control_results_limit: node["audit"]["control_results_limit"],
+ entity_uuid: node["chef_guid"],
+ insecure: node["audit"]["insecure"],
+ node_info: node_info,
+ run_id: run_id,
+ run_time_limit: node["audit"]["run_time_limit"],
+ url: chef_server_automate_url,
+ }
+ Chef::Compliance::Reporter::ChefServerAutomate.new(opts)
+ when "json-file"
+ path = node["audit"]["json_file"]["location"]
+ logger.info "Writing compliance report to #{path}"
+ Chef::Compliance::Reporter::JsonFile.new(file: path)
+ when "audit-enforcer"
+ Chef::Compliance::Reporter::ComplianceEnforcer.new
+ else
+ raise "'#{reporter_type}' is not a supported reporter for Compliance Phase."
+ end
+ end
+
+ def chef_server_automate_url
+ url = if node["audit"]["server"]
+ URI(node["audit"]["server"])
+ else
+ URI(Chef::Config[:chef_server_url]).tap do |u|
+ u.path = ""
+ end
+ end
+
+ org = Chef::Config[:chef_server_url].split("/").last
+ url.path = File.join(url.path, "organizations/#{org}/data-collector")
+ url
+ end
+ end
+ end
+end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 549872bfd7..f92aae87c0 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/log"
+require_relative "log"
require "chef-config/logger"
# DI our logger into ChefConfig before we load the config. Some defaults are
@@ -28,7 +28,8 @@ require "chef-config/logger"
ChefConfig.logger = Chef::Log
require "chef-config/config"
-require "chef/platform/query_helpers"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require_relative "platform/query_helpers"
# Ohai::Config defines its own log_level and log_location. When loaded, it will
# override the default ChefConfig::Config values. We save them here before
@@ -48,15 +49,14 @@ class Chef
# We re-open ChefConfig::Config to add additional settings. Generally,
# everything should go in chef-config so it's shared with whoever uses that.
- # We make execeptions to that rule when:
+ # We make exceptions to that rule when:
# * The functionality isn't likely to be useful outside of Chef
# * The functionality makes use of a dependency we don't want to add to chef-config
class Config
default :event_loggers do
evt_loggers = []
- if ChefConfig.windows? && !(Chef::Platform.windows_server_2003? ||
- Chef::Platform.windows_nano_server?)
+ if ChefUtils.windows?
evt_loggers << :win_evt
end
evt_loggers
@@ -75,7 +75,7 @@ class Chef
# by redefining the config_attr_writer to not warn for these options.
#
# REMOVEME once the warnings for these configurables are removed from Ohai.
- [ :log_level, :log_location ].each do |option|
+ %i{log_level log_location}.each do |option|
config_attr_writer option do |value|
value
end
diff --git a/lib/chef/config_fetcher.rb b/lib/chef/config_fetcher.rb
index ee1b64956a..0e4a7935ac 100644
--- a/lib/chef/config_fetcher.rb
+++ b/lib/chef/config_fetcher.rb
@@ -1,7 +1,7 @@
-require "chef/application"
-require "chef/chef_fs/path_utils"
-require "chef/http/simple"
-require "chef/json_compat"
+require_relative "application"
+require_relative "chef_fs/path_utils"
+require_relative "http/simple"
+require_relative "json_compat"
class Chef
class ConfigFetcher
@@ -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, Chef::Exceptions::DeprecatedExitCode.new)
+ Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message)
end
end
@@ -39,16 +39,16 @@ 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}", Chef::Exceptions::DeprecatedExitCode.new)
+ rescue SocketError, SystemCallError, Net::HTTPClientException => error
+ 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}", Chef::Exceptions::DeprecatedExitCode.new)
+ 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}", Chef::Exceptions::DeprecatedExitCode.new)
+ 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..0c78c9ee19 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 (c) 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..41a0e44c54 100644
--- a/lib/chef/cookbook/chefignore.rb
+++ b/lib/chef/cookbook/chefignore.rb
@@ -1,6 +1,5 @@
-#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,25 +19,27 @@ class Chef
class Cookbook
class Chefignore
- COMMENTS_AND_WHITESPACE = /^\s*(?:#.*)?$/
+ COMMENTS_AND_WHITESPACE = /^\s*(?:#.*)?$/.freeze
attr_reader :ignores
def initialize(ignore_file_or_repo)
- # Check the 'ignore_file_or_repo' path first and then look in the parent directory
+ # Check the 'ignore_file_or_repo' path first and then look in the parent directories till root
# to handle both the chef repo cookbook layout and a standalone cookbook
@ignore_file = find_ignore_file(ignore_file_or_repo)
- @ignore_file = find_ignore_file(File.dirname(ignore_file_or_repo)) unless readable_file_or_symlink?(@ignore_file)
-
@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
@@ -47,27 +48,34 @@ class Chef
def parse_ignore_file
ignore_globs = []
- if readable_file_or_symlink?(@ignore_file)
+ if @ignore_file && readable_file_or_symlink?(@ignore_file)
File.foreach(@ignore_file) do |line|
- ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE
+ ignore_globs << line.strip unless COMMENTS_AND_WHITESPACE.match?(line)
end
else
- Chef::Log.debug("No chefignore file found at #@ignore_file no files will be ignored")
+ Chef::Log.debug("No chefignore file found. No files will be ignored!")
end
ignore_globs
end
+ # Lookup of chefignore file till the root dir of the provided path.
+ # If file refer then lookup the parent dir till the root.
+ # eg. path: /var/.chef/cookbook_name
+ # Lookup at '/var/.chef/cookbook_name/chefignore', '/var/.chef/chefignore' '/var/chefignore' and '/chefignore' until exist
def find_ignore_file(path)
- if File.basename(path) =~ /chefignore/
- path
- else
- File.join(path, "chefignore")
+ Pathname.new(path).ascend do |dir|
+ next unless dir.directory?
+
+ file = dir.join("chefignore")
+ return file.expand_path.to_s if file.exist?
end
+
+ nil
end
def readable_file_or_symlink?(path)
- File.exist?(@ignore_file) && File.readable?(@ignore_file) &&
- (File.file?(@ignore_file) || File.symlink?(@ignore_file))
+ File.exist?(path) && File.readable?(path) &&
+ (File.file?(path) || File.symlink?(path))
end
end
end
diff --git a/lib/chef/cookbook/cookbook_collection.rb b/lib/chef/cookbook/cookbook_collection.rb
index d06b8fd042..d8dae875e0 100644
--- a/lib/chef/cookbook/cookbook_collection.rb
+++ b/lib/chef/cookbook/cookbook_collection.rb
@@ -1,7 +1,6 @@
-#--
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +16,8 @@
# limitations under the License.
#
-require "chef/mash"
-require "chef/cookbook/gem_installer"
+require_relative "../mash"
+require_relative "gem_installer"
class Chef
# == Chef::CookbookCollection
@@ -47,10 +46,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 af8b2e043e..faed509321 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -1,28 +1,41 @@
-
-require "chef/cookbook_version"
-require "chef/cookbook/chefignore"
-require "chef/cookbook/metadata"
-require "chef/util/path_helper"
-require "find"
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../cookbook_version"
+require_relative "chefignore"
+require_relative "metadata"
+require_relative "../util/path_helper"
+require "find" unless defined?(Find.find)
class Chef
class Cookbook
+ # This class is only used directly from the Chef::CookbookLoader and from chef-fs,
+ # so it only affects legacy-mode chef-client runs and knife. It is not used by
+ # server or zolo/zero modes.
+ #
+ # This seems to be mostly a glorified factory method for creating CookbookVersion
+ # objects now, with creating Metadata objects bolted onto the side? It used
+ # to be also responsible for the merging of multiple objects when creating
+ # shadowed/merged cookbook versions from multiple sources. It also handles
+ # Chefignore files.
+ #
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
@@ -35,25 +48,14 @@ class Chef
def initialize(path, chefignore = nil)
@cookbook_path = File.expand_path( path ) # cookbook_path from which this was loaded
- # We keep a list of all cookbook paths that have been merged in
- @cookbook_paths = [ cookbook_path ]
@inferred_cookbook_name = File.basename( path )
@chefignore = chefignore
@metadata = nil
- @relative_path = /#{Regexp.escape(@cookbook_path)}\/(.+)$/
+ @relative_path = %r{#{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 = []
@@ -63,18 +65,24 @@ class Chef
# Load the cookbook. Raises an error if the cookbook_path given to the
# constructor doesn't point to a valid cookbook.
def load!
- file_paths_map = load
+ metadata # force lazy evaluation to occur
+
+ # re-raise any exception that occurred when reading the metadata
+ raise_metadata_error!
+
+ load_all_files
+
+ remove_ignored_files
if empty?
raise Exceptions::CookbookNotFoundInRepo, "The directory #{cookbook_path} does not contain a cookbook"
end
- file_paths_map
+
+ cookbook_settings
end
- # Load the cookbook. Does not raise an error if given a non-cookbook
- # directory as the cookbook_path. This behavior is provided for
- # compatibility, it is recommended to use #load! instead.
def load
+ Chef.deprecated(:internal_api, "Chef::Cookbook::CookbookVersionLoader's load method is deprecated. Please use load! instead.")
metadata # force lazy evaluation to occur
# re-raise any exception that occurred when reading the metadata
@@ -84,76 +92,26 @@ 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
- @cookbook_settings
+
+ cookbook_settings
end
alias :load_cookbooks :load
- def metadata_filenames
- return @metadata_filenames unless @metadata_filenames.empty?
- if File.exists?(File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE))
- @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"))
- @metadata_filenames << File.join(cookbook_path, "metadata.json")
- elsif @uploaded_cookbook_version_file
- @metadata_filenames << @uploaded_cookbook_version_file
- end
-
- # Set frozen based on .uploaded-cookbook-version.json
- set_frozen
- @metadata_filenames
- end
-
def cookbook_version
return nil if empty?
- Chef::CookbookVersion.new(cookbook_name, *cookbook_paths).tap do |c|
+ Chef::CookbookVersion.new(cookbook_name, cookbook_path).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
+ c.freeze_version if frozen
end
end
- def cookbook_name
- # The `name` attribute is now required in metadata, so
- # inferred_cookbook_name generally should not be used. Per CHEF-2923,
- # we have to not raise errors in cookbook metadata immediately, so that
- # users can still `knife cookbook upload some-cookbook` when an
- # unrelated cookbook has an error in its metadata. This situation
- # could prevent us from reading the `name` attribute from the metadata
- # entirely, but the name is used as a hash key in CookbookLoader, so we
- # fall back to the inferred name here.
- (metadata.name || @inferred_cookbook_name).to_sym
- end
-
# Generates the Cookbook::Metadata object
def metadata
return @metadata unless @metadata.nil?
@@ -164,7 +122,7 @@ class Chef
case metadata_file
when /\.rb$/
apply_ruby_metadata(metadata_file)
- when @uploaded_cookbook_version_file
+ when uploaded_cookbook_version_file
apply_json_cookbook_version_metadata(metadata_file)
when /\.json$/
apply_json_metadata(metadata_file)
@@ -185,36 +143,59 @@ class Chef
@metadata
end
+ def cookbook_name
+ # The `name` attribute is now required in metadata, so
+ # inferred_cookbook_name generally should not be used. Per CHEF-2923,
+ # we have to not raise errors in cookbook metadata immediately, so that
+ # users can still `knife cookbook upload some-cookbook` when an
+ # unrelated cookbook has an error in its metadata. This situation
+ # could prevent us from reading the `name` attribute from the metadata
+ # entirely, but the name is used as a hash key in CookbookLoader, so we
+ # fall back to the inferred name here.
+ (metadata.name || inferred_cookbook_name).to_sym
+ end
+
+ private
+
+ def metadata_filenames
+ return @metadata_filenames unless @metadata_filenames.empty?
+
+ if File.exists?(File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE))
+ @uploaded_cookbook_version_file = File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE)
+ end
+
+ 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
+
+ # Set frozen based on .uploaded-cookbook-version.json
+ set_frozen
+ @metadata_filenames
+ end
+
def raise_metadata_error!
- raise @metadata_error unless @metadata_error.nil?
+ raise metadata_error unless metadata_error.nil?
+
# Metadata won't be valid if the cookbook is empty. If the cookbook is
# actually empty, a metadata error here would be misleading, so don't
# raise it (if called by #load!, a different error is raised).
if !empty? && !metadata.valid?
- message = "Cookbook loaded at path(s) [#{@cookbook_paths.join(', ')}] has invalid metadata: #{metadata.errors.join('; ')}"
+ message = "Cookbook loaded at path [#{cookbook_path}] has invalid metadata: #{metadata.errors.join("; ")}"
raise Exceptions::MetadataNotValid, message
end
false
end
def empty?
- cookbook_settings.values.all? { |files_hash| files_hash.empty? } && metadata_filenames.size == 0
- end
-
- def merge!(other_cookbook_loader)
- other_cookbook_settings = other_cookbook_loader.cookbook_settings
- cookbook_settings.each do |file_type, file_list|
- file_list.merge!(other_cookbook_settings[file_type])
- end
- metadata_filenames.concat(other_cookbook_loader.metadata_filenames)
- @cookbook_paths += other_cookbook_loader.cookbook_paths
- @frozen = true if other_cookbook_loader.frozen
- @metadata = nil # reset metadata so it gets reloaded and all metadata files applied.
- self
+ cookbook_settings.values.all?(&:empty?) && metadata_filenames.size == 0
end
def chefignore
- @chefignore ||= Chefignore.new(File.basename(cookbook_path))
+ @chefignore ||= Chefignore.new(cookbook_path)
end
# Enumerate all the files in a cookbook and assign the resulting list to
@@ -253,51 +234,6 @@ class Chef
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)
@@ -305,40 +241,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 cookbook 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
@@ -347,7 +277,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..a4a6711270 100644
--- a/lib/chef/cookbook/file_system_file_vendor.rb
+++ b/lib/chef/cookbook/file_system_file_vendor.rb
@@ -1,7 +1,6 @@
-#--
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +16,7 @@
# limitations under the License.
#
-require "chef/cookbook/file_vendor"
+require_relative "file_vendor"
class Chef
class Cookbook
@@ -28,28 +27,28 @@ class Chef
# and throws the rest away then re-builds the list of files on the
# disk. This is due to the manifest not having the on-disk file
# locations, since in the chef-client case, that information is
- # non-sensical.
+ # nonsensical.
class FileSystemFileVendor < FileVendor
attr_reader :cookbook_name
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.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/file_vendor.rb b/lib/chef/cookbook/file_vendor.rb
index f849f79296..2eecf0a789 100644
--- a/lib/chef/cookbook/file_vendor.rb
+++ b/lib/chef/cookbook/file_vendor.rb
@@ -1,7 +1,6 @@
-#
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,6 +54,7 @@ class Chef
if @vendor_class.nil?
raise "Must configure FileVendor to use a specific implementation before creating an instance"
end
+
@vendor_class.new(manifest, @initialization_options)
end
diff --git a/lib/chef/cookbook/gem_installer.rb b/lib/chef/cookbook/gem_installer.rb
index df73ce3ee4..d7c18627de 100644
--- a/lib/chef/cookbook/gem_installer.rb
+++ b/lib/chef/cookbook/gem_installer.rb
@@ -1,5 +1,4 @@
-#--
-# Copyright:: Copyright (c) 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +14,8 @@
# limitations under the License.
#
-require "tmpdir"
-require "chef/mixin/shell_out"
+require "tmpdir" unless defined?(Dir.mktmpdir)
+require_relative "../mixin/shell_out"
class Chef
class Cookbook
@@ -36,10 +35,20 @@ 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|
+ if cookbook_gems[args.first].last.is_a?(Hash)
+ args << {} unless args.last.is_a?(Hash)
+ args.last.merge!(cookbook_gems[args.first].pop) do |key, v1, v2|
+ raise Chef::Exceptions::GemRequirementConflict.new(args.first, key, v1, v2) if v1 != v2
+
+ v2
+ end
+ end
+ cookbook_gems[args.first] += args[1..]
+ end
end
events.cookbook_gem_start(cookbook_gems)
@@ -48,15 +57,22 @@ 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://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))
- so = shell_out!("bundle install", cwd: dir, env: { "PATH" => path_with_prepended_ruby_bin })
- Chef::Log.info(so.stdout)
+ Chef::Log.trace("generated Gemfile contents:")
+ Chef::Log.trace(IO.read(tf.path))
+ # Skip installation only if Chef::Config[:skip_gem_metadata_installation] option is true
+ unless Chef::Config[:skip_gem_metadata_installation]
+ # Add additional options to bundle install
+ cmd = [ "bundle", "install", Chef::Config[:gem_installer_bundler_options] ]
+ so = shell_out!(cmd, cwd: dir, env: { "PATH" => path_with_prepended_ruby_bin })
+ Chef::Log.info(so.stdout)
+ end
end
end
Gem.clear_paths
diff --git a/lib/chef/cookbook/manifest_v0.rb b/lib/chef/cookbook/manifest_v0.rb
new file mode 100644
index 0000000000..b2b39f32f3
--- /dev/null
+++ b/lib/chef/cookbook/manifest_v0.rb
@@ -0,0 +1,74 @@
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 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 "../json_compat"
+require_relative "../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 }.freeze
+
+ class << self
+
+ def 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"]}"
+ memo << file
+ end
+ response.delete(segment)
+ memo
+ end
+ response
+ end
+
+ def to_h(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]
+ 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
+
+ alias_method :to_hash, :to_h
+ end
+ end
+ end
+end
diff --git a/lib/chef/cookbook/manifest_v2.rb b/lib/chef/cookbook/manifest_v2.rb
new file mode 100644
index 0000000000..a502142e17
--- /dev/null
+++ b/lib/chef/cookbook/manifest_v2.rb
@@ -0,0 +1,45 @@
+# Copyright:: Copyright (c) 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 "../json_compat"
+require_relative "../mixin/versioned_api"
+
+class Chef
+ class Cookbook
+ class ManifestV2
+ extend Chef::Mixin::VersionedAPI
+
+ minimum_api_version 2
+
+ class << self
+ def from_hash(hash)
+ Chef::Log.trace "processing manifest: #{hash}"
+ Mash.new hash
+ end
+
+ def to_h(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
+
+ alias_method :to_hash, :to_h
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb
index ab83da9e55..7f6d972771 100644
--- a/lib/chef/cookbook/metadata.rb
+++ b/lib/chef/cookbook/metadata.rb
@@ -1,8 +1,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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +17,15 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/mash"
-require "chef/mixin/from_file"
-require "chef/mixin/params_validate"
-require "chef/log"
-require "chef/version_class"
-require "chef/version_constraint"
-require "chef/version_constraint/platform"
-require "chef/json_compat"
+require_relative "../exceptions"
+require_relative "../mash"
+require_relative "../mixin/from_file"
+require_relative "../mixin/params_validate"
+require_relative "../log"
+require_relative "../version_class"
+require_relative "../version_constraint"
+require_relative "../version_constraint/platform"
+require_relative "../json_compat"
class Chef
class Cookbook
@@ -44,13 +43,7 @@ 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
@@ -59,37 +52,26 @@ class Chef
CHEF_VERSIONS = "chef_versions".freeze
OHAI_VERSIONS = "ohai_versions".freeze
GEMS = "gems".freeze
+ EAGER_LOAD_LIBRARIES = "eager_load_libraries".freeze
+
+ COMPARISON_FIELDS = %i{name description long_description maintainer
+ maintainer_email license platforms dependencies
+ providing recipes version source_url issues_url
+ privacy chef_versions ohai_versions gems
+ eager_load_libraries}.freeze
- COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer,
- :maintainer_email, :license, :platforms, :dependencies,
- :recommendations, :suggestions, :conflicting, :providing,
- :replacing, :attributes, :groupings, :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 }
+ VERSION_CONSTRAINTS = { depends: DEPENDENCIES,
+ provides: PROVIDING,
+ chef_version: CHEF_VERSIONS,
+ ohai_version: OHAI_VERSIONS }.freeze
include Chef::Mixin::ParamsValidate
include Chef::Mixin::FromFile
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 +97,12 @@ 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 = ""
@@ -135,6 +111,7 @@ class Chef
@chef_versions = []
@ohai_versions = []
@gems = []
+ @eager_load_libraries = true
@errors = []
end
@@ -182,7 +159,7 @@ class Chef
set_or_return(
:maintainer,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -197,7 +174,7 @@ class Chef
set_or_return(
:maintainer_email,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -212,7 +189,7 @@ class Chef
set_or_return(
:license,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -227,7 +204,7 @@ class Chef
set_or_return(
:description,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -242,7 +219,7 @@ class Chef
set_or_return(
:long_description,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -273,7 +250,7 @@ class Chef
set_or_return(
:name,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -306,64 +283,14 @@ 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)
@dependencies[cookbook] = constraint.to_s
end
- @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]
+ @dependencies[cookbook]
end
# Adds a recipe, definition, or resource provided by this cookbook.
@@ -387,22 +314,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.
@@ -436,6 +347,25 @@ class Chef
@gems
end
+ # Metadata DSL to control the behavior of library loading.
+ #
+ # Can be set to:
+ #
+ # true - libraries are eagerly loaded in alphabetical order (backcompat)
+ # false - libraries are not eagerly loaded, the libraries dir is added to the LOAD_PATH
+ # String - a file or glob pattern to eagerly load, otherwise it is treated like `false`
+ # Array<String> - array of files or globs to eagerly load, otherwise it is treated like `false`
+ #
+ # @params arg [Array,String,TrueClass,FalseClass]
+ # @params [Array,TrueClass,FalseClass]
+ def eager_load_libraries(arg = nil)
+ set_or_return(
+ :eager_load_libraries,
+ arg,
+ kind_of: [ Array, String, TrueClass, FalseClass ]
+ )
+ end
+
# Adds a description for a recipe.
#
# === Parameters
@@ -461,8 +391,8 @@ class Chef
def recipes_from_cookbook_version(cookbook)
cookbook.fully_qualified_recipe_names.map do |recipe_name|
unqualified_name =
- if recipe_name =~ /::default$/
- self.name.to_s
+ if /::default$/.match?(recipe_name)
+ name.to_s
else
recipe_name
end
@@ -474,67 +404,10 @@ class Chef
end
end
- # Adds an attribute that a user needs to configure for this cookbook. Takes
- # a name (with the / notation for a nested attribute), followed by any of
- # these options
- #
- # display_name<String>:: What a UI should show for this attribute
- # description<String>:: A hint as to what this attr is for
- # choice<Array>:: An array of choices to present to the user.
- # calculated<Boolean>:: If true, the default value is calculated by the recipe and cannot be displayed.
- # type<String>:: "string" or "array" - default is "string" ("hash" is supported for backwards compatibility)
- # required<String>:: Whether this attr is 'required', 'recommended' or 'optional' - default 'optional' (true/false values also supported for backwards compatibility)
- # recipes<Array>:: An array of recipes which need this attr set.
- # default<String>,<Array>,<Hash>:: The default value
- #
- # === Parameters
- # name<String>:: The name of the attribute ('foo', or 'apache2/log_dir')
- # options<Hash>:: The description of the options
- #
- # === Returns
- # options<Hash>:: Returns the current options hash
- def attribute(name, options)
- validate(
- options,
- {
- :display_name => { :kind_of => String },
- :description => { :kind_of => String },
- :choice => { :kind_of => [ Array ], :default => [] },
- :calculated => { :equal_to => [ true, false ], :default => false },
- :type => { :equal_to => %w{string array hash symbol boolean numeric}, :default => "string" },
- :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" },
- :recipes => { :kind_of => [ Array ], :default => [] },
- :default => { :kind_of => [ String, Array, Hash, Symbol, Numeric, TrueClass, FalseClass ] },
- :source_url => { :kind_of => String },
- :issues_url => { :kind_of => String },
- :privacy => { :kind_of => [ TrueClass, FalseClass ] },
- }
- )
- options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil?
- validate_choice_array(options)
- validate_calculated_default_rule(options)
- validate_choice_default_rule(options)
-
- @attributes[name] = options
- @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
- # objets, so we have to write our own rendering method.
+ # Gem::Dependency#to_s is not useful, and there is no #to_json defined on it or its component
+ # objects, so we have to write our own rendering method.
#
# [ Gem::Dependency.new(">= 12.5"), Gem::Dependency.new(">= 11.18.0", "< 12.0") ]
#
@@ -565,84 +438,76 @@ class Chef
end
end
- def to_hash
+ def to_h
{
- 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,
+ 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,
+ EAGER_LOAD_LIBRARIES => eager_load_libraries,
}
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def self.from_hash(o)
- cm = self.new()
+ cm = new
cm.from_hash(o)
cm
end
def from_hash(o)
- @name = o[NAME] if o.has_key?(NAME)
- @description = o[DESCRIPTION] if o.has_key?(DESCRIPTION)
- @long_description = o[LONG_DESCRIPTION] if o.has_key?(LONG_DESCRIPTION)
- @maintainer = o[MAINTAINER] if o.has_key?(MAINTAINER)
- @maintainer_email = o[MAINTAINER_EMAIL] if o.has_key?(MAINTAINER_EMAIL)
- @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)
- @issues_url = o[ISSUES_URL] if o.has_key?(ISSUES_URL)
- @privacy = o[PRIVACY] if o.has_key?(PRIVACY)
- @chef_versions = gem_requirements_from_array("chef", o[CHEF_VERSIONS]) if o.has_key?(CHEF_VERSIONS)
- @ohai_versions = gem_requirements_from_array("ohai", o[OHAI_VERSIONS]) if o.has_key?(OHAI_VERSIONS)
- @gems = o[GEMS] if o.has_key?(GEMS)
+ @name = o[NAME] if o.key?(NAME)
+ @description = o[DESCRIPTION] if o.key?(DESCRIPTION)
+ @long_description = o[LONG_DESCRIPTION] if o.key?(LONG_DESCRIPTION)
+ @maintainer = o[MAINTAINER] if o.key?(MAINTAINER)
+ @maintainer_email = o[MAINTAINER_EMAIL] if o.key?(MAINTAINER_EMAIL)
+ @license = o[LICENSE] if o.key?(LICENSE)
+ @platforms = o[PLATFORMS] if o.key?(PLATFORMS)
+ @dependencies = handle_incorrect_constraints(o[DEPENDENCIES]) if o.key?(DEPENDENCIES)
+ @providing = o[PROVIDING] if o.key?(PROVIDING)
+ @recipes = o[RECIPES] if o.key?(RECIPES)
+ @version = o[VERSION] if o.key?(VERSION)
+ @source_url = o[SOURCE_URL] if o.key?(SOURCE_URL)
+ @issues_url = o[ISSUES_URL] if o.key?(ISSUES_URL)
+ @privacy = o[PRIVACY] if o.key?(PRIVACY)
+ @chef_versions = gem_requirements_from_array("chef", o[CHEF_VERSIONS]) if o.key?(CHEF_VERSIONS)
+ @ohai_versions = gem_requirements_from_array("ohai", o[OHAI_VERSIONS]) if o.key?(OHAI_VERSIONS)
+ @gems = o[GEMS] if o.key?(GEMS)
+ @eager_load_libraries = o[EAGER_LOAD_LIBRARIES] if o.key?(EAGER_LOAD_LIBRARIES)
self
end
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)
o = Chef::JSONCompat.from_json(json_str)
- metadata = new()
+ metadata = new
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
@@ -666,10 +531,29 @@ class Chef
set_or_return(
:source_url,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
+ # This method translates version constraint strings from
+ # cookbooks with the old format.
+ #
+ # Before we began respecting version constraints, we allowed
+ # multiple constraints to be placed on cookbooks, as well as the
+ # << and >> operators, which are now just < and >. For
+ # specifications with more than one constraint, we return an
+ # empty array (otherwise, we're silently abiding only part of
+ # the contract they have specified to us). If there is only one
+ # constraint, we are replacing the old << and >> with the new <
+ # and >.
+ def handle_incorrect_constraints(specification)
+ specification.inject(Mash.new) do |acc, (cb, constraints)|
+ constraints = Array(constraints)
+ acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first
+ acc
+ end
+ end
+
# Sets the cookbook's issues URL, or returns it.
#
# === Parameters
@@ -681,7 +565,7 @@ class Chef
set_or_return(
:issues_url,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -698,14 +582,14 @@ class Chef
set_or_return(
:privacy,
arg,
- :kind_of => [ TrueClass, FalseClass ]
+ kind_of: [ TrueClass, FalseClass ]
)
end
# 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,13 +599,21 @@ 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
+ 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
@@ -735,6 +627,7 @@ class Chef
def gem_dep_matches?(what, version, *deps)
# always match if we have no chef_version at all
return true unless deps.length > 0
+
# match if we match any of the chef_version lines
deps.any? { |dep| dep.match?(what, version) }
end
@@ -751,15 +644,15 @@ class Chef
elsif version_constraints.size == 1
version_constraints.first
else
- msg = <<-OBSOLETED
-The dependency specification syntax you are using is no longer valid. You may not
-specify more than one version constraint for a particular cookbook.
-Consult https://docs.chef.io/config_rb_metadata.html for the updated syntax.
-
-Called by: #{caller_name} '#{dep_name}', #{version_constraints.map { |vc| vc.inspect }.join(", ")}
-Called from:
-#{caller[0...5].map { |line| " " + line }.join("\n")}
-OBSOLETED
+ msg = <<~OBSOLETED
+ The dependency specification syntax you are using is no longer valid. You may not
+ specify more than one version constraint for a particular cookbook.
+ Consult https://docs.chef.io/config_rb_metadata/ for the updated syntax.
+
+ Called by: #{caller_name} '#{dep_name}', #{version_constraints.map(&:inspect).join(", ")}
+ Called from:
+ #{caller[0...5].map { |line| " " + line }.join("\n")}
+ OBSOLETED
raise Exceptions::ObsoleteDependencySyntax, msg
end
end
@@ -769,16 +662,17 @@ OBSOLETED
rescue Chef::Exceptions::InvalidVersionConstraint => e
Log.debug(e)
- msg = <<-INVALID
-The version constraint syntax you are using is not valid. If you recently
-upgraded to Chef 0.10.0, be aware that you no may longer use "<<" and ">>" for
-'less than' and 'greater than'; use '<' and '>' instead.
-Consult https://docs.chef.io/config_rb_metadata.html for more information.
-
-Called by: #{caller_name} '#{dep_name}', '#{constraint_str}'
-Called from:
-#{caller[0...5].map { |line| " " + line }.join("\n")}
-INVALID
+ msg = <<~INVALID
+ The version constraint syntax you are using is not valid. If you recently
+ upgraded from Chef Infra releases before 0.10, be aware that you no may
+ longer use "<<" and ">>" for 'less than' and 'greater than'; use '<' and
+ '>' instead. Consult https://docs.chef.io/config_rb_metadata/ for more
+ information.
+
+ Called by: #{caller_name} '#{dep_name}', '#{constraint_str}'
+ Called from:
+ #{caller[0...5].map { |line| " " + line }.join("\n")}
+ INVALID
raise Exceptions::InvalidVersionConstraint, msg
end
@@ -789,9 +683,9 @@ INVALID
# === Parameters
# arry<Array>:: An array to be validated
def validate_string_array(arry)
- if arry.kind_of?(Array)
+ if arry.is_a?(Array)
arry.each do |choice|
- validate( { :choice => choice }, { :choice => { :kind_of => String } } )
+ validate( { choice: choice }, { choice: { kind_of: String } } )
end
end
end
@@ -802,7 +696,7 @@ INVALID
# === Parameters
# opts<Hash>:: The options hash
def validate_choice_array(opts)
- if opts[:choice].kind_of?(Array)
+ if opts[:choice].is_a?(Array)
case opts[:type]
when "string"
validator = [ String ]
@@ -819,7 +713,7 @@ INVALID
end
opts[:choice].each do |choice|
- validate( { :choice => choice }, { :choice => { :kind_of => validator } } )
+ validate( { choice: choice }, { choice: { kind_of: validator } } )
end
end
end
@@ -854,35 +748,15 @@ 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
-
- # This method translates version constraint strings from
- # cookbooks with the old format.
- #
- # Before we began respecting version constraints, we allowed
- # multiple constraints to be placed on cookbooks, as well as the
- # << and >> operators, which are now just < and >. For
- # specifications with more than one constraint, we return an
- # empty array (otherwise, we're silently abiding only part of
- # the contract they have specified to us). If there is only one
- # constraint, we are replacing the old << and >> with the new <
- # and >.
- def handle_deprecated_constraints(specification)
- specification.inject(Mash.new) do |acc, (cb, constraints)|
- constraints = Array(constraints)
- acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first.gsub(/>>/, ">").gsub(/<</, "<")
- acc
- end
- end
-
end
#== Chef::Cookbook::MinimalMetadata
diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb
index e63d094dc4..a6a6e96e06 100644
--- a/lib/chef/cookbook/remote_file_vendor.rb
+++ b/lib/chef/cookbook/remote_file_vendor.rb
@@ -1,6 +1,5 @@
-#
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +15,7 @@
# limitations under the License.
#
-require "chef/cookbook/file_vendor"
+require_relative "file_vendor"
class Chef
class Cookbook
@@ -30,7 +29,7 @@ class Chef
def initialize(manifest, rest)
@manifest = manifest
- @cookbook_name = @manifest[:cookbook_name] || @manifest[:name]
+ @cookbook_name = @manifest.name
@rest = rest
end
@@ -38,14 +37,15 @@ class Chef
# Chef::Config.cookbook_path file hierarchy for the requested
# file.
def get_filename(filename)
- if filename =~ /([^\/]+)\/(.+)$/
+ if filename =~ %r{([^/]+)/(.+)$}
segment = $1
else
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"])
@@ -55,7 +55,7 @@ class Chef
validate_cached_copy(cache_filename)
current_checksum = nil
- if Chef::FileCache.has_key?(cache_filename)
+ if Chef::FileCache.key?(cache_filename)
current_checksum = Chef::CookbookVersion.checksum_cookbook_file(Chef::FileCache.load(cache_filename, false))
end
@@ -65,17 +65,15 @@ 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)
-
# return the filename, not the contents (second argument= false)
- full_path_cache_filename
+ Chef::FileCache.load(cache_filename, false)
end
def validate_cached_copy(cache_filename)
diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb
index bb44bc3d5c..53e874d0e8 100644
--- a/lib/chef/cookbook/synchronizer.rb
+++ b/lib/chef/cookbook/synchronizer.rb
@@ -1,7 +1,23 @@
-require "chef/client"
-require "chef/util/threaded_job_queue"
-require "chef/server_api"
-require "singleton"
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../client"
+require_relative "../util/threaded_job_queue"
+require_relative "../server_api"
+require "singleton" unless defined?(Singleton)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
@@ -31,6 +47,7 @@ class Chef
end
def reset!
+ @skip_removal = nil
@valid_cache_entries = {}
end
@@ -48,7 +65,7 @@ class Chef
# manifest.
cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename|
unless @valid_cache_entries[cache_filename]
- Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.")
+ Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by #{ChefUtils::Dist::Infra::CLIENT}.")
cache.delete(cache_filename)
end
end
@@ -62,22 +79,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 +113,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
@@ -116,7 +139,7 @@ class Chef
end
def files_by_cookbook
- files.group_by { |file| file.cookbook }
+ files.group_by(&:cookbook)
end
def files_remaining_by_cookbook
@@ -137,17 +160,18 @@ class Chef
end
# Synchronizes all the cookbooks from the chef-server.
- #)
+ # )
# === Returns
# 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.info("Loading cookbooks [#{cookbooks.map { |ckbk| ckbk.name + "@" + ckbk.version }.join(", ")}]")
+ 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)
@@ -162,8 +186,10 @@ class Chef
@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,16 +202,15 @@ 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
# (if we have an override run_list we may not want to do this)
def remove_old_cookbooks
cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file|
- cache_file =~ /^cookbooks\/([^\/]+)\//
+ cache_file =~ %r{^cookbooks/([^/]+)/}
unless have_cookbook?($1)
Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
cache.delete(cache_file)
@@ -197,8 +222,9 @@ class Chef
# remove deleted files in cookbooks that are being used on the node
def remove_deleted_files
cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file|
- md = cache_file.match(/^cookbooks\/([^\/]+)\/([^\/]+)\/(.*)/)
+ md = cache_file.match(%r{^cookbooks/([^/]+)/([^/]+)/(.*)})
next unless md
+
( cookbook_name, segment, file ) = md[1..3]
if have_cookbook?(cookbook_name)
manifest_segment = cookbook_segment(cookbook_name, segment)
@@ -229,10 +255,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 +290,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,11 +298,9 @@ 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
- if cache.has_key?(local_path)
+ return true if Chef::Config[:skip_cookbook_sync]
+
+ if cache.key?(local_path)
current_checksum = CookbookVersion.checksum_cookbook_file(cache.load(local_path, false))
expected_checksum == current_checksum
else
diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb
index f8559433dc..8b593eea81 100644
--- a/lib/chef/cookbook/syntax_check.rb
+++ b/lib/chef/cookbook/syntax_check.rb
@@ -1,6 +1,5 @@
-#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +15,12 @@
# limitations under the License.
#
-require "pathname"
-require "stringio"
-require "erubis"
-require "chef/mixin/shell_out"
-require "chef/mixin/checksum"
-require "chef/util/path_helper"
+require "pathname" unless defined?(Pathname)
+require "stringio" unless defined?(StringIO)
+require "erubis" unless defined?(Erubis)
+require_relative "../mixin/shell_out"
+require_relative "../mixin/checksum"
+require_relative "../util/path_helper"
class Chef
class Cookbook
@@ -63,6 +62,7 @@ class Chef
def ensure_cache_path_created
return true if @cache_path_created
+
FileUtils.mkdir_p(cache_path)
@cache_path_created = true
end
@@ -84,10 +84,13 @@ class Chef
# If no +cookbook_path+ is given, +Chef::Config.cookbook_path+ is used.
def self.for_cookbook(cookbook_name, cookbook_path = nil)
cookbook_path ||= Chef::Config.cookbook_path
- unless cookbook_path
- raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given"
+
+ Array(cookbook_path).each do |entry|
+ path = File.expand_path(File.join(entry, cookbook_name.to_s))
+ return new(path) if Dir.exist?(path)
end
- new(File.join(cookbook_path, cookbook_name.to_s))
+
+ raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given"
end
# Create a new SyntaxCheck object
@@ -110,21 +113,20 @@ 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
path = Chef::Util::PathHelper.escape_glob_dir(cookbook_path)
files = Dir[File.join(path, "**", "*.rb")]
files = remove_ignored_files(files)
- files = remove_uninteresting_ruby_files(files)
- files
+ remove_uninteresting_ruby_files(files)
end
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 +141,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
@@ -158,6 +160,7 @@ class Chef
def validate_ruby_files
untested_ruby_files.each do |ruby_file|
return false unless validate_ruby_file(ruby_file)
+
validated(ruby_file)
end
end
@@ -165,17 +168,18 @@ class Chef
def validate_templates
untested_template_files.each do |template|
return false unless validate_template(template)
+
validated(template)
end
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..f7efb2646e 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 (c) Chef Software Inc.
# Copyright:: Copyright 2009-2016, Daniel DeLeo
# License:: Apache License, Version 2.0
#
@@ -18,114 +18,90 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/config"
-require "chef/exceptions"
-require "chef/cookbook/cookbook_version_loader"
-require "chef/cookbook_version"
-require "chef/cookbook/chefignore"
-require "chef/cookbook/metadata"
+require_relative "config"
+require_relative "exceptions"
+require_relative "cookbook/cookbook_version_loader"
+require_relative "cookbook_version"
+require_relative "cookbook/chefignore"
+require_relative "cookbook/metadata"
-#
-# CookbookLoader class loads the cookbooks lazily as read
-#
class Chef
+ # This class is used by knife, cheffs and legacy chef-solo modes. It is not used by the server mode
+ # of chef-client or zolo/zero modes.
+ #
+ # This class implements orchestration around producing a single cookbook_version for a cookbook or
+ # loading a Mash of all cookbook_versions, using the cookbook_version_loader class, and doing
+ # lazy-access and memoization to only load each cookbook once on demand.
+ #
+ # This implements a key-value style each which makes it appear to be a Hash of String => CookbookVersion
+ # pairs where the String is the cookbook name. The use of Enumerable combined with the Hash-style
+ # each is likely not entirely sane.
+ #
+ # This object is also passed and injected into the CookbookCollection object where it is converted
+ # to a Mash that looks almost exactly like the cookbook_by_name Mash in this object.
+ #
class CookbookLoader
+ # @return [Array<String>] the array of repo paths containing cookbook dirs
+ attr_reader :repo_paths
- attr_reader :cookbooks_by_name
- attr_reader :merged_cookbooks
- attr_reader :cookbook_paths
- attr_reader :metadata
-
+ # XXX: this is highly questionable combined with the Hash-style each method
include Enumerable
+ # @param repo_paths [Array<String>] the array of repo paths containing cookbook dirs
def initialize(*repo_paths)
- repo_paths = repo_paths.flatten
- raise ArgumentError, "You must specify at least one cookbook repo path" if repo_paths.empty?
- @cookbooks_by_name = Mash.new
- @loaded_cookbooks = {}
- @metadata = Mash.new
- @cookbooks_paths = Hash.new { |h, k| h[k] = [] } # for deprecation warnings
- @chefignores = {}
- @repo_paths = repo_paths.map do |repo_path|
- File.expand_path(repo_path)
- end
-
- @preloaded_cookbooks = false
- @loaders_by_name = {}
-
- # Used to track which cookbooks appear in multiple places in the cookbook repos
- # and are merged in to a single cookbook by file shadowing. This behavior is
- # deprecated, so users of this class may issue warnings to the user by checking
- # this variable
- @merged_cookbooks = []
+ @repo_paths = repo_paths.flatten.compact.map { |p| File.expand_path(p) }
+ raise ArgumentError, "You must specify at least one cookbook repo path" if @repo_paths.empty?
end
- def merged_cookbook_paths # for deprecation warnings
- merged_cookbook_paths = {}
- @merged_cookbooks.each { |c| merged_cookbook_paths[c] = @cookbooks_paths[c] }
- merged_cookbook_paths
+ # The primary function of this class is to build this Mash mapping cookbook names as a string to
+ # the CookbookVersion objects for them. Callers must call "load_cookbooks" first.
+ #
+ # @return [Mash<String, Chef::CookbookVersion>]
+ def cookbooks_by_name
+ @cookbooks_by_name ||= Mash.new
end
- def warn_about_cookbook_shadowing
- unless merged_cookbooks.empty?
- Chef::Log.deprecation "The cookbook(s): #{merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path. " +
- "A composite version has been compiled. This has been deprecated since 0.10.4, in Chef 13 this behavior will be REMOVED."
- end
+ # This class also builds a mapping of cookbook names to their Metadata objects. Callers must call
+ # "load_cookbooks" first.
+ #
+ # @return [Mash<String, Chef::Cookbook::Metadata>]
+ def metadata
+ @metadata ||= Mash.new
end
- # Will be removed when cookbook shadowing is removed, do NOT create new consumers of this API.
+ # Loads all cookbooks across all repo_paths
#
- # @api private
- def load_cookbooks_without_shadow_warning
- preload_cookbooks
- @loaders_by_name.each do |cookbook_name, _loaders|
+ # @return [Mash<String, Chef::CookbookVersion>] the cookbooks_by_name Mash
+ def load_cookbooks
+ cookbook_version_loaders.each_key do |cookbook_name|
load_cookbook(cookbook_name)
end
- @cookbooks_by_name
- end
-
- def load_cookbooks
- ret = load_cookbooks_without_shadow_warning
- warn_about_cookbook_shadowing
- ret
+ cookbooks_by_name
end
+ # Loads a single cookbook by its name.
+ #
+ # @param [String]
+ # @return [Chef::CookbookVersion]
def load_cookbook(cookbook_name)
- preload_cookbooks
-
- return @cookbooks_by_name[cookbook_name] if @cookbooks_by_name.has_key?(cookbook_name)
-
- return nil unless @loaders_by_name.key?(cookbook_name.to_s)
-
- cookbook_loaders_for(cookbook_name).each do |loader|
- loader.load
+ unless cookbook_version_loaders.key?(cookbook_name)
+ raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook_name}; did you forget to add metadata to a cookbook? (https://docs.chef.io/config_rb_metadata/)"
+ end
- next if loader.empty?
+ return cookbooks_by_name[cookbook_name] if cookbooks_by_name.key?(cookbook_name)
- @cookbooks_paths[cookbook_name] << loader.cookbook_path # for deprecation warnings
+ loader = cookbook_version_loaders[cookbook_name]
- if @loaded_cookbooks.key?(cookbook_name)
- @merged_cookbooks << cookbook_name # for deprecation warnings
- @loaded_cookbooks[cookbook_name].merge!(loader)
- else
- @loaded_cookbooks[cookbook_name] = loader
- end
- end
+ loader.load!
- if @loaded_cookbooks.has_key?(cookbook_name)
- cookbook_version = @loaded_cookbooks[cookbook_name].cookbook_version
- @cookbooks_by_name[cookbook_name] = cookbook_version
- @metadata[cookbook_name] = cookbook_version.metadata
- end
- @cookbooks_by_name[cookbook_name]
+ cookbook_version = loader.cookbook_version
+ cookbooks_by_name[cookbook_name] = cookbook_version
+ metadata[cookbook_name] = cookbook_version.metadata unless cookbook_version.nil?
+ cookbook_version
end
def [](cookbook)
- if @cookbooks_by_name.has_key?(cookbook.to_sym) || load_cookbook(cookbook.to_sym)
- @cookbooks_by_name[cookbook.to_sym]
- else
- raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook}; did you forget to add metadata to a cookbook? (https://docs.chef.io/config_rb_metadata.html)"
- end
+ load_cookbook(cookbook)
end
alias :fetch :[]
@@ -133,41 +109,83 @@ class Chef
def has_key?(cookbook_name)
not self[cookbook_name.to_sym].nil?
end
+
alias :cookbook_exists? :has_key?
alias :key? :has_key?
def each
- @cookbooks_by_name.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |cname|
- yield(cname, @cookbooks_by_name[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
+ cookbooks_by_name.keys.sort
end
def values
- @cookbooks_by_name.values
+ cookbooks_by_name.values
+ end
+
+ # This method creates tmp directory and copies all cookbooks into it and creates cookbook loader object which points to tmp directory
+ def self.copy_to_tmp_dir_from_array(cookbooks)
+ Dir.mktmpdir do |tmp_dir|
+ cookbooks.each do |cookbook|
+ checksums_to_on_disk_paths = cookbook.checksums
+ 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_dir, cookbook.name.to_s, path_in_cookbook)
+ FileUtils.mkdir_p(File.dirname(dest))
+ FileUtils.cp_r(on_disk_path, dest)
+ end
+ end
+ tmp_cookbook_loader ||= begin
+ Chef::Cookbook::FileVendor.fetch_from_disk(tmp_dir)
+ CookbookLoader.new(tmp_dir)
+ end
+ yield tmp_cookbook_loader
+ end
end
- alias :cookbooks :values
- private
-
- def preload_cookbooks
- return false if @preloaded_cookbooks
+ # generates metadata.json adds it in the manifest
+ def compile_metadata
+ each do |cookbook_name, cookbook|
+ compiled_metadata = cookbook.compile_metadata
+ if compiled_metadata
+ cookbook.all_files << compiled_metadata
+ cookbook.cookbook_manifest.send(:generate_manifest)
+ end
+ end
+ end
- all_directories_in_repo_paths.each do |cookbook_path|
- preload_cookbook(cookbook_path)
+ # freeze versions of all the cookbooks
+ def freeze_versions
+ each do |cookbook_name, cookbook|
+ cookbook.freeze_version
end
- @preloaded_cookbooks = true
- true
end
- def preload_cookbook(cookbook_path)
- repo_path = File.dirname(cookbook_path)
+ alias :cookbooks :values
+
+ private
+
+ # Helper method to lazily create and remember the chefignore object
+ # for a given repo_path.
+ #
+ # @param [String] repo_path the full path to the cookbook directory of the repo
+ # @return [Chef::Cookbook::Chefignore] the chefignore object for the repo_path
+ def chefignore(repo_path)
+ @chefignores ||= {}
@chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path)
- loader = Cookbook::CookbookVersionLoader.new(cookbook_path, @chefignores[repo_path])
- add_cookbook_loader(loader)
end
def all_directories_in_repo_paths
@@ -178,23 +196,30 @@ class Chef
def all_files_in_repo_paths
@all_files_in_repo_paths ||=
begin
- @repo_paths.inject([]) do |all_children, repo_path|
+ repo_paths.inject([]) do |all_children, repo_path|
all_children + Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")]
end
end
end
- def add_cookbook_loader(loader)
- cookbook_name = loader.cookbook_name
-
- @loaders_by_name[cookbook_name.to_s] ||= []
- @loaders_by_name[cookbook_name.to_s] << loader
- loader
- end
-
- def cookbook_loaders_for(cookbook_name)
- @loaders_by_name[cookbook_name.to_s]
+ # This method creates a Mash of the CookbookVersionLoaders for each cookbook.
+ #
+ # @return [Mash<String, Cookbook::CookbookVersionLoader>]
+ def cookbook_version_loaders
+ @cookbook_version_loaders ||=
+ begin
+ mash = Mash.new
+ all_directories_in_repo_paths.each do |cookbook_path|
+ loader = Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore(cookbook_path))
+ cookbook_name = loader.cookbook_name
+ if mash.key?(cookbook_name)
+ raise Chef::Exceptions::CookbookMergingError, "Cookbook merging is no longer supported, the cookbook named #{cookbook_name} can only appear once in the cookbook_path"
+ end
+
+ mash[cookbook_name] = loader
+ end
+ mash
+ end
end
-
end
end
diff --git a/lib/chef/cookbook_manifest.rb b/lib/chef/cookbook_manifest.rb
index d6de9dd029..84b0c0d58c 100644
--- a/lib/chef/cookbook_manifest.rb
+++ b/lib/chef/cookbook_manifest.rb
@@ -1,5 +1,5 @@
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,9 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "forwardable"
-require "chef/util/path_helper"
-require "chef/log"
+require "forwardable" unless defined?(Forwardable)
+require_relative "mixin/versioned_api"
+require_relative "util/path_helper"
+require_relative "cookbook/manifest_v0"
+require_relative "cookbook/manifest_v2"
+require_relative "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
@@ -43,11 +40,11 @@ class Chef
def_delegator :@cookbook_version, :frozen_version?
# Create a new CookbookManifest object for the given `cookbook_version`.
- # You can subsequently call #to_hash to get a Hash representation of the
+ # You can subsequently call #to_h to get a Hash representation of the
# cookbook_version in the "manifest" format, or #to_json to get a JSON
# representation of the cookbook_version.
#
- # The inferface for this behavior is expected to change as we implement new
+ # The interface for this behavior is expected to change as we implement new
# manifest formats. The entire class should be considered a private API for
# now.
#
@@ -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
@@ -125,15 +119,14 @@ class Chef
@policy_mode
end
- def to_hash
- result = manifest.dup
- result["frozen?"] = frozen_version?
- result["chef_type"] = "cookbook_version"
- result.to_hash
+ def to_h
+ CookbookManifestVersions.to_h(self)
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- result = to_hash
+ result = to_h
result["json_class"] = "Chef::CookbookVersion"
Chef::JSONCompat.to_json(result, *a)
end
@@ -164,15 +157,59 @@ 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"
- cookbook_version.replace_segment_filenames(segment, filenames)
+ manifest[:all_files].select do |file|
+ seg = file[:name].split("/")[0]
+ part.to_s == seg
+ end
+ end
+
+ def each_file(excluded_parts: [], &block)
+ excluded_parts = Array(excluded_parts).map(&: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|
+ segment, name = file[:name].split("/")
+ name.nil? || segment == "root_files"
end
end
@@ -186,15 +223,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: [],
})
@checksums = {}
@@ -203,24 +232,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 +267,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 [ "root_files/#{pathname}", pathname.to_s, "default" ] if parts.length == 1
+
+ segment = parts[0]
+
+ name = File.join(segment, pathname.basename.to_s)
+
+ if %w{templates files}.include?(segment)
# 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 +310,22 @@ 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
+ def_versioned_delegator :to_h
end
end
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
index c0e85ff984..d7226b79b3 100644
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ b/lib/chef/cookbook_site_streaming_uploader.rb
@@ -2,7 +2,7 @@
# Author:: Stanislav Vitvitskiy
# Author:: Nuo Yan (nuo@chef.io)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2009-2016, 2010-2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,10 +18,17 @@
# limitations under the License.
#
-require "uri"
-require "net/http"
-require "mixlib/authentication/signedheaderauth"
-require "openssl"
+autoload :URI, "uri"
+module Net
+ autoload :HTTP, "net/http"
+end
+autoload :OpenSSL, "openssl"
+module Mixlib
+ module Authentication
+ autoload :SignedHeaderAuth, "mixlib/authentication/signedheaderauth"
+ end
+end
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
# == Chef::CookbookSiteStreamingUploader
@@ -31,31 +38,29 @@ 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 } # rubocop:disable Style/ConstantName
+ DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION }.freeze # rubocop:disable Naming/ConstantName
class << self
def create_build_dir(cookbook)
- tmp_cookbook_path = Tempfile.new("chef-#{cookbook.name}-build")
+ tmp_cookbook_path = Tempfile.new("#{ChefUtils::Dist::Infra::SHORT}-#{cookbook.name}-build")
tmp_cookbook_path.close
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 ]
@@ -81,7 +86,7 @@ class Chef
unless params.nil? || params.empty?
params.each do |key, value|
- if value.kind_of?(File)
+ if value.is_a?(File)
content_file = value
filepath = value.path
filename = File.basename(filepath)
@@ -117,10 +122,10 @@ class Chef
content_file.rewind if content_file # we consumed the file for the above operation, so rewind it.
signing_options = {
- :http_method => http_verb,
- :path => url.path,
- :user_id => user_id,
- :timestamp => timestamp }
+ http_method: http_verb,
+ path: url.path,
+ user_id: user_id,
+ timestamp: timestamp }
(content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || ""))
headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key))
@@ -148,7 +153,7 @@ class Chef
class << res
alias :to_s :body
- # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
+ # BUG this makes the response compatible with what response_steps expects to test headers (response.headers[] -> response[])
def headers # rubocop:disable Lint/NestedMethodDefinition
self
end
@@ -186,7 +191,7 @@ class Chef
@str.length
end
- # read the specified amount from the string startiung at the offset
+ # read the specified amount from the string starting at the offset
def read(offset, how_much)
@str[offset, how_much]
end
@@ -226,11 +231,7 @@ class Chef
@part_no += 1
@part_offset = 0
next_part = read(how_much_next_part)
- result = current_part + if next_part
- next_part
- else
- ""
- end
+ result = current_part + (next_part || "")
else
@part_offset += how_much_current_part
result = current_part
diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb
index bb75234563..235a539b94 100644
--- a/lib/chef/cookbook_uploader.rb
+++ b/lib/chef/cookbook_uploader.rb
@@ -1,15 +1,15 @@
-require "set"
-require "chef/exceptions"
-require "chef/knife/cookbook_metadata"
-require "chef/digester"
-require "chef/cookbook_manifest"
-require "chef/cookbook_version"
-require "chef/cookbook/syntax_check"
-require "chef/cookbook/file_system_file_vendor"
-require "chef/util/threaded_job_queue"
-require "chef/sandbox"
-require "chef/server_api"
+autoload :Set, "set"
+require_relative "exceptions"
+require_relative "knife/cookbook_metadata"
+require_relative "digester"
+require_relative "cookbook_manifest"
+require_relative "cookbook_version"
+require_relative "cookbook/syntax_check"
+require_relative "cookbook/file_system_file_vendor"
+require_relative "util/threaded_job_queue"
+require_relative "sandbox"
+require_relative "server_api"
class Chef
class CookbookUploader
@@ -40,14 +40,14 @@ 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
def upload_cookbooks
# Syntax Check
- validate_cookbooks
+ validate_cookbooks unless opts[:skip_syntax_check]
# generate checksums of cookbook files and create a sandbox
checksum_files = {}
cookbooks.each do |cb|
@@ -56,7 +56,7 @@ class Chef
end
checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil; memo }
- new_sandbox = rest.post("sandboxes", { :checksums => checksums })
+ new_sandbox = rest.post("sandboxes", { checksums: checksums })
Chef::Log.info("Uploading files")
@@ -68,23 +68,23 @@ class Chef
new_sandbox["checksums"].each do |checksum, info|
if info["needs_upload"] == true
checksums_to_upload << checksum
- Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}")
+ 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
begin
- rest.put(sandbox_url, { :is_completed => true })
- rescue Net::HTTPServerException => e
+ rest.put(sandbox_url, { is_completed: true })
+ rescue Net::HTTPClientException => e
if e.message =~ /^400/ && (retries += 1) <= 5
sleep 2
retry
@@ -101,7 +101,7 @@ class Chef
save_url = opts[:force] ? manifest.force_save_url : manifest.save_url
begin
rest.put(save_url, manifest)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
case e.response.code
when "409"
raise Chef::Exceptions::CookbookFrozen, "Version #{cb.version} of cookbook #{cb.name} is frozen. Use --force to override."
@@ -120,7 +120,7 @@ class Chef
# but we need the base64 encoding for the content-md5
# header
checksum64 = Base64.encode64([checksum].pack("H*")).strip
- file_contents = File.open(file, "rb") { |f| f.read }
+ file_contents = File.open(file, "rb", &:read)
# Custom headers. 'content-type' disables JSON serialization of the request body.
headers = { "content-type" => "application/x-binary", "content-md5" => checksum64, "accept" => "application/json" }
@@ -128,7 +128,7 @@ class Chef
begin
rest.put(url, file_contents, headers)
checksums_to_upload.delete(checksum)
- rescue Net::HTTPServerException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e
+ rescue Net::HTTPClientException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e
error_message = "Failed to upload #{file} (#{checksum}) to #{url} : #{e.message}"
error_message << "\n#{e.response.body}" if e.respond_to?(:response)
Chef::Knife.ui.error(error_message)
@@ -139,13 +139,14 @@ class Chef
def validate_cookbooks
cookbooks.each do |cb|
- syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cb.name)
+ next if cb.nil?
+
+ syntax_checker = Chef::Cookbook::SyntaxCheck.new(cb.root_dir)
Chef::Log.info("Validating ruby files")
exit(1) unless syntax_checker.validate_ruby_files
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 8de9cb26dd..420532585a 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,13 +19,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/log"
-require "chef/cookbook/file_vendor"
-require "chef/cookbook/metadata"
-require "chef/version_class"
-require "chef/digester"
-require "chef/cookbook_manifest"
-require "chef/server_api"
+require_relative "log"
+require_relative "cookbook/file_vendor"
+require_relative "cookbook/metadata"
+require_relative "version_class"
+require_relative "digester"
+require_relative "cookbook_manifest"
+require_relative "server_api"
class Chef
@@ -37,50 +37,23 @@ class Chef
class CookbookVersion
include Comparable
+ extend Forwardable
- COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
+ def_delegator :@cookbook_manifest, :files_for
+ def_delegator :@cookbook_manifest, :each_file
- attr_accessor :all_files
+ COOKBOOK_SEGMENTS = %i{resources providers recipes definitions libraries attributes files templates root_files}.freeze
+
+ 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
+ @all_files = []
+
@file_vendor = nil
+ @cookbook_manifest = Chef::CookbookManifest.new(self)
@metadata = Chef::Cookbook::Metadata.new
@chef_server_rest = chef_server_rest
end
@@ -143,9 +108,9 @@ class Chef
metadata.version
end
- # Indicates if this version is frozen or not. Freezing a coobkook version
+ # Indicates if this version is frozen or not. Freezing a cookbook version
# indicates that a new cookbook with the same name and version number
- # shoule
+ # should
def frozen_version?
@frozen
end
@@ -163,26 +128,52 @@ 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] == "root_files/attributes.rb" }
+ name_map["default"] = root_alias[:full_path] if root_alias
+ name_map
+ end
+ end
+
+ def recipe_yml_filenames_by_name
+ @recipe_ym_filenames_by_name ||= begin
+ name_map = yml_filenames_by_name(files_for("recipes"))
+ root_alias = cookbook_manifest.root_files.find { |record| record[:name] == "root_files/recipe.yml" }
+ if root_alias
+ Chef::Log.error("Cookbook #{name} contains both recipe.yml and and recipes/default.yml, ignoring recipes/default.yml") if name_map["default"]
+ name_map["default"] = root_alias[:full_path]
+ end
+ 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] == "root_files/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,83 +184,55 @@ 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)
+ if recipe_filenames_by_name.key?(recipe_name)
+ load_ruby_recipe(recipe_name, run_context)
+ elsif recipe_yml_filenames_by_name.key?(recipe_name)
+ load_yml_recipe(recipe_name, run_context)
+ else
raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
end
+ end
- Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}")
+ def load_yml_recipe(recipe_name, run_context)
+ 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]
+ recipe_filename = recipe_yml_filenames_by_name[recipe_name]
unless recipe_filename
raise Chef::Exceptions::RecipeNotFound, "could not find #{recipe_name} files for cookbook #{name}"
end
- recipe.from_file(recipe_filename)
+ recipe.from_yaml_file(recipe_filename)
recipe
end
- def segment_filenames(segment)
- unless COOKBOOK_SEGMENTS.include?(segment)
- raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}"
- end
+ def load_ruby_recipe(recipe_name, run_context)
+ 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]
- 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
+ unless recipe_filename
+ raise Chef::Exceptions::RecipeNotFound, "could not find #{recipe_name} files for cookbook #{name}"
end
+
+ recipe.from_file(recipe_filename)
+ recipe
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
+ def segment_filenames(segment)
+ 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
@@ -301,12 +264,13 @@ class Chef
if found_pref
manifest_records_by_path[found_pref]
else
- if segment == :files || segment == :templates
+ if %i{files templates}.include?(segment)
error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n"
error_locations = if filename.is_a?(Array)
filename.map { |name| " #{File.join(segment.to_s, name)}" }
else
[
+ " #{segment}/host-#{node[:fqdn]}/#{filename}",
" #{segment}/#{node[:platform]}-#{node[:platform_version]}/#{filename}",
" #{segment}/#{node[:platform]}/#{filename}",
" #{segment}/default/#{filename}",
@@ -346,10 +310,10 @@ class Chef
def relative_filenames_in_preferred_directory(node, segment, dirname)
preferences = preferences_for_path(node, segment, dirname)
- filenames_by_pref = Hash.new
- preferences.each { |pref| filenames_by_pref[pref] = Array.new }
+ filenames_by_pref = {}
+ preferences.each { |pref| filenames_by_pref[pref] = [] }
- 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.
@@ -363,9 +327,9 @@ class Chef
# we're just going to make cookbook_files out of these and make the
# cookbook find them by filespecificity again. but it's the shortest
# path to "success" for now.
- if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
+ if manifest_record_path =~ %r{(#{Regexp.escape(segment.to_s)}/[^/]*/?#{Regexp.escape(dirname)})/.+$}
specificity_dirname = $1
- non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1]
+ non_specific_path = manifest_record_path[%r{#{Regexp.escape(segment.to_s)}/[^/]*/?#{Regexp.escape(dirname)}/(.+)$}, 1]
# Record the specificity_dirname only if it's in the list of
# valid preferences
if filenames_by_pref[specificity_dirname]
@@ -386,18 +350,18 @@ class Chef
# description of entries of the returned Array.
def preferred_manifest_records_for_directory(node, segment, dirname)
preferences = preferences_for_path(node, segment, dirname)
- records_by_pref = Hash.new
- preferences.each { |pref| records_by_pref[pref] = Array.new }
+ records_by_pref = {}
+ preferences.each { |pref| records_by_pref[pref] = [] }
- 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.
- if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
- # Note the specificy_dirname includes the segment and
- # dirname argument as above, which is what
- # preferences_for_path returns. It could be
- # "files/ubuntu-9.10/dirname", for example.
+ if manifest_record_path =~ %r{(#{Regexp.escape(segment.to_s)}/[^/]+/#{Regexp.escape(dirname)})/.+$}
+ # Note the specificity_dirname includes the segment and
+ # dirname argument as above, which is what
+ # preferences_for_path returns. It could be
+ # "files/ubuntu-9.10/dirname", for example.
specificity_dirname = $1
# Record the specificity_dirname only if it's in the list of
@@ -428,7 +392,7 @@ class Chef
platform, version = Chef::Platform.find_platform_and_version(node)
rescue ArgumentError => e
# Skip platform/version if they were not found by find_platform_and_version
- if e.message =~ /Cannot find a (?:platform|version)/
+ if /Cannot find a (?:platform|version)/.match?(e.message)
platform = "/unknown_platform/"
version = "/unknown_platform_version/"
else
@@ -469,12 +433,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_h
+ 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 +458,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
@@ -534,26 +476,20 @@ class Chef
end
end
+ def has_metadata_file?
+ all_files.include?(metadata_json_file) || all_files.include?(metadata_rb_file)
+ end
+
##
# 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
@@ -590,8 +526,8 @@ class Chef
chef_server_rest.get("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map do |cb|
cb["version"]
end
- rescue Net::HTTPServerException => e
- if e.to_s =~ /^404/
+ rescue Net::HTTPClientException => e
+ if /^404/.match?(e.to_s)
Chef::Log.error("Cannot find a cookbook named #{cookbook_name}")
nil
else
@@ -600,37 +536,58 @@ class Chef
end
def <=>(other)
- raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != other.name
+ 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(other.version)
+ Chef::Version.new(version) <=> Chef::Version.new(other.version)
end
- private
-
def cookbook_manifest
@cookbook_manifest ||= CookbookManifest.new(self)
end
+ def compile_metadata(path = root_dir)
+ json_file = "#{path}/metadata.json"
+ rb_file = "#{path}/metadata.rb"
+ return nil if File.exist?(json_file)
+
+ md = Chef::Cookbook::Metadata.new
+ md.from_file(rb_file)
+ f = File.open(json_file, "w")
+ f.write(Chef::JSONCompat.to_json_pretty(md))
+ f.close
+ f.path
+ end
+
+ private
+
def find_preferred_manifest_record(node, segment, filename)
preferences = preferences_for_path(node, segment, filename)
- # in order of prefernce, look for the filename in the manifest
+ # in order of preference, look for the filename in the manifest
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 yml_filenames_by_name(records)
+ records.select { |record| record[:name] =~ /\.yml$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".yml")] = record[:full_path]; memo }
end
def file_vendor
- unless @file_vendor
- @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
- end
- @file_vendor
+ @file_vendor ||= Chef::Cookbook::FileVendor.create_from_manifest(cookbook_manifest)
end
end
diff --git a/lib/chef/daemon.rb b/lib/chef/daemon.rb
index 70bdddf457..41d93e1fe6 100644
--- a/lib/chef/daemon.rb
+++ b/lib/chef/daemon.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# I love you Merb (lib/merb-core/server.rb)
-require "chef/config"
-require "chef/run_lock"
-require "etc"
+require_relative "config"
+require_relative "run_lock"
+require "etc" unless defined?(Etc)
class Chef
class Daemon
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index f107f98f28..af0560a640 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,13 +18,13 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/data_bag_item"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/server_api"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "data_bag_item"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "server_api"
class Chef
class DataBag
@@ -32,14 +32,16 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- VALID_NAME = /^[\.\-[:alnum:]_]+$/
-
- attr_accessor :chef_server_rest
+ VALID_NAME = /^[\.\-[:alnum:]_]+$/.freeze
+ RESERVED_NAMES = /^(node|role|environment|client)$/.freeze
def self.validate_name!(name)
- unless name =~ VALID_NAME
+ unless VALID_NAME.match?(name)
raise Exceptions::InvalidDataBagName, "DataBags must have a name matching #{VALID_NAME.inspect}, you gave #{name.inspect}"
end
+ if RESERVED_NAMES.match?(name)
+ raise Exceptions::InvalidDataBagName, "DataBags may not have a name matching #{RESERVED_NAMES.inspect}, you gave #{name.inspect}"
+ end
end
# Create a new Chef::DataBag
@@ -52,22 +54,23 @@ class Chef
set_or_return(
:name,
arg,
- :regex => VALID_NAME
+ regex: VALID_NAME
)
end
- def to_hash
- result = {
- "name" => @name,
+ def to_h
+ {
+ "name" => @name,
"json_class" => self.class.name,
- "chef_type" => "data_bag",
+ "chef_type" => "data_bag",
}
- result
end
+ alias_method :to_hash, :to_h
+
# Serialize this object as a hash
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def chef_server_rest
@@ -78,12 +81,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"])
@@ -96,11 +93,12 @@ class Chef
names = []
paths.each do |path|
unless File.directory?(path)
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' not found. Please create this directory."
end
names += Dir.glob(File.join(
- Chef::Util::PathHelper.escape_glob_dir(path), "*")).map { |f| File.basename(f) }.sort
+ Chef::Util::PathHelper.escape_glob_dir(path), "*"
+ )).map { |f| File.basename(f) }.sort
end
names.inject({}) { |h, n| h[n] = n; h }
else
@@ -123,21 +121,21 @@ class Chef
data_bag = {}
paths.each do |path|
unless File.directory?(path)
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' not found. Please create this directory."
end
Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(path, name.to_s), "*.json")).inject({}) do |bag, f|
item = Chef::JSONCompat.parse(IO.read(f))
# Check if we have multiple items with similar names (ids) and raise if their content differs
- if data_bag.has_key?(item["id"]) && data_bag[item["id"]] != item
+ if data_bag.key?(item["id"]) && data_bag[item["id"]] != item
raise Chef::Exceptions::DuplicateDataBagItem, "Data bag '#{name}' has items with the same name '#{item["id"]}' but different content."
else
data_bag[item["id"]] = item
end
end
end
- return data_bag
+ data_bag
else
Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("data/#{name}")
end
@@ -155,13 +153,13 @@ class Chef
else
create
end
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise e unless e.response.code == "409"
end
self
end
- #create a data bag via RESTful API
+ # create a data bag via RESTful API
def create
chef_server_rest.post("data", self)
self
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 568c511c44..94cf2a5317 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +18,15 @@
# limitations under the License.
#
-require "forwardable"
+require "forwardable" unless defined?(Forwardable)
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/data_bag"
-require "chef/mash"
-require "chef/server_api"
-require "chef/json_compat"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "data_bag"
+require_relative "mash"
+require_relative "server_api"
+require_relative "json_compat"
class Chef
class DataBagItem
@@ -36,9 +36,7 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- VALID_ID = /^[\.\-[:alnum:]_]+$/
-
- attr_accessor :chef_server_rest
+ VALID_ID = /^[\.\-[:alnum:]_]+$/.freeze
def self.validate_id!(id_str)
if id_str.nil? || ( id_str !~ VALID_ID )
@@ -66,18 +64,16 @@ 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
+
validate_id!(new_data["id"])
@raw_data = new_data
end
@@ -86,7 +82,7 @@ class Chef
set_or_return(
:data_bag,
arg,
- :regex => /^[\-[:alnum:]_]+$/
+ regex: /^[\-[:alnum:]_]+$/
)
end
@@ -95,7 +91,7 @@ class Chef
end
def object_name
- raise Exceptions::ValidationFailed, "You must have an 'id' or :id key in the raw data" unless raw_data.has_key?("id")
+ raise Exceptions::ValidationFailed, "You must have an 'id' or :id key in the raw data" unless raw_data.key?("id")
raise Exceptions::ValidationFailed, "You must have declared what bag this item belongs to!" unless data_bag
id = raw_data["id"]
@@ -106,21 +102,23 @@ class Chef
"data_bag_item_#{data_bag_name}_#{id}"
end
- def to_hash
- result = self.raw_data.dup
+ def to_h
+ 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
+ alias_method :to_hash, :to_h
+
# Serialize this object as a hash
def to_json(*a)
result = {
- "name" => object_name,
+ "name" => object_name,
"json_class" => self.class.name,
- "chef_type" => "data_bag_item",
- "data_bag" => data_bag,
- "raw_data" => raw_data,
+ "chef_type" => "data_bag_item",
+ "data_bag" => data_bag,
+ "raw_data" => raw_data,
}
Chef::JSONCompat.to_json(result, *a)
end
@@ -132,30 +130,25 @@ 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_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]
else
item = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("data/#{data_bag}/#{name}")
end
- if item.kind_of?(DataBagItem)
+ if item.is_a?(DataBagItem)
item
else
item = from_hash(item)
@@ -164,7 +157,7 @@ class Chef
end
end
- def destroy(data_bag = self.data_bag(), databag_item = name)
+ def destroy(data_bag = self.data_bag, databag_item = name)
chef_server_rest.delete("data/#{data_bag}/#{databag_item}")
end
@@ -177,8 +170,9 @@ class Chef
else
r.put("data/#{data_bag}/#{item_id}", self)
end
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise e unless e.response.code == "404"
+
r.post("data/#{data_bag}", self)
end
self
@@ -191,9 +185,9 @@ class Chef
end
def ==(other)
- other.respond_to?(:to_hash) &&
+ other.respond_to?(:to_h) &&
other.respond_to?(:data_bag) &&
- (other.to_hash == to_hash) &&
+ (other.to_h == to_h) &&
(other.data_bag.to_s == data_bag.to_s)
end
@@ -203,11 +197,11 @@ class Chef
end
def inspect
- "data_bag_item[#{data_bag.inspect}, #{raw_data['id'].inspect}, #{raw_data.inspect}]"
+ "data_bag_item[#{data_bag.inspect}, #{raw_data["id"].inspect}, #{raw_data.inspect}]"
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
index dbb0b3771a..8d76f8a7b2 100644
--- a/lib/chef/data_collector.rb
+++ b/lib/chef/data_collector.rb
@@ -2,7 +2,7 @@
# Author:: Adam Leff (<adamleff@chef.io>)
# Author:: Ryan Cragun (<ryan@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,380 +18,273 @@
# limitations under the License.
#
-require "uri"
-require "chef/event_dispatch/base"
-require "chef/data_collector/messages"
-require "chef/data_collector/resource_report"
-require "ostruct"
+require_relative "server_api"
+require_relative "http/simple_json"
+require_relative "event_dispatch/base"
+autoload :Set, "set"
+require_relative "data_collector/run_end_message"
+require_relative "data_collector/run_start_message"
+require_relative "data_collector/config_validation"
+require_relative "data_collector/error_handlers"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
-
- # == Chef::DataCollector
- # Provides methods for determinine whether a reporter should be registered.
class DataCollector
- def self.register_reporter?
- Chef::Config[:data_collector][:server_url] &&
- !Chef::Config[:why_run] &&
- self.reporter_enabled_for_current_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.
+ # The DataCollector is mode-agnostic reporting tool which can be used with
+ # server-based and solo-based clients. It can report to a file, to an
+ # authenticated Chef Automate reporting endpoint, or to a user-supplied
+ # webhook. It sends two messages: one at the start of the run and one
+ # at the end of the run. Most early failures in the actual Chef::Client itself
+ # are reported, but parsing of the client.rb must have succeeded and some code
+ # in Chef::Application could throw so early as to prevent reporting. If
+ # exceptions are thrown both run-start and run-end messages are still sent in
+ # pairs.
+ #
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
-
- def initialize
- validate_data_collector_server_url!
-
- @all_resource_reports = []
- @current_resource_loaded = nil
- @error_descriptions = {}
- @expanded_run_list = {}
- @http = Chef::HTTP.new(data_collector_server_url)
- @enabled = true
- end
+ include Chef::DataCollector::ErrorHandlers
- # 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)
-
- disable_reporter_on_error do
- send_to_data_collector(
- Chef::DataCollector::Messages.run_start_message(current_run_status).to_json
- )
- end
- end
+ # @return [Chef::RunList::RunListExpansion] the expanded run list
+ attr_reader :expanded_run_list
- # 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
+ # @return [Chef::RunStatus] the run status
+ attr_reader :run_status
- # see EventDispatch::Base#run_failed
- def run_failed(exception)
- send_run_completion(status: "failure")
- end
+ # @return [Chef::Node] the chef node
+ attr_reader :node
- # 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
+ # @return [Set<Hash>] the accumulated list of deprecation warnings
+ attr_reader :deprecations
- # 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
+ # @return [Chef::ActionCollection] the action collection object
+ attr_reader :action_collection
- # 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
+ # @return [Chef::EventDispatch::Dispatcher] the event dispatcher
+ attr_reader :events
- # 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)
- update_current_resource_report(create_resource_report(new_resource, action, current_resource))
+ # @param events [Chef::EventDispatch::Dispatcher] the event dispatcher
+ def initialize(events)
+ @events = events
+ @expanded_run_list = {}
+ @deprecations = Set.new
end
- # see EventDispatch::Base#resource_up_to_date
- # Mark our ResourceReport status accordingly
- def resource_up_to_date(new_resource, action)
- current_resource_report.up_to_date unless nested_resource?(new_resource)
+ # Hook to grab the run_status. We also make the decision to run or not run here (our
+ # config has been parsed so we should know if we need to run, we unregister if we do
+ # not want to run).
+ #
+ # (see EventDispatch::Base#run_start)
+ #
+ def run_start(chef_version, run_status)
+ events.unregister(self) unless Chef::DataCollector::ConfigValidation.should_be_enabled?
+ @run_status = run_status
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)
-
- resource_report = create_resource_report(new_resource, action)
- resource_report.skipped(conditional)
- update_current_resource_report(resource_report)
+ # Hook to grab the node object after it has been successfully loaded
+ #
+ # (see EventDispatch::Base#node_load_success)
+ #
+ def node_load_success(node)
+ @node = node
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)
- current_resource_report.updated unless nested_resource?(new_resource)
+ # The expanded run list is stored for later use by the run_completed
+ # event and message.
+ #
+ # (see EventDispatch::Base#run_list_expanded)
+ #
+ def run_list_expanded(run_list_expansion)
+ @expanded_run_list = run_list_expansion
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)
- current_resource_report.failed(exception) unless nested_resource?(new_resource)
- update_error_description(
- Formatters::ErrorMapper.resource_failed(
- new_resource,
- action,
- exception
- ).for_json
- )
+ # Hook event to register with the action_collection if we are still enabled.
+ #
+ # This is also how we wire up to the action_collection since it passes itself as the argument.
+ #
+ # (see EventDispatch::Base#action_collection_registration)
+ #
+ def action_collection_registration(action_collection)
+ @action_collection = action_collection
+ action_collection.register(self)
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)
- update_current_resource_report(nil)
- end
- end
+ # - Creates and writes our NodeUUID back to the node object
+ # - Sanity checks the data collector
+ # - Sends the run start message
+ # - If the run_start message fails, this may disable the rest of data collection or fail hard
+ #
+ # (see EventDispatch::Base#run_started)
+ #
+ def run_started(run_status)
+ Chef::DataCollector::ConfigValidation.validate_server_url!
+ Chef::DataCollector::ConfigValidation.validate_output_locations!
- # 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
+ send_run_start
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
- )
+ # Hook event to accumulating deprecation messages
+ #
+ # (see EventDispatch::Base#deprecation)
+ #
+ def deprecation(message, location = caller(2..2)[0])
+ @deprecations << { message: message.message, url: message.url, location: message.location }
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
- )
+ # Hook to send the run completion message with a status of success
+ #
+ # (see EventDispatch::Base#run_completed)
+ #
+ def run_completed(node)
+ send_run_completion("success")
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
- )
+ # Hook to send the run completion message with a status of failed
+ #
+ # (see EventDispatch::Base#run_failed)
+ #
+ def run_failed(exception)
+ send_run_completion("failure")
end
private
+ # Construct a http client for either the main data collector or for the http output_locations.
#
- # 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.)
+ # Note that based on the token setting either the main data collector and all the http output_locations
+ # are going to all require chef-server authentication or not. There is no facility to mix-and-match on
+ # a per-url basis.
#
- # @param block [Proc] A ruby block to run. Ignored if a command is given.
+ # @param url [String] the string url to connect to
+ # @returns [Chef::HTTP] the appropriate Chef::HTTP subclass instance to use
#
- 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
- disable_data_collector_reporter
- 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
+ def setup_http_client(url)
+ if Chef::Config[:data_collector][:token].nil?
+ Chef::ServerAPI.new(url, validate_utf8: false)
else
- Chef::Log.warn(msg)
+ Chef::HTTP::SimpleJSON.new(url, validate_utf8: false)
end
end
- def send_to_data_collector(message)
- return unless data_collector_accessible?
-
- Chef::Log.debug("data_collector_reporter: POSTing the following message to #{data_collector_server_url}: #{message}")
- http.post(nil, message, headers)
- end
-
- #
- # Send any messages to the DataCollector endpoint that are necessary to
- # indicate the run has completed. Currently, two messages are sent:
+ # Handle POST'ing data to the data collector. Note that this is a totally separate concern
+ # from the array of URI's in the extra configured output_locations.
#
- # - 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.
+ # On failure this will unregister the data collector (if there are no other configured output_locations)
+ # and optionally will either silently continue or fail hard depending on configuration.
#
- # @param opts [Hash] Additional details about the run, such as its success/failure.
+ # @param message [Hash] message to send
#
- 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
-
- send_to_data_collector(
- 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
- ).to_json
- )
- end
+ def send_to_data_collector(message)
+ return unless Chef::Config[:data_collector][:server_url]
- def headers
- headers = { "Content-Type" => "application/json" }
+ @http ||= setup_http_client(Chef::Config[:data_collector][:server_url])
+ @http.post(nil, message, headers)
+ rescue => e
+ # Do not disable data collector reporter if additional output_locations have been specified
+ events.unregister(self) unless Chef::Config[:data_collector][:output_locations]
- unless data_collector_token.nil?
- headers["x-data-collector-token"] = data_collector_token
- headers["x-data-collector-auth"] = "version=1.0"
+ begin
+ code = e&.response&.code.to_s
+ rescue
+ # i really don't care
end
- headers
- end
-
- def data_collector_server_url
- Chef::Config[:data_collector][:server_url]
- end
+ code ||= "No HTTP Code"
- 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
+ msg = "Error while reporting run start to Data Collector. URL: #{Chef::Config[:data_collector][:server_url]} Exception: #{code} -- #{e.message} "
- def disable_data_collector_reporter
- @enabled = false
+ if Chef::Config[:data_collector][:raise_on_failure]
+ Chef::Log.error(msg)
+ raise
+ else
+ if code == "404"
+ # Make the message non-scary for folks who don't have automate:
+ msg << " (This is normal if you do not have #{ChefUtils::Dist::Automate::PRODUCT})"
+ Chef::Log.debug(msg)
+ else
+ Chef::Log.warn(msg)
+ end
+ end
end
- def data_collector_accessible?
- @enabled
+ # Process sending the configured message to all the extra output locations.
+ #
+ # @param message [Hash] message to send
+ #
+ def send_to_output_locations(message)
+ return unless Chef::Config[:data_collector][:output_locations]
+
+ Chef::DataCollector::ConfigValidation.validate_output_locations!
+ Chef::Config[:data_collector][:output_locations].each do |type, locations|
+ Array(locations).each do |location|
+ send_to_file_location(location, message) if type == :files
+ send_to_http_location(location, message) if type == :urls
+ end
+ end
end
- def update_run_status(run_status)
- @run_status = run_status
+ # Sends a single message to a file, rendered as JSON.
+ #
+ # @param file_name [String] the file to write to
+ # @param message [Hash] the message to render as JSON
+ #
+ def send_to_file_location(file_name, message)
+ File.open(File.expand_path(file_name), "a") do |fh|
+ fh.puts Chef::JSONCompat.to_json(message, validate_utf8: false)
+ end
end
- def update_current_resource_report(resource_report)
- @current_resource_report = resource_report
+ # Sends a single message to a http uri, rendered as JSON. Maintains a cache of Chef::HTTP
+ # objects to use on subsequent requests.
+ #
+ # @param http_url [String] the configured http uri string endpoint to send to
+ # @param message [Hash] the message to render as JSON
+ #
+ def send_to_http_location(http_url, message)
+ @http_output_locations_clients[http_url] ||= setup_http_client(http_url)
+ @http_output_locations_clients[http_url].post(nil, message, headers)
+ rescue
+ # FIXME: we do all kinds of complexity to deal with errors in send_to_data_collector and we just don't care here, which feels like
+ # like poor behavior on several different levels, at least its a warn now... (I don't quite understand why it was written this way)
+ Chef::Log.warn("Data collector failed to send to URL location #{http_url}. Please check your configured data_collector.output_locations")
end
- def update_error_description(discription_hash)
- @error_descriptions = discription_hash
+ # @return [Boolean] if we've sent a run_start message yet
+ def sent_run_start?
+ !!@sent_run_start
end
- def create_resource_report(new_resource, action, current_resource = nil)
- Chef::DataCollector::ResourceReport.new(
- new_resource,
- action,
- current_resource
- )
+ # Send the run start message to the configured server or output locations
+ #
+ def send_run_start
+ message = Chef::DataCollector::RunStartMessage.construct_message(self)
+ send_to_data_collector(message)
+ send_to_output_locations(message)
+ @sent_run_start = true
end
- def detect_unprocessed_resources
- # create a Set containing all resource+action combinations from
- # the Resource Collection
- collection_resources = Set.new
- run_context.resource_collection.all_resources.each do |resource|
- Array(resource.action).each do |action|
- collection_resources.add([resource, action])
- end
- end
-
- # Delete from the Set any resource+action combination we have
- # already processed.
- all_resource_reports.each do |report|
- collection_resources.delete([report.resource, report.action])
- end
-
- # The items remaining in the Set are unprocessed resource+actions,
- # so we'll create new resource reports for them which default to
- # a state of "unprocessed".
- collection_resources.each do |resource, action|
- add_resource_report(create_resource_report(resource, action))
- end
- end
+ # Send the run completion message to the configured server or output locations
+ #
+ # @param status [String] Either "success" or "failed"
+ #
+ def send_run_completion(status)
+ # this is necessary to send a run_start message when we fail before the run_started chef event.
+ # we adhere to a contract that run_start + run_completion events happen in pairs.
+ send_run_start unless sent_run_start?
- # 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
+ message = Chef::DataCollector::RunEndMessage.construct_message(self, status)
+ send_to_data_collector(message)
+ send_to_output_locations(message)
end
- def validate_data_collector_server_url!
- raise Chef::Exceptions::ConfigurationError,
- "Chef::Config[:data_collector][:server_url] is empty. Please supply a valid URL." if data_collector_server_url.empty?
+ # @return [Hash] HTTP headers for the data collector endpoint
+ def headers
+ headers = { "Content-Type" => "application/json" }
- begin
- uri = URI(data_collector_server_url)
- rescue URI::InvalidURIError
- raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is not a valid URI."
+ unless Chef::Config[:data_collector][:token].nil?
+ headers["x-data-collector-token"] = Chef::Config[:data_collector][:token]
+ headers["x-data-collector-auth"] = "version=1.0"
end
- 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." if uri.host.nil?
+ headers
end
end
end
diff --git a/lib/chef/data_collector/config_validation.rb b/lib/chef/data_collector/config_validation.rb
new file mode 100644
index 0000000000..1cdc400f48
--- /dev/null
+++ b/lib/chef/data_collector/config_validation.rb
@@ -0,0 +1,140 @@
+#
+# Copyright:: Copyright (c) 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" unless defined?(URI)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class DataCollector
+
+ # @api private
+ module ConfigValidation
+ class << self
+ def validate_server_url!
+ # if we have a server_url set we ALWAYS validate it, and we MUST have an output_location set to skip server_url validation
+ # (having output_locations set and no server_url is valid, but both of them unset blows up in here)
+ return if !Chef::Config[:data_collector][:server_url] && Chef::Config[:data_collector][:output_locations]
+
+ begin
+ uri = URI(Chef::Config[:data_collector][:server_url])
+ rescue
+ raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{Chef::Config[:data_collector][:server_url]}) is not a valid URI."
+ end
+
+ if uri.host.nil?
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] (#{Chef::Config[:data_collector][:server_url]}) is a URI with no host. Please supply a valid URL."
+ end
+ end
+
+ def validate_output_locations!
+ # not having an output_location set at all is fine, we just skip it then
+ output_locations = Chef::Config[:data_collector][:output_locations]
+ return unless output_locations
+
+ # but deliberately setting an empty output_location we consider to be an error (XXX: but should we?)
+ unless valid_hash_with_keys?(output_locations, :urls, :files)
+ 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
+
+ # loop through all the types and locations and validate each one-by-one
+ output_locations.each do |type, locations|
+ Array(locations).each do |location|
+ validate_url!(location) if type == :urls
+ validate_file!(location) if type == :files
+ end
+ end
+ end
+
+ # Main logic controlling the data collector being enabled or disabled:
+ #
+ # * disabled in why-run mode
+ # * disabled when `Chef::Config[:data_collector][:mode]` excludes the solo-vs-client mode
+ # * disabled if there is no server_url or no output_locations to log to
+ # * enabled if there is a configured output_location even without a token
+ # * disabled in solo mode if the user did not configure the auth token
+ #
+ # @return [Boolean] true if the data collector should be enabled
+ #
+ def should_be_enabled?
+ running_mode = ( Chef::Config[:solo_legacy_mode] || Chef::Config[:local_mode] ) ? :solo : :client
+ want_mode = Chef::Config[:data_collector][:mode]
+
+ case
+ when Chef::Config[:why_run]
+ Chef::Log.trace("data collector is disabled for why run mode")
+ false
+ when (want_mode != :both) && running_mode != want_mode
+ Chef::Log.trace("data collector is configured to only run in #{Chef::Config[:data_collector][:mode]} modes, disabling it")
+ false
+ when !(Chef::Config[:data_collector][:server_url] || Chef::Config[:data_collector][:output_locations])
+ Chef::Log.trace("Neither data collector URL or output locations have been configured, disabling data collector")
+ false
+ when running_mode == :client && Chef::Config[:data_collector][:token]
+ Chef::Log.warn("Data collector token authentication is not recommended for client-server mode. " \
+ "Please upgrade #{ChefUtils::Dist::Server::PRODUCT} to 12.11 or later and remove the token from your config file " \
+ "to use key based authentication instead")
+ true
+ when Chef::Config[:data_collector][:output_locations] && !valid_hash_with_keys?(Chef::Config[:data_collector][:output_locations], :urls)
+ # we can run fine to a file without a token, even in solo mode.
+ unless valid_hash_with_keys?(Chef::Config[:data_collector][:output_locations], :files)
+ 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
+
+ true
+ when running_mode == :solo && !Chef::Config[:data_collector][:token]
+ # we are in solo mode and are not logging to a file, so must have a token
+ Chef::Log.trace("Data collector token must be configured to use #{ChefUtils::Dist::Automate::PRODUCT} data collector with #{ChefUtils::Dist::Solo::PRODUCT}")
+ false
+ else
+ true
+ end
+ end
+
+ private
+
+ # validate an output_location file
+ def validate_file!(file)
+ return true if Chef::Config.path_accessible?(File.expand_path(file))
+
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which is a non existent file path."
+ end
+
+ # validate an output_location url
+ def validate_url!(url)
+ URI(url)
+ rescue
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations][:urls] contains the url #{url} which is not valid."
+ end
+
+ # Validate the hash contains at least one of the given keys.
+ #
+ # @param hash [Hash] the hash to be validated.
+ # @param keys [Array] an array of keys to check existence of in the hash.
+ # @return [Boolean] true if the hash contains any of the given keys.
+ #
+ def valid_hash_with_keys?(hash, *keys)
+ hash.is_a?(Hash) && keys.any? { |k| hash.key?(k) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/error_handlers.rb b/lib/chef/data_collector/error_handlers.rb
new file mode 100644
index 0000000000..5cda3bde10
--- /dev/null
+++ b/lib/chef/data_collector/error_handlers.rb
@@ -0,0 +1,116 @@
+#
+# Copyright:: Copyright (c) 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
+
+ # This module isolates the handling of collecting error descriptions to insert into the data_collector
+ # report output. For very early errors it is responsible for collecting the node_name for the report
+ # to use. For all failure conditions that have an ErrorMapper it collects the output.
+ #
+ # No external code should call anything in this module directly.
+ #
+ # @api private
+ #
+ module ErrorHandlers
+
+ # @return [String] the fallback node name if we do NOT have a node due to early failures
+ attr_reader :node_name
+
+ # @return [Hash] JSON-formatted error description from the Chef::Formatters::ErrorMapper
+ def error_description
+ @error_description ||= {}
+ end
+
+ # This is an exceptionally "early" failure that results in not having a valid Chef::Node object,
+ # so it must capture the node_name from the config.rb
+ #
+ # (see EventDispatch::Base#registration_failed)
+ #
+ def registration_failed(node_name, exception, config)
+ description = Formatters::ErrorMapper.registration_failed(node_name, exception, config)
+ @node_name = node_name
+ @error_description = description.for_json
+ end
+
+ # This is an exceptionally "early" failure that results in not having a valid Chef::Node object,
+ # so it must capture the node_name from the config.rb
+ #
+ # (see EventDispatch::Base#node_load_failed)
+ #
+ def node_load_failed(node_name, exception, config)
+ description = Formatters::ErrorMapper.node_load_failed(node_name, exception, config)
+ @node_name = node_name
+ @error_description = description.for_json
+ end
+
+ # This is an "early" failure during run_list expansion
+ #
+ # (see EventDispatch::Base#run_list_expand_failed)
+ #
+ def run_list_expand_failed(node, exception)
+ description = Formatters::ErrorMapper.run_list_expand_failed(node, exception)
+ @error_description = description.for_json
+ end
+
+ # This is an "early" failure during cookbook resolution / depsolving / talking to cookbook_version endpoint on a server
+ #
+ # (see EventDispatch::Base#cookbook_resolution_failed)
+ #
+ def cookbook_resolution_failed(expanded_run_list, exception)
+ description = Formatters::ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception)
+ @error_description = description.for_json
+ end
+
+ # This is an "early" failure during cookbook synchronization
+ #
+ # (see EventDispatch::Base#cookbook_sync_failed)
+ #
+ def cookbook_sync_failed(cookbooks, exception)
+ description = Formatters::ErrorMapper.cookbook_sync_failed(cookbooks, exception)
+ @error_description = description.for_json
+ end
+
+ # This failure happens during library loading / attribute file parsing, etc.
+ #
+ # (see EventDispatch::Base#file_load_failed)
+ #
+ def file_load_failed(path, exception)
+ description = Formatters::ErrorMapper.file_load_failed(path, exception)
+ @error_description = description.for_json
+ end
+
+ # This failure happens at converge time during recipe parsing
+ #
+ # (see EventDispatch::Base#recipe_not_failed)
+ #
+ def recipe_not_found(exception)
+ description = Formatters::ErrorMapper.file_load_failed(nil, exception)
+ @error_description = description.for_json
+ end
+
+ # This is a normal resource failure event during compile/converge phases
+ #
+ # (see EventDispatch::Base#resource_failed)
+ #
+ def resource_failed(new_resource, action, exception)
+ description = Formatters::ErrorMapper.resource_failed(new_resource, action, exception)
+ @error_description = description.for_json
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/message_helpers.rb b/lib/chef/data_collector/message_helpers.rb
new file mode 100644
index 0000000000..5441404bd6
--- /dev/null
+++ b/lib/chef/data_collector/message_helpers.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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
+
+ # This is for shared code between the run_start_message and run_end_message modules.
+ #
+ # No external code should call this module directly
+ #
+ # @api private
+ #
+ module MessageHelpers
+ private
+
+ # The organization name the node is associated with. For Chef Solo runs the default
+ # is "chef_solo" which can be overridden by the user.
+ #
+ # @return [String] Chef organization associated with the node
+ #
+ def organization
+ if solo_run?
+ # configurable fake organization name for chef-solo users
+ Chef::Config[:data_collector][:organization]
+ else
+ Chef::Config[:chef_server_url].match(%r{/+organizations/+([^\s/]+)}).nil? ? "unknown_organization" : $1
+ end
+ end
+
+ # @return [Boolean] True if we're in a chef-solo/chef-zero or legacy chef-solo run
+ def solo_run?
+ Chef::Config[:solo_legacy_mode] || Chef::Config[:local_mode]
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb
deleted file mode 100644
index 8c2a84b580..0000000000
--- a/lib/chef/data_collector/messages.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-# 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 "json"
-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(run_status),
- "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(run_status),
- "entity_uuid" => node_uuid,
- "expanded_run_list" => reporter_data[:expanded_run_list],
- "id" => run_status.run_id,
- "message_version" => "1.0.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,
- "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,
- }
-
- message["error"] = {
- "class" => run_status.exception.class,
- "message" => run_status.exception.message,
- "backtrace" => run_status.exception.backtrace,
- "description" => reporter_data[:error_descriptions],
- } if run_status.exception
-
- message
- end
- end
- end
-end
diff --git a/lib/chef/data_collector/messages/helpers.rb b/lib/chef/data_collector/messages/helpers.rb
deleted file mode 100644
index c0c700f847..0000000000
--- a/lib/chef/data_collector/messages/helpers.rb
+++ /dev/null
@@ -1,161 +0,0 @@
-#
-# 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.
- #
- # @param run_status [Chef::RunStatus] The RunStatus object for this Chef Run.
- #
- # @return [String] FQDN of the configured Chef Server, or node/localhost if not found.
- #
- def chef_server_fqdn(run_status)
- 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/+(\w+)}).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
- 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
- JSON.load(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, updated_metadata.to_json, 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
deleted file mode 100644
index dcaf9c8e44..0000000000
--- a/lib/chef/data_collector/resource_report.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# 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
- 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" => new_resource.identity.to_s,
- "after" => new_resource.state_for_resource_reporter,
- "before" => current_resource ? current_resource.state_for_resource_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
- end
- end
-end
diff --git a/lib/chef/data_collector/run_end_message.rb b/lib/chef/data_collector/run_end_message.rb
new file mode 100644
index 0000000000..1900effa26
--- /dev/null
+++ b/lib/chef/data_collector/run_end_message.rb
@@ -0,0 +1,191 @@
+#
+# Copyright:: Copyright (c) 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 "message_helpers"
+
+class Chef
+ class DataCollector
+ module RunEndMessage
+ extend Chef::DataCollector::MessageHelpers
+
+ # This module encapsulates rendering the run_end_message given the state gathered in the data_collector
+ # and the action_collection. It is deliberately a stateless module and is deliberately not mixed into
+ # the data_collector and only uses the public api methods of the data_collector and action_collection.
+ #
+ # No external code should call this module directly.
+ #
+ # @api private
+ class << self
+
+ # Construct the message payload that is sent to the DataCollector server at the
+ # end of a Chef run.
+ #
+ # @param data_collector [Chef::DataCollector::Reporter] the calling data_collector instance
+ # @param status [String] the overall status of the run, either "success" or "failure"
+ #
+ # @return [Hash] A hash containing the run end message data.
+ #
+ def construct_message(data_collector, status)
+ action_collection = data_collector.action_collection
+ run_status = data_collector.run_status
+ node = data_collector.node
+
+ message = {
+ "chef_server_fqdn" => URI(Chef::Config[:chef_server_url]).host,
+ "entity_uuid" => Chef::Config[:chef_guid],
+ "expanded_run_list" => data_collector.expanded_run_list,
+ "id" => run_status&.run_id,
+ "message_version" => "1.1.0",
+ "message_type" => "run_converge",
+ "node" => node || {},
+ "node_name" => node&.name || data_collector.node_name,
+ "organization_name" => organization,
+ "resources" => all_action_records(action_collection),
+ "run_id" => run_status&.run_id,
+ "run_list" => node&.run_list&.for_json || [],
+ "cookbooks" => ( node && node["cookbooks"] ) || {},
+ "policy_name" => node&.policy_name,
+ "policy_group" => node&.policy_group,
+ "start_time" => run_status&.start_time&.utc&.iso8601,
+ "end_time" => run_status&.end_time&.utc&.iso8601,
+ "source" => solo_run? ? "chef_solo" : "chef_client",
+ "status" => status,
+ "total_resource_count" => all_action_records(action_collection).count,
+ "updated_resource_count" => updated_resource_count(action_collection),
+ "deprecations" => data_collector.deprecations.to_a,
+ }
+
+ if run_status&.exception
+ message["error"] = {
+ "class" => run_status.exception.class,
+ "message" => run_status.exception.message,
+ "backtrace" => run_status.exception.backtrace,
+ "description" => data_collector.error_description,
+ }
+ end
+
+ message
+ end
+
+ private
+
+ # @return [Integer] the number of resources successfully updated in the chef-client run
+ def updated_resource_count(action_collection)
+ return 0 if action_collection.nil?
+
+ action_collection.filtered_collection(up_to_date: false, skipped: false, unprocessed: false, failed: false).count
+ end
+
+ # @return [Array<Chef::ActionCollection::ActionRecord>] list of all action_records for all resources
+ def action_records(action_collection)
+ return [] if action_collection.nil?
+
+ action_collection.action_records
+ end
+
+ # @return [Array<Hash>] list of all action_records rendered as a Hash for sending to JSON
+ def all_action_records(action_collection)
+ action_records(action_collection).map { |rec| action_record_for_json(rec) }
+ end
+
+ # @return [Hash] the Hash representation of the action_record for sending as JSON
+ def action_record_for_json(action_record)
+ new_resource = action_record.new_resource
+ current_resource = action_record.current_resource
+ after_resource = action_record.after_resource
+
+ hash = {
+ "type" => new_resource.resource_name.to_sym,
+ "name" => new_resource.name.to_s,
+ "id" => safe_resource_identity(new_resource),
+ "after" => safe_state_for_resource_reporter(after_resource || new_resource),
+ "before" => safe_state_for_resource_reporter(current_resource),
+ "duration" => action_record.elapsed_time.nil? ? "" : (action_record.elapsed_time * 1000).to_i.to_s,
+ "delta" => new_resource.respond_to?(:diff) && updated_or_failed?(action_record) ? new_resource.diff : "",
+ "ignore_failure" => new_resource.ignore_failure,
+ "result" => action_record.action.to_s,
+ "status" => action_record_status_for_json(action_record),
+ }
+
+ # don't use the new_resource for the after_resource if it is skipped or failed
+ if action_record.status == :skipped || action_record.status == :failed || action_record.status == :unprocessed
+ hash["after"] = {}
+ end
+
+ 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"] = action_record.conditional.to_text if action_record.status == :skipped
+
+ unless action_record.exception.nil?
+ hash["error_message"] = action_record.exception.message
+
+ hash["error"] = {
+ "class" => action_record.exception.class,
+ "message" => action_record.exception.message,
+ "backtrace" => action_record.exception.backtrace,
+ "description" => action_record.error_description,
+ }
+ end
+
+ hash
+ end
+
+ # If the identity property of a resource has been lazied (via a lazy name resource) evaluating it
+ # for an unprocessed resource (where the preconditions have not been met) may cause the lazy
+ # evaluator to throw -- and would otherwise crash the data collector.
+ #
+ # @return [String] the resource's identity property
+ #
+ def safe_resource_identity(new_resource)
+ new_resource.identity.to_s
+ rescue => e
+ "unknown identity (due to #{e.class})"
+ end
+
+ # FIXME: This is likely necessary due to the same lazy issue with properties and failing resources?
+ #
+ # @return [Hash] the resource's reported state properties
+ #
+ def safe_state_for_resource_reporter(resource)
+ resource ? resource.state_for_resource_reporter : {}
+ rescue
+ {}
+ end
+
+ # Helper to convert action record status (symbols) to strings for the Data Collector server.
+ # Does a bit of necessary underscores-to-dashes conversion to comply with the Data Collector API.
+ #
+ # @return [String] resource status (
+ #
+ def action_record_status_for_json(action_record)
+ action = action_record.status.to_s
+ action = "up-to-date" if action == "up_to_date"
+ action
+ end
+
+ # @return [Boolean] True if the resource was updated or failed
+ def updated_or_failed?(action_record)
+ action_record.status == :updated || action_record.status == :failed
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/data_collector/run_start_message.rb b/lib/chef/data_collector/run_start_message.rb
new file mode 100644
index 0000000000..20ac867ef1
--- /dev/null
+++ b/lib/chef/data_collector/run_start_message.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright (c) 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 "message_helpers"
+
+class Chef
+ class DataCollector
+ module RunStartMessage
+ extend Chef::DataCollector::MessageHelpers
+
+ # This module encapsulates rendering the run_start_message given the state gathered in the data_collector.
+ # It is deliberately a stateless module and is deliberately not mixed into the data_collector and only
+ # uses the public api methods of the data_collector.
+ #
+ # No external code should call this module directly.
+ #
+ # @api private
+ class << self
+
+ # Construct the message payload that is sent to the DataCollector server at the
+ # start of a Chef run.
+ #
+ # @param data_collector [Chef::DataCollector::Reporter] the calling data_collector instance
+ #
+ # @return [Hash] A hash containing the run start message data.
+ #
+ def construct_message(data_collector)
+ run_status = data_collector.run_status
+ node = data_collector.node
+ {
+ "chef_server_fqdn" => URI(Chef::Config[:chef_server_url]).host,
+ "entity_uuid" => Chef::Config[:chef_guid],
+ "id" => run_status&.run_id,
+ "message_version" => "1.0.0",
+ "message_type" => "run_start",
+ "node_name" => node&.name || data_collector.node_name,
+ "organization_name" => organization,
+ "run_id" => run_status&.run_id,
+ "source" => solo_run? ? "chef_solo" : "chef_client",
+ "start_time" => run_status&.start_time&.utc&.iso8601,
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/decorator.rb b/lib/chef/decorator.rb
index 546c49baed..9f8b304d31 100644
--- a/lib/chef/decorator.rb
+++ b/lib/chef/decorator.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,13 @@
#
require "delegate"
+require_relative "constants"
class Chef
class Decorator < SimpleDelegator
- NULL = ::Object.new
-
- def initialize(obj = NULL)
+ def initialize(obj = NOT_PASSED)
@__defined_methods__ = []
- super unless obj.equal?(NULL)
+ super unless obj.equal?(NOT_PASSED)
end
# if we wrap a nil then decorator.nil? should be true
@@ -38,7 +37,7 @@ class Chef
# if we wrap a Hash then decorator.kind_of?(Hash) should be true
def kind_of?(klass)
- __getobj__.kind_of?(klass) || super
+ __getobj__.is_a?(klass) || super
end
# reset our methods on the instance if the object changes under us (this also
@@ -52,7 +51,7 @@ class Chef
# adding the define_singleton_method call and @__defined_methods__ tracking
def method_missing(m, *args, &block)
r = true
- target = self.__getobj__ { r = false }
+ target = __getobj__ { r = false }
if r && target.respond_to?(m)
# these next 4 lines are the patched code
diff --git a/lib/chef/decorator/lazy.rb b/lib/chef/decorator/lazy.rb
index 71a2151d65..5badc05ade 100644
--- a/lib/chef/decorator/lazy.rb
+++ b/lib/chef/decorator/lazy.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +15,7 @@
# limitations under the License.
#
-require "chef/decorator"
+require_relative "../decorator"
class Chef
class Decorator
diff --git a/lib/chef/decorator/lazy_array.rb b/lib/chef/decorator/lazy_array.rb
index dc8ea832ee..9859c49d77 100644
--- a/lib/chef/decorator/lazy_array.rb
+++ b/lib/chef/decorator/lazy_array.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,14 @@
# limitations under the License.
#
-require "chef/decorator/lazy"
+require_relative "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
+ # This makes access lazy 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 evaluate
# 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.
#
diff --git a/lib/chef/decorator/unchain.rb b/lib/chef/decorator/unchain.rb
index 8093c70f4c..30179d4e63 100644
--- a/lib/chef/decorator/unchain.rb
+++ b/lib/chef/decorator/unchain.rb
@@ -38,22 +38,6 @@ class Chef
__path__.push(key)
@delegate_sd_obj.public_send(__method__, *__path__, value)
end
-
- # unfortunately we have to support method_missing for node.set_unless.foo.bar = 'baz' notation
- def method_missing(symbol, *args)
- if symbol == :to_ary
- merged_attributes.send(symbol, *args)
- elsif args.empty?
- Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
- self[symbol]
- elsif symbol.to_s =~ /=$/
- Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
- 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
end
end
end
diff --git a/lib/chef/delayed_evaluator.rb b/lib/chef/delayed_evaluator.rb
index 25cd3a17b8..df734c8303 100644
--- a/lib/chef/delayed_evaluator.rb
+++ b/lib/chef/delayed_evaluator.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser <jkeiser@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/deprecated.rb b/lib/chef/deprecated.rb
new file mode 100644
index 0000000000..992876c17d
--- /dev/null
+++ b/lib/chef/deprecated.rb
@@ -0,0 +1,262 @@
+#--
+# Copyright:: Copyright (c) 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 "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, location)
+ Chef::Deprecated.const_get(convert_to_class_name(type.to_s)).new(message, location)
+ end
+ end
+
+ class Base
+ BASE_URL = "https://docs.chef.io/deprecations_".freeze
+
+ attr_reader :message, :location
+
+ def initialize(msg = nil, location = nil)
+ @message = msg
+ @location = location
+ end
+
+ def link
+ "Please see #{url} for further details and information on how to correct this problem."
+ end
+
+ # Render the URL for the deprecation documentation page.
+ #
+ # @return [String]
+ def url
+ "#{BASE_URL}#{self.class.doc_page}/"
+ end
+
+ # Render the user-visible message for this deprecation.
+ #
+ # @return [String]
+ def to_s
+ "Deprecation CHEF-#{self.class.deprecation_id} from #{location}\n\n #{message}\n\n#{link}"
+ end
+
+ # Check if this deprecation has been silenced.
+ #
+ # @return [Boolean]
+ def silenced?
+ # Check if all warnings have been silenced.
+ return true if Chef::Config[:silence_deprecation_warnings] == true
+ # Check if this warning has been silenced by the config.
+ return true if Chef::Config[:silence_deprecation_warnings].any? do |silence_spec|
+ if silence_spec.is_a? Integer
+ # Integers can end up matching the line number in the `location` string
+ silence_spec = "CHEF-#{silence_spec}"
+ else
+ # Just in case someone uses a symbol in the config by mistake.
+ silence_spec = silence_spec.to_s
+ end
+ # Check for a silence by deprecation name, or by location.
+ self.class.deprecation_key == silence_spec || self.class.deprecation_id.to_s == silence_spec || "chef-#{self.class.deprecation_id}" == silence_spec.downcase || location.include?(silence_spec)
+ end
+ # check if this warning has been silenced by inline comment.
+ return true if location =~ /^(.*?):(\d+):in/ && begin
+ # Don't buffer the whole file in memory, so read it one line at a time.
+ line_no = $2.to_i
+ location_file = ::File.open($1)
+ (line_no - 1).times { location_file.readline } # Read all the lines we don't care about.
+ relevant_line = location_file.readline
+ relevant_line.match?(/#.*chef:silence_deprecation($|[^:]|:#{self.class.deprecation_key})/)
+ end
+
+ false
+ end
+
+ class << self
+ attr_reader :deprecation_id, :doc_page
+
+ # Return the deprecation key as would be used with {Chef::Deprecated.create}.
+ #
+ # @return [String]
+ def deprecation_key
+ Chef::Mixin::ConvertToClassName.convert_to_snake_case(name, "Chef::Deprecated")
+ end
+
+ # Set the ID and documentation page path for this deprecation.
+ #
+ # Used in subclasses to set the data for each type of deprecation.
+ #
+ # @example
+ # class MyDeprecation < Base
+ # target 123, "my_deprecation"
+ # end
+ # @param id [Integer] Deprecation ID number. This must be unique among
+ # all deprecations.
+ # @param page [String, nil] Optional documentation page path. If not
+ # specified, the deprecation key is used.
+ # @return [void]
+ def target(id, page = nil)
+ @deprecation_id = id
+ @doc_page = page || deprecation_key.to_s
+ end
+ end
+ end
+
+ class InternalApi < Base
+ target 0
+ end
+
+ class JsonAutoInflate < Base
+ target 1
+ end
+
+ class ExitCode < Base
+ target 2
+ end
+
+ # id 3 has been deleted
+
+ class Attributes < Base
+ target 4
+ end
+
+ class CustomResource < Base
+ target 5, "custom_resource_cleanups"
+ end
+
+ class EasyInstall < Base
+ target 6
+ end
+
+ class VerifyFile < Base
+ target 7
+ end
+
+ class SupportsProperty < Base
+ target 8
+ end
+
+ class ChefRest < Base
+ target 9
+ end
+
+ class DnfPackageAllowDowngrade < Base
+ target 10
+ end
+
+ class PropertyNameCollision < Base
+ target 11
+ end
+
+ class LaunchdHashProperty < Base
+ target 12
+ end
+
+ class ChefPlatformMethods < Base
+ target 13
+ end
+
+ class RunCommand < Base
+ target 14
+ end
+
+ class PackageMisc < Base
+ target 15
+ end
+
+ class MultiresourceMatch < Base
+ target 16
+ end
+
+ class UseInlineResources < Base
+ target 17
+ end
+
+ class LocalListen < Base
+ target 18
+ end
+
+ class NamespaceCollisions < Base
+ target 19
+ end
+
+ class DeployResource < Base
+ target 21
+ end
+
+ class ErlResource < Base
+ target 22
+ end
+
+ class FreebsdPkgProvider < Base
+ target 23
+ end
+
+ # id 25 was deleted
+
+ # id 3694 was deleted
+
+ # Returned when using the deprecated option on a property
+ class Property < Base
+ target 24
+
+ def to_s
+ "Deprecated resource property used from #{location}\n\n #{message}\n\nPlease consult the resource documentation for more information."
+ end
+ end
+
+ class ShellOut < Base
+ target 26
+ end
+
+ class LocaleLcAll < Base
+ target 27
+ end
+
+ class ChefSugar < Base
+ target 28
+ end
+
+ class KnifeBootstrapApis < Base
+ target 29
+ end
+
+ class ArchiveFileIntegerFileMode < Base
+ target 30
+ end
+
+ class ResourceNameWithoutProvides < Base
+ target 31
+ end
+
+ class AttributeBlacklistConfiguration < Base
+ target 32
+ end
+
+ class Generic < Base
+ def url
+ "https://docs.chef.io/chef_deprecations_client/"
+ end
+
+ def to_s
+ "Deprecation from #{location}\n\n #{message}\n\n#{link}"
+ 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 aefb04752e..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_legacy_mode]
- 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..f83101ca3e 100644
--- a/lib/chef/deprecation/warnings.rb
+++ b/lib/chef/deprecation/warnings.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,13 +20,15 @@ class Chef
module Deprecation
module Warnings
+ require_relative "../version"
+ require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
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 #{ChefUtils::Dist::Infra::PRODUCT} #{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/digester.rb b/lib/chef/digester.rb
index 6e4588a661..36df63f3a1 100644
--- a/lib/chef/digester.rb
+++ b/lib/chef/digester.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2009-2016, Daniel DeLeo
# License:: Apache License, Version 2.0
#
@@ -18,8 +18,9 @@
# limitations under the License.
#
-require "openssl"
-require "singleton"
+autoload :OpenSSL, "openssl"
+autoload :Digest, "digest"
+require "singleton" unless defined?(Singleton)
class Chef
class Digester
@@ -39,9 +40,9 @@ class Chef
def generate_checksum(file)
if file.is_a?(StringIO)
- checksum_io(file, OpenSSL::Digest::SHA256.new)
+ checksum_io(file, OpenSSL::Digest.new("SHA256"))
else
- checksum_file(file, OpenSSL::Digest::SHA256.new)
+ checksum_file(file, OpenSSL::Digest.new("SHA256"))
end
end
@@ -50,11 +51,11 @@ class Chef
end
def generate_md5_checksum_for_file(file)
- checksum_file(file, OpenSSL::Digest::MD5.new)
+ checksum_file(file, ::Digest::MD5.new)
end
def generate_md5_checksum(io)
- checksum_io(io, OpenSSL::Digest::MD5.new)
+ checksum_io(io, ::Digest::MD5.new)
end
private
diff --git a/lib/chef/dsl.rb b/lib/chef/dsl.rb
index 1fa0099e91..6893298d1d 100644
--- a/lib/chef/dsl.rb
+++ b/lib/chef/dsl.rb
@@ -1,6 +1,6 @@
-require "chef/dsl/recipe"
-require "chef/dsl/platform_introspection"
-require "chef/dsl/data_query"
-require "chef/dsl/include_recipe"
-require "chef/dsl/include_attribute"
-require "chef/dsl/registry_helper"
+require_relative "dsl/recipe"
+require_relative "dsl/platform_introspection"
+require_relative "dsl/data_query"
+require_relative "dsl/include_recipe"
+require_relative "dsl/include_attribute"
+require_relative "dsl/registry_helper"
diff --git a/lib/chef/dsl/audit.rb b/lib/chef/dsl/audit.rb
deleted file mode 100644
index 98271dc5cb..0000000000
--- a/lib/chef/dsl/audit.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "chef/exceptions"
-
-class Chef
- module DSL
- module Audit
-
- # Can encompass tests in a `control` block or `describe` block
- # Adds the controls group and block (containing controls to execute) to the runner's list of pending examples
- def control_group(*args, &block)
- raise Chef::Exceptions::NoAuditsProvided unless block
-
- name = args[0]
- if name.nil? || name.empty?
- raise Chef::Exceptions::AuditNameMissing
- elsif run_context.audits.has_key?(name)
- raise Chef::Exceptions::AuditControlGroupDuplicate.new(name)
- end
-
- # This DSL will only work in the Recipe class because that exposes the cookbook_name
- 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,
- line_number: block.source_location[1],
- }
-
- run_context.audits[name] = Struct.new(:args, :block, :metadata).new(args, block, metadata)
- end
-
- end
- end
-end
diff --git a/lib/chef/dsl/chef_provisioning.rb b/lib/chef/dsl/chef_provisioning.rb
deleted file mode 100644
index a91d297d02..0000000000
--- a/lib/chef/dsl/chef_provisioning.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@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.
-#
-
-class Chef
- module DSL
- # Lazy activation for the chef-provisioning gem. Specifically, we set up methods for
- # each resource and DSL method in chef-provisioning which, when invoked, will
- # require 'chef-provisioning' (which will define the actual method) and then call the
- # method chef-provisioning defined.
- module ChefProvisioning
- %w{
- add_machine_options
- current_image_options
- current_machine_options
- load_balancer
- machine_batch
- machine_execute
- machine_file
- machine_image
- machine
- with_driver
- with_image_options
- with_machine_options
- }.each do |method_name|
- eval(<<-EOM, binding, __FILE__, __LINE__ + 1)
- def #{method_name}(*args, &block)
- Chef::DSL::ChefProvisioning.load_chef_provisioning
- self.#{method_name}(*args, &block)
- end
- EOM
- end
-
- def self.load_chef_provisioning
- # Remove all chef-provisioning methods; they will be added back in by chef-provisioning
- public_instance_methods(false).each do |method_name|
- remove_method(method_name)
- end
- require "chef/provisioning"
- end
- end
- end
-end
diff --git a/lib/chef/dsl/chef_vault.rb b/lib/chef/dsl/chef_vault.rb
new file mode 100644
index 0000000000..031627c358
--- /dev/null
+++ b/lib/chef/dsl/chef_vault.rb
@@ -0,0 +1,84 @@
+#
+# Author: Joshua Timberman <joshua@chef.io>
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2014-2015 Bloomberg Finance L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+autoload :ChefVault, "chef-vault"
+require_relative "data_query"
+
+class Chef
+ module DSL
+ module ChefVault
+ include Chef::DSL::DataQuery
+
+ # Helper method which provides a Recipe/Resource DSL for wrapping
+ # creation of {ChefVault::Item}.
+ # @note
+ # Falls back to normal data bag item loading if the item is not
+ # actually a Chef Vault item. This is controlled via
+ # +node['chef-vault']['databag_fallback']+.
+ # @example
+ # item = chef_vault_item('secrets', 'bacon')
+ # log 'Yeah buddy!' if item['_default']['type']
+ # @param [String] bag Name of the data bag to load from.
+ # @param [String] id Identifier of the data bag item to load.
+ def chef_vault_item(bag, id)
+ if ::ChefVault::Item.vault?(bag, id)
+ ::ChefVault::Item.load(bag, id)
+ elsif node["chef-vault"]["databag_fallback"]
+ data_bag_item(bag, id)
+ else
+ raise "Trying to load a regular data bag item #{id} from #{bag}, and databag_fallback is disabled"
+ end
+ end
+
+ # Helper method that allows for listing the ids of a vault in a recipe.
+ # This method is needed because data_bag() returns the keys along with
+ # the items, so this method strips out the keys for users so that they
+ # don't have to do it in their recipes.
+ # @example
+ # ids = chef_vault('secrets')
+ # log 'Yeah buddy!' if ids[0] == 'bacon'
+ # @param [String] bag Name of the data bag to load from.
+ # @return [Array]
+ def chef_vault(bag)
+ raise "'#{bag}' is not a vault" unless Chef::DataBag.list.include? bag
+
+ pattern = Regexp.new(/_keys$/).freeze
+ data_bag(bag).each_with_object([]) do |id, acc|
+ acc << id unless pattern.match?(id)
+ end
+ end
+
+ # Helper method which provides an environment wrapper for a data bag.
+ # This allows for easy access to current environment secrets inside
+ # of an item.
+ # @example
+ # item = chef_vault_item_for_environment('secrets', 'bacon')
+ # log 'Yeah buddy!' if item['type'] == 'applewood_smoked'
+ # @param [String] bag Name of the data bag to load from.
+ # @param [String] id Identifier of the data bag item to load.
+ # @return [Hash]
+ def chef_vault_item_for_environment(bag, id)
+ item = chef_vault_item(bag, id)
+ return {} unless item[node.chef_environment]
+
+ item[node.chef_environment]
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/dsl/cheffish.rb b/lib/chef/dsl/cheffish.rb
index 03290b3674..91b87663c7 100644
--- a/lib/chef/dsl/cheffish.rb
+++ b/lib/chef/dsl/cheffish.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/dsl/core.rb b/lib/chef/dsl/core.rb
deleted file mode 100644
index 11507857cf..0000000000
--- a/lib/chef/dsl/core.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#--
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Christopher Walters (<cw@chef.io>)
-# 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.
-#
-
-require "chef/dsl/declare_resource"
-require "chef/dsl/universal"
-require "chef/mixin/notifying_block"
-require "chef/mixin/lazy_module_include"
-
-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 Core
- include Chef::DSL::Universal
- include Chef::DSL::DeclareResource
- include Chef::Mixin::NotifyingBlock
- 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..3b15affb5b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,18 @@
# limitations under the License.
#
-require "chef/search/query"
-require "chef/data_bag"
-require "chef/data_bag_item"
-require "chef/encrypted_data_bag_item"
-require "chef/encrypted_data_bag_item/check_encrypted"
+require_relative "../search/query"
+Chef.autoload :DataBag, File.expand_path("../data_bag", __dir__)
+Chef.autoload :DataBagItem, File.expand_path("../data_bag_item", __dir__)
+require_relative "../encrypted_data_bag_item"
+require_relative "../encrypted_data_bag_item/check_encrypted"
class Chef
module DSL
- # ==Chef::DSL::DataQuery
- # Provides DSL for querying data from the chef-server via search or data
- # bag.
+ # Provides DSL helper methods for querying the search interface, data bag
+ # interface or node interface.
+ #
module DataQuery
include Chef::EncryptedDataBagItem::CheckEncrypted
@@ -38,7 +38,7 @@ class Chef
if Kernel.block_given? || args.length >= 4
Chef::Search::Query.new.search(*args, &block)
else
- results = Array.new
+ results = []
Chef::Search::Query.new.search(*args) do |o|
results << o
end
@@ -80,10 +80,24 @@ class Chef
raise
end
+ #
+ # Note that this is mixed into the Universal DSL so access to the node needs to be done
+ # through the run_context and not accessing the node method directly, since the node method
+ # is not as universal as the run_context.
+ #
+
+ # True if all the tags are set on the node.
+ #
+ # @param [Array<String>] tags to check against
+ # @return boolean
+ #
+ def tagged?(*tags)
+ tags.each do |tag|
+ return false unless run_context.node.tags.include?(tag)
+ end
+ true
+ 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/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index 86227a0f9d..8053098085 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/exceptions"
+require_relative "../exceptions"
class Chef
module DSL
@@ -41,13 +41,14 @@ class Chef
#
def with_run_context(rc)
raise ArgumentError, "with_run_context is useless without a block" unless block_given?
+
old_run_context = @run_context
@run_context =
case rc
when Chef::RunContext
rc
when :root
- Chef.run_context
+ run_context.root_run_context
when :parent
run_context.parent_run_context
else
@@ -68,7 +69,7 @@ class Chef
# @return [Chef::Resource] The resource
#
# @example
- # delete_resource!(:template, '/x/y.txy')
+ # delete_resource!(:template, '/x/y.txt')
#
def delete_resource!(type, name, run_context: self.run_context)
run_context.resource_collection.delete("#{type}[#{name}]").tap do |resource|
@@ -92,7 +93,7 @@ class Chef
# @return [Chef::Resource] The resource
#
# @example
- # delete_resource(:template, '/x/y.txy')
+ # delete_resource(:template, '/x/y.txt')
#
def delete_resource(type, name, run_context: self.run_context)
delete_resource!(type, name, run_context: run_context)
@@ -113,13 +114,19 @@ class Chef
# @return [Chef::Resource] The updated resource
#
# @example
- # edit_resource!(:template, '/x/y.txy') do
+ # edit_resource!(:template, '/x/y.txt') do
# 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
@@ -140,16 +147,45 @@ class Chef
# @return [Chef::Resource] The updated or created resource
#
# @example
- # resource = edit_resource(:template, '/x/y.txy') do
- # source "y.txy.erb"
+ # resource = edit_resource(:template, '/x/y.txt') do
+ # source "y.txt.erb"
# variables {}
# end
- # resource.variables.merge!({ home: "/home/klowns" })
+ # resource.variables.merge!({ home: "/home/clowns" })
#
- 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
@@ -163,10 +199,11 @@ class Chef
# @return [Chef::Resource] The updated resource
#
# @example
- # resource = find_resource!(:template, '/x/y.txy')
+ # resource = find_resource!(:template, '/x/y.txt')
#
def find_resource!(type, name, run_context: self.run_context)
raise ArgumentError, "find_resource! does not take a block" if block_given?
+
run_context.resource_collection.find(type => name)
end
@@ -182,7 +219,7 @@ class Chef
# @return [Chef::Resource] The updated resource
#
# @example
- # if ( find_resource(:template, '/x/y.txy') )
+ # if ( find_resource(:template, '/x/y.txt') )
# # do something
# else
# # don't worry about the error
@@ -200,8 +237,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,7 +259,7 @@ class Chef
# @return [Chef::Resource] The new resource.
#
# @example
- # declare_resource(:file, '/x/y.txy', caller[0]) do
+ # declare_resource(:file, '/x/y.txt', caller[0]) do
# action :delete
# end
# # Equivalent to
@@ -230,18 +267,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, enclosing_provider: nil, &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, &resource_attrs_block)
+ resource = build_resource(type, name, created_at: created_at, enclosing_provider: enclosing_provider, &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
@@ -262,16 +293,18 @@ class Chef
# @return [Chef::Resource] The new resource.
#
# @example
- # build_resource(:file, '/x/y.txy', caller[0]) do
+ # build_resource(:file, '/x/y.txt', caller[0]) do
# 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, enclosing_provider: nil, &resource_attrs_block)
created_at ||= caller[0]
# this needs to be lazy in order to avoid circular dependencies since ResourceBuilder
# will requires the entire provider+resolver universe
- require "chef/resource_builder" unless defined?(Chef::ResourceBuilder)
+ require_relative "../resource_builder" unless defined?(Chef::ResourceBuilder)
+
+ enclosing_provider ||= self if is_a?(Chef::Provider)
Chef::ResourceBuilder.new(
type: type,
@@ -281,9 +314,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: enclosing_provider
).build(&resource_attrs_block)
end
+
end
end
end
diff --git a/lib/chef/dsl/definitions.rb b/lib/chef/dsl/definitions.rb
index 60b1cf6f61..00f6d91739 100644
--- a/lib/chef/dsl/definitions.rb
+++ b/lib/chef/dsl/definitions.rb
@@ -18,7 +18,7 @@ class Chef
# @api private
def has_resource_definition?(name)
- run_context.definitions.has_key?(name)
+ run_context.definitions.key?(name)
end
# Processes the arguments and block as a resource definition.
diff --git a/lib/chef/dsl/include_attribute.rb b/lib/chef/dsl/include_attribute.rb
index 6d27fefc25..d924974069 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 (c) 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.
#
-require "chef/log"
+require_relative "../log"
class Chef
module DSL
@@ -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..e7ac982d02 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 (c) 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.
#
-require "chef/log"
+require_relative "../log"
class Chef
module DSL
@@ -29,16 +29,6 @@ class Chef
def load_recipe(recipe_name)
run_context.load_recipe(recipe_name, current_cookbook: cookbook_name)
end
-
- def require_recipe(*args)
- Chef::Log.warn("require_recipe is deprecated and will be removed in a future release, please use include_recipe")
- include_recipe(*args)
- end
-
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 a0c2d33967..49ec396cf2 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,17 @@
# limitations under the License.
#
+autoload :ChefUtils, "chef-utils"
+require_relative "../mixin/chef_utils_wiring" unless defined?(Chef::Mixin::ChefUtilsWiring)
+
class Chef
module DSL
-
# == Chef::DSL::PlatformIntrospection
# Provides the DSL for platform-dependent switch logic, such as
# #value_for_platform.
module PlatformIntrospection
+ include ChefUtils
+ include Chef::Mixin::ChefUtilsWiring
# Implementation class for determining platform dependent values
class PlatformDependentValue
@@ -68,41 +72,41 @@ 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)
- end
- 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}"
+ 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|
+
+ if Chef::VersionConstraint::Platform.new(k).include?(node_version)
+ key_matches << k
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)
- return nil
+ 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
+ nil
+ when 1
+ @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)
@@ -126,7 +130,7 @@ class Chef
end
def assert_valid_platform_values!(platforms, value)
- unless value.kind_of?(Hash)
+ unless value.is_a?(Hash)
msg = "platform dependent values must be specified in the format :platform => {:version => value} "
msg << "you gave a value #{value.inspect} for platform(s) #{platforms}"
raise ArgumentError, msg
@@ -166,7 +170,7 @@ class Chef
has_platform
end
- # Implementation class for determining platform family dependent values
+ # Implementation class for determining platform family dependent values
class PlatformFamilyDependentValue
# Create a platform family dependent value object.
@@ -245,26 +249,17 @@ 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.
+ # a simple helper to determine if we're on a windows release pre-2012 / 8
#
- # === 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"
+ # @deprecated Windows releases before Windows 2012 and 8 are no longer supported
+ # @return [Boolean] Is the system older than Windows 8 / 2012
+ def older_than_win_2012_or_8?(node = run_context.nil? ? nil : run_context.node)
+ false # we don't support platforms that would be true
end
+ # ^^^^^^ NOTE: PLEASE DO NOT CONTINUE TO ADD THESE KINDS OF PLATFORM_VERSION APIS WITHOUT ^^^^^^^
+ # ^^^^^^ GOING THROUGH THE DESIGN REVIEW PROCESS AND ADDRESS THE EXISTING CHEF-SUGAR ONES ^^^^^^^
+ # ^^^^^^ DO "THE HARD RIGHT THING" AND ADDRESS THE BROADER PROBLEM AND FIX IT ALL. ^^^^^^^
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 7dc7a9a0f6..d01efc0b2a 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/dsl/powershell.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/util/powershell/ps_credential"
+require_relative "../util/powershell/ps_credential"
class Chef
module DSL
diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb
index e8982d290e..19d3a6b0bd 100644
--- a/lib/chef/dsl/reboot_pending.rb
+++ b/lib/chef/dsl/reboot_pending.rb
@@ -1,6 +1,6 @@
# Author:: Bryan McLellan <btm@loftninjas.org>
# Author:: Seth Chisamore <schisamo@chef.io>
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/dsl/platform_introspection"
-require "chef/dsl/registry_helper"
+require_relative "platform_introspection"
+require_relative "registry_helper"
class Chef
module DSL
@@ -30,28 +30,21 @@ 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
# due to a file being in use (usually a temporary file and a system file)
# \??\c:\temp\test.sys!\??\c:\winnt\system32\test.sys
# http://technet.microsoft.com/en-us/library/cc960241.aspx
- registry_value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => "PendingFileRenameOperations" }) ||
+ registry_value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { name: "PendingFileRenameOperations" }) ||
- # RebootRequired key contains Update IDs with a value of 1 if they require a reboot.
- # The existence of RebootRequired alone is sufficient on my Windows 8.1 workstation in Windows Update
+ # RebootRequired key contains Update IDs with a value of 1 if they require a reboot.
+ # The existence of RebootRequired alone is sufficient on my Windows 8.1 workstation in Windows Update
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]))
+ # Vista + Server 2008 and newer may have reboots pending from CBS
+ 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 1bb8f130af..ad85a9dd91 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,33 +17,24 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/dsl/resources"
-require "chef/dsl/definitions"
-require "chef/dsl/data_query"
-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"
+require_relative "../exceptions"
+require_relative "resources"
+require_relative "definitions"
+require_relative "include_recipe"
+require_relative "reboot_pending"
+require_relative "universal"
+require_relative "declare_resource"
+require_relative "../mixin/notifying_block"
+require_relative "../mixin/lazy_module_include"
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.
+ # Chef::DSL::Recipe mixes into Recipes 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.
@@ -51,17 +42,13 @@ class Chef
# - it also pollutes the namespace of nearly every context, watch out.
#
module Recipe
- include Chef::DSL::Core
- include Chef::DSL::DataQuery
+ include Chef::DSL::Universal
+ include Chef::DSL::DeclareResource
+ include Chef::Mixin::NotifyingBlock
include Chef::DSL::IncludeRecipe
- include Chef::DSL::RegistryHelper
include Chef::DSL::RebootPending
- include Chef::DSL::Audit
- 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)
@@ -77,19 +64,6 @@ 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/registry_helper.rb b/lib/chef/dsl/registry_helper.rb
index 63da635ef1..c1ebdf68e2 100644
--- a/lib/chef/dsl/registry_helper.rb
+++ b/lib/chef/dsl/registry_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index 7bbfeb2914..190d5be71a 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/dsl/cheffish"
-require "chef/dsl/chef_provisioning"
+require_relative "cheffish"
class Chef
module DSL
@@ -27,26 +26,16 @@ class Chef
#
# @api private
module Resources
- # Include the lazy loaders for cheffish and chef-provisioning, so that the
- # resource DSL is there but the gems aren't activated yet.
+ # Include the lazy loader for cheffish, so that the
+ # resource DSL is there but the gem isn't activated yet.
include Chef::DSL::Cheffish
- 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
+ EOM
end
def self.remove_resource_dsl(dsl_name)
diff --git a/lib/chef/dsl/universal.rb b/lib/chef/dsl/universal.rb
index 810792f542..1e4e4218d8 100644
--- a/lib/chef/dsl/universal.rb
+++ b/lib/chef/dsl/universal.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,24 +17,24 @@
# limitations under the License.
#
-require "chef/dsl/platform_introspection"
-require "chef/mixin/powershell_out"
-require "chef/mixin/shell_out"
+require_relative "platform_introspection"
+require_relative "data_query"
+require_relative "chef_vault"
+require_relative "registry_helper"
+require_relative "powershell"
+require_relative "../mixin/powershell_exec"
+require_relative "../mixin/powershell_out"
+require_relative "../mixin/shell_out"
+require_relative "../mixin/lazy_module_include"
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.
+ # Chef::DSL::Recipe mixes into Recipes 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.
@@ -43,8 +43,14 @@ class Chef
#
module Universal
include Chef::DSL::PlatformIntrospection
+ include Chef::DSL::DataQuery
+ include Chef::DSL::ChefVault
+ include Chef::DSL::RegistryHelper
+ include Chef::DSL::Powershell
+ include Chef::Mixin::PowershellExec
include Chef::Mixin::PowershellOut
include Chef::Mixin::ShellOut
+ extend Chef::Mixin::LazyModuleInclude
end
end
end
diff --git a/lib/chef/encrypted_data_bag_item.rb b/lib/chef/encrypted_data_bag_item.rb
index e696199c63..9cf1a71db2 100644
--- a/lib/chef/encrypted_data_bag_item.rb
+++ b/lib/chef/encrypted_data_bag_item.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,10 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/data_bag_item"
-require "chef/encrypted_data_bag_item/decryptor"
-require "chef/encrypted_data_bag_item/encryptor"
-require "open-uri"
+require_relative "config"
+Chef.autoload :DataBagItem, File.expand_path("data_bag_item", __dir__)
+require_relative "encrypted_data_bag_item/decryptor"
+require_relative "encrypted_data_bag_item/encryptor"
# An EncryptedDataBagItem represents a read-only data bag item where
# all values, except for the value associated with the id key, have
@@ -47,8 +46,8 @@ require "open-uri"
# such nodes in the infrastructure.
#
class Chef::EncryptedDataBagItem
- ALGORITHM = "aes-256-cbc"
- AEAD_ALGORITHM = "aes-256-gcm"
+ ALGORITHM = "aes-256-cbc".freeze
+ AEAD_ALGORITHM = "aes-256-gcm".freeze
#
# === Synopsis
@@ -84,10 +83,12 @@ class Chef::EncryptedDataBagItem
raise ArgumentError, "assignment not supported for #{self.class}"
end
- def to_hash
+ def to_h
@enc_hash.keys.inject({}) { |hash, key| hash[key] = self[key]; hash }
end
+ alias_method :to_hash, :to_h
+
def self.encrypt_data_bag_item(plain_hash, secret)
plain_hash.inject({}) do |h, (key, val)|
h[key] = if key != "id"
@@ -121,17 +122,19 @@ 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)
+ require "open-uri" unless defined?(OpenURI)
path ||= Chef::Config[:encrypted_data_bag_secret]
- if !path
- raise ArgumentError, "No secret specified and no secret found at #{Chef::Config.platform_specific_path('/etc/chef/encrypted_data_bag_secret')}"
+ unless path
+ raise ArgumentError, "No secret specified and no secret found at #{Chef::Config.platform_specific_path(ChefConfig::Config.etc_chef_dir) + "/encrypted_data_bag_secret"}"
end
+
secret = case path
- when /^\w+:\/\//
+ when %r{^\w+://}
# We have a remote key
begin
Kernel.open(path).read.strip
@@ -141,14 +144,16 @@ class Chef::EncryptedDataBagItem
raise ArgumentError, "Remote key not found at '#{path}'"
end
else
- if !File.exist?(path)
+ unless File.exist?(path)
raise Errno::ENOENT, "file not found '#{path}'"
end
+
IO.read(path).strip
end
if secret.size < 1
raise ArgumentError, "invalid zero length secret in '#{path}'"
end
+
secret
end
diff --git a/lib/chef/encrypted_data_bag_item/assertions.rb b/lib/chef/encrypted_data_bag_item/assertions.rb
index e8f8bfcbf2..13ed0de050 100644
--- a/lib/chef/encrypted_data_bag_item/assertions.rb
+++ b/lib/chef/encrypted_data_bag_item/assertions.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format"
-require "chef/encrypted_data_bag_item/unsupported_cipher"
+require_relative "unacceptable_encrypted_data_bag_item_format"
+require_relative "unsupported_cipher"
class Chef::EncryptedDataBagItem
@@ -27,10 +27,10 @@ class Chef::EncryptedDataBagItem
module Assertions
def assert_format_version_acceptable!(format_version)
- unless format_version.kind_of?(Integer) && format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
+ unless format_version.is_a?(Integer) && format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
raise UnacceptableEncryptedDataBagItemFormat,
"The encrypted data bag item has format version `#{format_version}', " +
- "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
+ "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
end
end
diff --git a/lib/chef/encrypted_data_bag_item/check_encrypted.rb b/lib/chef/encrypted_data_bag_item/check_encrypted.rb
index cc378194ff..fc3a232fc8 100644
--- a/lib/chef/encrypted_data_bag_item/check_encrypted.rb
+++ b/lib/chef/encrypted_data_bag_item/check_encrypted.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/encrypted_data_bag_item/encryptor"
+require_relative "encryptor"
class Chef::EncryptedDataBagItem
# Common code for checking if a data bag appears encrypted
@@ -39,7 +39,8 @@ class Chef::EncryptedDataBagItem
# true only when there is an exact match between the VersionXEncryptor
# keys and the hash's keys.
def looks_like_encrypted?(data)
- return false unless data.is_a?(Hash) && data.has_key?("version")
+ return false unless data.is_a?(Hash) && data.key?("version")
+
case data["version"]
when 1
Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor.encryptor_keys.sort == data.keys.sort
diff --git a/lib/chef/encrypted_data_bag_item/decryption_failure.rb b/lib/chef/encrypted_data_bag_item/decryption_failure.rb
index 9d2d998057..5a57be9934 100644
--- a/lib/chef/encrypted_data_bag_item/decryption_failure.rb
+++ b/lib/chef/encrypted_data_bag_item/decryption_failure.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/encrypted_data_bag_item/decryptor.rb b/lib/chef/encrypted_data_bag_item/decryptor.rb
index 773ff4e154..57119796a9 100644
--- a/lib/chef/encrypted_data_bag_item/decryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/decryptor.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "yaml"
-require "chef/json_compat"
-require "openssl"
-require "base64"
-require "digest/sha2"
-require "chef/encrypted_data_bag_item"
-require "chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format"
-require "chef/encrypted_data_bag_item/decryption_failure"
-require "chef/encrypted_data_bag_item/assertions"
+autoload :YAML, "yaml"
+require_relative "../json_compat"
+autoload :OpenSSL, "openssl"
+autoload :Base64, "base64"
+require "digest/sha2" unless defined?(Digest::SHA2)
+require_relative "../encrypted_data_bag_item"
+require_relative "unsupported_encrypted_data_bag_item_format"
+require_relative "decryption_failure"
+require_relative "assertions"
class Chef::EncryptedDataBagItem
@@ -88,13 +88,14 @@ class Chef::EncryptedDataBagItem
end
def decrypted_data
- @decrypted_data ||= begin
- plaintext = openssl_decryptor.update(encrypted_bytes)
- 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.' : '' }"
- end
+ @decrypted_data ||=
+ begin
+ plaintext = openssl_decryptor.update(encrypted_bytes)
+ 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." : ""}"
+ end
end
def encrypted_bytes
@@ -102,12 +103,13 @@ class Chef::EncryptedDataBagItem
end
def openssl_decryptor
- @openssl_decryptor ||= begin
- d = OpenSSL::Cipher.new(algorithm)
- d.decrypt
- d.pkcs5_keyivgen(key)
- d
- end
+ @openssl_decryptor ||=
+ begin
+ d = OpenSSL::Cipher.new(algorithm)
+ d.decrypt
+ d.pkcs5_keyivgen(key)
+ d
+ end
end
end
@@ -139,25 +141,27 @@ class Chef::EncryptedDataBagItem
end
def decrypted_data
- @decrypted_data ||= begin
- plaintext = openssl_decryptor.update(encrypted_bytes)
- 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.' : '' }"
- end
+ @decrypted_data ||=
+ begin
+ plaintext = openssl_decryptor.update(encrypted_bytes)
+ 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." : ""}"
+ end
end
def openssl_decryptor
- @openssl_decryptor ||= begin
- assert_valid_cipher!(@encrypted_data["cipher"], algorithm)
- d = OpenSSL::Cipher.new(algorithm)
- d.decrypt
- # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
- d.key = OpenSSL::Digest::SHA256.digest(key)
- d.iv = iv
- d
- end
+ @openssl_decryptor ||=
+ begin
+ assert_valid_cipher!(@encrypted_data["cipher"], algorithm)
+ d = OpenSSL::Cipher.new(algorithm)
+ d.decrypt
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
+ d.key = OpenSSL::Digest.digest("SHA256", key)
+ d.iv = iv
+ d
+ end
end
end
@@ -184,6 +188,7 @@ class Chef::EncryptedDataBagItem
def candidate_hmac_matches?(expected_hmac)
return false unless @encrypted_data["hmac"]
+
expected_bytes = expected_hmac.bytes.to_a
candidate_hmac_bytes = Base64.decode64(@encrypted_data["hmac"]).bytes.to_a
valid = expected_bytes.size ^ candidate_hmac_bytes.size
@@ -209,16 +214,18 @@ class Chef::EncryptedDataBagItem
if auth_tag_b64.nil?
raise DecryptionFailure, "Error decrypting data bag value: invalid authentication tag. Most likely the data is corrupted"
end
+
Base64.decode64(auth_tag_b64)
end
def openssl_decryptor
- @openssl_decryptor ||= begin
- d = super
- d.auth_tag = auth_tag
- d.auth_data = ""
- d
- end
+ @openssl_decryptor ||=
+ begin
+ d = super
+ d.auth_tag = auth_tag
+ d.auth_data = ""
+ d
+ end
end
end
diff --git a/lib/chef/encrypted_data_bag_item/encryptor.rb b/lib/chef/encrypted_data_bag_item/encryptor.rb
index 8d34033db6..f068d134d8 100644
--- a/lib/chef/encrypted_data_bag_item/encryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/encryptor.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "base64"
-require "digest/sha2"
-require "openssl"
-require "ffi_yajl"
-require "chef/encrypted_data_bag_item"
-require "chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format"
-require "chef/encrypted_data_bag_item/encryption_failure"
-require "chef/encrypted_data_bag_item/assertions"
+autoload :Base64, "base64"
+require "digest/sha2" unless defined?(Digest::SHA2)
+autoload :OpenSSL, "openssl"
+autoload :FFI_Yajl, "ffi_yajl"
+require_relative "../encrypted_data_bag_item"
+require_relative "unsupported_encrypted_data_bag_item_format"
+require_relative "encryption_failure"
+require_relative "assertions"
class Chef::EncryptedDataBagItem
@@ -102,7 +102,7 @@ class Chef::EncryptedDataBagItem
encryptor = OpenSSL::Cipher.new(algorithm)
encryptor.encrypt
# We must set key before iv: https://bugs.ruby-lang.org/issues/8221
- encryptor.key = OpenSSL::Digest::SHA256.digest(key)
+ encryptor.key = OpenSSL::Digest.digest("SHA256", key)
@iv ||= encryptor.random_iv
encryptor.iv = @iv
encryptor
@@ -123,7 +123,7 @@ class Chef::EncryptedDataBagItem
# Strings) that do not produce valid JSON when serialized without the
# wrapper.
def serialized_data
- FFI_Yajl::Encoder.encode(:json_wrapper => plaintext_data)
+ FFI_Yajl::Encoder.encode(json_wrapper: plaintext_data)
end
def self.encryptor_keys
@@ -193,6 +193,7 @@ class Chef::EncryptedDataBagItem
if @auth_tag.nil?
raise EncryptionFailure, "Internal Error: GCM authentication tag read before encryption"
end
+
@auth_tag
end
diff --git a/lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb b/lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb
index 362954b24d..89e03ea6d8 100644
--- a/lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb
+++ b/lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/encrypted_data_bag_item/unsupported_cipher.rb b/lib/chef/encrypted_data_bag_item/unsupported_cipher.rb
index 6f1221bb68..228043e526 100644
--- a/lib/chef/encrypted_data_bag_item/unsupported_cipher.rb
+++ b/lib/chef/encrypted_data_bag_item/unsupported_cipher.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb b/lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb
index ea464636db..ed6e6bd850 100644
--- a/lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb
+++ b/lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/environment.rb b/lib/chef/environment.rb
index 32ebde6f4f..e651e1b4aa 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,31 +19,30 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/mash"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/version_constraint"
-require "chef/server_api"
+require_relative "config"
+require_relative "mash"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "version_constraint"
+require_relative "server_api"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Environment
- DEFAULT = "default"
+ DEFAULT = "default".freeze
include Chef::Mixin::ParamsValidate
include Chef::Mixin::FromFile
- attr_accessor :chef_server_rest
-
- COMBINED_COOKBOOK_CONSTRAINT = /(.+)(?:[\s]+)((?:#{Chef::VersionConstraint::OPS.join('|')})(?:[\s]+).+)$/
+ COMBINED_COOKBOOK_CONSTRAINT = /(.+)(?:\s+)((?:#{Chef::VersionConstraint::OPS.join('|')})(?:\s+).+)$/.freeze
def initialize(chef_server_rest: nil)
@name = ""
@description = ""
@default_attributes = Mash.new
@override_attributes = Mash.new
- @cookbook_versions = Hash.new
+ @cookbook_versions = {}
@chef_server_rest = chef_server_rest
end
@@ -59,7 +58,7 @@ class Chef
set_or_return(
:name,
arg,
- { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String }
+ { regex: /^[\-[:alnum:]_]+$/, kind_of: String }
)
end
@@ -67,7 +66,7 @@ class Chef
set_or_return(
:description,
arg,
- :kind_of => String
+ kind_of: String
)
end
@@ -75,7 +74,7 @@ class Chef
set_or_return(
:default_attributes,
arg,
- :kind_of => Hash
+ kind_of: Hash
)
end
@@ -87,7 +86,7 @@ class Chef
set_or_return(
:override_attributes,
arg,
- :kind_of => Hash
+ kind_of: Hash
)
end
@@ -100,8 +99,8 @@ class Chef
:cookbook_versions,
arg,
{
- :kind_of => Hash,
- :callbacks => {
+ kind_of: Hash,
+ callbacks: {
"should be a valid set of cookbook version requirements" => lambda { |cv| Chef::Environment.validate_cookbook_versions(cv) },
},
}
@@ -110,17 +109,17 @@ class Chef
def cookbook(cookbook, version)
validate({
- :version => version,
+ version: version,
}, {
- :version => {
- :callbacks => { "should be a valid version requirement" => lambda { |v| Chef::Environment.validate_cookbook_version(v) } },
+ version: {
+ callbacks: { "should be a valid version requirement" => lambda { |v| Chef::Environment.validate_cookbook_version(v) } },
},
})
@cookbook_versions[cookbook] = version
end
- def to_hash
- result = {
+ def to_h
+ {
"name" => @name,
"description" => @description,
"cookbook_versions" => @cookbook_versions,
@@ -129,11 +128,12 @@ class Chef
"default_attributes" => @default_attributes,
"override_attributes" => @override_attributes,
}
- result
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def update_from!(o)
@@ -157,7 +157,7 @@ class Chef
# reset because everything we need will be in the params, this is necessary because certain constraints
# may have been removed in the params and need to be removed from cookbook_versions as well.
bkup_cb_versions = cookbook_versions
- cookbook_versions(Hash.new)
+ cookbook_versions({})
valid = true
begin
@@ -171,7 +171,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 +216,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"])
@@ -233,7 +228,7 @@ class Chef
def self.list(inflate = false)
if inflate
- response = Hash.new
+ response = {}
Chef::Search::Query.new.search(:environment) do |e|
response[e.name] = e unless e.nil?
end
@@ -247,7 +242,7 @@ class Chef
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
@@ -259,11 +254,11 @@ class Chef
js_file = File.join(Chef::Config[:environment_path], "#{name}.json")
rb_file = File.join(Chef::Config[:environment_path], "#{name}.rb")
- if File.exists?(js_file)
+ if File.exist?(js_file)
# from_json returns object.class => json_class in the JSON.
hash = Chef::JSONCompat.parse(IO.read(js_file))
from_hash(hash)
- elsif File.exists?(rb_file)
+ elsif File.exist?(rb_file)
environment = Chef::Environment.new
environment.name(name)
environment.from_file(rb_file)
@@ -280,8 +275,9 @@ class Chef
def save
begin
chef_server_rest.put("environments/#{@name}", self)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise e unless e.response.code == "404"
+
chef_server_rest.post("environments", self)
end
self
@@ -301,25 +297,24 @@ class Chef
end
def self.validate_cookbook_versions(cv)
- return false unless cv.kind_of?(Hash)
- cv.each do |cookbook, version|
+ return false unless cv.is_a?(Hash)
+
+ 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_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
+ if Chef::Config[:solo_legacy_mode]
+ raise Chef::Exceptions::IllegalVersionConstraint,
+ "Environment cookbook version constraints not allowed in #{ChefUtils::Dist::Solo::PRODUCT}"
+ 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 04c960c7af..f7b706cb2c 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -29,266 +29,206 @@ class Chef
class Base
# Called at the very start of a Chef Run
- def run_start(version)
- end
+ def run_start(version, run_status); end
- def run_started(run_status)
- end
+ def run_started(run_status); end
# Called at the end a successful Chef run.
- def run_completed(node)
- end
+ def run_completed(node, run_status); end
# Called at the end of a failed Chef run.
- def run_failed(exception)
- end
+ def run_failed(exception, run_status); end
# Called right after ohai runs.
- def ohai_completed(node)
- end
+ # NOTE: the node object here is always nil because of when it is called
+ def ohai_completed(node); end
# Announce that we're not going to register the client. Generally because
# we already have the private key, or because we're deliberately not using
# a key.
- def skipping_registration(node_name, config)
- end
+ def skipping_registration(node_name, config); end
# About to attempt to create a private key registered to the server with
# client +node_name+.
- def registration_start(node_name, config)
- end
+ def registration_start(node_name, config); end
# Successfully created the private key and registered this client with the
# server.
- def registration_completed
- end
+ def registration_completed; end
# Failed to register this client with the server.
- def registration_failed(node_name, exception, config)
- end
+ def registration_failed(node_name, exception, config); end
# Called before Chef client loads the node data from the server
- def node_load_start(node_name, config)
- end
+ def node_load_start(node_name, config); end
# TODO: def node_run_list_overridden(*args)
+ # Called once the node is loaded by the policy builder
+ def node_load_success(node); end
+
# Failed to load node data from the server
- def node_load_failed(node_name, exception, config)
- end
+ def node_load_failed(node_name, exception, config); end
# Error expanding the run list
- def run_list_expand_failed(node, exception)
- end
+ def run_list_expand_failed(node, exception); end
# Called after Chef client has loaded the node data.
# Default and override attrs from roles have been computed, but not yet applied.
# Normal attrs from JSON have been added to the node.
- def node_load_completed(node, expanded_run_list, config)
- end
+ def node_load_completed(node, expanded_run_list, config); end
# Called after the Policyfile was loaded. This event only occurs when
# chef is in policyfile mode.
- def policyfile_loaded(policy)
- end
+ def policyfile_loaded(policy); end
# Called before the cookbook collection is fetched from the server.
- def cookbook_resolution_start(expanded_run_list)
- end
+ def cookbook_resolution_start(expanded_run_list); end
# Called when there is an error getting the cookbook collection from the
# server.
- def cookbook_resolution_failed(expanded_run_list, exception)
- end
+ def cookbook_resolution_failed(expanded_run_list, exception); end
# Called when the cookbook collection is returned from the server.
- def cookbook_resolution_complete(cookbook_collection)
- end
+ def cookbook_resolution_complete(cookbook_collection); end
# Called before unneeded cookbooks are removed
- def cookbook_clean_start
- end
+ def cookbook_clean_start; end
# Called after the file at +path+ is removed. It may be removed if the
# cookbook containing it was removed from the run list, or if the file was
# removed from the cookbook.
- def removed_cookbook_file(path)
- end
+ def removed_cookbook_file(path); end
# Called when cookbook cleaning is finished.
- def cookbook_clean_complete
- end
+ def cookbook_clean_complete; end
# Called before cookbook sync starts
- def cookbook_sync_start(cookbook_count)
- end
+ def cookbook_sync_start(cookbook_count); end
# Called when cookbook +cookbook+ has been sync'd
- def synchronized_cookbook(cookbook_name, cookbook)
- end
+ def synchronized_cookbook(cookbook_name, cookbook); end
# Called when an individual file in a cookbook has been updated
- def updated_cookbook_file(cookbook_name, path)
- end
+ def updated_cookbook_file(cookbook_name, path); end
# Called when an error occurs during cookbook sync
- def cookbook_sync_failed(cookbooks, exception)
- end
+ def cookbook_sync_failed(cookbooks, exception); end
# Called after all cookbooks have been sync'd.
- def cookbook_sync_complete
- end
+ def cookbook_sync_complete; end
# Called when starting to collect gems from the cookbooks
- def cookbook_gem_start(gems)
- end
+ def cookbook_gem_start(gems); end
# Called when the result of installing the bundle is to install the gem
- def cookbook_gem_installing(gem, version)
- end
+ def cookbook_gem_installing(gem, version); end
# Called when the result of installing the bundle is to use the gem
- def cookbook_gem_using(gem, version)
- end
+ def cookbook_gem_using(gem, version); end
# Called when finished installing cookbook gems
- def cookbook_gem_finished
- end
+ def cookbook_gem_finished; end
# Called when cookbook gem installation fails
- def cookbook_gem_failed(exception)
- end
+ def cookbook_gem_failed(exception); end
## TODO: add cookbook name to the API for file load callbacks
## TODO: add callbacks for overall cookbook eval start and complete.
+ # Called immediately after creating the run_context and before any cookbook compilation happens
+ def cookbook_compilation_start(run_context); end
+
# Called when library file loading starts
- def library_load_start(file_count)
- end
+ def library_load_start(file_count); end
# Called when library file has been loaded
- def library_file_loaded(path)
- end
+ def library_file_loaded(path); end
# Called when a library file has an error on load.
- def library_file_load_failed(path, exception)
- end
+ def library_file_load_failed(path, exception); end
# Called when library file loading has finished
- def library_load_complete
- end
+ def library_load_complete; end
# Called when LWRP loading starts
- def lwrp_load_start(lwrp_file_count)
- end
+ def lwrp_load_start(lwrp_file_count); end
# Called after a LWR or LWP has been loaded
- def lwrp_file_loaded(path)
- end
+ def lwrp_file_loaded(path); end
# Called after a LWR or LWP file errors on load
- def lwrp_file_load_failed(path, exception)
- end
+ def lwrp_file_load_failed(path, exception); end
# Called when LWRPs are finished loading
- def lwrp_load_complete
- end
+ 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
+ def attribute_load_start(attribute_file_count); end
# Called after the attribute file is loaded
- def attribute_file_loaded(path)
- end
+ def attribute_file_loaded(path); end
# Called when an attribute file fails to load.
- def attribute_file_load_failed(path, exception)
- end
+ def attribute_file_load_failed(path, exception); end
# Called when attribute file loading is finished
- def attribute_load_complete
- end
+ def attribute_load_complete; end
# Called before resource definitions are loaded
- def definition_load_start(definition_file_count)
- end
+ def definition_load_start(definition_file_count); end
# Called when a resource definition has been loaded
- def definition_file_loaded(path)
- end
+ def definition_file_loaded(path); end
# Called when a resource definition file fails to load
- def definition_file_load_failed(path, exception)
- end
+ def definition_file_load_failed(path, exception); end
# Called when resource definitions are done loading
- def definition_load_complete
- end
+ def definition_load_complete; end
# Called before recipes are loaded
- def recipe_load_start(recipe_count)
- end
+ def recipe_load_start(recipe_count); end
# Called after the recipe has been loaded
- def recipe_file_loaded(path, recipe)
- end
+ def recipe_file_loaded(path, recipe); end
# Called after a recipe file fails to load
- def recipe_file_load_failed(path, exception, recipe)
- end
+ def recipe_file_load_failed(path, exception, recipe); end
# Called when a recipe cannot be resolved
- def recipe_not_found(exception)
- end
+ def recipe_not_found(exception); end
# Called when recipes have been loaded.
- def recipe_load_complete
- end
+ def recipe_load_complete; end
+
+ # This is called after all cookbook compilation phases are completed.
+ def cookbook_compilation_complete(run_context); end
# Called before convergence starts
- def converge_start(run_context)
- end
+ def converge_start(run_context); end
+
+ # Callback hook for handlers to register their interest in the action_collection
+ def action_collection_registration(action_collection); end
# Called when the converge phase is finished.
- def converge_complete
- end
+ def converge_complete; end
# Called if the converge phase fails
- def converge_failed(exception)
- end
-
- ##################################
- # Audit Mode Events
- # This phase is currently experimental and these event APIs are subject to change
- ##################################
-
- # Called before audit phase starts
- def audit_phase_start(run_status)
- end
-
- # Called when audit phase successfully finishes
- def audit_phase_complete(audit_output)
- end
-
- # Called if there is an uncaught exception during the audit phase. The audit runner should
- # be catching and handling errors from the examples, so this is only uncaught errors (like
- # bugs in our handling code)
- def audit_phase_failed(exception, audit_output)
- end
-
- # Signifies the start of a `control_group` block with a defined name
- def control_group_started(name)
- end
-
- # An example in a `control_group` block completed successfully
- def control_example_success(control_group_name, example_data)
- end
-
- # An example in a `control_group` block failed with the provided error
- def control_example_failure(control_group_name, example_data, error)
- end
+ def converge_failed(exception); end
# TODO: need events for notification resolve?
# def notifications_resolved
@@ -318,109 +258,91 @@ class Chef
#
# Called before action is executed on a resource.
- def resource_action_start(resource, action, notification_type = nil, notifier = nil)
- end
+ def resource_action_start(resource, action, notification_type = nil, notifier = nil); end
# Called when a resource action has been skipped b/c of a conditional
- def resource_skipped(resource, action, conditional)
- end
+ def resource_skipped(resource, action, conditional); end
# Called after #load_current_resource has run.
- def resource_current_state_loaded(resource, action, current_resource)
- end
+ def resource_current_state_loaded(resource, action, current_resource); end
+
+ # Called after #load_after_resource has run.
+ def resource_after_state_loaded(resource, action, after_resource); end
# Called when resource current state load is skipped due to the provider
# not supporting whyrun mode.
- def resource_current_state_load_bypassed(resource, action, current_resource)
- end
+ def resource_current_state_load_bypassed(resource, action, current_resource); end
# Called when evaluating a resource that does not support whyrun in whyrun mode
- def resource_bypassed(resource, action, current_resource)
- end
+ def resource_bypassed(resource, action, current_resource); end
# Called when a change has been made to a resource. May be called multiple
# times per resource, e.g., a file may have its content updated, and then
# its permissions updated.
- def resource_update_applied(resource, action, update)
- end
+ def resource_update_applied(resource, action, update); end
# Called when a progress notification should be sent to the user to
# indicate the overall progress of a long running operation, such as
# a large file download.
- def resource_update_progress(resource, current, total, interval)
- end
+ def resource_update_progress(resource, current, total, interval); end
# Called when a resource fails, but will retry.
- def resource_failed_retriable(resource, action, retry_count, exception)
- end
+ def resource_failed_retriable(resource, action, retry_count, exception); end
# Called when a resource fails and will not be retried.
- def resource_failed(resource, action, exception)
- end
+ def resource_failed(resource, action, exception); end
# Called after a resource has been completely converged, but only if
# modifications were made.
- def resource_updated(resource, action)
- end
+ def resource_updated(resource, action); end
# Called when a resource has no converge actions, e.g., it was already correct.
- def resource_up_to_date(resource, action)
- end
+ def resource_up_to_date(resource, action); end
# Called when a resource action has been completed
- def resource_completed(resource)
- end
+ def resource_completed(resource); end
# A stream has opened.
- def stream_opened(stream, options = {})
- end
+ def stream_opened(stream, options = {}); end
# A stream has closed.
- def stream_closed(stream, options = {})
- end
+ def stream_closed(stream, options = {}); end
# A chunk of data from a stream. The stream is managed by "stream," which
# can be any tag whatsoever. Data in different "streams" may not be placed
# on the same line or even sent to the same console.
- def stream_output(stream, output, options = {})
- end
+ def stream_output(stream, output, options = {}); end
# Called before handlers run
- def handlers_start(handler_count)
- end
+ def handlers_start(handler_count); end
# Called after an individual handler has run
- def handler_executed(handler)
- end
+ def handler_executed(handler); end
# Called after all handlers have executed
- def handlers_completed
- end
+ def handlers_completed; end
# Called when an assertion declared by a provider fails
- def provider_requirement_failed(action, resource, exception, message)
- end
+ def provider_requirement_failed(action, resource, exception, message); end
# Called when a provider makes an assumption after a failed assertion
# in whyrun mode, in order to allow execution to continue
- def whyrun_assumption(action, resource, message)
- end
+ def whyrun_assumption(action, resource, message); end
# Emit a message about something being deprecated.
- def deprecation(message, location = caller(2..2)[0])
- end
+ def deprecation(message, location = caller(2..2)[0]); end
- def run_list_expanded(run_list_expansion)
- end
+ def run_list_expanded(run_list_expansion); end
# An uncategorized message. This supports the case that a user needs to
# pass output that doesn't fit into one of the callbacks above. Note that
# there's no semantic information about the content or importance of the
# message. That means that if you're using this too often, you should add a
# callback for it.
- def msg(message)
- end
+ 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/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index 69419a393b..b4a2d7879b 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -1,4 +1,4 @@
-require "chef/event_dispatch/base"
+require_relative "base"
class Chef
module EventDispatch
@@ -15,14 +15,46 @@ class Chef
@subscribers = subscribers
end
+ # Since the cookbook synchronizer will call this object from threads, we
+ # have to deal with concurrent access to this object. Since we don't want
+ # threads to handle events from other threads, we just use thread local
+ # storage.
+ #
+ def event_list
+ Thread.current[:chef_client_event_list] ||= []
+ end
+
# Add a new subscriber to the list of registered subscribers
def register(subscriber)
- @subscribers << subscriber
+ subscribers << subscriber
+ end
+
+ def unregister(subscriber)
+ subscribers.reject! { |x| x == subscriber }
+ end
+
+ def enqueue(method_name, *args)
+ event_list << [ method_name, *args ]
+ process_events_until_done unless @in_call
+ end
+
+ (Base.instance_methods - Object.instance_methods).each do |method_name|
+ class_eval <<-EOM
+ def #{method_name}(*args)
+ enqueue(#{method_name.inspect}, *args)
+ end
+ EOM
+ end
+
+ # Special case deprecation, since it needs to know its caller
+ def deprecation(message, location = caller(2..2)[0])
+ enqueue(:deprecation, message, location)
end
# Check to see if we are dispatching to a formatter
+ # @api private
def formatter?
- @subscribers.any? { |s| s.respond_to?(:is_formatter?) && s.is_formatter? }
+ subscribers.any? { |s| s.respond_to?(:is_formatter?) && s.is_formatter? }
end
####
@@ -30,10 +62,13 @@ class Chef
# define the forwarding in one go:
#
+ # @api private
def call_subscribers(method_name, *args)
- @subscribers.each do |s|
- # Skip new/unsupported event names.
- next if !s.respond_to?(method_name)
+ @in_call = true
+ subscribers.each do |s|
+ # Skip new/unsupported event names
+ next unless s.respond_to?(method_name)
+
mth = s.method(method_name)
# Trim arguments to match what the subscriber expects to allow
# adding new arguments without breaking compat.
@@ -43,20 +78,19 @@ class Chef
mth.call(*args)
end
end
+ ensure
+ @in_call = false
end
- (Base.instance_methods - Object.instance_methods).each do |method_name|
- class_eval <<-EOM
- def #{method_name}(*args)
- call_subscribers(#{method_name.inspect}, *args)
- end
- EOM
- end
+ private
- # Special case deprecation, since it needs to know its caller
- def deprecation(message, location = caller(2..2)[0])
- call_subscribers(:deprecation, message, location)
+ # events are allowed to enqueue chained events, so pop them off until
+ # empty, rather than iterating over the list.
+ #
+ def process_events_until_done
+ call_subscribers(*event_list.shift) until event_list.empty?
end
+
end
end
end
diff --git a/lib/chef/event_dispatch/dsl.rb b/lib/chef/event_dispatch/dsl.rb
index 999d536fbe..275506a4ec 100644
--- a/lib/chef/event_dispatch/dsl.rb
+++ b/lib/chef/event_dispatch/dsl.rb
@@ -15,9 +15,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/event_dispatch/base"
-require "chef/exceptions"
-require "chef/config"
+require_relative "base"
+require_relative "../exceptions"
+require_relative "../config"
class Chef
module EventDispatch
@@ -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/base.rb b/lib/chef/event_loggers/base.rb
index 3c11e809f6..1ef7f7de6c 100644
--- a/lib/chef/event_loggers/base.rb
+++ b/lib/chef/event_loggers/base.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require "chef/event_dispatch/base"
+require_relative "../event_dispatch/base"
class Chef
module EventLoggers
@@ -43,8 +43,9 @@ class Chef
def self.new(name)
event_logger_class = by_name(name.to_s)
- raise UnknownEventLogger, "No event logger found for #{name} (available: #{available_event_loggers.join(', ')})" unless event_logger_class
+ raise UnknownEventLogger, "No event logger found for #{name} (available: #{available_event_loggers.join(", ")})" unless event_logger_class
raise UnavailableEventLogger unless available_event_loggers.include? name.to_s
+
event_logger_class.new
end
diff --git a/lib/chef/event_loggers/windows_eventlog.rb b/lib/chef/event_loggers/windows_eventlog.rb
index f8c3d346c5..6b290eb8a7 100644
--- a/lib/chef/event_loggers/windows_eventlog.rb
+++ b/lib/chef/event_loggers/windows_eventlog.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,9 +16,10 @@
# limitations under the License.
#
-require "chef/event_loggers/base"
-require "chef/platform/query_helpers"
-require "chef/win32/eventlog"
+require_relative "base"
+require_relative "../platform/query_helpers"
+require_relative "../win32/eventlog"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
class Chef
module EventLoggers
@@ -35,48 +36,48 @@ class Chef
LOG_CATEGORY_ID = 11001
# Since we must install the event logger, this is not really configurable
- SOURCE = "Chef"
+ SOURCE = ChefUtils::Dist::Infra::SHORT.freeze
def self.available?
- return Chef::Platform.windows?
+ ChefUtils.windows?
end
def initialize
@eventlog = ::Win32::EventLog.open("Application")
end
- def run_start(version)
+ def run_start(version, run_status)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::INFO_TYPE,
- :source => SOURCE,
- :event_id => RUN_START_EVENT_ID,
- :data => [version]
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: SOURCE,
+ event_id: RUN_START_EVENT_ID,
+ data: [version]
)
end
def run_started(run_status)
@run_status = run_status
@eventlog.report_event(
- :event_type => ::Win32::EventLog::INFO_TYPE,
- :source => SOURCE,
- :event_id => RUN_STARTED_EVENT_ID,
- :data => [run_status.run_id]
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: SOURCE,
+ event_id: RUN_STARTED_EVENT_ID,
+ data: [run_status.run_id]
)
end
def run_completed(node)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::INFO_TYPE,
- :source => SOURCE,
- :event_id => RUN_COMPLETED_EVENT_ID,
- :data => [@run_status.run_id, @run_status.elapsed_time.to_s]
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: SOURCE,
+ event_id: RUN_COMPLETED_EVENT_ID,
+ data: [@run_status.run_id, @run_status.elapsed_time.to_s]
)
end
- #Failed chef-client run %1 in %2 seconds.
- #Exception type: %3
- #Exception message: %4
- #Exception backtrace: %5
+ # Failed chef-client run %1 in %2 seconds.
+ # Exception type: %3
+ # Exception message: %4
+ # Exception backtrace: %5
def run_failed(e)
data =
if @run_status
@@ -87,10 +88,10 @@ class Chef
end
@eventlog.report_event(
- :event_type => ::Win32::EventLog::ERROR_TYPE,
- :source => SOURCE,
- :event_id => RUN_FAILED_EVENT_ID,
- :data => data + [e.class.name,
+ event_type: ::Win32::EventLog::ERROR_TYPE,
+ source: SOURCE,
+ event_id: RUN_FAILED_EVENT_ID,
+ data: data + [e.class.name,
e.message,
e.backtrace.join("\n")]
)
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index a4d5ff60e2..a0afd25208 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,8 @@
# limitations under the License.
require "chef-config/exceptions"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "constants"
class Chef
# == Chef::Exceptions
@@ -45,8 +47,9 @@ class Chef
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
@@ -58,14 +61,6 @@ class Chef
class UnsupportedAction < RuntimeError; end
class MissingLibrary < RuntimeError; end
- class DeprecatedExitCode < RuntimeError
- def initalize
- super "Exiting with a non RFC 062 Exit Code."
- require "chef/application/exit_code"
- Chef::Application::ExitCode.notify_deprecated_exit_code
- end
- end
-
class CannotDetermineNodeName < RuntimeError
def initialize
super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn"
@@ -76,9 +71,10 @@ class Chef
class Group < RuntimeError; end
class Link < RuntimeError; end
class Mount < RuntimeError; end
- class Reboot < Exception; end
- class RebootPending < Exception; 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
@@ -88,11 +84,13 @@ class Chef
class InvalidPrivateKey < ArgumentError; end
class MissingKeyAttribute < ArgumentError; end
class KeyCommandInputError < ArgumentError; end
+
class BootstrapCommandInputError < ArgumentError
def initialize
super "You cannot pass both --json-attributes and --json-attribute-file. Please pass one or none."
end
end
+
class InvalidKeyArgument < ArgumentError; end
class InvalidKeyAttribute < ArgumentError; end
class InvalidUserAttribute < ArgumentError; end
@@ -105,6 +103,7 @@ class Chef
# Cookbook loader used to raise an argument error when cookbook not found.
# for back compat, need to raise an error that inherits from ArgumentError
class CookbookNotFoundInRepo < ArgumentError; end
+ class CookbookMergingError < RuntimeError; end
class RecipeNotFound < ArgumentError; end
# AttributeNotFound really means the attribute file could not be found
class AttributeNotFound < RuntimeError; end
@@ -136,7 +135,7 @@ class Chef
# Can't find a Resource of this type that is valid on this platform.
class NoSuchResourceType < NameError
def initialize(short_name, node)
- super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]}"
+ super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]} with target_mode? #{Chef::Config.target_mode?}"
end
end
@@ -158,7 +157,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
@@ -195,12 +194,14 @@ 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
attr_reader :install_path
attr_reader :cookbook_name
+
def initialize(install_path, cookbook_name)
@install_path = install_path
@cookbook_name = cookbook_name
@@ -245,6 +246,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
@@ -257,21 +262,19 @@ class Chef
class ChildConvergeError < RuntimeError; end
- class DeprecatedFeatureError < RuntimeError;
- def initalize(message)
+ class DeprecatedFeatureError < RuntimeError
+ def initialize(message)
super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)")
end
end
class MissingRole < RuntimeError
- NULL = Object.new
-
attr_reader :expansion
- def initialize(message_or_expansion = NULL)
+ def initialize(message_or_expansion = NOT_PASSED)
@expansion = nil
case message_or_expansion
- when NULL
+ when NOT_PASSED
super()
when String
super
@@ -283,6 +286,7 @@ class Chef
end
end
+
# Exception class for collecting multiple failures. Used when running
# delayed notifications so that chef can process each delayed
# notification even if chef client or other notifications fail.
@@ -301,7 +305,7 @@ class Chef
def client_run_failure(exception)
set_backtrace(exception.backtrace)
- @all_failures << [ "chef run", exception ]
+ @all_failures << [ "#{ChefUtils::Dist::Infra::PRODUCT} run", exception ]
end
def notification_failure(exception)
@@ -310,7 +314,7 @@ class Chef
def raise!
unless empty?
- raise self.for_raise
+ raise for_raise
end
end
@@ -400,9 +404,9 @@ class Chef
# length declared in the http response.
class ContentLengthMismatch < RuntimeError
def initialize(response_length, content_length)
- super <<-EOF
-Response body length #{response_length} does not match HTTP Content-Length header #{content_length}.
-This error is most often caused by network issues (proxies, etc) outside of chef-client.
+ super <<~EOF
+ Response body length #{response_length} does not match HTTP Content-Length header #{content_length}.
+ This error is most often caused by network issues (proxies, etc) outside of #{ChefUtils::Dist::Infra::CLIENT}.
EOF
end
end
@@ -414,7 +418,7 @@ This error is most often caused by network issues (proxies, etc) outside of chef
end
# Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails
- # to release the run lock becure Chef::Config[:run_lock_timeout] seconds pass.
+ # to release the run lock before Chef::Config[:run_lock_timeout] seconds pass.
class RunLockTimeout < RuntimeError
def initialize(duration, blocking_pid)
super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release."
@@ -423,7 +427,7 @@ This error is most often caused by network issues (proxies, etc) outside of chef
class ChecksumMismatch < RuntimeError
def initialize(res_cksum, cont_cksum)
- super "Checksum on resource (#{res_cksum}) does not match checksum on content (#{cont_cksum})"
+ super "Checksum on resource (#{res_cksum}...) does not match checksum on content (#{cont_cksum}...)"
end
end
@@ -444,32 +448,14 @@ This error is most often caused by network issues (proxies, etc) outside of chef
end
end
- class AuditError < RuntimeError; end
-
- class AuditControlGroupDuplicate < AuditError
- def initialize(name)
- super "Control group with name '#{name}' has already been defined"
- end
- end
- class AuditNameMissing < AuditError; end
- class NoAuditsProvided < AuditError
- def initialize
- super "You must provide a block with controls"
- end
- end
- class AuditsFailed < AuditError
- def initialize(num_failed, num_total)
- super "Audit phase found failures - #{num_failed}/#{num_total} controls failed"
- end
- end
-
- # If a converge or audit fails, we want to wrap the output from those errors into 1 error so we can
+ # If a converge fails, we want to wrap the output from those errors into 1 error so we can
# see both issues in the output. It is possible that nil will be provided. You must call `fill_backtrace`
# to correctly populate the backtrace with the wrapped backtraces.
class RunFailedWrappingError < RuntimeError
attr_reader :wrapped_errors
+
def initialize(*errors)
- errors = errors.select { |e| !e.nil? }
+ errors = errors.compact
output = "Found #{errors.size} errors, they are stored in the backtrace"
@wrapped_errors = errors
super output
@@ -495,7 +481,7 @@ This error is most often caused by network issues (proxies, etc) outside of chef
class CookbookChefVersionMismatch < RuntimeError
def initialize(chef_version, cookbook_name, cookbook_version, *constraints)
constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(", ")
- super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on chef version #{constraint_str}, but the running chef version is #{chef_version}"
+ super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on #{ChefUtils::Dist::Infra::PRODUCT} version #{constraint_str}, but the running #{ChefUtils::Dist::Infra::PRODUCT} version is #{chef_version}"
end
end
@@ -508,16 +494,38 @@ This error is most often caused by network issues (proxies, etc) outside of chef
class MultipleDscResourcesFound < RuntimeError
attr_reader :resources_found
+
def initialize(resources_found)
@resources_found = resources_found
matches_info = @resources_found.each do |r|
if r["Module"].nil?
- "Resource #{r['Name']} was found in #{r['Module']['Name']}"
+ "Resource #{r["Name"]} was found in #{r["Module"]["Name"]}"
else
- "Resource #{r['Name']} is a binary resource"
+ "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
+
+ class GemRequirementConflict < RuntimeError
+ def initialize(gem_name, option, value1, value2)
+ super "Conflicting requirements for gem '#{gem_name}': Both #{value1.inspect} and #{value2.inspect} given for option #{option.inspect}"
+ end
+ end
+
+ class UnifiedModeImmediateSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "immediate subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
+ end
+ end
+
+ class UnifiedModeBeforeSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "before subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
end
end
end
diff --git a/lib/chef/file_access_control.rb b/lib/chef/file_access_control.rb
index 50a1ea29bb..c192a24d4d 100644
--- a/lib/chef/file_access_control.rb
+++ b/lib/chef/file_access_control.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/log"
+require_relative "log"
class Chef
@@ -26,11 +26,11 @@ class Chef
# the values specified by a value object, usually a Chef::Resource.
class FileAccessControl
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
- require "chef/file_access_control/windows"
+ if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
+ require_relative "file_access_control/windows"
include FileAccessControl::Windows
else
- require "chef/file_access_control/unix"
+ require_relative "file_access_control/unix"
include FileAccessControl::Unix
end
diff --git a/lib/chef/file_access_control/unix.rb b/lib/chef/file_access_control/unix.rb
index 1746db44d3..9ea24b3b39 100644
--- a/lib/chef/file_access_control/unix.rb
+++ b/lib/chef/file_access_control/unix.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
# limitations under the License.
#
-require "chef/log"
+require_relative "../log"
class Chef
class FileAccessControl
@@ -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,14 @@ class Chef
end
def gid_from_resource(resource)
- return nil if resource == nil || resource.group.nil?
- if resource.group.kind_of?(String)
+ return nil if resource.nil? || resource.group.nil?
+
+ if resource.group.is_a?(String)
diminished_radix_complement( Etc.getgrnam(resource.group).gid )
- elsif resource.group.kind_of?(Integer)
+ elsif resource.group.is_a?(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 +133,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 +169,8 @@ 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 +189,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 +266,14 @@ class Chef
end
def uid_from_resource(resource)
- return nil if resource == nil || resource.owner.nil?
- if resource.owner.kind_of?(String)
+ return nil if resource.nil? || resource.owner.nil?
+
+ if resource.owner.is_a?(String)
diminished_radix_complement( Etc.getpwnam(resource.owner).uid )
- elsif resource.owner.kind_of?(Integer)
+ elsif resource.owner.is_a?(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 +282,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 6f1ac5f581..cc1b96a84d 100644
--- a/lib/chef/file_access_control/windows.rb
+++ b/lib/chef/file_access_control/windows.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/file"
+require_relative "../win32/security"
+require_relative "../win32/file"
class Chef
class FileAccessControl
@@ -34,7 +34,8 @@ class Chef
# We want to mix these in as class methods
def writable?(path)
::File.exists?(path) && Chef::ReservedNames::Win32::File.file_access_check(
- path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE)
+ path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE
+ )
end
end
@@ -90,17 +91,19 @@ class Chef
target_acl.each do |target_ace|
if target_ace.flags & INHERIT_ONLY_ACE == 0
self_ace = target_ace.dup
- self_ace.flags = 0
+ # We need flag value which is already being set in case of WRITE permissions as 3, so we will not be overwriting it with the hard coded value.
+ self_ace.flags = 0 unless target_ace.mask == Chef::ReservedNames::Win32::API::Security::WRITE
self_ace.mask = securable_object.predict_rights_mask(target_ace.mask)
new_target_acl << self_ace
end
- if target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0
+ # As there is no inheritance needed in case of WRITE permissions.
+ if target_ace.mask != Chef::ReservedNames::Win32::API::Security::WRITE && target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0
children_ace = target_ace.dup
children_ace.flags |= INHERIT_ONLY_ACE
new_target_acl << children_ace
end
end
- return actual_acl == new_target_acl
+ actual_acl == new_target_acl
end
def existing_descriptor
@@ -108,9 +111,13 @@ class Chef
end
def get_sid(value)
- if value.kind_of?(String)
- SID.from_account(value)
- elsif value.kind_of?(SID)
+ if value.is_a?(String)
+ begin
+ Security.convert_string_sid_to_sid(value)
+ rescue Chef::Exceptions::Win32APIError
+ SID.from_account(value)
+ end
+ elsif value.is_a?(SID)
value
else
raise "Must specify username, group or SID: #{value}"
@@ -119,16 +126,18 @@ class Chef
def securable_object
@securable_object ||= begin
- if file.kind_of?(String)
+ if file.is_a?(String)
so = Chef::ReservedNames::Win32::Security::SecurableObject.new(file.dup)
end
- raise ArgumentError, "'file' must be a valid path or object of type 'Chef::ReservedNames::Win32::Security::SecurableObject'" unless so.kind_of? Chef::ReservedNames::Win32::Security::SecurableObject
+ raise ArgumentError, "'file' must be a valid path or object of type 'Chef::ReservedNames::Win32::Security::SecurableObject'" unless so.is_a? Chef::ReservedNames::Win32::Security::SecurableObject
+
so
end
end
def should_update_dacl?
return true unless ::File.exists?(file) || ::File.symlink?(file)
+
dacl = target_dacl
existing_dacl = existing_descriptor.dacl
inherits = target_inherits
@@ -162,6 +171,7 @@ class Chef
def should_update_group?
return true unless ::File.exists?(file) || ::File.symlink?(file)
+
(group = target_group) && (group != existing_descriptor.group)
end
@@ -181,6 +191,7 @@ class Chef
def should_update_owner?
return true unless ::File.exists?(file) || ::File.symlink?(file)
+
(owner = target_owner) && (owner != existing_descriptor.owner)
end
@@ -204,6 +215,7 @@ class Chef
mask |= (GENERIC_WRITE | DELETE) if mode & 2 != 0
mask |= GENERIC_EXECUTE if mode & 1 != 0
return [] if mask == 0
+
[ ACE.access_allowed(sid, mask) ]
end
@@ -220,7 +232,7 @@ class Chef
when :read_execute
mask |= GENERIC_READ | GENERIC_EXECUTE
when :write
- mask |= GENERIC_WRITE
+ mask |= WRITE
else
# Otherwise, assume it's an integer specifying the actual flags
mask |= permission
@@ -234,7 +246,7 @@ class Chef
flags = 0
#
- # Configure child inheritence only if the resource is some
+ # Configure child inheritance only if the resource is some
# type of a directory.
#
if resource.is_a? Chef::Resource::Directory
@@ -243,10 +255,7 @@ class Chef
flags |= CONTAINER_INHERIT_ACE
when :objects_only
flags |= OBJECT_INHERIT_ACE
- when true
- flags |= CONTAINER_INHERIT_ACE
- flags |= OBJECT_INHERIT_ACE
- when nil
+ when true, nil
flags |= CONTAINER_INHERIT_ACE
flags |= OBJECT_INHERIT_ACE
end
@@ -264,9 +273,10 @@ class Chef
def target_dacl
return nil if resource.rights.nil? && resource.deny_rights.nil? && resource.mode.nil?
+
acls = nil
- if !resource.deny_rights.nil?
+ unless resource.deny_rights.nil?
acls = [] if acls.nil?
resource.deny_rights.each do |rights|
@@ -279,7 +289,7 @@ class Chef
end
end
- if !resource.rights.nil?
+ unless resource.rights.nil?
acls = [] if acls.nil?
resource.rights.each do |rights|
@@ -292,7 +302,7 @@ class Chef
end
end
- if !resource.mode.nil?
+ unless resource.mode.nil?
acls = [] if acls.nil?
mode = (resource.mode.respond_to?(:oct) ? resource.mode.oct : resource.mode.to_i) & 0777
@@ -319,6 +329,7 @@ class Chef
def target_group
return nil if resource.group.nil?
+
get_sid(resource.group)
end
@@ -328,6 +339,7 @@ class Chef
def target_owner
return nil if resource.owner.nil?
+
get_sid(resource.owner)
end
end
diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb
index 8e9bb1e3e4..22060869da 100644
--- a/lib/chef/file_cache.rb
+++ b/lib/chef/file_cache.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +15,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/mixin/params_validate"
-require "chef/mixin/create_path"
-require "chef/exceptions"
-require "chef/json_compat"
-require "fileutils"
-require "chef/util/path_helper"
+require_relative "mixin/params_validate"
+require_relative "mixin/create_path"
+require_relative "exceptions"
+require_relative "json_compat"
+require "fileutils" unless defined?(FileUtils)
+require_relative "util/path_helper"
class Chef
class FileCache
@@ -42,12 +42,12 @@ class Chef
def store(path, contents, perm = 0640)
validate(
{
- :path => path,
- :contents => contents,
+ path: path,
+ contents: contents,
},
{
- :path => { :kind_of => String },
- :contents => { :kind_of => String },
+ path: { kind_of: String },
+ contents: { kind_of: String },
}
)
@@ -68,12 +68,12 @@ class Chef
def move_to(file, path)
validate(
{
- :file => file,
- :path => path,
+ file: file,
+ path: path,
},
{
- :file => { :kind_of => String },
- :path => { :kind_of => String },
+ file: { kind_of: String },
+ path: { kind_of: String },
}
)
@@ -105,14 +105,15 @@ class Chef
def load(path, read = true)
validate(
{
- :path => path,
+ path: path,
},
{
- :path => { :kind_of => String },
+ path: { kind_of: String },
}
)
cache_path = create_cache_path(path, false)
raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path)
+
if read
File.read(cache_path)
else
@@ -131,10 +132,10 @@ class Chef
def delete(path)
validate(
{
- :path => path,
+ path: path,
},
{
- :path => { :kind_of => String },
+ path: { kind_of: String },
}
)
cache_path = create_cache_path(path, false)
@@ -157,7 +158,7 @@ class Chef
# === Returns
# [String] - An array of file cache keys matching the glob
def find(glob_pattern)
- keys = Array.new
+ keys = []
Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(file_cache_path), glob_pattern)].each do |f|
if File.file?(f)
keys << f[/^#{Regexp.escape(Dir[Chef::Util::PathHelper.escape_glob_dir(file_cache_path)].first) + File::Separator}(.+)/, 1]
@@ -175,13 +176,13 @@ class Chef
# === Returns
# True:: If the file exists
# False:: If it does not
- def has_key?(path)
+ def key?(path)
validate(
{
- :path => path,
+ path: path,
},
{
- :path => { :kind_of => String },
+ path: { kind_of: String },
}
)
full_path = create_cache_path(path, false)
@@ -192,6 +193,8 @@ class Chef
end
end
+ alias_method :has_key?, :key?
+
# Create a full path to a given file in the cache. By default,
# also creates the path if it does not exist.
#
diff --git a/lib/chef/file_content_management/content_base.rb b/lib/chef/file_content_management/content_base.rb
index 6080e37180..8098ea14a2 100644
--- a/lib/chef/file_content_management/content_base.rb
+++ b/lib/chef/file_content_management/content_base.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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.rb b/lib/chef/file_content_management/deploy.rb
index 1e3ae55c21..7098817183 100644
--- a/lib/chef/file_content_management/deploy.rb
+++ b/lib/chef/file_content_management/deploy.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/file_content_management/deploy/cp"
-require "chef/file_content_management/deploy/mv_unix"
-if Chef::Platform.windows?
- require "chef/file_content_management/deploy/mv_windows"
+require_relative "deploy/cp"
+require_relative "deploy/mv_unix"
+if ChefUtils.windows?
+ require_relative "deploy/mv_windows"
end
class Chef
@@ -27,9 +27,9 @@ class Chef
class Deploy
def self.strategy(atomic_update)
if atomic_update
- Chef::Platform.windows? ? MvWindows.new() : MvUnix.new()
+ ChefUtils.windows? ? MvWindows.new : MvUnix.new
else
- Cp.new()
+ Cp.new
end
end
end
diff --git a/lib/chef/file_content_management/deploy/cp.rb b/lib/chef/file_content_management/deploy/cp.rb
index 14dde85d13..010271d1db 100644
--- a/lib/chef/file_content_management/deploy/cp.rb
+++ b/lib/chef/file_content_management/deploy/cp.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..19345a2b26 100644
--- a/lib/chef/file_content_management/deploy/mv_unix.rb
+++ b/lib/chef/file_content_management/deploy/mv_unix.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,22 +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}")
-
- # i own the inode, so should be able to at least chmod it
- ::File.chmod(mode, src)
+ Chef::Log.trace("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
# we may be running as non-root in which case because we are doing an mv we cannot preserve
# the file modes. after the mv we have a different inode and if we don't have rights to
@@ -54,7 +51,7 @@ class Chef
# in the case where i'm running chef-solo on my homedir as myself and some root-shell
# work has caused dotfiles of mine to change to root-owned, i'm fine with this not being
# exceptional, and i think most use cases will consider this to not be exceptional, and
- # the right thing is to fix the ownership of the file to the user running the commmand
+ # the right thing is to fix the ownership of the file to the user running the command
# (which requires write perms to the directory, or mv will throw an exception)
begin
::File.chown(uid, nil, src)
@@ -67,7 +64,11 @@ 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}")
+ # i own the inode, so should be able to at least chmod it
+ # NOTE: this must come last due to POSIX stripping sticky mode bits on chown/chgrp
+ ::File.chmod(mode, src)
+
+ 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..32f6fcd105 100644
--- a/lib/chef/file_content_management/deploy/mv_windows.rb
+++ b/lib/chef/file_content_management/deploy/mv_windows.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,9 +21,9 @@
# ACL information on the dst file.
#
-require "chef/platform/query_helpers"
-if Chef::Platform.windows?
- require "chef/win32/security"
+require_relative "../../platform/query_helpers"
+if ChefUtils.windows?
+ require_relative "../../win32/security"
end
class Chef
@@ -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 cf59a87996..27efe34191 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 (c) 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.
#
-require "tempfile"
+require "tempfile" unless defined?(Tempfile)
class Chef
class FileContentManagement
@@ -39,15 +39,15 @@ class Chef
errors = [ ]
tempfile_dirnames.each do |tempfile_dirname|
- begin
- # 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)
- errors << message
- end
+
+ # 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.trace(message)
+ errors << message
+
end
raise Chef::Exceptions::FileContentStagingError, errors if tf.nil?
@@ -67,8 +67,8 @@ class Chef
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.insert 0, "." unless ChefUtils.windows? # dotfile if we're not on windows
+ basename.scrub
end
# this is similar to File.extname() but greedy about the extension (from the first dot, not the last dot)
@@ -76,7 +76,7 @@ class Chef
# 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]
+ i.nil? ? "" : b[i..].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 536bf72e02..2471cfe844 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -1,7 +1,7 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,11 @@
# limitations under the License.
#
-require "chef/event_dispatch/base"
-require "chef/formatters/error_inspectors"
-require "chef/formatters/error_description"
-require "chef/formatters/error_mapper"
-require "chef/formatters/indentable_output_stream"
+require_relative "../event_dispatch/base"
+require_relative "error_inspectors"
+require_relative "error_description"
+require_relative "error_mapper"
+require_relative "indentable_output_stream"
class Chef
@@ -52,7 +52,7 @@ class Chef
# TODO: is it too clever to be defining new() on a module like this?
def self.new(name, out, err)
formatter_class = by_name(name.to_s)
- raise UnknownFormatter, "No output formatter found for #{name} (available: #{available_formatters.join(', ')})" unless formatter_class
+ raise UnknownFormatter, "No output formatter found for #{name} (available: #{available_formatters.join(", ")})" unless formatter_class
formatter_class.new(out, err)
end
@@ -96,7 +96,7 @@ class Chef
if @output.indent < 0
# This is left commented out for now. We need to uncomment it and fix at least one bug in
# the formatter, and then leave this line uncommented in the future.
- #Chef::Log.warn "Internal Formatter Error -- Attempt to indent by negative number of spaces"
+ # Chef::Log.warn "Internal Formatter Error -- Attempt to indent by negative number of spaces"
@output.indent = 0
end
@output.indent
@@ -110,7 +110,7 @@ class Chef
end
def registration_failed(node_name, exception, config)
- #A Formatters::ErrorDescription object
+ # A Formatters::ErrorDescription object
description = ErrorMapper.registration_failed(node_name, exception, config)
display_error(description)
end
@@ -137,17 +137,16 @@ 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
# cookbook getting loaded. The per-filetype callbacks for file load are
- # overriden so that they call this instead. This means that a subclass of
+ # overridden so that they call this instead. This means that a subclass of
# Formatters::Base can implement #file_loaded to do the same thing for
# every kind of file that Chef loads from a recipe instead of
# implementing all the per-filetype callbacks.
- def file_loaded(path)
- end
+ def file_loaded(path); end
# Generic callback for any attribute/library/lwrp/recipe file throwing an
# exception when loaded. Default behavior is to use CompileErrorInspector
@@ -212,8 +211,17 @@ class Chef
file_load_failed(path, exception)
end
- def deprecation(message, location = caller(2..2)[0])
- Chef::Log.deprecation("#{message} at #{location}")
+ # Log a deprecation warning object.
+ #
+ # @param deprecation [Chef::Deprecated::Base] Deprecation object to log.
+ # In previous versions, this could be a string. Don't do that anymore.
+ # @param location [Object] Unused, present only for compatibility.
+ def deprecation(deprecation, _location = nil)
+ Chef::Log.deprecation(deprecation.to_s) unless deprecation.silenced?
+ end
+
+ def is_structured_deprecation?(deprecation)
+ deprecation.is_a?(Chef::Deprecated::Base)
end
def is_formatter?
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index 7dbbf1d948..513ac45471 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -1,5 +1,6 @@
-require "chef/formatters/base"
-require "chef/config"
+require_relative "base"
+require_relative "../config"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
module Formatters
@@ -8,8 +9,7 @@ class Chef
# show context.
class Doc < Formatters::Base
- attr_reader :start_time, :end_time, :successful_audits, :failed_audits
- private :successful_audits, :failed_audits
+ attr_reader :start_time, :end_time
cli_name(:doc)
@@ -18,8 +18,6 @@ class Chef
@updated_resources = 0
@up_to_date_resources = 0
- @successful_audits = 0
- @failed_audits = 0
@start_time = Time.now
@end_time = @start_time
@skipped_resources = 0
@@ -42,8 +40,10 @@ class Chef
message
end
- def run_start(version)
- puts_line "Starting Chef Client, version #{version}"
+ def run_start(version, run_status)
+ puts_line "Starting #{ChefUtils::Dist::Infra::PRODUCT}, version #{version}"
+ puts_line "Patents: #{ChefUtils::Dist::Org::PATENTS}"
+ puts_line "Targeting node: #{Chef::Config.target_mode.host}" if Chef::Config.target_mode?
puts_line "OpenSSL FIPS 140 mode enabled" if Chef::Config[:fips]
end
@@ -51,19 +51,16 @@ class Chef
@up_to_date_resources + @updated_resources + @skipped_resources
end
- def total_audits
- successful_audits + failed_audits
- end
-
def run_completed(node)
@end_time = Time.now
# Print out deprecations.
- if !deprecations.empty?
+ unless 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,49 +71,42 @@ class Chef
prefix = " "
end
end
+ unless details[:url].nil?
+ puts_line " See #{details[:url]} for further details."
+ end
end
puts_line ""
end
if Chef::Config[:why_run]
- puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
+ puts_line "#{ChefUtils::Dist::Infra::PRODUCT} finished, #{@updated_resources}/#{total_resources} resources would have been updated"
else
- puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{pretty_elapsed_time}"
- if total_audits > 0
- puts_line " #{successful_audits}/#{total_audits} controls succeeded"
- end
+ puts_line "#{ChefUtils::Dist::Infra::PRODUCT} finished, #{@updated_resources}/#{total_resources} resources updated in #{pretty_elapsed_time}"
end
end
def run_failed(exception)
@end_time = Time.now
if Chef::Config[:why_run]
- puts_line "Chef Client failed. #{@updated_resources} resources would have been updated"
+ puts_line "#{ChefUtils::Dist::Infra::PRODUCT} failed. #{@updated_resources} resources would have been updated"
else
- puts_line "Chef Client failed. #{@updated_resources} resources updated in #{pretty_elapsed_time}"
- if total_audits > 0
- puts_line " #{successful_audits} controls succeeded"
- end
+ puts_line "#{ChefUtils::Dist::Infra::PRODUCT} failed. #{@updated_resources} resources updated in #{pretty_elapsed_time}"
end
end
# Called right after ohai runs.
- def ohai_completed(node)
- end
+ def ohai_completed(node); end
# Already have a client key, assuming this node has registered.
- def skipping_registration(node_name, config)
- end
+ def skipping_registration(node_name, config); end
# About to attempt to register as +node_name+
def registration_start(node_name, config)
puts_line "Creating a new client identity for #{node_name} using the validator key."
end
- def registration_completed
- end
+ def registration_completed; end
- def node_load_start(node_name, config)
- end
+ def node_load_start(node_name, config); end
# Failed to load node data from the server
def node_load_failed(node_name, exception, config)
@@ -125,8 +115,7 @@ class Chef
# Default and override attrs from roles have been computed, but not yet applied.
# Normal attrs from JSON have been added to the node.
- def node_load_completed(node, expanded_run_list, config)
- end
+ def node_load_completed(node, expanded_run_list, config); end
def policyfile_loaded(policy)
puts_line "Using policy '#{policy["name"]}' at revision '#{policy["revision_id"]}'"
@@ -144,22 +133,18 @@ class Chef
end
# Called when the cookbook collection is returned from the server.
- def cookbook_resolution_complete(cookbook_collection)
- end
+ def cookbook_resolution_complete(cookbook_collection); end
# Called before unneeded cookbooks are removed
- def cookbook_clean_start
- end
+ def cookbook_clean_start; end
# Called after the file at +path+ is removed. It may be removed if the
# cookbook containing it was removed from the run list, or if the file was
# removed from the cookbook.
- def removed_cookbook_file(path)
- end
+ def removed_cookbook_file(path); end
# Called when cookbook cleaning is finished.
- def cookbook_clean_complete
- end
+ def cookbook_clean_complete; end
# Called before cookbook sync starts
def cookbook_sync_start(cookbook_count)
@@ -173,8 +158,7 @@ class Chef
end
# Called when an individual file in a cookbook has been updated
- def updated_cookbook_file(cookbook_name, path)
- end
+ def updated_cookbook_file(cookbook_name, path); end
# Called after all cookbooks have been sync'd.
def cookbook_sync_complete
@@ -213,12 +197,10 @@ class Chef
end
# Called after a file in a cookbook is loaded.
- def file_loaded(path)
- end
+ def file_loaded(path); end
# Called when recipes have been loaded.
- def recipe_load_complete
- end
+ def recipe_load_complete; end
# Called before convergence starts
def converge_start(run_context)
@@ -235,37 +217,6 @@ class Chef
converge_complete
end
- # Called before audit phase starts
- def audit_phase_start(run_status)
- puts_line "Starting audit phase"
- end
-
- def audit_phase_complete(audit_output)
- puts_line audit_output
- puts_line "Auditing complete"
- end
-
- def audit_phase_failed(error, audit_output)
- puts_line audit_output
- puts_line ""
- puts_line "Audit phase exception:"
- indent
- puts_line "#{error.message}"
- if error.backtrace
- error.backtrace.each do |l|
- puts_line l
- end
- end
- end
-
- def control_example_success(control_group_name, example_data)
- @successful_audits += 1
- end
-
- def control_example_failure(control_group_name, example_data, error)
- @failed_audits += 1
- end
-
# Called before action is executed on a resource.
def resource_action_start(resource, action, notification_type = nil, notifier = nil)
if resource.cookbook_name && resource.recipe_name
@@ -280,17 +231,17 @@ class Chef
@current_recipe = resource_recipe
indent
end
- # TODO: info about notifies
- start_line "* #{resource} action #{action}", :stream => resource
+ # @todo info about notifies
+ start_line "* #{resource} action #{action}", stream: resource
indent
end
def resource_update_progress(resource, current, total, interval)
- @progress[resource] ||= 0
+ @progress[resource] ||= -1
- percent_complete = (current.to_f / total.to_f * 100).to_i
+ percent_complete = (current.to_f / total.to_f * 100).to_i unless total.to_f == 0.0
- if percent_complete > @progress[resource]
+ if percent_complete && percent_complete > @progress[resource]
@progress[resource] = percent_complete
@@ -301,8 +252,7 @@ class Chef
end
# Called when a resource fails, but will retry.
- def resource_failed_retriable(resource, action, retry_count, exception)
- end
+ def resource_failed_retriable(resource, action, retry_count, exception); end
# Called when a resource fails and will not be retried.
def resource_failed(resource, action, exception)
@@ -314,28 +264,26 @@ class Chef
def resource_skipped(resource, action, conditional)
@skipped_resources += 1
# TODO: more info about conditional
- puts " (skipped due to #{conditional.short_description})", :stream => resource
+ puts " (skipped due to #{conditional.short_description})", stream: resource
unindent
end
# Called after #load_current_resource has run.
- def resource_current_state_loaded(resource, action, current_resource)
- end
+ def resource_current_state_loaded(resource, action, current_resource); end
# Called when a resource has no converge actions, e.g., it was already correct.
def resource_up_to_date(resource, action)
@up_to_date_resources += 1
- puts " (up to date)", :stream => resource
+ puts " (up to date)", stream: resource unless resource.suppress_up_to_date_messages?
unindent
end
def resource_bypassed(resource, action, provider)
- puts " (Skipped: whyrun not supported by provider #{provider.class.name})", :stream => resource
+ puts " (Skipped: whyrun not supported by provider #{provider.class.name})", stream: resource
unindent
end
- def output_record(line)
- end
+ def output_record(line); end
# Called when a change has been made to a resource. May be called multiple
# times per resource, e.g., a file may have its content updated, and then
@@ -344,10 +292,11 @@ class Chef
prefix = Chef::Config[:why_run] ? "Would " : ""
Array(update).each do |line|
next if line.nil?
+
output_record line
- if line.kind_of? String
+ if line.is_a? String
start_line "- #{prefix}#{line}", :green
- elsif line.kind_of? Array
+ elsif line.is_a? Array
# Expanded output - delta
# @todo should we have a resource_update_delta callback?
line.each do |detail|
@@ -371,7 +320,7 @@ class Chef
end
def stream_output(stream, output, options = {})
- print(output, { :stream => stream }.merge(options))
+ print(output, { stream: stream }.merge(options))
end
# Called before handlers run
@@ -396,6 +345,7 @@ class Chef
# in whyrun mode, in order to allow execution to continue
def whyrun_assumption(action, resource, message)
return unless message
+
[ message ].flatten.each do |line|
start_line("* #{line}", :yellow)
end
@@ -404,20 +354,22 @@ class Chef
# Called when an assertion declared by a provider fails
def provider_requirement_failed(action, resource, exception, message)
return unless message
+
color = Chef::Config[:why_run] ? :yellow : :red
[ message ].flatten.each do |line|
start_line("* #{line}", color)
end
end
- def deprecation(message, location = caller(2..2)[0])
+ # (see Base#deprecation)
+ def deprecation(deprecation, _location = nil)
if Chef::Config[:treat_deprecation_warnings_as_errors]
super
+ elsif !deprecation.silenced?
+ # Save non-silenced deprecations to the screen until the end.
+ deprecations[deprecation.message] ||= { url: deprecation.url, locations: Set.new }
+ deprecations[deprecation.message][:locations] << deprecation.location
end
-
- # Save deprecations to the screen until the end
- deprecations[message] ||= Set.new
- deprecations[message] << location
end
def indent
diff --git a/lib/chef/formatters/error_description.rb b/lib/chef/formatters/error_description.rb
index 8d7f940181..dd64d37c5c 100644
--- a/lib/chef/formatters/error_description.rb
+++ b/lib/chef/formatters/error_description.rb
@@ -1,7 +1,7 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,8 @@
# limitations under the License.
#
+require_relative "../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.rb b/lib/chef/formatters/error_inspectors.rb
index 9221ecda4d..e83d527e05 100644
--- a/lib/chef/formatters/error_inspectors.rb
+++ b/lib/chef/formatters/error_inspectors.rb
@@ -1,10 +1,10 @@
-require "chef/formatters/error_inspectors/node_load_error_inspector"
-require "chef/formatters/error_inspectors/registration_error_inspector"
-require "chef/formatters/error_inspectors/compile_error_inspector"
-require "chef/formatters/error_inspectors/resource_failure_inspector"
-require "chef/formatters/error_inspectors/run_list_expansion_error_inspector"
-require "chef/formatters/error_inspectors/cookbook_resolve_error_inspector"
-require "chef/formatters/error_inspectors/cookbook_sync_error_inspector"
+require_relative "error_inspectors/node_load_error_inspector"
+require_relative "error_inspectors/registration_error_inspector"
+require_relative "error_inspectors/compile_error_inspector"
+require_relative "error_inspectors/resource_failure_inspector"
+require_relative "error_inspectors/run_list_expansion_error_inspector"
+require_relative "error_inspectors/cookbook_resolve_error_inspector"
+require_relative "error_inspectors/cookbook_sync_error_inspector"
class Chef
module Formatters
diff --git a/lib/chef/formatters/error_inspectors/api_error_formatting.rb b/lib/chef/formatters/error_inspectors/api_error_formatting.rb
index 2415d0f4bb..ee4583c89b 100644
--- a/lib/chef/formatters/error_inspectors/api_error_formatting.rb
+++ b/lib/chef/formatters/error_inspectors/api_error_formatting.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,90 +16,92 @@
# limitations under the License.
#
-require "chef/http/authenticator"
+require_relative "../../http/authenticator"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "timeout" unless defined?(Timeout)
class Chef
module Formatters
module APIErrorFormatting
- NETWORK_ERROR_CLASSES = [Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError]
+ NETWORK_ERROR_CLASSES = [Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError].freeze
def describe_network_errors(error_description)
- error_description.section("Networking Error:", <<-E)
-#{exception.message}
-
-Your chef_server_url may be misconfigured, or the network could be down.
-E
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-E
+ error_description.section("Networking Error:", <<~E)
+ #{exception.message}
+
+ Your chef_server_url may be misconfigured, or the network could be down.
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ E
end
def describe_eof_error(error_description)
- error_description.section("Authentication Error:", <<-E)
-Received an EOF on transport socket. This almost always indicates a network
-error external to chef-client. Some causes include:
+ error_description.section("Authentication Error:", <<~E)
+ Received an EOF on transport socket. This almost always indicates a network
+ error external to #{ChefUtils::Dist::Infra::CLIENT}. Some causes include:
- - Blocking ICMP Dest Unreachable (breaking Path MTU Discovery)
- - IPsec or VPN tunnelling / TCP Encapsulation MTU issues
- - Jumbo frames configured only on one side (breaking Path MTU)
- - Jumbo frames configured on a LAN that does not support them
- - Proxies or Load Balancers breaking large POSTs
- - Broken TCP offload in network drivers/hardware
+ - Blocking ICMP Dest Unreachable (breaking Path MTU Discovery)
+ - IPsec or VPN tunnelling / TCP Encapsulation MTU issues
+ - Jumbo frames configured only on one side (breaking Path MTU)
+ - Jumbo frames configured on a LAN that does not support them
+ - Proxies or Load Balancers breaking large POSTs
+ - Broken TCP offload in network drivers/hardware
-Try sending large pings to the destination:
+ Try sending large pings to the destination:
- windows: ping server.example.com -f -l 9999
- unix: ping server.example.com -s 9999
+ windows: ping server.example.com -f -l 9999
+ unix: ping server.example.com -s 9999
-Try sending large POSTs to the destination (any HTTP code returned is success):
+ Try sending large POSTs to the destination (any HTTP code returned is success):
- e.g.: curl http://server.example.com/`printf '%*s' 9999 '' | tr ' ' 'a'`
+ e.g.: curl http://server.example.com/`printf '%*s' 9999 '' | tr ' ' 'a'`
-Try disabling TCP Offload Engines (TOE) in your ethernet drivers.
+ Try disabling TCP Offload Engines (TOE) in your ethernet drivers.
- windows:
- Disable-NetAdapterChecksumOffload * -TcpIPv4 -UdpIPv4 -IpIPv4 -NoRestart
- Disable-NetAdapterLso * -IPv4 -NoRestart
- Set-NetAdapterAdvancedProperty * -DisplayName "Large Receive Offload (IPv4)" -DisplayValue Disabled –NoRestart
- Restart-NetAdapter *
- unix(bash):
- for i in rx tx sg tso ufo gso gro lro rxvlan txvlan rxhash; do /sbin/ethtool -K eth0 $i off; done
+ windows:
+ Disable-NetAdapterChecksumOffload * -TcpIPv4 -UdpIPv4 -IpIPv4 -NoRestart
+ Disable-NetAdapterLso * -IPv4 -NoRestart
+ Set-NetAdapterAdvancedProperty * -DisplayName "Large Receive Offload (IPv4)" -DisplayValue Disabled –NoRestart
+ Restart-NetAdapter *
+ unix(bash):
+ for i in rx tx sg tso ufo gso gro lro rxvlan txvlan rxhash; do /sbin/ethtool -K eth0 $i off; done
-In some cases the underlying virtualization layer (Xen, VMware, KVM, Hyper-V, etc) may have
-broken virtual networking code.
+ In some cases the underlying virtualization layer (Xen, VMware, KVM, Hyper-V, etc) may have
+ broken virtual networking code.
E
end
def describe_401_error(error_description)
if clock_skew?
- error_description.section("Authentication Error:", <<-E)
-Failed to authenticate to the chef server (http 401).
-The request failed because your clock has drifted by more than 15 minutes.
-Syncing your clock to an NTP Time source should resolve the issue.
-E
+ error_description.section("Authentication Error:", <<~E)
+ Failed to authenticate to the chef server (http 401).
+ The request failed because your clock has drifted by more than 15 minutes.
+ Syncing your clock to an NTP Time source should resolve the issue.
+ E
else
- error_description.section("Authentication Error:", <<-E)
-Failed to authenticate to the chef server (http 401).
-E
+ error_description.section("Authentication Error:", <<~E)
+ Failed to authenticate to the chef server (http 401).
+ E
error_description.section("Server Response:", format_rest_error)
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-node_name "#{username}"
-client_key "#{api_key}"
-
-If these settings are correct, your client_key may be invalid, or
-you may have a chef user with the same client name as this node.
-E
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ node_name "#{username}"
+ client_key "#{api_key}"
+
+ If these settings are correct, your client_key may be invalid, or
+ you may have a chef user with the same client name as this node.
+ E
end
end
def describe_400_error(error_description)
- error_description.section("Invalid Request Data:", <<-E)
-The data in your request was invalid (HTTP 400).
-E
+ error_description.section("Invalid Request Data:", <<~E)
+ The data in your request was invalid (HTTP 400).
+ E
error_description.section("Server Response:", format_rest_error)
end
@@ -110,26 +112,26 @@ E
min_server_version = version_header["min_version"]
max_server_version = version_header["max_version"]
- error_description.section("Incompatible server API version:", <<-E)
-This version of the API that this Chef request specified is not supported by the Chef server you sent this request to.
-The server supports a min API version of #{min_server_version} and a max API version of #{max_server_version}.
-Chef just made a request with an API version of #{client_api_version}.
-Please either update your Chef client or server to be a compatible set.
-E
+ error_description.section("Incompatible server API version:", <<~E)
+ This version of the API that this request specified is not supported by the server you sent this request to.
+ The server supports a min API version of #{min_server_version} and a max API version of #{max_server_version}.
+ #{ChefUtils::Dist::Infra::PRODUCT} just made a request with an API version of #{client_api_version}.
+ Please either update your #{ChefUtils::Dist::Infra::PRODUCT} or the server to be a compatible set.
+ E
else
describe_http_error(error_description)
end
end
def describe_500_error(error_description)
- error_description.section("Unknown Server Error:", <<-E)
-The server had a fatal error attempting to load the node data.
-E
+ error_description.section("Unknown Server Error:", <<~E)
+ The server had a fatal error attempting to load the node data.
+ E
error_description.section("Server Response:", format_rest_error)
end
def describe_503_error(error_description)
- error_description.section("Server Unavailable", "The Chef Server is temporarily unavailable")
+ error_description.section("Server Unavailable", "The #{ChefUtils::Dist::Server::PRODUCT} is temporarily unavailable")
error_description.section("Server Response:", format_rest_error)
end
@@ -172,11 +174,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..e42340ff3a 100644
--- a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,7 +41,7 @@ class Chef
if found_error_in_cookbooks?
traceback = filtered_bt.map { |line| " #{line}" }.join("\n")
- error_description.section("Cookbook Trace:", traceback)
+ error_description.section("Cookbook Trace: (most recent call first)", traceback)
error_description.section("Relevant File Content:", context)
end
@@ -108,21 +108,21 @@ 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
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}'")
+ line_number = culprit_backtrace_entry[/^(?:.\:)?[^:]+:(\d+)/, 1].to_i
+ Chef::Log.trace("Line number of compile error: '#{line_number}'")
line_number
end
end
def culprit_file
- @culprit_file ||= culprit_backtrace_entry[/^((?:.\:)?[^:]+):([\d]+)/, 1]
+ @culprit_file ||= culprit_backtrace_entry[/^((?:.\:)?[^:]+):(\d+)/, 1]
end
def filtered_bt
@@ -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_inspectors/cookbook_resolve_error_inspector.rb b/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb
index eb1aa629ff..b5029c082a 100644
--- a/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/formatters/error_inspectors/api_error_formatting"
+require_relative "api_error_formatting"
class Chef
module Formatters
@@ -35,7 +35,7 @@ class Chef
def add_explanation(error_description)
case exception
- when Net::HTTPServerException, Net::HTTPFatalError
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(error_description)
when EOFError
describe_eof_error(error_description)
@@ -56,13 +56,13 @@ class Chef
# TODO: we're rescuing errors from Node.find_or_create
# * could be no write on nodes container
# * could be no read on the node
- error_description.section("Authorization Error", <<-E)
-This client is not authorized to read some of the information required to
-access its cookbooks (HTTP 403).
+ error_description.section("Authorization Error", <<~E)
+ This client is not authorized to read some of the information required to
+ access its cookbooks (HTTP 403).
-To access its cookbooks, a client needs to be able to read its environment and
-all of the cookbooks in its expanded run list.
-E
+ To access its cookbooks, a client needs to be able to read its environment and
+ all of the cookbooks in its expanded run list.
+ E
error_description.section("Expanded Run List:", expanded_run_list_ul)
error_description.section("Server Response:", format_rest_error)
when Net::HTTPPreconditionFailed
@@ -115,12 +115,12 @@ E
explanation << "Error message: #{error_reasons["message"]}\n"
end
- explanation << <<EOM
-You might be able to resolve this issue with:
- 1-) Removing cookbook versions that depend on deleted cookbooks.
- 2-) Removing unused cookbook versions.
- 3-) Pinning exact cookbook versions using environments.
-EOM
+ explanation << <<~EOM
+ You might be able to resolve this issue with:
+ 1-) Removing cookbook versions that depend on deleted cookbooks.
+ 2-) Removing unused cookbook versions.
+ 3-) Pinning exact cookbook versions using environments.
+ EOM
error_description.section("Cookbook dependency resolution error:", explanation)
end
@@ -143,14 +143,15 @@ EOM
# "{\"error\":[\"{\\\"non_existent_cookbooks\\\":[\\\"nope\\\"],\\\"cookbooks_with_no_versions\\\":[],\\\"message\\\":\\\"Run list contains invalid items: no such cookbook nope.\\\"}\"]}"
wrapped_error_message = attempt_json_parse(exception.response.body)
- unless wrapped_error_message.kind_of?(Hash) && wrapped_error_message.key?("error")
+ unless wrapped_error_message.is_a?(Hash) && wrapped_error_message.key?("error")
return wrapped_error_message.to_s
end
error_description = Array(wrapped_error_message["error"]).first
- if error_description.kind_of?(Hash)
+ if error_description.is_a?(Hash)
return error_description
end
+
attempt_json_parse(error_description)
end
diff --git a/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb b/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb
index 3bd9b419fa..45b550e5ab 100644
--- a/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/formatters/error_inspectors/api_error_formatting"
+require_relative "api_error_formatting"
class Chef
module Formatters
@@ -41,7 +41,7 @@ class Chef
def add_explanation(error_description)
case exception
- when Net::HTTPServerException, Net::HTTPFatalError
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(error_description)
when EOFError
describe_eof_error(error_description)
diff --git a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb
index c52dad4c09..7e904c9ee2 100644
--- a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,8 @@
# limitations under the License.
#
-require "chef/formatters/error_inspectors/api_error_formatting"
+require_relative "api_error_formatting"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
module Formatters
@@ -40,16 +41,16 @@ class Chef
def add_explanation(error_description)
case exception
- when Net::HTTPServerException, Net::HTTPFatalError
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(error_description)
when Chef::Exceptions::PrivateKeyMissing
- error_description.section("Private Key Not Found:", <<-E)
-Your private key could not be loaded. If the key file exists, ensure that it is
-readable by chef-client.
-E
- error_description.section("Relevant Config Settings:", <<-E)
-client_key "#{api_key}"
-E
+ error_description.section("Private Key Not Found:", <<~E)
+ Your private key could not be loaded. If the key file exists, ensure that it is
+ readable by #{ChefUtils::Dist::Infra::PRODUCT}.
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ client_key "#{api_key}"
+ E
when EOFError
describe_eof_error(error_description)
when *NETWORK_ERROR_CLASSES
@@ -69,14 +70,14 @@ E
# TODO: we're rescuing errors from Node.find_or_create
# * could be no write on nodes container
# * could be no read on the node
- error_description.section("Authorization Error", <<-E)
-Your client is not authorized to load the node data (HTTP 403).
-E
+ error_description.section("Authorization Error", <<~E)
+ Your client is not authorized to load the node data (HTTP 403).
+ E
error_description.section("Server Response:", format_rest_error)
- error_description.section("Possible Causes:", <<-E)
-* Your client (#{username}) may have misconfigured authorization permissions.
-E
+ error_description.section("Possible Causes:", <<~E)
+ * Your client (#{username}) may have misconfigured authorization permissions.
+ E
when Net::HTTPBadRequest
describe_400_error(error_description)
when Net::HTTPNotFound
@@ -97,12 +98,12 @@ E
# one, e.g., PUT http://wrong.url/nodes/node-name becomes a GET after a
# redirect.
def describe_404_error(error_description)
- error_description.section("Resource Not Found:", <<-E)
-The server returned a HTTP 404. This usually indicates that your chef_server_url is incorrect.
-E
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-E
+ error_description.section("Resource Not Found:", <<~E)
+ The #{ChefUtils::Dist::Server::PRODUCT} returned a HTTP 404. This usually indicates that your chef_server_url is incorrect.
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ E
end
def username
diff --git a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb
index c7c1454311..4897ac6e1b 100644
--- a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb
@@ -1,7 +1,8 @@
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
class Chef
module Formatters
module ErrorInspectors
-
# == RegistrationErrorInspector
# Wraps exceptions that occur during the client registration process and
# suggests possible causes.
@@ -23,30 +24,30 @@ class Chef
def add_explanation(error_description)
case exception
- when Net::HTTPServerException, Net::HTTPFatalError
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(error_description)
when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError
- error_description.section("Network Error:", <<-E)
-There was a network error connecting to the Chef Server:
-#{exception.message}
-E
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
+ error_description.section("Network Error:", <<~E)
+ There was a network error connecting to the #{ChefUtils::Dist::Server::PRODUCT}:
+ #{exception.message}
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
-If your chef_server_url is correct, your network could be down.
-E
+ If your chef_server_url is correct, your network could be down.
+ E
when Chef::Exceptions::PrivateKeyMissing
- error_description.section("Private Key Not Found:", <<-E)
-Your private key could not be loaded. If the key file exists, ensure that it is
-readable by chef-client.
-E
- error_description.section("Relevant Config Settings:", <<-E)
-validation_key "#{api_key}"
-E
+ error_description.section("Private Key Not Found:", <<~E)
+ Your private key could not be loaded. If the key file exists, ensure that it is
+ readable by #{ChefUtils::Dist::Infra::PRODUCT}.
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ validation_key "#{api_key}"
+ E
when Chef::Exceptions::InvalidRedirect
- error_description.section("Invalid Redirect:", <<-E)
-Change your server location in client.rb to the server's FQDN to avoid unwanted redirections.
-E
+ error_description.section("Invalid Redirect:", <<~E)
+ Change your #{ChefUtils::Dist::Server::PRODUCT} location in client.rb to the #{ChefUtils::Dist::Server::PRODUCT}'s FQDN to avoid unwanted redirections.
+ E
when EOFError
describe_eof_error(error_description)
else
@@ -59,54 +60,54 @@ E
case response
when Net::HTTPUnauthorized
if clock_skew?
- error_description.section("Authentication Error:", <<-E)
-Failed to authenticate to the chef server (http 401).
-The request failed because your clock has drifted by more than 15 minutes.
-Syncing your clock to an NTP Time source should resolve the issue.
-E
+ error_description.section("Authentication Error:", <<~E)
+ Failed to authenticate to the #{ChefUtils::Dist::Server::PRODUCT} (http 401).
+ The request failed because your clock has drifted by more than 15 minutes.
+ Syncing your clock to an NTP Time source should resolve the issue.
+ E
else
- error_description.section("Authentication Error:", <<-E)
-Failed to authenticate to the chef server (http 401).
-E
+ error_description.section("Authentication Error:", <<~E)
+ Failed to authenticate to the #{ChefUtils::Dist::Server::PRODUCT} (http 401).
+ E
error_description.section("Server Response:", format_rest_error)
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-validation_client_name "#{username}"
-validation_key "#{api_key}"
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ validation_client_name "#{username}"
+ validation_key "#{api_key}"
-If these settings are correct, your validation_key may be invalid.
-E
+ If these settings are correct, your validation_key may be invalid.
+ E
end
when Net::HTTPForbidden
- error_description.section("Authorization Error:", <<-E)
-Your validation client is not authorized to create the client for this node (HTTP 403).
-E
- error_description.section("Possible Causes:", <<-E)
-* There may already be a client named "#{config[:node_name]}"
-* Your validation client (#{username}) may have misconfigured authorization permissions.
-E
+ error_description.section("Authorization Error:", <<~E)
+ Your validation client is not authorized to create the client for this node on the #{ChefUtils::Dist::Server::PRODUCT} (HTTP 403).
+ E
+ error_description.section("Possible Causes:", <<~E)
+ * There may already be a client named "#{config[:node_name]}"
+ * Your validation client (#{username}) may have misconfigured authorization permissions.
+ E
when Net::HTTPBadRequest
- error_description.section("Invalid Request Data:", <<-E)
-The data in your request was invalid (HTTP 400).
-E
+ error_description.section("Invalid Request Data:", <<~E)
+ The data in your request was invalid (HTTP 400).
+ E
error_description.section("Server Response:", format_rest_error)
when Net::HTTPNotFound
- error_description.section("Resource Not Found:", <<-E)
-The server returned a HTTP 404. This usually indicates that your chef_server_url is incorrect.
-E
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-E
+ error_description.section("Resource Not Found:", <<~E)
+ The #{ChefUtils::Dist::Server::PRODUCT} returned a HTTP 404. This usually indicates that your chef_server_url configuration is incorrect.
+ E
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ E
when Net::HTTPNotAcceptable
describe_406_error(error_description, response)
when Net::HTTPInternalServerError
- error_description.section("Unknown Server Error:", <<-E)
-The server had a fatal error attempting to load the node data.
-E
+ error_description.section("Unknown Server Error:", <<~E)
+ The server had a fatal error attempting to load the node data.
+ E
error_description.section("Server Response:", format_rest_error)
when Net::HTTPBadGateway, Net::HTTPServiceUnavailable
- error_description.section("Server Unavailable", "The Chef Server is temporarily unavailable")
+ error_description.section("Server Unavailable", "The #{ChefUtils::Dist::Server::PRODUCT} is temporarily unavailable")
error_description.section("Server Response:", format_rest_error)
else
error_description.section("Unexpected API Request Failure:", format_rest_error)
@@ -114,13 +115,13 @@ E
end
def username
- #config[:node_name]
+ # config[:node_name]
config[:validation_client_name]
end
def api_key
config[:validation_key]
- #config[:client_key]
+ # config[:client_key]
end
def server_url
diff --git a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
index 94ecce88de..905a438f56 100644
--- a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb
@@ -1,7 +1,7 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+require "chef-utils" unless defined?(ChefUtils::CANARY)
class Chef
module Formatters
@@ -36,14 +37,14 @@ class Chef
error_description.section(exception.class.name, exception.message)
unless filtered_bt.empty?
- error_description.section("Cookbook Trace:", filtered_bt.join("\n"))
+ error_description.section("Cookbook Trace: (most recent call first)", filtered_bt.join("\n"))
end
unless dynamic_resource?
error_description.section("Resource Declaration:", resource.sensitive ? "suppressed sensitive resource output" : recipe_snippet)
end
- error_description.section("Compiled Resource:", "#{resource.to_text}")
+ error_description.section("Compiled Resource:", (resource.to_text).to_s)
# Template errors get wrapped in an exception class that can show the relevant template code,
# so add them to the error output.
@@ -51,20 +52,22 @@ class Chef
error_description.section("Template Context:", "#{exception.source_location}\n#{exception.source_listing}")
end
- if Chef::Platform.windows?
- require "chef/win32/security"
+ if ChefUtils.windows?
+ require_relative "../../win32/security"
- if !Chef::ReservedNames::Win32::Security.has_admin_privileges?
- error_description.section("Missing Windows Admin Privileges", "chef-client doesn't have administrator privileges. This can be a possible reason for the resource failure.")
+ unless Chef::ReservedNames::Win32::Security.has_admin_privileges?
+ error_description.section("Missing Windows Admin Privileges", "#{ChefUtils::Dist::Infra::CLIENT} doesn't have administrator privileges. This can be a possible reason for the resource failure.")
end
end
end
def recipe_snippet
return nil if dynamic_resource?
+
@snippet ||= begin
if (file = parse_source) && (line = parse_line(file))
return nil unless ::File.exists?(file)
+
lines = IO.readlines(file)
relevant_lines = ["# In #{file}\n\n"]
@@ -76,8 +79,8 @@ class Chef
loop do
# low rent parser. try to gracefully handle nested blocks in resources
- nesting += 1 if lines[current_line] =~ /[\s]+do[\s]*/
- nesting -= 1 if lines[current_line] =~ /end[\s]*$/
+ nesting += 1 if /\s+do\s*/.match?(lines[current_line])
+ nesting -= 1 if /end\s*$/.match?(lines[current_line])
relevant_lines << format_line(current_line, lines[current_line])
@@ -111,11 +114,11 @@ class Chef
end
def parse_source
- resource.source_line[/^(([\w]:)?[^:]+):([\d]+)/, 1]
+ resource.source_line[/^((\w:)?[^:]+):(\d+)/, 1]
end
def parse_line(source)
- resource.source_line[/^#{Regexp.escape(source)}:([\d]+)/, 1].to_i
+ resource.source_line[/^#{Regexp.escape(source)}:(\d+)/, 1].to_i
end
end
diff --git a/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb b/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb
index e94b347378..6e452c959b 100644
--- a/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb
@@ -1,7 +1,7 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,8 @@
# limitations under the License.
#
-require "chef/formatters/error_inspectors/api_error_formatting"
+require_relative "api_error_formatting"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
module Formatters
@@ -36,12 +37,12 @@ class Chef
def add_explanation(error_description)
case exception
when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError
- error_description.section("Networking Error:", <<-E)
-#{exception.message}
+ error_description.section("Networking Error:", <<~E)
+ #{exception.message}
-Your chef_server_url may be misconfigured, or the network could be down.
-E
- when Net::HTTPServerException, Net::HTTPFatalError
+ Your chef_server_url may be misconfigured, or the network could be down.
+ E
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(error_description)
when Chef::Exceptions::MissingRole
describe_missing_role(error_description)
@@ -76,39 +77,39 @@ E
response = exception.response
case response
when Net::HTTPUnauthorized
- error_description.section("Authentication Error:", <<-E)
-Failed to authenticate to the chef server (http 401).
-E
+ error_description.section("Authentication Error:", <<~E)
+ Failed to authenticate to the #{ChefUtils::Dist::Server::PRODUCT} (http 401).
+ E
error_description.section("Server Response:", format_rest_error)
- error_description.section("Relevant Config Settings:", <<-E)
-chef_server_url "#{server_url}"
-node_name "#{username}"
-client_key "#{api_key}"
+ error_description.section("Relevant Config Settings:", <<~E)
+ chef_server_url "#{server_url}"
+ node_name "#{username}"
+ client_key "#{api_key}"
-If these settings are correct, your client_key may be invalid.
-E
+ If these settings are correct, your client_key may be invalid.
+ E
when Net::HTTPForbidden
# TODO: we're rescuing errors from Node.find_or_create
# * could be no write on nodes container
# * could be no read on the node
- error_description.section("Authorization Error", <<-E)
-Your client is not authorized to load one or more of your roles (HTTP 403).
-E
+ error_description.section("Authorization Error", <<~E)
+ Your client is not authorized to load one or more of your roles (HTTP 403).
+ E
error_description.section("Server Response:", format_rest_error)
- error_description.section("Possible Causes:", <<-E)
-* Your client (#{username}) may have misconfigured authorization permissions.
-E
+ error_description.section("Possible Causes:", <<~E)
+ * Your client (#{username}) may have misconfigured authorization permissions.
+ E
when Net::HTTPNotAcceptable
describe_406_error(error_description, response)
when Net::HTTPInternalServerError
- error_description.section("Unknown Server Error:", <<-E)
-The server had a fatal error attempting to load a role.
-E
+ error_description.section("Unknown Server Error:", <<~E)
+ The server had a fatal error attempting to load a role.
+ E
error_description.section("Server Response:", format_rest_error)
when Net::HTTPBadGateway, Net::HTTPServiceUnavailable
- error_description.section("Server Unavailable", "The Chef Server is temporarily unavailable")
+ error_description.section("Server Unavailable", "The #{ChefUtils::Dist::Server::PRODUCT} is temporarily unavailable")
error_description.section("Server Response:", format_rest_error)
else
error_description.section("Unexpected API Request Failure:", format_rest_error)
diff --git a/lib/chef/formatters/error_mapper.rb b/lib/chef/formatters/error_mapper.rb
index 4786a20465..b0d25149dd 100644
--- a/lib/chef/formatters/error_mapper.rb
+++ b/lib/chef/formatters/error_mapper.rb
@@ -1,6 +1,6 @@
#--
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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/formatters/indentable_output_stream.rb b/lib/chef/formatters/indentable_output_stream.rb
index e5e84e0f65..4943041b37 100644
--- a/lib/chef/formatters/indentable_output_stream.rb
+++ b/lib/chef/formatters/indentable_output_stream.rb
@@ -17,37 +17,40 @@ class Chef
@semaphore = Mutex.new
end
- def highline
- @highline ||= begin
- require "highline"
- HighLine.new
+ # pastel.decorate is a lightweight replacement for highline.color
+ def pastel
+ @pastel ||= begin
+ require "pastel" unless defined?(Pastel)
+ Pastel.new
end
end
- # Print text. This will start a new line and indent if necessary
- # but will not terminate the line (future print and puts statements
- # will start off where this print left off).
- def color(string, *args)
- print(string, from_args(args))
- end
-
# Print the start of a new line. This will terminate any existing lines and
# cause indentation but will not move to the next line yet (future 'print'
# and 'puts' statements will stay on this line).
+ #
+ # @param string [String]
+ # @param args [Array<Hash,Symbol>]
def start_line(string, *args)
- print(string, from_args(args, :start_line => true))
+ print(string, from_args(args, start_line: true))
end
# Print a line. This will continue from the last start_line or print,
# or start a new line and indent if necessary.
+ #
+ # @param string [String]
+ # @param args [Array<Hash,Symbol>]
def puts(string, *args)
- print(string, from_args(args, :end_line => true))
+ print(string, from_args(args, end_line: true))
end
# Print an entire line from start to end. This will terminate any existing
# lines and cause indentation.
+ #
+ # @param string [String]
+ # @param args [Array<Hash,Symbol>]
def puts_line(string, *args)
- print(string, from_args(args, :start_line => true, :end_line => true))
+ print(string, from_args(args, start_line: true, end_line: true))
end
# Print a raw chunk
@@ -71,7 +74,7 @@ class Chef
#
# == Alternative
#
- # You may also call print('string', :red) (a list of colors a la Highline.color)
+ # You may also call print('string', :red) (https://github.com/piotrmurach/pastel#3-supported-colors)
def print(string, *args)
options = from_args(args)
@@ -100,10 +103,10 @@ class Chef
end
def from_args(colors, merge_options = {})
- if colors.size == 1 && colors[0].kind_of?(Hash)
+ if colors.size == 1 && colors[0].is_a?(Hash)
merge_options.merge(colors[0])
else
- merge_options.merge({ :colors => colors })
+ merge_options.merge({ colors: colors })
end
end
@@ -123,12 +126,12 @@ class Chef
indent_line(options)
# Note that the next line will need to be started
- if line[-1..-1] == "\n"
+ if line[-1..] == "\n"
@line_started = false
end
if Chef::Config[:color] && options[:colors]
- @out.print highline.color(line, *options[:colors])
+ @out.print pastel.decorate(line, *options[:colors])
else
@out.print line
end
@@ -142,14 +145,14 @@ class Chef
end
def indent_line(options)
- if !@line_started
+ unless @line_started
# Print indents. If there is a stream name, either print it (if we're
# switching streams) or print enough blanks to match
# the indents.
if options[:name]
if @current_stream != options[:stream]
- @out.print "#{(' ' * indent)}[#{options[:name]}] "
+ @out.print "#{(" " * indent)}[#{options[:name]}] "
else
@out.print " " * (indent + 3 + options[:name].size)
end
diff --git a/lib/chef/formatters/minimal.rb b/lib/chef/formatters/minimal.rb
index c8fc504eb0..6a067c4f86 100644
--- a/lib/chef/formatters/minimal.rb
+++ b/lib/chef/formatters/minimal.rb
@@ -1,4 +1,5 @@
-require "chef/formatters/base"
+require_relative "base"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
@@ -26,52 +27,47 @@ class Chef
end
# Called at the very start of a Chef Run
- def run_start(version)
- puts_line "Starting Chef Client, version #{version}"
+ def run_start(version, run_status)
+ puts_line "Starting #{ChefUtils::Dist::Infra::PRODUCT}, version #{version}"
+ puts_line "Patents: #{ChefUtils::Dist::Org::PATENTS}"
+ puts_line "Targeting node: #{Chef::Config.target_mode.host}" if Chef::Config.target_mode?
puts_line "OpenSSL FIPS 140 mode enabled" if Chef::Config[:fips]
end
# Called at the end of the Chef run.
def run_completed(node)
- puts "chef client finished, #{@updated_resources.size} resources updated"
+ puts "#{ChefUtils::Dist::Infra::PRODUCT} finished, #{@updated_resources.size} resources updated"
end
# called at the end of a failed run
def run_failed(exception)
- puts "chef client failed. #{@updated_resources.size} resources updated"
+ puts "#{ChefUtils::Dist::Infra::PRODUCT} failed. #{@updated_resources.size} resources updated"
end
# Called right after ohai runs.
- def ohai_completed(node)
- end
+ def ohai_completed(node); end
# Already have a client key, assuming this node has registered.
- def skipping_registration(node_name, config)
- end
+ def skipping_registration(node_name, config); end
# About to attempt to register as +node_name+
- def registration_start(node_name, config)
- end
+ def registration_start(node_name, config); end
- def registration_completed
- end
+ def registration_completed; end
# Failed to register this client with the server.
def registration_failed(node_name, exception, config)
super
end
- def node_load_start(node_name, config)
- end
+ def node_load_start(node_name, config); end
# Failed to load node data from the server
- def node_load_failed(node_name, exception, config)
- end
+ def node_load_failed(node_name, exception, config); end
# Default and override attrs from roles have been computed, but not yet applied.
# Normal attrs from JSON have been added to the node.
- def node_load_completed(node, expanded_run_list, config)
- end
+ def node_load_completed(node, expanded_run_list, config); end
# Called before the cookbook collection is fetched from the server.
def cookbook_resolution_start(expanded_run_list)
@@ -80,28 +76,23 @@ class Chef
# Called when there is an error getting the cookbook collection from the
# server.
- def cookbook_resolution_failed(expanded_run_list, exception)
- end
+ def cookbook_resolution_failed(expanded_run_list, exception); end
# Called when the cookbook collection is returned from the server.
- def cookbook_resolution_complete(cookbook_collection)
- end
+ def cookbook_resolution_complete(cookbook_collection); end
# Called before unneeded cookbooks are removed
#--
# TODO: Should be called in CookbookVersion.sync_cookbooks
- def cookbook_clean_start
- end
+ def cookbook_clean_start; end
# Called after the file at +path+ is removed. It may be removed if the
# cookbook containing it was removed from the run list, or if the file was
# removed from the cookbook.
- def removed_cookbook_file(path)
- end
+ def removed_cookbook_file(path); end
# Called when cookbook cleaning is finished.
- def cookbook_clean_complete
- end
+ def cookbook_clean_complete; end
# Called before cookbook sync starts
def cookbook_sync_start(cookbook_count)
@@ -114,8 +105,7 @@ class Chef
end
# Called when an individual file in a cookbook has been updated
- def updated_cookbook_file(cookbook_name, path)
- end
+ def updated_cookbook_file(cookbook_name, path); end
# Called after all cookbooks have been sync'd.
def cookbook_sync_complete
@@ -166,16 +156,13 @@ class Chef
end
# Called before action is executed on a resource.
- def resource_action_start(resource, action, notification_type = nil, notifier = nil)
- end
+ def resource_action_start(resource, action, notification_type = nil, notifier = nil); end
# Called when a resource fails, but will retry.
- def resource_failed_retriable(resource, action, retry_count, exception)
- end
+ def resource_failed_retriable(resource, action, retry_count, exception); end
# Called when a resource fails and will not be retried.
- def resource_failed(resource, action, exception)
- end
+ def resource_failed(resource, action, exception); end
# Called when a resource action has been skipped b/c of a conditional
def resource_skipped(resource, action, conditional)
@@ -183,8 +170,7 @@ class Chef
end
# Called after #load_current_resource has run.
- def resource_current_state_loaded(resource, action, current_resource)
- end
+ def resource_current_state_loaded(resource, action, current_resource); end
# Called when a resource has no converge actions, e.g., it was already correct.
def resource_up_to_date(resource, action)
@@ -209,24 +195,20 @@ class Chef
end
# Called before handlers run
- def handlers_start(handler_count)
- end
+ def handlers_start(handler_count); end
# Called after an individual handler has run
- def handler_executed(handler)
- end
+ def handler_executed(handler); end
# Called after all handlers have executed
- def handlers_completed
- end
+ def handlers_completed; end
# An uncategorized message. This supports the case that a user needs to
# pass output that doesn't fit into one of the callbacks above. Note that
# there's no semantic information about the content or importance of the
# message. That means that if you're using this too often, you should add a
# callback for it.
- def msg(message)
- end
+ def msg(message); end
end
end
diff --git a/lib/chef/guard_interpreter.rb b/lib/chef/guard_interpreter.rb
index e11201e50b..9deee91c1f 100644
--- a/lib/chef/guard_interpreter.rb
+++ b/lib/chef/guard_interpreter.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/guard_interpreter/default_guard_interpreter"
-require "chef/guard_interpreter/resource_guard_interpreter"
+require_relative "guard_interpreter/default_guard_interpreter"
+require_relative "guard_interpreter/resource_guard_interpreter"
class Chef
class GuardInterpreter
diff --git a/lib/chef/guard_interpreter/default_guard_interpreter.rb b/lib/chef/guard_interpreter/default_guard_interpreter.rb
index 449ca9a316..7b4a22c9cc 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,24 +16,27 @@
# limitations under the License.
#
-require "chef/mixin/shell_out"
+require_relative "../mixin/shell_out"
+require_relative "../exceptions"
class Chef
class GuardInterpreter
class DefaultGuardInterpreter
include Chef::Mixin::ShellOut
-
- protected
+ attr_reader :output
def initialize(command, opts)
@command = command
@command_opts = opts
end
- public
-
def evaluate
- shell_out(@command, @command_opts).status.success?
+ result = shell_out(@command, default_env: false, **@command_opts)
+ @output = "STDOUT: #{result.stdout}\nSTDERR: #{result.stderr}\n"
+ 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..a1e4205c03 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,70 +16,72 @@
# limitations under the License.
#
-require "chef/guard_interpreter"
+require_relative "../guard_interpreter"
class Chef
class GuardInterpreter
- class ResourceGuardInterpreter < DefaultGuardInterpreter
-
+ class ResourceGuardInterpreter
def initialize(parent_resource, command, opts)
- super(command, opts)
+ @command = command
+ @opts = opts
+
@parent_resource = parent_resource
@resource = get_interpreter_resource(parent_resource)
end
+ # This class used to inherit from DefaultGuardInterpreter and it responds
+ # to #output, so leave this in for potential backwards compatibility.
+ def output
+ nil
+ end
+
def evaluate
# Add attributes inherited from the parent class
# to the resource
merge_inherited_attributes
+ @opts.each do |attribute, value|
+ @resource.send(attribute, value)
+ end
+
# Only execute and script resources and use guard attributes.
# The command to be executed on them are passed via different attributes.
# Script resources use code attribute and execute resources use
- # command attribute. Moreover script resources are also execute
+ # command property. Moreover script resources are also execute
# resources. Here we make sure @command is assigned to the right
# attribute by checking the type of the resources.
# We need to make sure we check for Script first because any resource
# that can get to here is an Execute resource.
if @resource.is_a? Chef::Resource::Script
- block_attributes = @command_opts.merge({ :code => @command })
+ @resource.code @command
else
- block_attributes = @command_opts.merge({ :command => @command })
+ @resource.command @command
end
# Handles cases like powershell_script where default
# attributes are different when used in a guard vs. not. For
# powershell_script in particular, this will go away when
- # the one attribue that causes this changes its default to be
+ # the one attribute that causes this changes its default to be
# the same after some period to prepare for deprecation
if @resource.class.respond_to?(:get_default_attributes)
- block_attributes = @resource.class.send(:get_default_attributes, @command_opts).merge(block_attributes)
+ @resource.class.send(:get_default_attributes).each do |attribute, value|
+ @resource.send(attribute, value)
+ end
end
- resource_block = block_from_attributes(block_attributes)
- evaluate_action(nil, &resource_block)
- end
-
- protected
-
- def evaluate_action(action = nil, &block)
- @resource.instance_eval(&block)
-
- run_action = action || @resource.action
-
begin
# Coerce to an array to be safe. This could happen with a legacy
# resource or something overriding the default_action code in a
# subclass.
- Array(run_action).each { |action_to_run| @resource.run_action(action_to_run) }
- resource_updated = @resource.updated
+ Array(@resource.action).each { |action_to_run| @resource.run_action(action_to_run) }
+ @resource.updated
rescue Mixlib::ShellOut::ShellCommandFailed
- resource_updated = nil
+ nil
end
-
- resource_updated
end
+ private
+
def get_interpreter_resource(parent_resource)
if parent_resource.nil? || parent_resource.node.nil?
raise ArgumentError, "Node for guard resource parent must not be nil"
@@ -91,7 +93,7 @@ class Chef
raise ArgumentError, "Specified guard_interpreter resource #{parent_resource.guard_interpreter} unknown for this platform"
end
- if ! resource_class.ancestors.include?(Chef::Resource::Execute)
+ unless resource_class.ancestors.include?(Chef::Resource::Execute)
raise ArgumentError, "Specified guard interpreter class #{resource_class} must be a kind of Chef::Resource::Execute resource"
end
@@ -106,14 +108,6 @@ class Chef
interpreter_resource
end
- def block_from_attributes(attributes)
- Proc.new do
- attributes.keys.each do |attribute_name|
- send(attribute_name, attributes[attribute_name]) if respond_to?(attribute_name)
- end
- end
- end
-
def merge_inherited_attributes
inherited_attributes = []
@@ -121,15 +115,10 @@ class Chef
inherited_attributes = @parent_resource.class.send(:guard_inherited_attributes)
end
- if inherited_attributes && !inherited_attributes.empty?
- inherited_attributes.each do |attribute|
- if @parent_resource.respond_to?(attribute) && @resource.respond_to?(attribute)
- parent_value = @parent_resource.send(attribute)
- child_value = @resource.send(attribute)
- if parent_value || child_value
- @resource.send(attribute, parent_value)
- end
- end
+ inherited_attributes.each do |attribute|
+ if @parent_resource.respond_to?(attribute) && @resource.respond_to?(attribute)
+ parent_value = @parent_resource.send(attribute)
+ @resource.send(attribute, parent_value)
end
end
end
diff --git a/lib/chef/handler.rb b/lib/chef/handler.rb
index 100ed23d9e..97562ea31b 100644
--- a/lib/chef/handler.rb
+++ b/lib/chef/handler.rb
@@ -1,6 +1,6 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,18 +15,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/client"
-require "forwardable"
+require_relative "client"
+require "forwardable" unless defined?(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
@@ -236,13 +234,14 @@ class Chef
# The main entry point for report handling. Subclasses should override this
# method with their own report handling logic.
- def report
- end
+ def report; end
# Runs the report handler, rescuing and logging any errors it may cause.
# This ensures that all handlers get a chance to run even if one fails.
# This method should not be overridden by subclasses unless you know what
# you're doing.
+ #
+ # @api private
def run_report_safely(run_status)
run_report_unsafe(run_status)
rescue Exception => e
@@ -261,7 +260,7 @@ class Chef
# Return the Hash representation of the run_status
def data
- @run_status.to_hash
+ @run_status.to_h
end
end
diff --git a/lib/chef/handler/error_report.rb b/lib/chef/handler/error_report.rb
index e84a817e06..83d1d174db 100644
--- a/lib/chef/handler/error_report.rb
+++ b/lib/chef/handler/error_report.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/handler"
-require "chef/resource/directory"
+require_relative "../handler"
+require_relative "../resource/directory"
class Chef
class Handler
diff --git a/lib/chef/handler/json_file.rb b/lib/chef/handler/json_file.rb
index 9ba3d70383..6318c30d74 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/handler"
-require "chef/resource/directory"
+require_relative "../handler"
+require_relative "../resource/directory"
class Chef
class Handler
@@ -28,7 +28,6 @@ class Chef
def initialize(config = {})
@config = config
@config[:path] ||= "/var/chef/reports"
- @config
end
def report
@@ -42,7 +41,7 @@ class Chef
savetime = Time.now.strftime("%Y%m%d%H%M%S")
File.open(File.join(config[:path], "chef-run-report-#{savetime}.json"), "w") do |file|
- #ensure start time and end time are output in the json properly in the event activesupport happens to be on the system
+ # ensure start time and end time are output in the json properly in the event activesupport happens to be on the system
run_data = data
run_data[:start_time] = run_data[:start_time].to_s
run_data[:end_time] = run_data[:end_time].to_s
diff --git a/lib/chef/http.rb b/lib/chef/http.rb
index 924081bc6b..162998b7f3 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,14 +21,17 @@
# limitations under the License.
#
-require "tempfile"
-require "net/https"
-require "uri"
-require "chef/http/basic_client"
-require "chef/monkey_patches/net_http"
-require "chef/config"
-require "chef/platform/query_helpers"
-require "chef/exceptions"
+require "tempfile" unless defined?(Tempfile)
+autoload :OpenSSL, "openssl"
+autoload :URI, "uri"
+module Net
+ autoload :HTTP, "net/http"
+ autoload :HTTPClientException, "net/http"
+end
+require_relative "http/basic_client"
+require_relative "config"
+require_relative "platform/query_helpers"
+require_relative "exceptions"
class Chef
@@ -52,13 +55,12 @@ class Chef
def handle_chunk(next_chunk)
# stream handlers handle responses so must be applied in reverse order
- # (same as #apply_stream_complete_middleware or #apply_response_midddleware)
+ # (same as #apply_stream_complete_middleware or #apply_response_middleware)
@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
-
end
def self.middlewares
@@ -142,46 +144,62 @@ 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 Exception => exception
- log_failed_request(response, return_value) unless response.nil?
- if exception.respond_to?(:chef_rest_request=)
- exception.chef_rest_request = rest_request
+ rescue Net::HTTPClientException => e
+ http_attempts += 1
+ response = e.response
+ if response.is_a?(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?
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|
- if http_response.kind_of?(Net::HTTPSuccess)
- tempfile = stream_to_tempfile(url, http_response, &progress_block)
+ response, rest_request, return_value = send_http_request(method, url, processed_headers, data) do |http_response|
+ if http_response.is_a?(Net::HTTPSuccess)
+ tempfile = stream_to_tempfile(url, http_response, tempfile, &progress_block)
end
apply_stream_complete_middleware(http_response, rest_request, return_value)
end
- return nil if response.kind_of?(Net::HTTPRedirection)
- unless response.kind_of?(Net::HTTPSuccess)
+ return nil if response.is_a?(Net::HTTPRedirection)
+
+ unless response.is_a?(Net::HTTPSuccess)
response.error!
end
tempfile
+ rescue Net::HTTPClientException => e
+ http_attempts += 1
+ response = e.response
+ if response.is_a?(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=)
- e.chef_rest_request = rest_request
- end
raise
end
@@ -193,24 +211,26 @@ class Chef
# you to unlink the tempfile when you're done with it.
#
# @yield [tempfile] block to process the tempfile
- # @yieldparams [tempfile<Tempfile>] tempfile
- def streaming_request(path, headers = {})
+ # @yieldparam [tempfile<Tempfile>] tempfile
+ 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|
- if http_response.kind_of?(Net::HTTPSuccess)
- tempfile = stream_to_tempfile(url, http_response)
+ response, rest_request, return_value = send_http_request(method, url, processed_headers, data) do |http_response|
+ if http_response.is_a?(Net::HTTPSuccess)
+ tempfile = stream_to_tempfile(url, http_response, tempfile)
end
apply_stream_complete_middleware(http_response, rest_request, return_value)
end
- return nil if response.kind_of?(Net::HTTPRedirection)
- unless response.kind_of?(Net::HTTPSuccess)
+ return nil if response.is_a?(Net::HTTPRedirection)
+
+ unless response.is_a?(Net::HTTPSuccess)
response.error!
end
@@ -222,11 +242,17 @@ class Chef
end
end
tempfile
+ rescue Net::HTTPClientException => e
+ http_attempts += 1
+ response = e.response
+ if response.is_a?(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=)
- e.chef_rest_request = rest_request
- end
raise
end
@@ -235,7 +261,7 @@ class Chef
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
+ # the per-host per-port cache here gets persistent 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)
@@ -258,6 +284,21 @@ class Chef
private
# @api private
+ def ssl_policy
+ return Chef::HTTP::APISSLPolicy unless @options[:ssl_verify_mode]
+
+ case @options[:ssl_verify_mode]
+ when :verify_none
+ Chef::HTTP::VerifyNoneSSLPolicy
+ when :verify_peer
+ Chef::HTTP::VerifyPeerSSLPolicy
+ else
+ Chef::Log.error("Chef::HTTP was passed an ssl_verify_mode of #{@options[:ssl_verify_mode]} which is unsupported. Falling back to the API policy")
+ Chef::HTTP::APISSLPolicy
+ end
+ end
+
+ # @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
@@ -265,19 +306,20 @@ class Chef
# when for most knife/chef-client work we never need/want this loaded.
unless defined?(SocketlessChefZeroClient)
- require "chef/http/socketless_chef_zero_client"
+ require_relative "http/socketless_chef_zero_client"
end
SocketlessChefZeroClient.new(base_url)
else
- BasicClient.new(base_url, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: keepalives)
+ BasicClient.new(base_url, ssl_policy: ssl_policy, keepalives: keepalives)
end
end
# @api private
def create_url(path)
return path if path.is_a?(URI)
- if path =~ /^(http|https|chefzero):\/\//i
+
+ if %r{^(http|https|chefzero)://}i.match?(path)
URI.parse(path)
elsif path.nil? || path.empty?
URI.parse(@url)
@@ -292,7 +334,7 @@ class Chef
# @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
@@ -300,7 +342,7 @@ class Chef
# @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
@@ -308,7 +350,7 @@ class Chef
# @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
@@ -323,34 +365,40 @@ class Chef
# @api private
def success_response?(response)
- response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection)
+ response.is_a?(Net::HTTPSuccess) || response.is_a?(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.
# @api private
- def send_http_request(method, url, headers, body, &response_handler)
- headers = build_headers(method, url, headers, body)
-
+ 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?
request, response = client.request(method, url, body, headers, &response_handler)
else
- request, response = client.request(method, url, body, headers) { |r| r.read_body }
+ request, response = client.request(method, url, body, headers, &:read_body)
return_value = response.read_body
end
@last_response = response
- if response.kind_of?(Net::HTTPSuccess)
+ if response.is_a?(Net::HTTPSuccess)
[response, request, return_value]
- elsif response.kind_of?(Net::HTTPNotModified) # Must be tested before Net::HTTPRedirection because it's subclass.
+ elsif response.is_a?(Net::HTTPNotModified) # Must be tested before Net::HTTPRedirection because it's subclass.
[response, request, false]
elsif redirect_location = redirected_to(response)
- if [:GET, :HEAD].include?(method)
+ if %i{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."
@@ -372,8 +420,8 @@ class Chef
http_attempts += 1
response, request, return_value = yield
# handle HTTP 50X Error
- if response.kind_of?(Net::HTTPServerError) && !Chef::Config.local_mode
- if http_retry_count - http_attempts + 1 > 0
+ if response.is_a?(Net::HTTPServerError) && !Chef::Config.local_mode
+ if http_retry_count - http_attempts >= 0
sleep_time = 1 + (2**http_attempts) + rand(2**http_attempts)
Chef::Log.error("Server returned error #{response.code} for #{url}, retrying #{http_attempts}/#{http_retry_count} in #{sleep_time}s")
sleep(sleep_time)
@@ -383,7 +431,7 @@ class Chef
return [response, request, return_value]
end
rescue SocketError, Errno::ETIMEDOUT, Errno::ECONNRESET => e
- if http_retry_count - http_attempts + 1 > 0
+ if http_retry_count - http_attempts >= 0
Chef::Log.error("Error connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
@@ -391,21 +439,21 @@ class Chef
e.message.replace "Error connecting to #{url} - #{e.message}"
raise e
rescue Errno::ECONNREFUSED
- if http_retry_count - http_attempts + 1 > 0
+ if http_retry_count - http_attempts >= 0
Chef::Log.error("Connection refused connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
raise Errno::ECONNREFUSED, "Connection refused connecting to #{url}, giving up"
rescue Timeout::Error
- if http_retry_count - http_attempts + 1 > 0
+ if http_retry_count - http_attempts >= 0
Chef::Log.error("Timeout connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
raise Timeout::Error, "Timeout connecting to #{url}, giving up"
rescue OpenSSL::SSL::SSLError => e
- if (http_retry_count - http_attempts + 1 > 0) && !e.message.include?("certificate verify failed")
+ if (http_retry_count - http_attempts >= 0) && !e.message.include?("certificate verify failed")
Chef::Log.error("SSL Error connecting to #{url}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
@@ -414,6 +462,10 @@ class Chef
end
end
+ def version_retries
+ @version_retries ||= options[:version_class]&.possible_requests || 1
+ end
+
# @api private
def http_retry_delay
config[:http_retry_delay]
@@ -425,6 +477,11 @@ class Chef
end
# @api private
+ def http_disable_auth_on_redirect
+ config[:http_disable_auth_on_redirect]
+ end
+
+ # @api private
def config
Chef::Config
end
@@ -432,8 +489,9 @@ class Chef
# @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
@@ -448,9 +506,10 @@ class Chef
# @api private
def redirected_to(response)
- return nil unless response.kind_of?(Net::HTTPRedirection)
+ return nil unless response.is_a?(Net::HTTPRedirection)
# Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this
- return nil if response.kind_of?(Net::HTTPNotModified)
+ return nil if response.is_a?(Net::HTTPNotModified)
+
response["location"]
end
@@ -463,13 +522,15 @@ class Chef
end
# @api private
- def stream_to_tempfile(url, response, &progress_block)
+ 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 ChefUtils.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!
diff --git a/lib/chef/http/api_versions.rb b/lib/chef/http/api_versions.rb
new file mode 100644
index 0000000000..701b8746b0
--- /dev/null
+++ b/lib/chef/http/api_versions.rb
@@ -0,0 +1,55 @@
+#--
+# Copyright:: Copyright (c) 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 "../server_api_versions"
+require_relative "../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 053b2c938e..1702ae7e24 100644
--- a/lib/chef/http/auth_credentials.rb
+++ b/lib/chef/http/auth_credentials.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,16 +20,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/log"
-require "mixlib/authentication/signedheaderauth"
+require_relative "../log"
+module Mixlib
+ module Authentication
+ autoload :SignedHeaderAuth, "mixlib/authentication/signedheaderauth"
+ end
+end
class Chef
class HTTP
class AuthCredentials
attr_reader :client_name, :key
- def initialize(client_name = nil, key = nil)
- @client_name, @key = client_name, key
+ def initialize(client_name = nil, key = nil, use_ssh_agent: false)
+ @client_name = client_name
+ @key = key
+ @use_ssh_agent = use_ssh_agent
end
def sign_requests?
@@ -38,7 +44,8 @@ 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
@@ -48,8 +55,8 @@ class Chef
host = request_params.delete(:host) || "localhost"
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 = sign_obj.sign(key, use_ssh_agent: @use_ssh_agent).merge({ host: host })
+ signed.inject({}) { |memo, kv| memo[(kv[0].to_s.upcase).to_s] = kv[1]; memo }
end
end
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index 84065bf816..80b32be750 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +16,22 @@
# limitations under the License.
#
-require "chef/http/auth_credentials"
-require "chef/exceptions"
-require "openssl"
+require_relative "auth_credentials"
+require_relative "../exceptions"
+autoload :OpenSSL, "openssl"
class Chef
class HTTP
class Authenticator
- DEFAULT_SERVER_API_VERSION = "1"
+ DEFAULT_SERVER_API_VERSION = "2".freeze
attr_reader :signing_key_filename
attr_reader :raw_key
attr_reader :attr_names
attr_reader :auth_credentials
+ attr_reader :version_class
+ attr_reader :api_version
attr_accessor :sign_request
@@ -38,16 +40,13 @@ class Chef
@sign_request = true
@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
+ @auth_credentials = AuthCredentials.new(opts[:client_name], @key, use_ssh_agent: opts[:ssh_agent_signing])
+ @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,18 @@ 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
+ elsif Chef::ServerAPIVersions.instance.negotiated?
+ Chef::ServerAPIVersions.instance.max_server_version.to_s
+ else
+ DEFAULT_SERVER_API_VERSION
+ end
+ end
+
def sign_requests?
auth_credentials.sign_requests? && @sign_request
end
@@ -80,23 +91,26 @@ class Chef
else
return nil
end
- @key = OpenSSL::PKey::RSA.new(@raw_key)
+ # Pass in '' as the passphrase to avoid OpenSSL prompting on the TTY if
+ # given an encrypted key. This also helps if using a single file for
+ # both the public and private key with ssh-agent mode.
+ @key = OpenSSL::PKey::RSA.new(@raw_key, "")
rescue SystemCallError, IOError => e
Chef::Log.warn "Failed to read the private key #{key_file}: #{e.inspect}"
raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
rescue OpenSSL::PKey::RSAError
- msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key.\n"
+ msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key or the key is encrypted.\n"
msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
raise Chef::Exceptions::InvalidPrivateKey, msg
end
def authentication_headers(method, url, json_body = nil, headers = nil)
request_params = {
- :http_method => method,
- :path => url.path,
- :body => json_body,
- :host => "#{url.host}:#{url.port}",
- :headers => headers,
+ http_method: method,
+ path: url.path,
+ body: json_body,
+ host: "#{url.host}:#{url.port}",
+ headers: headers,
}
request_params[:body] ||= ""
auth_credentials.signature_headers(request_params)
diff --git a/lib/chef/http/basic_client.rb b/lib/chef/http/basic_client.rb
index 460744ea2a..2513b750eb 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,10 +20,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "uri"
-require "net/http"
-require "chef/http/ssl_policies"
-require "chef/http/http_request"
+autoload :URI, "uri"
+module Net
+ autoload :HTTP, "net/http"
+end
+require_relative "ssl_policies"
+require_relative "http_request"
class Chef
class HTTP
@@ -32,7 +34,6 @@ class Chef
HTTPS = "https".freeze
attr_reader :url
- attr_reader :http_client
attr_reader :ssl_policy
attr_reader :keepalives
@@ -61,32 +62,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
@@ -111,7 +112,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)
@@ -134,9 +135,9 @@ 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))
+ http_proxy_pass(proxy_uri))
end
end
diff --git a/lib/chef/http/cookie_jar.rb b/lib/chef/http/cookie_jar.rb
index 4908fde0c9..f0afbc6f52 100644
--- a/lib/chef/http/cookie_jar.rb
+++ b/lib/chef/http/cookie_jar.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "singleton"
+require "singleton" unless defined?(Singleton)
class Chef
class HTTP
diff --git a/lib/chef/http/cookie_manager.rb b/lib/chef/http/cookie_manager.rb
index 18824a8eff..85b1a78e49 100644
--- a/lib/chef/http/cookie_manager.rb
+++ b/lib/chef/http/cookie_manager.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/http/cookie_jar"
+require_relative "cookie_jar"
class Chef
class HTTP
@@ -33,7 +33,7 @@ class Chef
def handle_request(method, url, headers = {}, data = false)
@host, @port = url.host, url.port
- if @cookies.has_key?("#{@host}:#{@port}")
+ if @cookies.key?("#{@host}:#{@port}")
headers["Cookie"] = @cookies["#{@host}:#{@port}"]
end
[method, url, headers, data]
diff --git a/lib/chef/http/decompressor.rb b/lib/chef/http/decompressor.rb
index 1bba4cc492..984c1deef1 100644
--- a/lib/chef/http/decompressor.rb
+++ b/lib/chef/http/decompressor.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "zlib"
-require "chef/http/http_request"
+require "zlib" unless defined?(Zlib)
+require_relative "http_request"
class Chef
class HTTP
- # Middleware-esque class for handling compression in HTTP responses.
+ # Middleware-ish class for handling compression in HTTP responses.
class Decompressor
class NoopInflater
def inflate(chunk)
@@ -64,6 +64,7 @@ class Chef
# temporary hack, skip processing if return_value is false
# needed to keep conditional get stuff working correctly.
return [http_response, rest_request, return_value] if return_value == false
+
response_body = decompress_body(http_response)
http_response.body.replace(response_body) if http_response.body.respond_to?(:replace)
[http_response, rest_request, return_value]
@@ -79,10 +80,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 +95,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..a188fe947e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,19 +20,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "uri"
-require "net/http"
+autoload :URI, "uri"
+autoload :CGI, "cgi"
+module Net
+ autoload :HTTP, "net/http"
+end
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
# To load faster, we only want ohai's version string.
# However, in ohai before 0.6.0, the version is defined
# in ohai, not ohai/version
begin
- require "ohai/version" #used in user agent string.
+ require "ohai/version" # used in user agent string.
rescue LoadError
- require "ohai"
+ require "ohai" unless defined?(Ohai::System)
end
-require "chef/version"
+require_relative "../version"
class Chef
class HTTP
@@ -40,7 +44,7 @@ class Chef
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
- UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +https://chef.io)"
+ UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +#{ChefUtils::Dist::Org::WEBSITE})".freeze
DEFAULT_UA = "Chef Client" << UA_COMMON
USER_AGENT = "User-Agent".freeze
@@ -49,6 +53,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 +75,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
@@ -99,7 +104,7 @@ class Chef
@url.path.empty? ? SLASH : @url.path
end
- # DEPRECATED. Call request on an HTTP client object instead.
+ # @deprecated Call request on an HTTP client object instead.
def call
hide_net_http_bug do
http_client.request(http_request) do |response|
@@ -113,7 +118,7 @@ class Chef
Chef::Config
end
- # DEPRECATED. Call request on an HTTP client object instead.
+ # @deprecated Call request on an HTTP client object instead.
def http_client
@http_client ||= BasicClient.new(url).http_client
end
@@ -125,10 +130,10 @@ class Chef
rescue NoMethodError => e
# 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"))
+ if /#{Regexp.escape(%q{undefined method `closed?' for nil:NilClass})}/.match?(e.to_s)
+ 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
@@ -151,7 +156,7 @@ class Chef
end
def configure_http_request(request_body = nil)
- req_path = "#{path}"
+ req_path = path.to_s.dup
req_path << "?#{query}" if query
@http_request = case method.to_s.downcase
@@ -161,6 +166,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
@@ -172,8 +179,8 @@ class Chef
@http_request.body = request_body if request_body && @http_request.request_body_permitted?
# Optionally handle HTTP Basic Authentication
if url.user
- user = URI.unescape(url.user)
- password = URI.unescape(url.password) if url.password
+ user = CGI.unescape(url.user)
+ password = CGI.unescape(url.password) if url.password
@http_request.basic_auth(user, password)
end
diff --git a/lib/chef/http/json_input.rb b/lib/chef/http/json_input.rb
index 4cc1aa2e10..1d96743770 100644
--- a/lib/chef/http/json_input.rb
+++ b/lib/chef/http/json_input.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/json_compat"
+require_relative "../json_compat"
class Chef
class HTTP
@@ -33,10 +33,10 @@ 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)
+ json_opts[:validate_utf8] = opts[:validate_utf8] if opts.key?(:validate_utf8)
data = Chef::JSONCompat.to_json(data, json_opts)
# Force encoding to binary to fix SSL related EOFErrors
# cf. http://tickets.opscode.com/browse/CHEF-2363
@@ -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..bad009bd14 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/json_compat"
-require "chef/log"
+require_relative "../json_compat"
+require_relative "../log"
class Chef
class HTTP
@@ -46,7 +46,8 @@ class Chef
# temporary hack, skip processing if return_value is false
# needed to keep conditional get stuff working correctly.
return [http_response, rest_request, return_value] if return_value == false
- if http_response["content-type"] =~ /json/
+
+ if /json/.match?(http_response["content-type"])
if http_response.body.nil?
return_value = nil
elsif raw_output
@@ -60,8 +61,11 @@ class Chef
end
[http_response, rest_request, return_value]
else
- Chef::Log.debug("Expected JSON response, but got content-type '#{http_response['content-type']}'")
- return [http_response, rest_request, http_response.body.to_s]
+ 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
+ [http_response, rest_request, http_response.body.to_s]
end
end
diff --git a/lib/chef/http/json_to_model_output.rb b/lib/chef/http/json_to_model_output.rb
index 12ca1a90aa..20ebbdea29 100644
--- a/lib/chef/http/json_to_model_output.rb
+++ b/lib/chef/http/json_to_model_output.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/http/json_output"
+require_relative "json_output"
class Chef
class HTTP
@@ -26,7 +26,7 @@ class Chef
# a `json_class` key.
class JSONToModelOutput < JSONOutput
def initialize(opts = {})
- opts[:inflate_json_class] = true if !opts.has_key?(:inflate_json_class)
+ opts[:inflate_json_class] = true unless opts.key?(:inflate_json_class)
super
end
end
diff --git a/lib/chef/http/remote_request_id.rb b/lib/chef/http/remote_request_id.rb
index a779df805e..7b0bd0f830 100644
--- a/lib/chef/http/remote_request_id.rb
+++ b/lib/chef/http/remote_request_id.rb
@@ -1,5 +1,5 @@
# Author:: Prajakta Purohit (<prajakta@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,13 @@
# limitations under the License.
#
-require "chef/request_id"
+require_relative "../request_id"
class Chef
class HTTP
class RemoteRequestID
- def initialize(opts = {})
- end
+ def initialize(opts = {}); end
def handle_request(method, url, headers = {}, data = false)
headers["X-REMOTE-REQUEST-ID"] = Chef::RequestID.instance.request_id
diff --git a/lib/chef/http/simple.rb b/lib/chef/http/simple.rb
index bdbc31c9f1..84bc621e74 100644
--- a/lib/chef/http/simple.rb
+++ b/lib/chef/http/simple.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/http"
-require "chef/http/authenticator"
-require "chef/http/decompressor"
-require "chef/http/cookie_manager"
-require "chef/http/validate_content_length"
+require_relative "../http"
+require_relative "authenticator"
+require_relative "decompressor"
+require_relative "cookie_manager"
+require_relative "validate_content_length"
class Chef
class HTTP
diff --git a/lib/chef/http/simple_json.rb b/lib/chef/http/simple_json.rb
index 7357d859ee..0e1aad99ac 100644
--- a/lib/chef/http/simple_json.rb
+++ b/lib/chef/http/simple_json.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/http"
-require "chef/http/authenticator"
-require "chef/http/decompressor"
-require "chef/http/cookie_manager"
-require "chef/http/validate_content_length"
+require_relative "../http"
+require_relative "authenticator"
+require_relative "decompressor"
+require_relative "cookie_manager"
+require_relative "validate_content_length"
class Chef
class HTTP
diff --git a/lib/chef/http/socketless_chef_zero_client.rb b/lib/chef/http/socketless_chef_zero_client.rb
index d2f1f45b1d..8c7ee8d8bc 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,6 +44,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require "chef_zero/server"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+module Net
+ autoload :HTTPResponse, "net/http"
+end
class Chef
class HTTP
@@ -63,7 +67,7 @@ class Chef
# or else streaming-style responses won't work.
def read_body(dest = nil, &block)
if dest
- raise "responses from socketless chef zero can't be written to specific destination"
+ raise "responses from socketless #{ChefUtils::Dist::Zero::PRODUCT} can't be written to specific destination"
end
if block_given?
@@ -131,9 +135,9 @@ class Chef
505 => "HTTP Version Not Supported",
507 => "Insufficient Storage",
511 => "Network Authentication Required",
- }
+ }.freeze
- STATUS_MESSAGE.values.each { |v| v.freeze }
+ STATUS_MESSAGE.each_value(&:freeze)
STATUS_MESSAGE.freeze
def initialize(base_url)
@@ -163,15 +167,16 @@ class Chef
def req_to_rack(method, url, body, headers)
body_str = body || ""
{
- "SCRIPT_NAME" => "",
- "SERVER_NAME" => "localhost",
- "REQUEST_METHOD" => method.to_s.upcase,
- "PATH_INFO" => url.path,
- "QUERY_STRING" => url.query,
- "SERVER_PORT" => url.port,
- "HTTP_HOST" => "localhost:#{url.port}",
+ "SCRIPT_NAME" => "",
+ "SERVER_NAME" => "localhost",
+ "REQUEST_METHOD" => method.to_s.upcase,
+ "PATH_INFO" => url.path,
+ "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),
+ "rack.input" => StringIO.new(body_str),
}
end
@@ -179,6 +184,7 @@ class Chef
body = chunked_body.join("")
msg = STATUS_MESSAGE[code]
raise "Cannot determine HTTP status message for code #{code}" unless msg
+
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
response.instance_variable_set(:@body, body)
headers.each do |name, value|
@@ -198,7 +204,7 @@ class Chef
def headers_extracted_from_options
options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map do |name, value|
- [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
+ [name.to_s.split("_").map(&:capitalize).join("-"), value]
end
end
diff --git a/lib/chef/http/ssl_policies.rb b/lib/chef/http/ssl_policies.rb
index a3692b81b6..bc688f13a6 100644
--- a/lib/chef/http/ssl_policies.rb
+++ b/lib/chef/http/ssl_policies.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,8 +21,8 @@
# limitations under the License.
#
-require "openssl"
-require "chef/util/path_helper"
+autoload :OpenSSL, "openssl"
+require_relative "../util/path_helper"
class Chef
class HTTP
@@ -62,12 +62,20 @@ class Chef
unless ::File.exist?(config[:ssl_ca_path])
raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_path #{config[:ssl_ca_path]} does not exist"
end
+
http_client.ca_path = config[:ssl_ca_path]
elsif config[:ssl_ca_file]
unless ::File.exist?(config[:ssl_ca_file])
raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{config[:ssl_ca_file]} does not exist"
end
+
http_client.ca_file = config[:ssl_ca_file]
+ elsif ENV["SSL_CERT_FILE"]
+ unless ::File.exist?(ENV["SSL_CERT_FILE"])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{ENV["SSL_CERT_FILE"]} does not exist"
+ end
+
+ http_client.ca_file = ENV["SSL_CERT_FILE"]
end
end
@@ -77,27 +85,41 @@ class Chef
http_client.cert_store.set_default_paths
end
if config.trusted_certs_dir
- certs = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(config.trusted_certs_dir), "*.{crt,pem}"))
+ certs = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob_dir(config.trusted_certs_dir), "*.{crt,pem}"))
certs.each do |cert_file|
- cert = OpenSSL::X509::Certificate.new(File.read(cert_file))
+ cert = begin
+ OpenSSL::X509::Certificate.new(::File.binread(cert_file))
+ rescue OpenSSL::X509::CertificateError => e
+ raise Chef::Exceptions::ConfigurationError, "Error reading cert file '#{cert_file}', original error '#{e.class}: #{e.message}'"
+ end
add_trusted_cert(cert)
end
end
end
def set_client_credentials
- if config[:ssl_client_cert] || config[:ssl_client_key]
- unless config[:ssl_client_cert] && config[:ssl_client_key]
- raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
- end
- unless ::File.exists?(config[:ssl_client_cert])
- raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
- end
- unless ::File.exists?(config[:ssl_client_key])
- raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
- end
- http_client.cert = OpenSSL::X509::Certificate.new(::File.read(config[:ssl_client_cert]))
- http_client.key = OpenSSL::PKey::RSA.new(::File.read(config[:ssl_client_key]))
+ return unless config[:ssl_client_cert] || config[:ssl_client_key]
+
+ unless config[:ssl_client_cert] && config[:ssl_client_key]
+ raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
+ end
+ unless ::File.exists?(config[:ssl_client_cert])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
+ end
+ unless ::File.exists?(config[:ssl_client_key])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
+ end
+
+ begin
+ http_client.cert = OpenSSL::X509::Certificate.new(::File.binread(config[:ssl_client_cert]))
+ rescue OpenSSL::X509::CertificateError => e
+ raise Chef::Exceptions::ConfigurationError, "Error reading cert file '#{config[:ssl_client_cert]}', original error '#{e.class}: #{e.message}'"
+ end
+
+ begin
+ http_client.key = OpenSSL::PKey::RSA.new(::File.binread(config[:ssl_client_key]))
+ rescue OpenSSL::PKey::RSAError => e
+ raise Chef::Exceptions::ConfigurationError, "Error reading key file '#{config[:ssl_client_key]}', original error '#{e.class}: #{e.message}'"
end
end
@@ -126,5 +148,23 @@ class Chef
end
end
+ # This policy is used when we want to explicitly turn on verification
+ # for a specific request regardless of the API Policy. For example, when
+ # doing a `remote_file` where the user specified `verify_mode :verify_peer`
+ class VerifyPeerSSLPolicy < DefaultSSLPolicy
+ def set_verify_mode
+ http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ end
+ end
+
+ # This policy is used when we want to explicitly turn off verification
+ # for a specific request regardless of the API Policy. For example, when
+ # doing a `remote_file` where the user specified `verify_mode :verify_none`
+ class VerifyNoneSSLPolicy < DefaultSSLPolicy
+ def set_verify_mode
+ http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ end
+
end
end
diff --git a/lib/chef/http/validate_content_length.rb b/lib/chef/http/validate_content_length.rb
index c1073867d3..fb20227e31 100644
--- a/lib/chef/http/validate_content_length.rb
+++ b/lib/chef/http/validate_content_length.rb
@@ -1,6 +1,6 @@
#--
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "pp"
-require "chef/log"
+require "pp" unless defined?(PP)
+require_relative "../log"
class Chef
class HTTP
@@ -41,8 +41,7 @@ class Chef
end
end
- def initialize(opts = {})
- end
+ def initialize(opts = {}); end
def handle_request(method, url, headers = {}, data = false)
[method, url, headers, data]
@@ -50,12 +49,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 +62,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)
@@ -74,6 +73,7 @@ class Chef
def response_content_length(response)
return nil if response["content-length"].nil?
+
if response["content-length"].is_a?(Array)
response["content-length"].first.to_i
else
@@ -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..b5c9072f6f 100644
--- a/lib/chef/json_compat.rb
+++ b/lib/chef/json_compat.rb
@@ -1,6 +1,6 @@
#
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,148 +17,47 @@
# Wrapper class for interacting with JSON.
-require "ffi_yajl"
-require "chef/exceptions"
+autoload :FFI_Yajl, "ffi_yajl"
+require_relative "exceptions"
# We're requiring this to prevent breaking consumers using Hash.to_json
-require "json"
+require "json" unless defined?(JSON)
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
def from_json(source, opts = {})
obj = parse(source, opts)
# JSON gem requires top level object to be a Hash or Array (otherwise
# you get the "must contain two octets" error). Yajl doesn't impose the
# same limitation. For compatibility, we re-impose this condition.
- unless obj.kind_of?(Hash) || obj.kind_of?(Array)
+ unless obj.is_a?(Hash) || obj.is_a?(Array)
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)
- opts ||= {}
- options_map = {}
- options_map[:pretty] = true
- options_map[:indent] = opts[:indent] if opts.has_key?(:indent)
+ options_map = { pretty: true }
+ options_map[:indent] = opts[:indent] if opts.respond_to?(:key?) && opts.key?(:indent)
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 38822e8b26..f1c046e6ba 100644
--- a/lib/chef/key.rb
+++ b/lib/chef/key.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/json_compat"
-require "chef/mixin/params_validate"
-require "chef/exceptions"
-require "chef/server_api"
+require_relative "json_compat"
+require_relative "mixin/params_validate"
+require_relative "exceptions"
+require_relative "server_api"
class Chef
# Class for interacting with a chef key object. Can be used to create new keys,
@@ -46,7 +46,7 @@ class Chef
# Actor that the key is for, either a client or a user.
@actor = actor
- unless actor_field_name == "user" || actor_field_name == "client"
+ unless %w{user client}.include?(actor_field_name)
raise Chef::Exceptions::InvalidKeyArgument, "the second argument to initialize must be either 'user' or 'client'"
end
@@ -77,23 +77,24 @@ class Chef
def actor(arg = nil)
set_or_return(:actor, arg,
- :regex => /^[a-z0-9\-_]+$/)
+ regex: /^[a-z0-9\-_]+$/)
end
def name(arg = nil)
set_or_return(:name, arg,
- :kind_of => String)
+ kind_of: String)
end
def public_key(arg = nil)
raise Chef::Exceptions::InvalidKeyAttribute, "you cannot set the public_key if create_key is true" if !arg.nil? && @create_key
+
set_or_return(:public_key, arg,
- :kind_of => String)
+ kind_of: String)
end
def private_key(arg = nil)
set_or_return(:private_key, arg,
- :kind_of => String)
+ kind_of: String)
end
def delete_public_key
@@ -106,16 +107,17 @@ class Chef
def create_key(arg = nil)
raise Chef::Exceptions::InvalidKeyAttribute, "you cannot set create_key to true if the public_key field exists" if arg == true && !@public_key.nil?
+
set_or_return(:create_key, arg,
- :kind_of => [TrueClass, FalseClass])
+ kind_of: [TrueClass, FalseClass])
end
def expiration_date(arg = nil)
set_or_return(:expiration_date, arg,
- :regex => /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z|infinity)$/)
+ regex: /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z|infinity)$/)
end
- def to_hash
+ def to_h
result = {
@actor_field_name => @actor,
}
@@ -127,8 +129,10 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def create
@@ -140,7 +144,7 @@ class Chef
# defaults the key name to the fingerprint of the key
if @name.nil?
# if they didn't pass a public_key,
- #then they must supply a name because we can't generate a fingerprint
+ # then they must supply a name because we can't generate a fingerprint
unless @public_key.nil?
@name = fingerprint
else
@@ -155,7 +159,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_h
new_key["private_key"] = result["private_key"] if result["private_key"]
Chef::Key.from_hash(new_key)
end
@@ -175,17 +179,17 @@ class Chef
# to @name.
put_name = @name if put_name.nil?
- new_key = chef_rest.put("#{api_base}/#{@actor}/keys/#{put_name}", to_hash)
+ new_key = chef_rest.put("#{api_base}/#{@actor}/keys/#{put_name}", to_h)
# 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_h.merge(new_key))
end
def save
create
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "409"
update
else
@@ -203,9 +207,9 @@ class Chef
class << self
def from_hash(key_hash)
- if key_hash.has_key?("user")
+ if key_hash.key?("user")
key = Chef::Key.new(key_hash["user"], "user")
- elsif key_hash.has_key?("client")
+ elsif key_hash.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."
@@ -222,19 +226,14 @@ class Chef
Chef::Key.from_hash(Chef::JSONCompat.from_json(json))
end
- def 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 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)
+ list(keys, actor, :load_by_user, inflate)
end
def 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)
+ list(keys, actor, :load_by_client, inflate)
end
def load_by_user(actor, key_name)
@@ -253,7 +252,7 @@ class Chef
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(":")
+ OpenSSL::Digest.hexdigest("SHA1", data_string.to_der).scan(/../).join(":")
end
def list(keys, actor, load_method_symbol, inflate)
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index c9ecfbf0cc..ac7a68d0fc 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,34 +17,36 @@
# limitations under the License.
#
-require "forwardable"
-require "chef/version"
-require "mixlib/cli"
-require "chef/workstation_config_loader"
-require "chef/mixin/convert_to_class_name"
-require "chef/mixin/path_sanity"
-require "chef/knife/core/subcommand_loader"
-require "chef/knife/core/ui"
-require "chef/local_mode"
-require "chef/server_api"
-require "chef/http/authenticator"
-require "chef/http/http_request"
-require "chef/http"
-require "pp"
+require "forwardable" unless defined?(Forwardable)
+require_relative "version"
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require "chef-utils/dsl/default_paths" unless defined?(ChefUtils::DSL::DefaultPaths)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "workstation_config_loader"
+require_relative "mixin/convert_to_class_name"
+require_relative "mixin/default_paths"
+require_relative "knife/core/subcommand_loader"
+require_relative "knife/core/ui"
+require_relative "local_mode"
+require_relative "server_api"
+require_relative "http/authenticator"
+require_relative "http/http_request"
+require_relative "http"
+require "pp" unless defined?(PP)
class Chef
class Knife
- Chef::HTTP::HTTPRequest.user_agent = "Chef Knife#{Chef::HTTP::HTTPRequest::UA_COMMON}"
+ Chef::HTTP::HTTPRequest.user_agent = "#{ChefUtils::Dist::Infra::PRODUCT} Knife#{Chef::HTTP::HTTPRequest::UA_COMMON}"
include Mixlib::CLI
- include Chef::Mixin::PathSanity
+ include ChefUtils::DSL::DefaultPaths
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
@@ -63,6 +65,12 @@ class Chef
attr_accessor :name_args
attr_accessor :ui
+ # knife acl subcommands are grouped in this category using this constant to verify.
+ OPSCODE_HOSTED_CHEF_ACCESS_CONTROL = %w{acl group user}.freeze
+
+ # knife opc subcommands are grouped in this category using this constant to verify.
+ CHEF_ORGANIZATION_MANAGEMENT = %w{opc}.freeze
+
# Configure mixlib-cli to always separate defaults from user-supplied CLI options
def self.use_separate_defaults?
true
@@ -87,12 +95,13 @@ class Chef
end
def self.inherited(subclass)
+ super
unless subclass.unnamed?
subcommands[subclass.snake_case_name] = subclass
subcommand_files[subclass.snake_case_name] +=
if subclass.superclass.to_s == "Chef::ChefFS::Knife"
# ChefFS-based commands have a superclass that defines an
- # inhereited method which calls super. This means that the
+ # inherited method which calls super. This means that the
# top of the call stack is not the class definition for
# our subcommand. Try the second entry in the call stack.
[path_from_caller(caller[1])]
@@ -105,12 +114,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 +153,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 +186,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/ for details.") if config_loader.no_config_found?
config_loader
rescue Exceptions::ConfigurationError => e
@@ -196,10 +205,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
@@ -228,14 +238,27 @@ class Chef
end
def self.load_deps
- dependency_loaders.each do |dep_loader|
- dep_loader.call
- end
+ dependency_loaders.each(&:call)
end
- OFFICIAL_PLUGINS = %w{ec2 rackspace windows openstack terremark bluebox}
+ OFFICIAL_PLUGINS = %w{lpar openstack push rackspace vcenter}.freeze
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 /deprecated/i.match?(category)
+
+ 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
+
private
# @api private
@@ -243,25 +266,23 @@ class Chef
caller_line.split(/:\d+/).first
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(' ')}'")
+ ui.fatal("Cannot find subcommand for: '#{args.join(" ")}'")
# 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)
+ 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
- if category_commands = guess_category(args)
+ if CHEF_ORGANIZATION_MANAGEMENT.include?(args[0])
+ list_commands("CHEF ORGANIZATION MANAGEMENT")
+ elsif 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")
+ elsif OFFICIAL_PLUGINS.include?(args[0]) # command was an uninstalled official chef knife plugin
+ ui.info("Use `#{ChefUtils::Dist::Infra::EXEC} gem install knife-#{args[0]}` to install the plugin into Chef Workstation")
else
list_commands
end
@@ -270,21 +291,6 @@ class Chef
end
# @api private
- 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
-
- # @api private
def reset_config_path!
@@chef_config_dir = nil
end
@@ -308,15 +314,23 @@ class Chef
# knife node run_list add requires that we have extra logic to handle
# the case that command name words could be joined by an underscore :/
- command_name_words = command_name_words.join("_")
- @name_args.reject! { |name_arg| command_name_words == name_arg }
+ command_name_joined = command_name_words.join("_")
+ @name_args.reject! { |name_arg| command_name_joined == name_arg }
+
+ # Similar handling for hyphens.
+ command_name_joined = command_name_words.join("-")
+ @name_args.reject! { |name_arg| command_name_joined == name_arg }
if config[:help]
msg opt_parser
exit 1
end
- # copy Mixlib::CLI over so that it can be configured in knife.rb
+ # Grab a copy before config merge occurs, so that we can later identify
+ # where a given config value is sourced from.
+ @original_config = config.dup
+
+ # copy Mixlib::CLI over so that it can be configured in config.rb/knife.rb
# config file
Chef::Config[:verbosity] = config[:verbosity] if config[:verbosity]
end
@@ -329,35 +343,65 @@ class Chef
exit(1)
end
- # keys from mixlib-cli options
- def cli_keys
- self.class.options.keys
+ # This is all set and default mixlib-config values. We only need the default
+ # values here (the set values are explicitly mixed in again later), but there is
+ # no mixlib-config API to get a Hash back with only the default values.
+ #
+ # Assumption: since config_file_defaults is the lowest precedence it doesn't matter
+ # that we include the set values here, but this is a hack and makes the name of the
+ # method a lie. FIXME: make the name not a lie by adding an API to mixlib-config.
+ #
+ # @api private
+ #
+ def config_file_defaults
+ Chef::Config[:knife].save(true) # this is like "dup" to a (real) Hash, and includes default values (and user set values)
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
+ # This is only the user-set mixlib-config values. We do not include the defaults
+ # here so that the config defaults do not override the cli defaults.
+ #
+ # @api private
#
- # 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
- cli_keys.each_with_object({}) do |key, memo|
- memo[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key)
- end
+ Chef::Config[:knife].save(false) # this is like "dup" to a (real) Hash, and does not include default values (just user set values)
end
# 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)
+ # config_file_defaults - Chef::Config[:knife] defaults from chef-config (XXX: this also includes the settings, but they get overwritten)
+ # default_config - mixlib-cli defaults (accessor from mixlib-cli)
+ # config_file_settings - Chef::Config[:knife] user settings from the client.rb file
+ # config - mixlib-cli settings (accessor from mixlib-cli)
+ #
def merge_configs
+ # Update our original_config - if someone has created a knife command
+ # instance directly, they are likely ot have set cmd.config values directly
+ # as well, at which point our saved original config is no longer up to date.
+ @original_config = config.dup
# 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)
- )
+ config.replace(config_file_defaults.merge(default_config).merge(config_file_settings).merge(config))
+ end
+
+ #
+ # Determine the source of a given configuration key
+ #
+ # @argument key [Symbol] a configuration key
+ # @return [Symbol,NilClass] return the source of the config key,
+ # one of:
+ # - :cli - this was explicitly provided on the CLI
+ # - :config - this came from Chef::Config[:knife] explicitly being set
+ # - :cli_default - came from a declared CLI `option`'s `default` value.
+ # - :config_default - this came from Chef::Config[:knife]'s defaults
+ # - nil - if the key could not be found in any source.
+ # This can happen when it is invalid, or has been
+ # set directly into #config without then calling #merge_config
+ def config_source(key)
+ return :cli if @original_config.include? key
+ return :config if config_file_settings.key? key
+ return :cli_default if default_config.include? key
+ return :config_default if config_file_defaults.key? key # must come after :config check
+
+ nil
end
# Catch-all method that does any massaging needed for various config
@@ -371,22 +415,24 @@ 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]
Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url]
Chef::Config[:environment] = config[:environment] if config[:environment]
- Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode)
+ Chef::Config.local_mode = config[:local_mode] if config.key?(:local_mode)
- Chef::Config.listen = config[:listen] if config.has_key?(:listen)
+ Chef::Config.listen = config[:listen] if config.key?(:listen)
- if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
+ if Chef::Config.local_mode && !Chef::Config.key?(:cookbook_path) && !Chef::Config.key?(:chef_repo_path)
Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd)
end
Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host]
@@ -406,31 +452,46 @@ 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
+ ENV["PATH"] = default_paths if Chef::Config[:enforce_default_paths] || Chef::Config[:enforce_path_sanity]
maybe_setup_fips
Chef::LocalMode.with_server_connectivity do
run
end
rescue Exception => e
- raise if raise_exception || Chef::Config[:verbosity] == 2
+ raise if raise_exception || ( Chef::Config[:verbosity] && Chef::Config[:verbosity] >= 2 )
+
humanize_exception(e)
exit 100
end
@@ -439,12 +500,12 @@ class Chef
case e
when SystemExit
raise # make sure exit passes through.
- when Net::HTTPServerException, Net::HTTPFatalError
+ when Net::HTTPClientException, Net::HTTPFatalError
humanize_http_exception(e)
when OpenSSL::SSL::SSLError
ui.error "Could not establish a secure connection to the server."
ui.info "Use `knife ssl check` to troubleshoot your SSL configuration."
- ui.info "If your Chef Server uses a self-signed certificate, you can use"
+ ui.info "If your server uses a self-signed certificate, you can use"
ui.info "`knife ssl fetch` to make knife trust the server's certificates."
ui.info ""
ui.info "Original Exception: #{e.class.name}: #{e.message}"
@@ -454,14 +515,14 @@ 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}"
ui.info "Check your configuration file and ensure that your private key is readable"
when Chef::Exceptions::InvalidRedirect
ui.error "Invalid Redirect: #{e.message}"
- ui.info "Change your server location in knife.rb to the server's FQDN to avoid unwanted redirections."
+ ui.info "Change your server location in config.rb/knife.rb to the server's FQDN to avoid unwanted redirections."
else
ui.error "#{e.class.name}: #{e.message}"
end
@@ -474,7 +535,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 server url 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"
@@ -496,10 +561,10 @@ class Chef
client_api_version = version_header["request_version"]
min_server_version = version_header["min_version"]
max_server_version = version_header["max_version"]
- ui.error "The version of Chef that Knife is using is not supported by the Chef server you sent this request to"
- ui.info "The request that Knife sent was using API version #{client_api_version}"
- ui.info "The Chef server you sent the request to supports a min API verson of #{min_server_version} and a max API version of #{max_server_version}"
- ui.info "Please either update your Chef client or server to be a compatible set"
+ ui.error "The API version that Knife is using is not supported by the server you sent this request to."
+ ui.info "The request that Knife sent was using API version #{client_api_version}."
+ ui.info "The server you sent the request to supports a min API version of #{min_server_version} and a max API version of #{max_server_version}."
+ ui.info "Please either update your #{ChefUtils::Dist::Infra::PRODUCT} or the server to be a compatible set."
else
ui.error response.message
ui.info "Response: #{format_rest_error(response)}"
@@ -526,7 +591,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)
@@ -536,7 +605,7 @@ class Chef
pretty_name ||= output
- self.msg("Created #{pretty_name}")
+ msg("Created #{pretty_name}")
output(output) if config[:print_after]
end
@@ -555,7 +624,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
@@ -570,14 +639,14 @@ class Chef
def rest
@rest ||= begin
- require "chef/server_api"
+ require_relative "server_api"
Chef::ServerAPI.new(Chef::Config[:chef_server_url])
end
end
def noauth_rest
@rest ||= begin
- require "chef/http/simple_json"
+ require_relative "http/simple_json"
Chef::HTTP::SimpleJSON.new(Chef::Config[:chef_server_url])
end
end
@@ -587,7 +656,7 @@ class Chef
end
def maybe_setup_fips
- if !config[:fips].nil?
+ unless config[:fips].nil?
Chef::Config[:fips] = config[:fips]
end
Chef::Config.init_openssl
diff --git a/lib/chef/knife/acl_add.rb b/lib/chef/knife/acl_add.rb
new file mode 100644
index 0000000000..144a18fcb1
--- /dev/null
+++ b/lib/chef/knife/acl_add.rb
@@ -0,0 +1,57 @@
+#
+# Author:: Steven Danna (steve@chef.io)
+# Author:: Jeremiah Snapp (jeremiah@chef.io)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class AclAdd < Chef::Knife
+ category "acl"
+ banner "knife acl add MEMBER_TYPE MEMBER_NAME OBJECT_TYPE OBJECT_NAME PERMS"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, object_type, object_name, perms = name_args
+
+ if name_args.length != 5
+ show_usage
+ ui.fatal "You must specify the member type [client|group], member name, object type, object name and perms"
+ exit 1
+ end
+
+ unless %w{client group}.include?(member_type)
+ ui.fatal "ERROR: To enforce best practice, knife-acl can only add a client or a group to an ACL."
+ ui.fatal " See the knife-acl README for more information."
+ exit 1
+ end
+ validate_perm_type!(perms)
+ validate_member_name!(member_name)
+ validate_object_name!(object_name)
+ validate_object_type!(object_type)
+ validate_member_exists!(member_type, member_name)
+
+ add_to_acl!(member_type, member_name, object_type, object_name, perms)
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/acl_base.rb b/lib/chef/knife/acl_base.rb
new file mode 100644
index 0000000000..0835d1ac05
--- /dev/null
+++ b/lib/chef/knife/acl_base.rb
@@ -0,0 +1,183 @@
+#
+# Author:: Steven Danna (steve@chef.io)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ module AclBase
+
+ PERM_TYPES = %w{create read update delete grant}.freeze unless defined? PERM_TYPES
+ MEMBER_TYPES = %w{client group user}.freeze unless defined? MEMBER_TYPES
+ OBJECT_TYPES = %w{clients containers cookbooks data environments groups nodes roles policies policy_groups}.freeze unless defined? OBJECT_TYPES
+ OBJECT_NAME_SPEC = /^[\-[:alnum:]_\.]+$/.freeze unless defined? OBJECT_NAME_SPEC
+
+ def validate_object_type!(type)
+ unless OBJECT_TYPES.include?(type)
+ ui.fatal "Unknown object type \"#{type}\". The following types are permitted: #{OBJECT_TYPES.join(", ")}"
+ exit 1
+ end
+ end
+
+ def validate_object_name!(name)
+ unless OBJECT_NAME_SPEC.match(name)
+ ui.fatal "Invalid name: #{name}"
+ exit 1
+ end
+ end
+
+ def validate_member_type!(type)
+ unless MEMBER_TYPES.include?(type)
+ ui.fatal "Unknown member type \"#{type}\". The following types are permitted: #{MEMBER_TYPES.join(", ")}"
+ exit 1
+ end
+ end
+
+ def validate_member_name!(name)
+ # Same rules apply to objects and members
+ validate_object_name!(name)
+ end
+
+ def validate_perm_type!(perms)
+ perms.split(",").each do |perm|
+ unless PERM_TYPES.include?(perm)
+ ui.fatal "Invalid permission \"#{perm}\". The following permissions are permitted: #{PERM_TYPES.join(",")}"
+ exit 1
+ end
+ end
+ end
+
+ def validate_member_exists!(member_type, member_name)
+ true if rest.get_rest("#{member_type}s/#{member_name}")
+ rescue NameError
+ # ignore "NameError: uninitialized constant Chef::ApiClient" when finding a client
+ true
+ rescue
+ ui.fatal "#{member_type} '#{member_name}' does not exist"
+ exit 1
+ end
+
+ def is_usag?(gname)
+ gname.length == 32 && gname =~ /^[0-9a-f]+$/
+ end
+
+ def get_acl(object_type, object_name)
+ rest.get_rest("#{object_type}/#{object_name}/_acl?detail=granular")
+ end
+
+ def get_ace(object_type, object_name, perm)
+ get_acl(object_type, object_name)[perm]
+ end
+
+ def add_to_acl!(member_type, member_name, object_type, object_name, perms)
+ acl = get_acl(object_type, object_name)
+ perms.split(",").each do |perm|
+ ui.msg "Adding '#{member_name}' to '#{perm}' ACE of '#{object_name}'"
+ ace = acl[perm]
+
+ case member_type
+ when "client", "user"
+ # Our PUT body depends on the type of reply we get from _acl?detail=granular
+ # When the server replies with json attributes 'users' and 'clients',
+ # we'll want to modify entries under the same keys they arrived.- their presence
+ # in the body tells us that CS will accept them in a PUT.
+ # Older version of chef-server will continue to use 'actors' for a combined list
+ # and expect the same in the body.
+ key = "#{member_type}s"
+ key = "actors" unless ace.key? key
+ next if ace[key].include?(member_name)
+
+ ace[key] << member_name
+ when "group"
+ next if ace["groups"].include?(member_name)
+
+ ace["groups"] << member_name
+ end
+
+ update_ace!(object_type, object_name, perm, ace)
+ end
+ end
+
+ def remove_from_acl!(member_type, member_name, object_type, object_name, perms)
+ acl = get_acl(object_type, object_name)
+ perms.split(",").each do |perm|
+ ui.msg "Removing '#{member_name}' from '#{perm}' ACE of '#{object_name}'"
+ ace = acl[perm]
+
+ case member_type
+ when "client", "user"
+ key = "#{member_type}s"
+ key = "actors" unless ace.key? key
+ next unless ace[key].include?(member_name)
+
+ ace[key].delete(member_name)
+ when "group"
+ next unless ace["groups"].include?(member_name)
+
+ ace["groups"].delete(member_name)
+ end
+
+ update_ace!(object_type, object_name, perm, ace)
+ end
+ end
+
+ def update_ace!(object_type, object_name, ace_type, ace)
+ rest.put_rest("#{object_type}/#{object_name}/_acl/#{ace_type}", ace_type => ace)
+ end
+
+ def add_to_group!(member_type, member_name, group_name)
+ validate_member_exists!(member_type, member_name)
+ existing_group = rest.get_rest("groups/#{group_name}")
+ ui.msg "Adding '#{member_name}' to '#{group_name}' group"
+ unless existing_group["#{member_type}s"].include?(member_name)
+ existing_group["#{member_type}s"] << member_name
+ new_group = {
+ "groupname" => existing_group["groupname"],
+ "orgname" => existing_group["orgname"],
+ "actors" => {
+ "users" => existing_group["users"],
+ "clients" => existing_group["clients"],
+ "groups" => existing_group["groups"],
+ },
+ }
+ rest.put_rest("groups/#{group_name}", new_group)
+ end
+ end
+
+ def remove_from_group!(member_type, member_name, group_name)
+ validate_member_exists!(member_type, member_name)
+ existing_group = rest.get_rest("groups/#{group_name}")
+ ui.msg "Removing '#{member_name}' from '#{group_name}' group"
+ if existing_group["#{member_type}s"].include?(member_name)
+ existing_group["#{member_type}s"].delete(member_name)
+ new_group = {
+ "groupname" => existing_group["groupname"],
+ "orgname" => existing_group["orgname"],
+ "actors" => {
+ "users" => existing_group["users"],
+ "clients" => existing_group["clients"],
+ "groups" => existing_group["groups"],
+ },
+ }
+ rest.put_rest("groups/#{group_name}", new_group)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/acl_bulk_add.rb b/lib/chef/knife/acl_bulk_add.rb
new file mode 100644
index 0000000000..4992fe2afa
--- /dev/null
+++ b/lib/chef/knife/acl_bulk_add.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Jeremiah Snapp (jeremiah@chef.io)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class AclBulkAdd < Chef::Knife
+ category "acl"
+ banner "knife acl bulk add MEMBER_TYPE MEMBER_NAME OBJECT_TYPE REGEX PERMS"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, object_type, regex, perms = name_args
+ object_name_matcher = /#{regex}/
+
+ if name_args.length != 5
+ show_usage
+ ui.fatal "You must specify the member type [client|group], member name, object type, object name REGEX and perms"
+ exit 1
+ end
+
+ unless %w{client group}.include?(member_type)
+ ui.fatal "ERROR: To enforce best practice, knife-acl can only add a client or a group to an ACL."
+ ui.fatal " See the knife-acl README for more information."
+ exit 1
+ end
+ validate_perm_type!(perms)
+ validate_member_name!(member_name)
+ validate_object_type!(object_type)
+ validate_member_exists!(member_type, member_name)
+
+ if %w{containers groups}.include?(object_type)
+ ui.fatal "bulk modifying the ACL of #{object_type} is not permitted"
+ exit 1
+ end
+
+ objects_to_modify = []
+ all_objects = rest.get_rest(object_type)
+ objects_to_modify = all_objects.keys.select { |object_name| object_name =~ object_name_matcher }
+
+ if objects_to_modify.empty?
+ ui.info "No #{object_type} match the expression /#{regex}/"
+ exit 0
+ end
+
+ ui.msg("The ACL of the following #{object_type} will be modified:")
+ ui.msg("")
+ ui.msg(ui.list(objects_to_modify.sort, :columns_down))
+ ui.msg("")
+ ui.confirm("Are you sure you want to modify the ACL of these #{object_type}?")
+
+ objects_to_modify.each do |object_name|
+ add_to_acl!(member_type, member_name, object_type, object_name, perms)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/acl_bulk_remove.rb b/lib/chef/knife/acl_bulk_remove.rb
new file mode 100644
index 0000000000..0f35f1e2fb
--- /dev/null
+++ b/lib/chef/knife/acl_bulk_remove.rb
@@ -0,0 +1,83 @@
+#
+# Author:: Jeremiah Snapp (jeremiah@chef.io)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class AclBulkRemove < Chef::Knife
+ category "acl"
+ banner "knife acl bulk remove MEMBER_TYPE MEMBER_NAME OBJECT_TYPE REGEX PERMS"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, object_type, regex, perms = name_args
+ object_name_matcher = /#{regex}/
+
+ if name_args.length != 5
+ show_usage
+ ui.fatal "You must specify the member type [client|group|user], member name, object type, object name REGEX and perms"
+ exit 1
+ end
+
+ if member_name == "pivotal" && %w{client user}.include?(member_type)
+ ui.fatal "ERROR: 'pivotal' is a system user so knife-acl will not remove it from an ACL."
+ exit 1
+ end
+ if member_name == "admins" && member_type == "group" && perms.to_s.split(",").include?("grant")
+ ui.fatal "ERROR: knife-acl will not remove the 'admins' group from the 'grant' ACE."
+ ui.fatal " Removal could prevent future attempts to modify permissions."
+ exit 1
+ end
+ validate_perm_type!(perms)
+ validate_member_type!(member_type)
+ validate_member_name!(member_name)
+ validate_object_type!(object_type)
+ validate_member_exists!(member_type, member_name)
+
+ if %w{containers groups}.include?(object_type)
+ ui.fatal "bulk modifying the ACL of #{object_type} is not permitted"
+ exit 1
+ end
+
+ objects_to_modify = []
+ all_objects = rest.get_rest(object_type)
+ objects_to_modify = all_objects.keys.select { |object_name| object_name =~ object_name_matcher }
+
+ if objects_to_modify.empty?
+ ui.info "No #{object_type} match the expression /#{regex}/"
+ exit 0
+ end
+
+ ui.msg("The ACL of the following #{object_type} will be modified:")
+ ui.msg("")
+ ui.msg(ui.list(objects_to_modify.sort, :columns_down))
+ ui.msg("")
+ ui.confirm("Are you sure you want to modify the ACL of these #{object_type}?")
+
+ objects_to_modify.each do |object_name|
+ remove_from_acl!(member_type, member_name, object_type, object_name, perms)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/acl_remove.rb b/lib/chef/knife/acl_remove.rb
new file mode 100644
index 0000000000..13f089ff3e
--- /dev/null
+++ b/lib/chef/knife/acl_remove.rb
@@ -0,0 +1,62 @@
+#
+# Author:: Steven Danna (steve@chef.io)
+# Author:: Jeremiah Snapp (jeremiah@chef.io)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class AclRemove < Chef::Knife
+ category "acl"
+ banner "knife acl remove MEMBER_TYPE MEMBER_NAME OBJECT_TYPE OBJECT_NAME PERMS"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, object_type, object_name, perms = name_args
+
+ if name_args.length != 5
+ show_usage
+ ui.fatal "You must specify the member type [client|group|user], member name, object type, object name and perms"
+ exit 1
+ end
+
+ if member_name == "pivotal" && %w{client user}.include?(member_type)
+ ui.fatal "ERROR: 'pivotal' is a system user so knife-acl will not remove it from an ACL."
+ exit 1
+ end
+ if member_name == "admins" && member_type == "group" && perms.to_s.split(",").include?("grant")
+ ui.fatal "ERROR: knife-acl will not remove the 'admins' group from the 'grant' ACE."
+ ui.fatal " Removal could prevent future attempts to modify permissions."
+ exit 1
+ end
+ validate_perm_type!(perms)
+ validate_member_type!(member_type)
+ validate_member_name!(member_name)
+ validate_object_name!(object_name)
+ validate_object_type!(object_type)
+ validate_member_exists!(member_type, member_name)
+
+ remove_from_acl!(member_type, member_name, object_type, object_name, perms)
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/acl_show.rb b/lib/chef/knife/acl_show.rb
new file mode 100644
index 0000000000..d3a5002b30
--- /dev/null
+++ b/lib/chef/knife/acl_show.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Steven Danna (steve@chef.io)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class AclShow < Chef::Knife
+ category "acl"
+ banner "knife acl show OBJECT_TYPE OBJECT_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ object_type, object_name = name_args
+
+ if name_args.length != 2
+ show_usage
+ ui.fatal "You must specify an object type and object name"
+ exit 1
+ end
+
+ validate_object_type!(object_type)
+ validate_object_name!(object_name)
+ acl = get_acl(object_type, object_name)
+ PERM_TYPES.each do |perm|
+ # Filter out the actors field if we have
+ # users and clients. Note that if one is present,
+ # both will be - but we're checking both for completeness.
+ if acl[perm].key?("users") && acl[perm].key?("clients")
+ acl[perm].delete "actors"
+ end
+ end
+ ui.output acl
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index ee4d9ce7af..1550c62dc1 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,268 +16,446 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/data_bag_secret_options"
-require "erubis"
-require "chef/knife/bootstrap/chef_vault_handler"
-require "chef/knife/bootstrap/client_builder"
-require "chef/util/path_helper"
+require_relative "../knife"
+require_relative "data_bag_secret_options"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "license_acceptance/cli_flags/mixlib_cli"
+module LicenseAcceptance
+ autoload :Acceptor, "license_acceptance/acceptor"
+end
class Chef
class Knife
class Bootstrap < Knife
include DataBagSecretOptions
-
- attr_accessor :client_builder
- attr_accessor :chef_vault_handler
-
- deps do
- require "chef/knife/core/bootstrap_context"
- require "chef/json_compat"
- require "tempfile"
- require "highline"
- require "net/ssh"
- require "net/ssh/multi"
- require "chef/knife/ssh"
- Chef::Knife::Ssh.load_deps
- end
-
- banner "knife bootstrap [SSH_USER@]FQDN (options)"
-
- option :ssh_user,
- :short => "-x USERNAME",
- :long => "--ssh-user USERNAME",
- :description => "The ssh username",
- :default => "root"
-
- option :ssh_password,
- :short => "-P PASSWORD",
- :long => "--ssh-password PASSWORD",
- :description => "The ssh password"
-
- option :ssh_port,
- :short => "-p PORT",
- :long => "--ssh-port PORT",
- :description => "The ssh port",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
-
+ include LicenseAcceptance::CLIFlags::MixlibCLI
+
+ SUPPORTED_CONNECTION_PROTOCOLS ||= %w{ssh winrm}.freeze
+ WINRM_AUTH_PROTOCOL_LIST ||= %w{plaintext kerberos ssl negotiate}.freeze
+
+ # Common connectivity options
+ option :connection_user,
+ short: "-U USERNAME",
+ long: "--connection-user USERNAME",
+ description: "Authenticate to the target host with this user account."
+
+ option :connection_password,
+ short: "-P PASSWORD",
+ long: "--connection-password PASSWORD",
+ description: "Authenticate to the target host with this password."
+
+ option :connection_port,
+ short: "-p PORT",
+ long: "--connection-port PORT",
+ description: "The port on the target node to connect to."
+
+ option :connection_protocol,
+ short: "-o PROTOCOL",
+ long: "--connection-protocol PROTOCOL",
+ description: "The protocol to use to connect to the target node.",
+ in: SUPPORTED_CONNECTION_PROTOCOLS
+
+ option :max_wait,
+ short: "-W SECONDS",
+ long: "--max-wait SECONDS",
+ description: "The maximum time to wait for the initial connection to be established."
+
+ option :session_timeout,
+ long: "--session-timeout SECONDS",
+ description: "The number of seconds to wait for each connection operation to be acknowledged while running bootstrap.",
+ default: 60
+
+ # WinRM Authentication
+ option :winrm_ssl_peer_fingerprint,
+ long: "--winrm-ssl-peer-fingerprint FINGERPRINT",
+ description: "SSL certificate fingerprint expected from the target."
+
+ option :ca_trust_file,
+ short: "-f CA_TRUST_PATH",
+ long: "--ca-trust-file CA_TRUST_PATH",
+ description: "The Certificate Authority (CA) trust file used for SSL transport."
+
+ option :winrm_no_verify_cert,
+ long: "--winrm-no-verify-cert",
+ description: "Do not verify the SSL certificate of the target node for WinRM.",
+ boolean: true
+
+ option :winrm_ssl,
+ long: "--winrm-ssl",
+ description: "Use SSL in the WinRM connection."
+
+ option :winrm_auth_method,
+ short: "-w AUTH-METHOD",
+ long: "--winrm-auth-method AUTH-METHOD",
+ description: "The WinRM authentication method to use.",
+ in: WINRM_AUTH_PROTOCOL_LIST
+
+ option :winrm_basic_auth_only,
+ long: "--winrm-basic-auth-only",
+ description: "For WinRM basic authentication when using the 'ssl' auth method.",
+ boolean: true
+
+ # This option was provided in knife bootstrap windows winrm,
+ # but it is ignored in knife-windows/WinrmSession, and so remains unimplemented here.
+ # option :kerberos_keytab_file,
+ # :short => "-T KEYTAB_FILE",
+ # :long => "--keytab-file KEYTAB_FILE",
+ # :description => "The Kerberos keytab file used for authentication"
+
+ option :kerberos_realm,
+ short: "-R KERBEROS_REALM",
+ long: "--kerberos-realm KERBEROS_REALM",
+ description: "The Kerberos realm used for authentication."
+
+ option :kerberos_service,
+ short: "-S KERBEROS_SERVICE",
+ long: "--kerberos-service KERBEROS_SERVICE",
+ description: "The Kerberos service used for authentication."
+
+ ## SSH Authentication
option :ssh_gateway,
- :short => "-G GATEWAY",
- :long => "--ssh-gateway GATEWAY",
- :description => "The ssh gateway",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
+ short: "-G GATEWAY",
+ long: "--ssh-gateway GATEWAY",
+ description: "The SSH gateway."
- option :forward_agent,
- :short => "-A",
- :long => "--forward-agent",
- :description => "Enable SSH agent forwarding",
- :boolean => true
+ option :ssh_gateway_identity,
+ long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
+ description: "The SSH identity file used for gateway authentication."
- option :identity_file,
- :long => "--identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication. [DEPRECATED] Use --ssh-identity-file instead."
+ option :ssh_forward_agent,
+ short: "-A",
+ long: "--ssh-forward-agent",
+ description: "Enable SSH agent forwarding.",
+ boolean: true
option :ssh_identity_file,
- :short => "-i IDENTITY_FILE",
- :long => "--ssh-identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication"
+ short: "-i IDENTITY_FILE",
+ long: "--ssh-identity-file IDENTITY_FILE",
+ description: "The SSH identity file used for authentication."
- option :chef_node_name,
- :short => "-N NAME",
- :long => "--node-name NAME",
- :description => "The Chef node name for your new node"
+ option :ssh_verify_host_key,
+ long: "--ssh-verify-host-key VALUE",
+ description: "Verify host key. Default is 'always'.",
+ in: %w{always accept_new accept_new_or_local_tunnel never},
+ default: "always"
- option :prerelease,
- :long => "--prerelease",
- :description => "Install the pre-release chef gems"
+ #
+ # bootstrap options
+ #
+ # client.rb content via chef-full/bootstrap_context
option :bootstrap_version,
- :long => "--bootstrap-version VERSION",
- :description => "The version of Chef to install",
- :proc => lambda { |v| Chef::Config[:knife][:bootstrap_version] = v }
+ long: "--bootstrap-version VERSION",
+ description: "The version of #{ChefUtils::Dist::Infra::PRODUCT} to install."
+
+ option :channel,
+ long: "--channel CHANNEL",
+ description: "Install from the given channel. Default is 'stable'.",
+ default: "stable",
+ in: %w{stable current unstable}
+ # client.rb content via chef-full/bootstrap_context
option :bootstrap_proxy,
- :long => "--bootstrap-proxy PROXY_URL",
- :description => "The proxy server for the node being bootstrapped",
- :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
+ long: "--bootstrap-proxy PROXY_URL",
+ description: "The proxy server for the node being bootstrapped."
+ # client.rb content via bootstrap_context
option :bootstrap_proxy_user,
- :long => "--bootstrap-proxy-user PROXY_USER",
- :description => "The proxy authentication username for the node being bootstrapped"
+ long: "--bootstrap-proxy-user PROXY_USER",
+ description: "The proxy authentication username for the node being bootstrapped."
+ # client.rb content via bootstrap_context
option :bootstrap_proxy_pass,
- :long => "--bootstrap-proxy-pass PROXY_PASS",
- :description => "The proxy authentication password for the node being bootstrapped"
+ long: "--bootstrap-proxy-pass PROXY_PASS",
+ description: "The proxy authentication password for the node being bootstrapped."
+ # client.rb content via bootstrap_context
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.")
+ long: "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
+ description: "Do not proxy locations for the node being bootstrapped"
+
+ # client.rb content via bootstrap_context
+ option :bootstrap_template,
+ short: "-t TEMPLATE",
+ long: "--bootstrap-template TEMPLATE",
+ description: "Bootstrap #{ChefUtils::Dist::Infra::PRODUCT} using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
+
+ # client.rb content via bootstrap_context
+ option :node_ssl_verify_mode,
+ long: "--node-ssl-verify-mode [peer|none]",
+ description: "Whether or not to verify the SSL cert for all HTTPS requests.",
+ proc: Proc.new { |v|
+ valid_values = %w{none peer}
+ unless valid_values.include?(v)
+ raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
+ end
+
v
}
- option :bootstrap_template,
- :short => "-t TEMPLATE",
- :long => "--bootstrap-template TEMPLATE",
- :description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
+ # bootstrap_context - client.rb
+ option :node_verify_api_cert,
+ long: "--[no-]node-verify-api-cert",
+ description: "Verify the SSL cert for HTTPS requests to the #{ChefUtils::Dist::Server::PRODUCT} API.",
+ boolean: true
+ # runtime - sudo settings (train handles sudo)
option :use_sudo,
- :long => "--sudo",
- :description => "Execute the bootstrap via sudo",
- :boolean => true
+ long: "--sudo",
+ description: "Execute the bootstrap via sudo.",
+ boolean: true
+ # runtime - sudo settings (train handles sudo)
option :preserve_home,
- :long => "--sudo-preserve-home",
- :description => "Preserve non-root user HOME environment variable with sudo",
- :boolean => true
+ long: "--sudo-preserve-home",
+ description: "Preserve non-root user HOME environment variable with sudo.",
+ boolean: true
+ # runtime - sudo settings (train handles sudo)
option :use_sudo_password,
- :long => "--use-sudo-password",
- :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
- }
+ long: "--use-sudo-password",
+ description: "Execute the bootstrap via sudo with password.",
+ boolean: false
+
+ # runtime - client_builder
+ option :chef_node_name,
+ short: "-N NAME",
+ long: "--node-name NAME",
+ description: "The node name for your new node."
+ # runtime - client_builder - set runlist when creating node
option :run_list,
- :short => "-r RUN_LIST",
- :long => "--run-list RUN_LIST",
- :description => "Comma separated list of roles/recipes to apply",
- :proc => lambda { |o| o.split(/[\s,]+/) },
- :default => []
+ short: "-r RUN_LIST",
+ long: "--run-list RUN_LIST",
+ description: "Comma separated list of roles/recipes to apply.",
+ proc: lambda { |o| o.split(/[\s,]+/) },
+ default: []
+ # runtime - client_builder - set policy name when creating node
option :policy_name,
- :long => "--policy-name POLICY_NAME",
- :description => "Policyfile name to use (--policy-group must also be given)",
- :default => nil
+ long: "--policy-name POLICY_NAME",
+ description: "Policyfile name to use (--policy-group must also be given).",
+ default: nil
+ # runtime - client_builder - set policy group when creating node
option :policy_group,
- :long => "--policy-group POLICY_GROUP",
- :description => "Policy group name to use (--policy-name must also be given)",
- :default => nil
+ long: "--policy-group POLICY_GROUP",
+ description: "Policy group name to use (--policy-name must also be given).",
+ default: nil
+ # runtime - client_builder - node tags
option :tags,
- :long => "--tags TAGS",
- :description => "Comma separated list of tags to apply to the node",
- :proc => lambda { |o| o.split(/[\s,]+/) },
- :default => []
+ long: "--tags TAGS",
+ description: "Comma separated list of tags to apply to the node.",
+ proc: lambda { |o| o.split(/[\s,]+/) },
+ default: []
+ # bootstrap template
option :first_boot_attributes,
- :short => "-j JSON_ATTRIBS",
- :long => "--json-attributes",
- :description => "A JSON string to be added to the first run of chef-client",
- :proc => lambda { |o| Chef::JSONCompat.parse(o) },
- :default => nil
+ short: "-j JSON_ATTRIBS",
+ long: "--json-attributes",
+ description: "A JSON string to be added to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
+ proc: lambda { |o| Chef::JSONCompat.parse(o) },
+ default: nil
+ # bootstrap template
option :first_boot_attributes_from_file,
- :long => "--json-attribute-file FILE",
- :description => "A JSON file to be used to the first run of chef-client",
- :proc => lambda { |o| Chef::JSONCompat.parse(File.read(o)) },
- :default => nil
-
- option :host_key_verify,
- :long => "--[no-]host-key-verify",
- :description => "Verify host key, enabled by default.",
- :boolean => true,
- :default => true
-
- option :hint,
- :long => "--hint HINT_NAME[=HINT_FILE]",
- :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
- :proc => Proc.new { |h|
- Chef::Config[:knife][:hints] ||= Hash.new
- name, path = h.split("=")
- Chef::Config[:knife][:hints][name] = path ? Chef::JSONCompat.parse(::File.read(path)) : Hash.new
+ long: "--json-attribute-file FILE",
+ description: "A JSON file to be used to the first run of #{ChefUtils::Dist::Infra::CLIENT}.",
+ proc: lambda { |o| Chef::JSONCompat.parse(File.read(o)) },
+ default: nil
+
+ # bootstrap template
+ # Create ohai hints in /etc/chef/ohai/hints, fname=hintname, content=value
+ option :hints,
+ long: "--hint HINT_NAME[=HINT_FILE]",
+ description: "Specify an Ohai hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
+ proc: Proc.new { |hint, accumulator|
+ accumulator ||= {}
+ name, path = hint.split("=", 2)
+ accumulator[name] = path ? Chef::JSONCompat.parse(::File.read(path)) : {}
+ accumulator
}
+ # bootstrap override: url of a an installer shell script to use in place of omnitruck
+ # Note that the bootstrap template _only_ references this out of Chef::Config, and not from
+ # the provided options to knife bootstrap, so we set the Chef::Config option here.
option :bootstrap_url,
- :long => "--bootstrap-url URL",
- :description => "URL to a custom installation script",
- :proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
+ long: "--bootstrap-url URL",
+ description: "URL to a custom installation script."
+
+ option :bootstrap_product,
+ long: "--bootstrap-product PRODUCT",
+ description: "Product to install.",
+ default: "chef"
+ option :msi_url, # Windows target only
+ short: "-m URL",
+ long: "--msi-url URL",
+ description: "Location of the #{ChefUtils::Dist::Infra::PRODUCT} MSI. The default templates will prefer to download from this location. The MSI will be downloaded from #{ChefUtils::Dist::Org::WEBSITE} if not provided (Windows).",
+ default: ""
+
+ # bootstrap override: Do this instead of our own setup.sh from omnitruck. Causes bootstrap_url to be ignored.
option :bootstrap_install_command,
- :long => "--bootstrap-install-command COMMANDS",
- :description => "Custom command to install chef-client",
- :proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
+ long: "--bootstrap-install-command COMMANDS",
+ description: "Custom command to install #{ChefUtils::Dist::Infra::PRODUCT}."
+
+ # bootstrap template: Run this command first in the bootstrap script
+ option :bootstrap_preinstall_command,
+ long: "--bootstrap-preinstall-command COMMANDS",
+ description: "Custom commands to run before installing #{ChefUtils::Dist::Infra::PRODUCT}."
+ # bootstrap template
option :bootstrap_wget_options,
- :long => "--bootstrap-wget-options OPTIONS",
- :description => "Add options to wget when installing chef-client",
- :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
+ long: "--bootstrap-wget-options OPTIONS",
+ description: "Add options to wget when installing #{ChefUtils::Dist::Infra::PRODUCT}."
+ # bootstrap template
option :bootstrap_curl_options,
- :long => "--bootstrap-curl-options OPTIONS",
- :description => "Add options to curl when install chef-client",
- :proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
-
- option :node_ssl_verify_mode,
- :long => "--node-ssl-verify-mode [peer|none]",
- :description => "Whether or not to verify the SSL cert for all HTTPS requests.",
- :proc => Proc.new { |v|
- valid_values = %w{none peer}
- 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,
- :long => "--[no-]node-verify-api-cert",
- :description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
- :boolean => true
+ long: "--bootstrap-curl-options OPTIONS",
+ description: "Add options to curl when install #{ChefUtils::Dist::Infra::PRODUCT}."
+ # chef_vault_handler
option :bootstrap_vault_file,
- :long => "--bootstrap-vault-file VAULT_FILE",
- :description => "A JSON file with a list of vault(s) and item(s) to be updated"
+ long: "--bootstrap-vault-file VAULT_FILE",
+ description: "A JSON file with a list of vault(s) and item(s) to be updated."
+ # chef_vault_handler
option :bootstrap_vault_json,
- :long => "--bootstrap-vault-json VAULT_JSON",
- :description => "A JSON string with the vault(s) and item(s) to be updated"
+ long: "--bootstrap-vault-json VAULT_JSON",
+ description: "A JSON string with the vault(s) and item(s) to be updated."
+ # chef_vault_handler
option :bootstrap_vault_item,
- :long => "--bootstrap-vault-item VAULT_ITEM",
- :description => 'A single vault and item to update as "vault:item"',
- :proc => Proc.new { |i|
- (vault, item) = i.split(/:/)
- Chef::Config[:knife][:bootstrap_vault_item] ||= {}
- Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
- Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
- Chef::Config[:knife][:bootstrap_vault_item]
+ long: "--bootstrap-vault-item VAULT_ITEM",
+ description: 'A single vault and item to update as "vault:item".',
+ proc: Proc.new { |i, accumulator|
+ (vault, item) = i.split(":")
+ accumulator ||= {}
+ accumulator[vault] ||= []
+ accumulator[vault].push(item)
+ accumulator
}
- def initialize(argv = [])
- super
- @client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(
+ # Deprecated options. These must be declared after
+ # regular options because they refer to the replacement
+ # option definitions implicitly.
+ deprecated_option :auth_timeout,
+ replacement: :max_wait,
+ long: "--max-wait SECONDS"
+
+ deprecated_option :forward_agent,
+ replacement: :ssh_forward_agent,
+ boolean: true, long: "--forward-agent"
+
+ deprecated_option :host_key_verify,
+ replacement: :ssh_verify_host_key,
+ boolean: true, long: "--[no-]host-key-verify",
+ value_mapper: Proc.new { |verify| verify ? "always" : "never" }
+
+ deprecated_option :prerelease,
+ replacement: :channel,
+ long: "--prerelease",
+ boolean: true, value_mapper: Proc.new { "current" }
+
+ deprecated_option :ssh_user,
+ replacement: :connection_user,
+ long: "--ssh-user USERNAME"
+
+ deprecated_option :ssh_password,
+ replacement: :connection_password,
+ long: "--ssh-password PASSWORD"
+
+ deprecated_option :ssh_port,
+ replacement: :connection_port,
+ long: "--ssh-port PASSWORD"
+
+ deprecated_option :ssl_peer_fingerprint,
+ replacement: :winrm_ssl_peer_fingerprint,
+ long: "--ssl-peer-fingerprint FINGERPRINT"
+
+ deprecated_option :winrm_user,
+ replacement: :connection_user,
+ long: "--winrm-user USERNAME", short: "-x USERNAME"
+
+ deprecated_option :winrm_password,
+ replacement: :connection_password,
+ long: "--winrm-password PASSWORD"
+
+ deprecated_option :winrm_port,
+ replacement: :connection_port,
+ long: "--winrm-port PORT"
+
+ deprecated_option :winrm_authentication_protocol,
+ replacement: :winrm_auth_method,
+ long: "--winrm-authentication-protocol PROTOCOL"
+
+ deprecated_option :winrm_session_timeout,
+ replacement: :session_timeout,
+ long: "--winrm-session-timeout MINUTES"
+
+ deprecated_option :winrm_ssl_verify_mode,
+ replacement: :winrm_no_verify_cert,
+ long: "--winrm-ssl-verify-mode MODE"
+
+ deprecated_option :winrm_transport, replacement: :winrm_ssl,
+ long: "--winrm-transport TRANSPORT",
+ value_mapper: Proc.new { |value| value == "ssl" }
+
+ attr_reader :connection
+
+ deps do
+ require "erubis" unless defined?(Erubis)
+
+ require "net/ssh" unless defined?(Net::SSH)
+ require_relative "../json_compat"
+ require_relative "../util/path_helper"
+ require_relative "bootstrap/chef_vault_handler"
+ require_relative "bootstrap/client_builder"
+ require_relative "bootstrap/train_connector"
+ end
+
+ banner "knife bootstrap [PROTOCOL://][USER@]FQDN (options)"
+
+ def client_builder
+ @client_builder ||= Chef::Knife::Bootstrap::ClientBuilder.new(
chef_config: Chef::Config,
- knife_config: config,
+ config: config,
ui: ui
)
- @chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(
- knife_config: config,
+ end
+
+ def chef_vault_handler
+ @chef_vault_handler ||= Chef::Knife::Bootstrap::ChefVaultHandler.new(
+ config: config,
ui: ui
)
end
- # The default bootstrap template to use to bootstrap a server This is a public API hook
- # which knife plugins use or inherit and override.
+ # Determine if we need to accept the Chef Infra license locally in order to successfully bootstrap
+ # the remote node. Remote 'chef-client' run will fail if it is >= 15 and the license is not accepted locally.
+ def check_license
+ Chef::Log.debug("Checking if we need to accept Chef license to bootstrap node")
+ version = config[:bootstrap_version] || Chef::VERSION.split(".").first
+ acceptor = LicenseAcceptance::Acceptor.new(logger: Chef::Log, provided: Chef::Config[:chef_license])
+ if acceptor.license_required?("chef", version)
+ Chef::Log.debug("License acceptance required for chef version: #{version}")
+ license_id = acceptor.id_from_mixlib("chef")
+ acceptor.check_and_persist(license_id, version)
+ Chef::Config[:chef_license] ||= acceptor.acceptance_value
+ end
+ end
+
+ # The default bootstrap template to use to bootstrap a server.
+ # This is a public API hook which knife plugins use or inherit and override.
#
# @return [String] Default bootstrap template
def default_bootstrap_template
- "chef-full"
+ if connection.windows?
+ "windows-chef-client-msi"
+ else
+ "chef-full"
+ end
end
def host_descriptor
@@ -295,39 +473,32 @@ class Chef
end
end
- def user_name
- if host_descriptor
- @user_name ||= host_descriptor.split("@").reverse[1]
- end
- end
-
+ # @return [String] The CLI specific bootstrap template or the default
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
+ config[:bootstrap_template] || default_bootstrap_template
end
def find_template
template = bootstrap_template
# 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)}")
+ if File.exist?(template)
+ Chef::Log.trace("Using the specified bootstrap template: #{File.dirname(template)}")
return template
end
# Otherwise search the template directories until we find the right one
bootstrap_files = []
- bootstrap_files << File.join(File.dirname(__FILE__), "bootstrap/templates", "#{template}.erb")
+ bootstrap_files << File.join(__dir__, "bootstrap/templates", "#{template}.erb")
bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir
Chef::Util::PathHelper.home(".chef", "bootstrap", "#{template}.erb") { |p| bootstrap_files << p }
bootstrap_files << Gem.find_files(File.join("chef", "knife", "bootstrap", "#{template}.erb"))
bootstrap_files.flatten!
template_file = Array(bootstrap_files).find do |bootstrap_template|
- Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
- File.exists?(bootstrap_template)
+ Chef::Log.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
+ File.exist?(bootstrap_template)
end
unless template_file
@@ -335,7 +506,7 @@ class Chef
raise Errno::ENOENT
end
- Chef::Log.debug("Found bootstrap template in #{File.dirname(template_file)}")
+ Chef::Log.trace("Found bootstrap template: #{template_file}")
template_file
end
@@ -344,13 +515,18 @@ class Chef
@secret ||= encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil
end
+ # Establish bootstrap context for template rendering.
+ # Requires connection to be a live connection in order to determine
+ # the correct platform.
def bootstrap_context
- @bootstrap_context ||= Knife::Core::BootstrapContext.new(
- config,
- config[:run_list],
- Chef::Config,
- secret
- )
+ @bootstrap_context ||=
+ if connection.windows?
+ require_relative "core/windows_bootstrap_context"
+ Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config, secret)
+ else
+ require_relative "core/bootstrap_context"
+ Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config, secret)
+ end
end
def first_boot_attributes
@@ -365,61 +541,218 @@ class Chef
end
def run
- if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file]
- raise Chef::Exceptions::BootstrapCommandInputError
- end
+ check_license if ChefUtils::Dist::Org::ENFORCE_LICENSE
+ plugin_setup!
validate_name_args!
- validate_options!
+ validate_protocol!
+ validate_first_boot_attributes!
+ validate_winrm_transport_opts!
+ validate_policy_options!
+ plugin_validate_options!
+
+ winrm_warn_no_ssl_verification
+ warn_on_short_session_timeout
+ plugin_create_instance!
$stdout.sync = true
+ connect!
+ register_client
+
+ content = render_template
+ bootstrap_path = upload_bootstrap(content)
+ perform_bootstrap(bootstrap_path)
+ plugin_finalize
+ ensure
+ connection.del_file!(bootstrap_path) if connection && bootstrap_path
+ end
+ def register_client
# chef-vault integration must use the new client-side hawtness, otherwise to use the
# new client-side hawtness, just delete your validation key.
if chef_vault_handler.doing_chef_vault? ||
- (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
+ (Chef::Config[:validation_key] &&
+ !File.exist?(File.expand_path(Chef::Config[:validation_key])))
unless config[:chef_node_name]
ui.error("You must pass a node name with -N when bootstrapping with user credentials")
exit 1
end
-
client_builder.run
-
chef_vault_handler.run(client_builder.client)
bootstrap_context.client_pem = client_builder.client_path
else
- ui.info("Doing old-style registration with the validation key at #{Chef::Config[:validation_key]}...")
- ui.info("Delete your validation key in order to use your user credentials instead")
- ui.info("")
+ ui.warn "Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}..."
+ ui.warn "Remove the key file or remove the 'validation_key' configuration option from your config.rb (knife.rb) to use more secure user credentials for client registration."
+ end
+ end
+
+ def perform_bootstrap(remote_bootstrap_script_path)
+ ui.info("Bootstrapping #{ui.color(server_name, :bold)}")
+ cmd = bootstrap_command(remote_bootstrap_script_path)
+ r = connection.run_command(cmd) do |data|
+ ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}")
end
+ if r.exit_status != 0
+ ui.error("The following error occurred on #{server_name}:")
+ ui.error(r.stderr)
+ exit 1
+ end
+ end
- ui.info("Connecting to #{ui.color(server_name, :bold)}")
+ def connect!
+ ui.info("Connecting to #{ui.color(server_name, :bold)} using #{connection_protocol}")
+ opts ||= connection_opts.dup
+ do_connect(opts)
+ rescue Train::Error => e
+ # We handle these by message text only because train only loads the
+ # transports and protocols that it needs - so the exceptions may not be defined,
+ # and we don't want to require files internal to train.
+ if e.message =~ /fingerprint (\S+) is unknown for "(.+)"/ # Train::Transports::SSHFailed
+ fingerprint = $1
+ hostname, ip = $2.split(",")
+ # TODO: convert the SHA256 base64 value to hex with colons
+ # 'ssh' example output:
+ # RSA key fingerprint is e5:cb:c0:e2:21:3b:12:52:f8:ce:cb:00:24:e2:0c:92.
+ # ECDSA key fingerprint is 5d:67:61:08:a9:d7:01:fd:5e:ae:7e:09:40:ef:c0:3c.
+ # will exit 3 on N
+ ui.confirm <<~EOM
+ The authenticity of host '#{hostname} (#{ip})' can't be established.
+ fingerprint is #{fingerprint}.
+
+ Are you sure you want to continue connecting
+ EOM
+ # FIXME: this should save the key to known_hosts but doesn't appear to be
+ config[:ssh_verify_host_key] = :accept_new
+ conn_opts = connection_opts(reset: true)
+ opts.merge! conn_opts
+ retry
+ elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available)
+ if connection.password_auth?
+ raise
+ else
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
+ end
- begin
- knife_ssh.run
- rescue Net::SSH::AuthenticationFailed
- if config[:ssh_password]
+ opts.merge! force_ssh_password_opts(password)
+ retry
+ else
+ raise
+ end
+ rescue RuntimeError => e
+ if winrm? && e.message == "password is a required option"
+ if connection.password_auth?
raise
else
- ui.info("Failed to authenticate #{knife_ssh.config[:ssh_user]} - trying password auth")
- knife_ssh_with_password_auth.run
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false)
end
+
+ opts.merge! force_winrm_password_opts(password)
+ retry
+ else
+ raise
end
end
+ def handle_ssh_error(e); end
+
+ # url values override CLI flags, if you provide both
+ # we'll use the one that you gave in the URL.
+ def connection_protocol
+ return @connection_protocol if @connection_protocol
+
+ from_url = host_descriptor =~ %r{^(.*)://} ? $1 : nil
+ from_knife = config[:connection_protocol]
+ @connection_protocol = from_url || from_knife || "ssh"
+ end
+
+ def do_connect(conn_options)
+ @connection = TrainConnector.new(host_descriptor, connection_protocol, conn_options)
+ connection.connect!
+ rescue Train::UserError => e
+ limit ||= 1
+ if !conn_options.key?(:pty) && e.reason == :sudo_no_tty
+ ui.warn("#{e.message} - trying with pty request")
+ conn_options[:pty] = true # ensure we can talk to systems with requiretty set true in sshd config
+ retry
+ elsif config[:use_sudo_password] && (e.reason == :sudo_password_required || e.reason == :bad_sudo_password) && limit < 3
+ ui.warn("Failed to authenticate #{conn_options[:user]} to #{server_name} - #{e.message} \n sudo: #{limit} incorrect password attempt")
+ sudo_password = ui.ask("Enter sudo password for #{conn_options[:user]}@#{server_name}:", echo: false)
+ limit += 1
+ conn_options[:sudo_password] = sudo_password
+
+ retry
+ else
+ raise
+ end
+ end
+
+ # Fail if both first_boot_attributes and first_boot_attributes_from_file
+ # are set.
+ def validate_first_boot_attributes!
+ if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file]
+ raise Chef::Exceptions::BootstrapCommandInputError
+ end
+
+ true
+ end
+
+ # FIXME: someone needs to clean this up properly: https://github.com/chef/chef/issues/9645
+ # This code is deliberately left without an abstraction around deprecating the config options to avoid knife plugins from
+ # using those methods (which will need to be deprecated and break them) via inheritance (ruby does not have a true `private`
+ # so the lack of any inheritable implementation is because of that).
+ #
+ def winrm_auth_method
+ config.key?(:winrm_auth_method) ? config[:winrm_auth_method] : config.key?(:winrm_authentications_protocol) ? config[:winrm_authentication_protocol] : "negotiate" # rubocop:disable Style/NestedTernaryOperator
+ end
+
+ def ssh_verify_host_key
+ config.key?(:ssh_verify_host_key) ? config[:ssh_verify_host_key] : config.key?(:host_key_verify) ? config[:host_key_verify] : "always" # rubocop:disable Style/NestedTernaryOperator
+ end
+
+ # Fail if using plaintext auth without ssl because
+ # this can expose keys in plaintext on the wire.
+ # TODO test for this method
+ # TODO check that the protocol is valid.
+ def validate_winrm_transport_opts!
+ return true unless winrm?
+
+ if Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key]))
+ if winrm_auth_method == "plaintext" &&
+ config[:winrm_ssl] != true
+ ui.error <<~EOM
+ Validatorless bootstrap over unsecure winrm channels could expose your
+ key to network sniffing.
+ Please use a 'winrm_auth_method' other than 'plaintext',
+ or enable ssl on #{server_name} then use the ---winrm-ssl flag
+ to connect.
+ EOM
+
+ exit 1
+ end
+ end
+ true
+ end
+
+ # fail if the server_name is nil
def validate_name_args!
if server_name.nil?
ui.error("Must pass an FQDN or ip to bootstrap")
exit 1
- elsif server_name == "windows"
- # catches "knife bootstrap windows" when that command is not installed
- ui.warn("Hostname containing 'windows' specified. Please install 'knife-windows' if you are attempting to bootstrap a Windows node via WinRM.")
end
end
- def validate_options!
+ # Ensure options are valid by checking policyfile values.
+ #
+ # The method call will cause the program to exit(1) if:
+ # * Only one of --policy-name and --policy-group is specified
+ # * Policyfile options are set and --run-list is set as well
+ #
+ # @return [TrueClass] If options are valid.
+ def validate_policy_options!
if incomplete_policyfile_options?
ui.error("--policy-name and --policy-group must be specified together")
exit 1
@@ -427,45 +760,357 @@ class Chef
ui.error("Policyfile options and --run-list are exclusive")
exit 1
end
+ end
+
+ # Ensure a valid protocol is provided for target host connection
+ #
+ # The method call will cause the program to exit(1) if:
+ # * Conflicting protocols are given via the target URI and the --protocol option
+ # * The protocol is not a supported protocol
+ #
+ # @return [TrueClass] If options are valid.
+ def validate_protocol!
+ from_cli = config[:connection_protocol]
+ if from_cli && connection_protocol != from_cli
+ # Hanging indent to align with the ERROR: prefix
+ ui.error <<~EOM
+ The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}'
+ while the --protocol flag specifies '#{from_cli}'. Please include
+ only one or the other.
+ EOM
+ exit 1
+ end
+
+ unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol)
+ ui.error <<~EOM
+ Unsupported protocol '#{connection_protocol}'.
+
+ Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")}
+ EOM
+ exit 1
+ end
+ true
+ end
+
+ # Validate any additional options
+ #
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to validate any additional options before any other actions are executed
+ #
+ # @return [TrueClass] If options are valid or exits
+ def plugin_validate_options!
true
end
- def knife_ssh
- ssh = Chef::Knife::Ssh.new
- ssh.ui = ui
- ssh.name_args = [ server_name, ssh_command ]
- ssh.config[:ssh_user] = user_name || config[:ssh_user]
- ssh.config[:ssh_password] = config[:ssh_password]
- ssh.config[:ssh_port] = config[:ssh_port]
- ssh.config[:ssh_gateway] = config[:ssh_gateway]
- 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
+ # Create the server that we will bootstrap, if necessary
+ #
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to call out to an API to build an instance of the server we wish to bootstrap
+ #
+ # @return [TrueClass] If instance successfully created, or exits
+ def plugin_create_instance!
+ true
+ end
+
+ # Perform any setup necessary by the plugin
+ #
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to create connection objects
+ #
+ # @return [TrueClass] If instance successfully created, or exits
+ def plugin_setup!; end
+
+ # Perform any teardown or cleanup necessary by the plugin
+ #
+ # Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to display a message or perform any cleanup
+ #
+ # @return [void]
+ def plugin_finalize; end
+
+ # If session_timeout is too short, it is likely
+ # a holdover from "--winrm-session-timeout" which used
+ # minutes as its unit, instead of seconds.
+ # Warn the human so that they are not surprised.
+ #
+ def warn_on_short_session_timeout
+ if session_timeout && session_timeout <= 15
+ ui.warn <<~EOM
+ You provided '--session-timeout #{session_timeout}' second(s).
+ Did you mean '--session-timeout #{session_timeout * 60}' seconds?
+ EOM
+ end
+ end
+
+ def winrm_warn_no_ssl_verification
+ return unless winrm?
+
+ # REVIEWER NOTE
+ # The original check from knife plugin did not include winrm_ssl_peer_fingerprint
+ # Reference:
+ # https://github.com/chef/knife-windows/blob/92d151298142be4a4750c5b54bb264f8d5b81b8a/lib/chef/knife/winrm_knife_base.rb#L271-L273
+ # TODO Seems like we should also do a similar warning if ssh_verify_host == false
+ if config[:ca_trust_file].nil? &&
+ config[:winrm_no_verify_cert] &&
+ config[:winrm_ssl_peer_fingerprint].nil?
+ ui.warn <<~WARN
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ SSL validation of HTTPS requests for the WinRM transport is disabled.
+ HTTPS WinRM connections are still encrypted, but knife is not able
+ to detect forged replies or spoofing attacks.
+
+ To work around this issue you can use the flag `--winrm-no-verify-cert`
+ or add an entry like this to your knife configuration file:
+
+ # Verify all WinRM HTTPS connections
+ knife[:winrm_no_verify_cert] = true
+
+ You can also specify a ca_trust_file via --ca-trust-file,
+ or the expected fingerprint of the target host's certificate
+ via --winrm-ssl-peer-fingerprint.
+ WARN
+ end
+ end
+
+ # @return a configuration hash suitable for connecting to the remote
+ # host via train
+ def connection_opts(reset: false)
+ return @connection_opts unless @connection_opts.nil? || reset == true
+
+ @connection_opts = {}
+ @connection_opts.merge! base_opts
+ @connection_opts.merge! host_verify_opts
+ @connection_opts.merge! gateway_opts
+ @connection_opts.merge! sudo_opts
+ @connection_opts.merge! winrm_opts
+ @connection_opts.merge! ssh_opts
+ @connection_opts.merge! ssh_identity_opts
+ @connection_opts
+ end
+
+ def winrm?
+ connection_protocol == "winrm"
+ end
+
+ def ssh?
+ connection_protocol == "ssh"
+ end
+
+ # Common configuration for all protocols
+ def base_opts
+ port = config_for_protocol(:port)
+ user = config_for_protocol(:user)
+ {}.tap do |opts|
+ opts[:logger] = Chef::Log
+ opts[:password] = config[:connection_password] if config.key?(:connection_password)
+ opts[:user] = user if user
+ opts[:max_wait_until_ready] = config[:max_wait].to_f unless config[:max_wait].nil?
+ # TODO - when would we need to provide rdp_port vs port? Or are they not mutually exclusive?
+ opts[:port] = port if port
+ end
+ end
+
+ def host_verify_opts
+ if winrm?
+ { self_signed: config[:winrm_no_verify_cert] === true }
+ elsif ssh?
+ # Fall back to the old knife config key name for back compat.
+ { verify_host_key: ssh_verify_host_key }
+ else
+ {}
+ end
end
- def knife_ssh_with_password_auth
- ssh = knife_ssh
- ssh.config[:ssh_identity_file] = nil
- ssh.config[:ssh_password] = ssh.get_password
- ssh
+ def ssh_opts
+ opts = {}
+ return opts if winrm?
+
+ opts[:non_interactive] = true # Prevent password prompts from underlying net/ssh
+ opts[:forward_agent] = (config[:ssh_forward_agent] === true)
+ opts[:connection_timeout] = session_timeout
+ opts
+ end
+
+ def ssh_identity_opts
+ opts = {}
+ return opts if winrm?
+
+ identity_file = config[:ssh_identity_file]
+ if identity_file
+ opts[:key_files] = [identity_file]
+ # We only set keys_only based on the explicit ssh_identity_file;
+ # someone may use a gateway key and still expect password auth
+ # on the target. Similarly, someone may have a default key specified
+ # in knife config, but have provided a password on the CLI.
+
+ # REVIEW NOTE: this is a new behavior. Originally, ssh_identity_file
+ # could only be populated from CLI options, so there was no need to check
+ # for this. We will also set keys_only to false only if there are keys
+ # and no password.
+ # If both are present, train(via net/ssh) will prefer keys, falling back to password.
+ # Reference: https://github.com/chef/chef/blob/master/lib/chef/knife/ssh.rb#L272
+ opts[:keys_only] = config.key?(:connection_password) == false
+ else
+ opts[:key_files] = []
+ opts[:keys_only] = false
+ end
+
+ gateway_identity_file = config[:ssh_gateway] ? config[:ssh_gateway_identity] : nil
+ unless gateway_identity_file.nil?
+ opts[:key_files] << gateway_identity_file
+ end
+
+ opts
end
- def ssh_command
- command = render_template
+ def gateway_opts
+ opts = {}
+ if config[:ssh_gateway]
+ split = config[:ssh_gateway].split("@", 2)
+ if split.length == 1
+ gw_host = split[0]
+ else
+ gw_user = split[0]
+ gw_host = split[1]
+ end
+ gw_host, gw_port = gw_host.split(":", 2)
+ # TODO - validate convertible port in config validation?
+ gw_port = Integer(gw_port) rescue nil
+ opts[:bastion_host] = gw_host
+ opts[:bastion_user] = gw_user
+ opts[:bastion_port] = gw_port
+ end
+ opts
+ end
+ # use_sudo - tells bootstrap to use the sudo command to run bootstrap
+ # use_sudo_password - tells bootstrap to use the sudo command to run bootstrap
+ # and to use the password specified with --password
+ # TODO: I'd like to make our sudo options sane:
+ # --sudo (bool) - use sudo
+ # --sudo-password PASSWORD (default: :password) - use this password for sudo
+ # --sudo-options "opt,opt,opt" to pass into sudo
+ # --sudo-command COMMAND sudo command other than sudo
+ # REVIEW NOTE: knife bootstrap did not pull sudo values from Chef::Config,
+ # should we change that for consistency?
+ def sudo_opts
+ return {} if winrm?
+
+ opts = { sudo: false }
if config[:use_sudo]
- sudo_prefix = config[:use_sudo_password] ? "echo '#{config[:ssh_password]}' | sudo -S " : "sudo "
- command = config[:preserve_home] ? "#{sudo_prefix} #{command}" : "#{sudo_prefix} -H #{command}"
+ opts[:sudo] = true
+ if config[:use_sudo_password]
+ opts[:sudo_password] = config[:connection_password]
+ end
+ if config[:preserve_home]
+ opts[:sudo_options] = "-H"
+ end
+ end
+ opts
+ end
+
+ def winrm_opts
+ return {} unless winrm?
+
+ opts = {
+ winrm_transport: winrm_auth_method, # winrm gem and train calls auth method 'transport'
+ winrm_basic_auth_only: config[:winrm_basic_auth_only] || false,
+ ssl: config[:winrm_ssl] === true,
+ ssl_peer_fingerprint: config[:winrm_ssl_peer_fingerprint],
+ }
+
+ if winrm_auth_method == "kerberos"
+ opts[:kerberos_service] = config[:kerberos_service] if config[:kerberos_service]
+ opts[:kerberos_realm] = config[:kerberos_realm] if config[:kerberos_service]
+ end
+
+ if config[:ca_trust_file]
+ opts[:ca_trust_path] = config[:ca_trust_file]
+ end
+
+ opts[:operation_timeout] = session_timeout
+
+ opts
+ end
+
+ # Config overrides to force password auth.
+ def force_ssh_password_opts(password)
+ {
+ password: password,
+ non_interactive: false,
+ keys_only: false,
+ key_files: [],
+ auth_methods: %i{password keyboard_interactive},
+ }
+ end
+
+ def force_winrm_password_opts(password)
+ {
+ password: password,
+ }
+ end
+
+ # This is for deprecating config options. The fallback_key can be used
+ # to pull an old knife config option out of the config file when the
+ # cli value has been renamed. This is different from the deprecated
+ # cli values, since these are for config options that have no corresponding
+ # cli value.
+ #
+ # DO NOT USE - this whole API is considered deprecated
+ #
+ # @api deprecated
+ #
+ def config_value(key, fallback_key = nil, default = nil)
+ Chef.deprecated(:knife_bootstrap_apis, "Use of config_value is deprecated. Knife plugin authors should access the config hash directly, which does correct merging of cli and config options.")
+ if config.key?(key)
+ # the first key is the primary key so we check the merged hash first
+ config[key]
+ elsif config.key?(fallback_key)
+ # we get the old config option here (the deprecated cli option shouldn't exist)
+ config[fallback_key]
+ else
+ default
end
+ end
- command
+ def upload_bootstrap(content)
+ script_name = connection.windows? ? "bootstrap.bat" : "bootstrap.sh"
+ remote_path = connection.normalize_path(File.join(connection.temp_dir, script_name))
+ connection.upload_file_content!(content, remote_path)
+ remote_path
+ end
+
+ # build the command string for bootstrapping
+ # @return String
+ def bootstrap_command(remote_path)
+ if connection.windows?
+ "cmd.exe /C #{remote_path}"
+ else
+ "sh #{remote_path}"
+ end
end
private
+ # To avoid cluttering the CLI options, some flags (such as port and user)
+ # are shared between protocols. However, there is still a need to allow the operator
+ # to specify defaults separately, since they may not be the same values for different
+ # protocols.
+
+ # These keys are available in Chef::Config, and are prefixed with the protocol name.
+ # For example, :user CLI option will map to :winrm_user and :ssh_user Chef::Config keys,
+ # based on the connection protocol in use.
+
+ # @api private
+ def config_for_protocol(option)
+ if option == :port
+ config[:connection_port] || config[knife_key_for_protocol(option)]
+ else
+ config[:connection_user] || config[knife_key_for_protocol(option)]
+ end
+ end
+
+ # @api private
+ def knife_key_for_protocol(option)
+ "#{connection_protocol}_#{option}".to_sym
+ end
+
# True if policy_name and run_list are both given
def policyfile_and_run_list_given?
run_list_given? && policyfile_options_given?
@@ -484,6 +1129,14 @@ class Chef
(!!config[:policy_name] ^ config[:policy_group])
end
+ # session_timeout option has a default that may not arrive, particularly if
+ # we're being invoked from a plugin that doesn't merge_config.
+ def session_timeout
+ timeout = config[:session_timeout]
+ return options[:session_timeout][:default] if timeout.nil?
+
+ timeout.to_i
+ end
end
end
end
diff --git a/lib/chef/knife/bootstrap/chef_vault_handler.rb b/lib/chef/knife/bootstrap/chef_vault_handler.rb
index 9990565856..20759d6fdf 100644
--- a/lib/chef/knife/bootstrap/chef_vault_handler.rb
+++ b/lib/chef/knife/bootstrap/chef_vault_handler.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,15 +15,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/knife/bootstrap"
-
class Chef
class Knife
class Bootstrap < Knife
class ChefVaultHandler
# @return [Hash] knife merged config, typically @config
- attr_accessor :knife_config
+ attr_accessor :config
# @return [Chef::Knife::UI] ui object for output
attr_accessor :ui
@@ -31,11 +29,15 @@ class Chef
# @return [Chef::ApiClient] vault client
attr_reader :client
- # @param knife_config [Hash] knife merged config, typically @config
+ # @param config [Hash] knife merged config, typically @config
# @param ui [Chef::Knife::UI] ui object for output
- def initialize(knife_config: {}, ui: nil)
- @knife_config = knife_config
- @ui = ui
+ def initialize(config: {}, knife_config: nil, ui: nil)
+ @config = config
+ unless knife_config.nil?
+ @config = knife_config
+ Chef.deprecated(:knife_bootstrap_apis, "The knife_config option to the Bootstrap::ClientBuilder object is deprecated and has been renamed to just 'config'")
+ end
+ @ui = ui
end
# Updates the chef vault items for the newly created client.
@@ -87,20 +89,20 @@ class Chef
# @return [String] string with serialized JSON representing the chef vault items
def bootstrap_vault_json
- knife_config[:bootstrap_vault_json]
+ config[:bootstrap_vault_json]
end
# @return [String] JSON text in a file representing the chef vault items
def bootstrap_vault_file
- knife_config[:bootstrap_vault_file]
+ config[:bootstrap_vault_file]
end
# @return [Hash] Ruby object representing the chef vault items to create
def bootstrap_vault_item
- knife_config[:bootstrap_vault_item]
+ config[:bootstrap_vault_item]
end
- # Helper to return a ruby object represeting all the data bags and items
+ # Helper to return a ruby object representing all the data bags and items
# to update via chef-vault.
#
# @return [Hash] deserialized ruby hash with all the vault items
@@ -110,7 +112,7 @@ class Chef
if bootstrap_vault_item
bootstrap_vault_item
else
- json = bootstrap_vault_json ? bootstrap_vault_json : File.read(bootstrap_vault_file)
+ json = bootstrap_vault_json || File.read(bootstrap_vault_file)
Chef::JSONCompat.from_json(json)
end
end
@@ -131,7 +133,7 @@ class Chef
#
# @param vault [String] name of the chef-vault encrypted data bag
# @param item [String] name of the chef-vault encrypted item
- # @returns [ChefVault::Item] ChefVault::Item object
+ # @return [ChefVault::Item] ChefVault::Item object
def load_chef_bootstrap_vault_item(vault, item)
ChefVault::Item.load(vault, item)
end
@@ -142,11 +144,12 @@ class Chef
def require_chef_vault!
@require_chef_vault ||=
begin
- error_message = "Knife bootstrap requires version 2.6.0 or higher of the chef-vault gem to configure chef vault items"
+ error_message = "Knife bootstrap requires version 2.6.0 or higher of the chef-vault gem to configure vault items"
require "chef-vault"
if Gem::Version.new(ChefVault::VERSION) < Gem::Version.new("2.6.0")
raise error_message
end
+
true
rescue LoadError
raise error_message
diff --git a/lib/chef/knife/bootstrap/client_builder.rb b/lib/chef/knife/bootstrap/client_builder.rb
index cab33cd811..d9c3d83d06 100644
--- a/lib/chef/knife/bootstrap/client_builder.rb
+++ b/lib/chef/knife/bootstrap/client_builder.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,11 @@
# limitations under the License.
#
-require "chef/node"
-require "chef/server_api"
-require "chef/api_client/registration"
-require "chef/api_client"
-require "chef/knife/bootstrap"
-require "tmpdir"
+require_relative "../../node"
+require_relative "../../server_api"
+require_relative "../../api_client/registration"
+require_relative "../../api_client"
+require "tmpdir" unless defined?(Dir.mktmpdir)
class Chef
class Knife
@@ -29,7 +28,7 @@ class Chef
class ClientBuilder
# @return [Hash] knife merged config, typically @config
- attr_accessor :knife_config
+ attr_accessor :config
# @return [Hash] chef config object
attr_accessor :chef_config
# @return [Chef::Knife::UI] ui object for output
@@ -37,13 +36,17 @@ class Chef
# @return [Chef::ApiClient] client saved on run
attr_reader :client
- # @param knife_config [Hash] Hash of knife config settings
+ # @param config [Hash] Hash of knife config settings
# @param chef_config [Hash] Hash of chef config settings
# @param ui [Chef::Knife::UI] UI object for output
- def initialize(knife_config: {}, chef_config: {}, ui: nil)
- @knife_config = knife_config
- @chef_config = chef_config
- @ui = ui
+ def initialize(config: {}, knife_config: nil, chef_config: {}, ui: nil)
+ @config = config
+ unless knife_config.nil?
+ @config = knife_config
+ Chef.deprecated(:knife_bootstrap_apis, "The knife_config option to the Bootstrap::ClientBuilder object is deprecated and has been renamed to just 'config'")
+ end
+ @chef_config = chef_config
+ @ui = ui
end
# Main entry. Prompt the user to clean up any old client or node objects. Then create
@@ -78,34 +81,34 @@ class Chef
private
- # @return [String] node name from the knife_config
+ # @return [String] node name from the config
def node_name
- knife_config[:chef_node_name]
+ config[:chef_node_name]
end
- # @return [String] enviroment from the knife_config
+ # @return [String] environment from the config
def environment
- knife_config[:environment]
+ config[:environment]
end
- # @return [String] run_list from the knife_config
+ # @return [String] run_list from the config
def run_list
- knife_config[:run_list]
+ config[:run_list]
end
- # @return [String] policy_name from the knife_config
+ # @return [String] policy_name from the config
def policy_name
- knife_config[:policy_name]
+ config[:policy_name]
end
- # @return [String] policy_group from the knife_config
+ # @return [String] policy_group from the config
def policy_group
- knife_config[:policy_group]
+ config[:policy_group]
end
- # @return [Hash,Array] Object representation of json first-boot attributes from the knife_config
+ # @return [Hash,Array] Object representation of json first-boot attributes from the config
def first_boot_attributes
- knife_config[:first_boot_attributes]
+ config[:first_boot_attributes]
end
# @return [String] chef server url from the Chef::Config
@@ -155,7 +158,7 @@ class Chef
node.environment(environment) if environment
node.policy_name = policy_name if policy_name
node.policy_group = policy_group if policy_group
- (knife_config[:tags] || []).each do |tag|
+ (config[:tags] || []).each do |tag|
node.tags << tag
end
node
@@ -187,14 +190,15 @@ class Chef
def resource_exists?(relative_path)
rest.get(relative_path)
true
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise unless e.response.code == "404"
+
false
end
# @return [Chef::ServerAPI] REST client using the client credentials
def client_rest
- @client_rest ||= Chef::ServerAPI.new(chef_server_url, :client_name => node_name, :signing_key_filename => client_path)
+ @client_rest ||= Chef::ServerAPI.new(chef_server_url, client_name: node_name, signing_key_filename: client_path)
end
# @return [Chef::ServerAPI] REST client using the cli user's knife credentials
diff --git a/lib/chef/knife/bootstrap/templates/README.md b/lib/chef/knife/bootstrap/templates/README.md
index b5bca25d8e..7f28f8f40f 100644
--- a/lib/chef/knife/bootstrap/templates/README.md
+++ b/lib/chef/knife/bootstrap/templates/README.md
@@ -5,7 +5,7 @@ standardized on the [Omnibus](https://github.com/chef/omnibus) built installatio
packages.
The 'chef-full' template downloads a script which is used to determine the correct
-Omnibus package for this system from the [Omnitruck](https://docs.chef.io/api_omnitruck.html) API.
+Omnibus package for this system from the [Omnitruck](https://docs.chef.io/api_omnitruck/) API.
You can still utilize custom bootstrap templates on your system if your installation
-needs are unique. Additional information can be found on the [docs site](https://docs.chef.io/knife_bootstrap.html#custom-templates).
+needs are unique. Additional information can be found on the [docs site](https://docs.chef.io/knife_bootstrap/#custom-templates).
diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb
index 6007ff9859..2e0c80eaef 100644
--- a/lib/chef/knife/bootstrap/templates/chef-full.erb
+++ b/lib/chef/knife/bootstrap/templates/chef-full.erb
@@ -1,5 +1,5 @@
-sh -c '
-<%= "export https_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+<%= "https_proxy=\"#{@config[:bootstrap_proxy]}\" export https_proxy" if @config[:bootstrap_proxy] %>
+<%= "no_proxy=\"#{@config[:bootstrap_no_proxy]}\" export no_proxy" if @config[:bootstrap_no_proxy] %>
if test "x$TMPDIR" = "x"; then
tmp="/tmp"
@@ -37,7 +37,7 @@ capture_tmp_stderr() {
# do_wget URL FILENAME
do_wget() {
echo "trying wget..."
- wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %> <%= knife_config[:bootstrap_wget_options] %> -O "$2" "$1" 2>$tmp_dir/stderr
+ wget <%= "--proxy=on " if @config[:bootstrap_proxy] %> <%= @config[:bootstrap_wget_options] %> -O "$2" "$1" 2>$tmp_dir/stderr
rc=$?
# check for 404
grep "ERROR 404" $tmp_dir/stderr 2>&1 >/dev/null
@@ -57,7 +57,7 @@ do_wget() {
# do_curl URL FILENAME
do_curl() {
echo "trying curl..."
- curl -sL <%= "--proxy \"#{knife_config[:bootstrap_proxy]}\" " if knife_config[:bootstrap_proxy] %> <%= knife_config[:bootstrap_curl_options] %> -D $tmp_dir/stderr -o "$2" "$1" 2>$tmp_dir/stderr
+ curl -sL <%= "--proxy \"#{@config[:bootstrap_proxy]}\" " if @config[:bootstrap_proxy] %> <%= @config[:bootstrap_curl_options] %> -D $tmp_dir/stderr -o "$2" "$1" 2>$tmp_dir/stderr
rc=$?
# check for 404
grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null
@@ -162,16 +162,22 @@ do_download() {
return 16
}
-<% if knife_config[:bootstrap_install_command] %>
- <%= knife_config[:bootstrap_install_command] %>
+<%# Run any custom commands before installing chef-client -%>
+<%# Ex. wait for cloud-init to complete -%>
+<% if @config[:bootstrap_preinstall_command] %>
+ <%= @config[:bootstrap_preinstall_command] %>
+<% end %>
+
+<% if @config[:bootstrap_install_command] %>
+ <%= @config[:bootstrap_install_command] %>
<% else %>
- install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://omnitruck-direct.chef.io/chef/install.sh" %>"
- if test -f /usr/bin/chef-client; then
- echo "-----> Existing Chef installation detected"
+ install_sh="<%= @config[:bootstrap_url] ? @config[:bootstrap_url] : "https://omnitruck.chef.io/chef/install.sh" %>"
+ if test -f /usr/bin/<%= ChefUtils::Dist::Infra::CLIENT %>; then
+ echo "-----> Existing <%= ChefUtils::Dist::Infra::PRODUCT %> installation detected"
else
- echo "-----> Installing Chef Omnibus (<%= latest_current_chef_version_string %>)"
+ echo "-----> Installing Chef Omnibus (<%= @config[:channel] %>/<%= version_to_install %>)"
do_download ${install_sh} $tmp_dir/install.sh
- sh $tmp_dir/install.sh -P chef <%= latest_current_chef_version_string %>
+ sh $tmp_dir/install.sh -P <%= @config[:bootstrap_product] %> -c <%= @config[:channel] %> -v <%= version_to_install %>
fi
<% end %>
@@ -182,24 +188,24 @@ fi
mkdir -p /etc/chef
<% if client_pem -%>
-cat > /etc/chef/client.pem <<EOP
+(umask 077 && (cat > /etc/chef/client.pem <<'EOP'
<%= ::File.read(::File.expand_path(client_pem)) %>
EOP
-chmod 0600 /etc/chef/client.pem
+)) || exit 1
<% end -%>
<% if validation_key -%>
-cat > /etc/chef/validation.pem <<EOP
+(umask 077 && (cat > /etc/chef/validation.pem <<'EOP'
<%= validation_key %>
EOP
-chmod 0600 /etc/chef/validation.pem
+)) || exit 1
<% end -%>
<% if encrypted_data_bag_secret -%>
-cat > /etc/chef/encrypted_data_bag_secret <<EOP
+(umask 077 && (cat > /etc/chef/encrypted_data_bag_secret <<'EOP'
<%= encrypted_data_bag_secret %>
EOP
-chmod 0600 /etc/chef/encrypted_data_bag_secret
+)) || exit 1
<% end -%>
<% unless trusted_certs.empty? -%>
@@ -208,21 +214,21 @@ mkdir -p /etc/chef/trusted_certs
<% end -%>
<%# Generate Ohai Hints -%>
-<% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%>
+<% unless @config[:hints].nil? || @config[:hints].empty? -%>
mkdir -p /etc/chef/ohai/hints
-<% @chef_config[:knife][:hints].each do |name, hash| -%>
-cat > /etc/chef/ohai/hints/<%= name %>.json <<EOP
+<% @config[:hints].each do |name, hash| -%>
+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
@@ -231,6 +237,6 @@ mkdir -p /etc/chef/client.d
<%= client_d %>
<% end -%>
-echo "Starting the first Chef Client run..."
+echo "Starting the first <%= ChefUtils::Dist::Infra::PRODUCT %> Client run..."
-<%= start_chef %>'
+<%= start_chef %>
diff --git a/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb b/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
new file mode 100644
index 0000000000..7aa7be49f8
--- /dev/null
+++ b/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb
@@ -0,0 +1,278 @@
+@rem
+@rem Author:: Seth Chisamore (<schisamo@chef.io>)
+@rem Copyright:: Copyright (c) 2011-2019 Chef Software, Inc.
+@rem License:: Apache License, Version 2.0
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@rem Use delayed environment expansion so that ERRORLEVEL can be evaluated with the
+@rem !ERRORLEVEL! syntax which evaluates at execution of the line of script, not when
+@rem the line is read. See help for the /E switch from cmd.exe /? .
+@setlocal ENABLEDELAYEDEXPANSION
+
+<%= "SETX HTTP_PROXY \"#{@config[:bootstrap_proxy]}\"" if @config[:bootstrap_proxy] %>
+
+@set BOOTSTRAP_DIRECTORY=<%= bootstrap_directory %>
+@echo Checking for existing directory "%BOOTSTRAP_DIRECTORY%"...
+@if NOT EXIST %BOOTSTRAP_DIRECTORY% (
+ @echo Existing directory not found, creating.
+ @mkdir %BOOTSTRAP_DIRECTORY%
+) else (
+ @echo Existing directory found, skipping creation.
+)
+
+> <%= bootstrap_directory %>\wget.vbs (
+ <%= win_wget %>
+)
+
+> <%= bootstrap_directory %>\wget.ps1 (
+ <%= win_wget_ps %>
+)
+
+@rem Determine the version and the architecture
+
+@FOR /F "usebackq tokens=1-8 delims=.[] " %%A IN (`ver`) DO (
+@set WinMajor=%%D
+@set WinMinor=%%E
+@set WinBuild=%%F
+)
+
+@echo Detected Windows Version %WinMajor%.%WinMinor% Build %WinBuild%
+
+@set LATEST_OS_VERSION_MAJOR=10
+@set LATEST_OS_VERSION_MINOR=1
+
+@if /i %WinMajor% GTR %LATEST_OS_VERSION_MAJOR% goto VersionUnknown
+@if /i %WinMajor% EQU %LATEST_OS_VERSION_MAJOR% (
+ @if /i %WinMinor% GTR %LATEST_OS_VERSION_MINOR% goto VersionUnknown
+)
+
+goto Version%WinMajor%.%WinMinor%
+
+:VersionUnknown
+@rem If this is an unknown version of windows set the default
+@set MACHINE_OS=2012r2
+@echo Warning: Unknown version of Windows, assuming default of Windows %MACHINE_OS%
+goto architecture_select
+
+:Version6.0
+@set MACHINE_OS=2008
+goto architecture_select
+
+:Version6.1
+@set MACHINE_OS=2008r2
+goto architecture_select
+
+:Version6.2
+@set MACHINE_OS=2012
+goto architecture_select
+
+@rem Currently Windows Server 2012 R2 is treated as equivalent to Windows Server 2012
+:Version6.3
+@set MACHINE_OS=2012r2
+goto architecture_select
+
+:Version10.0
+@set MACHINE_OS=2016
+goto architecture_select
+
+@rem Currently Windows Server 2019 is treated as equivalent to Windows Server 2016
+:Version10.1
+goto Version10.0
+
+:architecture_select
+<% if @config[:architecture] %>
+ @set MACHINE_ARCH=<%= @config[:architecture] %>
+
+ <% if @config[:architecture] == "x86_64" %>
+ IF "%PROCESSOR_ARCHITECTURE%"=="x86" IF not defined PROCESSOR_ARCHITEW6432 (
+ echo You specified bootstrap_architecture as x86_64 but the target machine is i386. A 64 bit program cannot run on a 32 bit machine. > "&2"
+ echo Exiting without bootstrapping. > "&2"
+ exit /b 1
+ )
+ <% end %>
+<% else %>
+ @set MACHINE_ARCH=x86_64
+ IF "%PROCESSOR_ARCHITECTURE%"=="x86" IF not defined PROCESSOR_ARCHITEW6432 @set MACHINE_ARCH=i686
+<% end %>
+goto chef_installed
+
+:chef_installed
+@echo Checking for existing <%= ChefUtils::Dist::Infra::PRODUCT %> installation
+WHERE <%= ChefUtils::Dist::Infra::CLIENT %> >nul 2>nul
+If !ERRORLEVEL!==0 (
+ @echo Existing <%= ChefUtils::Dist::Infra::PRODUCT %> installation detected, skipping download
+ goto key_create
+) else (
+ @echo No existing installation of <%= ChefUtils::Dist::Infra::PRODUCT %> detected
+ goto install
+)
+
+:install
+@rem If user has provided the custom installation command, execute it
+<% if @config[:bootstrap_install_command] %>
+ <%= @config[:bootstrap_install_command] %>
+<% else %>
+ @rem Install Chef using the MSI installer
+
+ @set "LOCAL_DESTINATION_MSI_PATH=<%= local_download_path %>"
+ @set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\<%= ChefUtils::Dist::Infra::CLIENT %>-msi%RANDOM%.log"
+
+ @rem Clear any pre-existing downloads
+ @echo Checking for existing downloaded package at "%LOCAL_DESTINATION_MSI_PATH%"
+ @if EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
+ @echo Found existing downloaded package, deleting.
+ @del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
+ @if ERRORLEVEL 1 (
+ echo Warning: Failed to delete pre-existing package with status code !ERRORLEVEL! > "&2"
+ )
+ ) else (
+ echo No existing downloaded packages to delete.
+ )
+
+ @rem If there is somehow a name collision, remove pre-existing log
+ @if EXIST "%CHEF_CLIENT_MSI_LOG_PATH%" del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
+
+ @echo Attempting to download client package using PowerShell if available...
+ @set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%', 'PowerShell') %>"
+ @set powershell_download=powershell.exe -ExecutionPolicy Unrestricted -InputFormat None -NoProfile -NonInteractive -File <%= bootstrap_directory %>\wget.ps1 "%REMOTE_SOURCE_MSI_URL%" "%LOCAL_DESTINATION_MSI_PATH%"
+ @echo !powershell_download!
+ @call !powershell_download!
+
+ @set DOWNLOAD_ERROR_STATUS=!ERRORLEVEL!
+
+ @if ERRORLEVEL 1 (
+ @echo Failed PowerShell download with status code !DOWNLOAD_ERROR_STATUS! > "&2"
+ @if !DOWNLOAD_ERROR_STATUS!==0 set DOWNLOAD_ERROR_STATUS=2
+ ) else (
+ @rem Sometimes the error level is not set even when the download failed,
+ @rem so check for the file to be sure it is there -- if it is not, we will retry
+ @if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
+ echo Failed download: download completed, but downloaded file not found > "&2"
+ set DOWNLOAD_ERROR_STATUS=2
+ ) else (
+ echo Download via PowerShell succeeded.
+ )
+ )
+
+ @if NOT %DOWNLOAD_ERROR_STATUS%==0 (
+ @echo Warning: Failed to download "%REMOTE_SOURCE_MSI_URL%" to "%LOCAL_DESTINATION_MSI_PATH%"
+ @echo Warning: Retrying download with cscript ...
+
+ @if EXIST "%LOCAL_DESTINATION_MSI_PATH%" del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
+
+ @set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%') %>"
+ cscript /nologo <%= bootstrap_directory %>\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL%" /path:"%LOCAL_DESTINATION_MSI_PATH%"
+
+ @if NOT ERRORLEVEL 1 (
+ @rem Sometimes the error level is not set even when the download failed,
+ @rem so check for the file to be sure it is there.
+ @if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
+ echo Failed download: download completed, but downloaded file not found > "&2"
+ echo Exiting without bootstrapping due to download failure. > "&2"
+ exit /b 1
+ ) else (
+ echo Download via cscript succeeded.
+ )
+ ) else (
+ echo Failed to download "%REMOTE_SOURCE_MSI_URL%" with status code !ERRORLEVEL!. > "&2"
+ echo Exiting without bootstrapping due to download failure. > "&2"
+ exit /b 1
+ )
+ )
+
+ @echo Installing downloaded client package...
+
+ <%= install_chef %>
+
+ @if ERRORLEVEL 1 (
+ echo <%= ChefUtils::Dist::Infra::CLIENT %> package failed to install with status code !ERRORLEVEL!. > "&2"
+ echo See installation log for additional detail: %CHEF_CLIENT_MSI_LOG_PATH%. > "&2"
+ ) else (
+ @echo Installation completed successfully
+ del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
+ )
+
+<% end %>
+
+@rem This line is required to separate the key_create label from the "block boundary"
+@rem Removing these lines will cause the error "The system cannot find the batch label specified - key_create"
+:key_create
+@endlocal
+
+@echo off
+
+<% if client_pem -%>
+> <%= bootstrap_directory %>\client.pem (
+ <%= escape_and_echo(::File.read(::File.expand_path(client_pem))) %>
+)
+<% end -%>
+
+echo Writing validation key...
+
+<% if validation_key -%>
+> <%= bootstrap_directory %>\validation.pem (
+ <%= escape_and_echo(validation_key) %>
+)
+<% end -%>
+
+echo Validation key written.
+@echo on
+
+<% if secret -%>
+> <%= bootstrap_directory %>\encrypted_data_bag_secret (
+ <%= encrypted_data_bag_secret %>
+)
+<% end -%>
+
+<% unless trusted_certs_script.empty? -%>
+ @if NOT EXIST <%= bootstrap_directory %>\trusted_certs (
+ mkdir <%= bootstrap_directory %>\trusted_certs
+ )
+ )
+
+<%= trusted_certs_script %>
+<% end -%>
+
+<%# Generate Ohai Hints -%>
+<% unless @config[:hints].nil? || @config[:hints].empty? -%>
+ @if NOT EXIST <%= bootstrap_directory %>\ohai\hints (
+ mkdir <%= bootstrap_directory %>\ohai\hints
+ )
+
+<% @config[:hints].each do |name, hash| -%>
+> <%= bootstrap_directory %>\ohai\hints\<%= name %>.json (
+ <%= escape_and_echo(hash.to_json) %>
+)
+<% end -%>
+<% end -%>
+
+> <%= bootstrap_directory %>\client.rb (
+ <%= config_content %>
+)
+
+> <%= bootstrap_directory %>\first-boot.json (
+ <%= first_boot %>
+)
+
+<% unless client_d.empty? -%>
+ @if NOT EXIST <%= bootstrap_directory %>\client.d (
+ mkdir <%= bootstrap_directory %>\client.d
+ )
+
+ <%= client_d %>
+<% end -%>
+
+@echo Starting <%= ChefUtils::Dist::Infra::CLIENT %> to bootstrap the node...
+<%= start_chef %>
diff --git a/lib/chef/knife/bootstrap/train_connector.rb b/lib/chef/knife/bootstrap/train_connector.rb
new file mode 100644
index 0000000000..a220ece5bc
--- /dev/null
+++ b/lib/chef/knife/bootstrap/train_connector.rb
@@ -0,0 +1,336 @@
+# Copyright:: Copyright (c) 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 "train"
+require "tempfile" unless defined?(Tempfile)
+require "uri" unless defined?(URI)
+require "securerandom" unless defined?(SecureRandom)
+
+class Chef
+ class Knife
+ class Bootstrap < Knife
+ class TrainConnector
+ SSH_CONFIG_OVERRIDE_KEYS ||= %i{user port proxy}.freeze
+
+ MKTEMP_WIN_COMMAND ||= <<~EOM.freeze
+ $parent = [System.IO.Path]::GetTempPath();
+ [string] $name = [System.Guid]::NewGuid();
+ $tmp = New-Item -ItemType Directory -Path (Join-Path $parent $name);
+ $tmp.FullName
+ EOM
+
+ DEFAULT_REMOTE_TEMP ||= "/tmp".freeze
+
+ def initialize(host_url, default_protocol, opts)
+ @host_url = host_url
+ @default_protocol = default_protocol
+ @opts_in = opts
+ end
+
+ def config
+ @config ||= begin
+ uri_opts = opts_from_uri(@host_url, @default_protocol)
+ transport_config(@host_url, @opts_in.merge(uri_opts))
+ end
+ end
+
+ def connection
+ @connection ||= begin
+ Train.validate_backend(config)
+ train = Train.create(config[:backend], config)
+ # Note that the train connection is not currently connected
+ # to the remote host, but it's ready to go.
+ train.connection
+ end
+ end
+
+ #
+ # Establish a connection to the configured host.
+ #
+ # @raise [TrainError]
+ # @raise [TrainUserError]
+ #
+ # @return [TrueClass] true if the connection could be established.
+ def connect!
+ # Force connection to establish
+ connection.wait_until_ready
+ true
+ end
+
+ #
+ # @return [String] the configured hostname
+ def hostname
+ config[:host]
+ end
+
+ # Answers the question, "is this connection configured for password auth?"
+ # @return [Boolean] true if the connection is configured with password auth
+ def password_auth?
+ config.key? :password
+ end
+
+ # Answers the question, "Am I connected to a linux host?"
+ #
+ # @return [Boolean] true if the connected host is linux.
+ def linux?
+ connection.platform.linux?
+ end
+
+ # Answers the question, "Am I connected to a unix host?"
+ #
+ # @note this will always return true for a linux host
+ # because train classifies linux as a unix
+ #
+ # @return [Boolean] true if the connected host is unix or linux
+ def unix?
+ connection.platform.unix?
+ end
+
+ #
+ # Answers the question, "Am I connected to a Windows host?"
+ #
+ # @return [Boolean] true if the connected host is Windows
+ def windows?
+ connection.platform.windows?
+ end
+
+ #
+ # Creates a temporary directory on the remote host if it
+ # hasn't already. Caches directory location. For *nix,
+ # it will ensure that the directory is owned by the logged-in user
+ #
+ # @return [String] the temporary path created on the remote host.
+ def temp_dir
+ @tmpdir ||= begin
+ if windows?
+ run_command!(MKTEMP_WIN_COMMAND).stdout.split.last
+ else
+ # Get a 6 chars string using secure random
+ # eg. /tmp/chef_XXXXXX.
+ # Use mkdir to create TEMP dir to get rid of mktemp
+ dir = "#{DEFAULT_REMOTE_TEMP}/chef_#{SecureRandom.alphanumeric(6)}"
+ run_command!("mkdir -p '#{dir}'")
+ # Ensure that dir has the correct owner. We are possibly
+ # running with sudo right now - so this directory would be owned by root.
+ # File upload is performed over SCP as the current logged-in user,
+ # so we'll set ownership to ensure that works.
+ run_command!("chown #{config[:user]} '#{dir}'") if config[:sudo]
+
+ dir
+ end
+ end
+ end
+
+ #
+ # Uploads a file from "local_path" to "remote_path"
+ #
+ # @param local_path [String] The path to a file on the local file system
+ # @param remote_path [String] The destination path on the remote file system.
+ # @return NilClass
+ def upload_file!(local_path, remote_path)
+ connection.upload(local_path, remote_path)
+ nil
+ end
+
+ #
+ # Uploads the provided content into the file "remote_path" on the remote host.
+ #
+ # @param content [String] The content to upload into remote_path
+ # @param remote_path [String] The destination path on the remote file system.
+ # @return NilClass
+ def upload_file_content!(content, remote_path)
+ t = Tempfile.new("chef-content")
+ t.binmode
+ t << content
+ t.close
+ upload_file!(t.path, remote_path)
+ nil
+ ensure
+ t.close
+ t.unlink
+ end
+
+ #
+ # Force-deletes the file at "path" from the remote host.
+ #
+ # @param path [String] The path of the file on the remote host
+ def del_file!(path)
+ if windows?
+ run_command!("If (Test-Path \"#{path}\") { Remove-Item -Force -Path \"#{path}\" }")
+ else
+ run_command!("rm -f \"#{path}\"")
+ end
+ nil
+ end
+
+ #
+ # normalizes path across OS's - always use forward slashes, which
+ # Windows and *nix understand.
+ #
+ # @param path [String] The path to normalize
+ #
+ # @return [String] the normalized path
+ def normalize_path(path)
+ path.tr("\\", "/")
+ end
+
+ #
+ # Runs a command on the remote host.
+ #
+ # @param command [String] The command to run.
+ # @param data_handler [Proc] An optional block. When provided, inbound data will be
+ # published via `data_handler.call(data)`. This can allow
+ # callers to receive and render updates from remote command execution.
+ #
+ # @return [Train::Extras::CommandResult] an object containing stdout, stderr, and exit_status
+ def run_command(command, &data_handler)
+ connection.run_command(command, &data_handler)
+ end
+
+ #
+ # Runs a command the remote host
+ #
+ # @param command [String] The command to run.
+ # @param data_handler [Proc] An optional block. When provided, inbound data will be
+ # published via `data_handler.call(data)`. This can allow
+ # callers to receive and render updates from remote command execution.
+ #
+ # @raise Chef::Knife::Bootstrap::RemoteExecutionFailed if an error occurs (non-zero exit status)
+ # @return [Train::Extras::CommandResult] an object containing stdout, stderr, and exit_status
+ def run_command!(command, &data_handler)
+ result = run_command(command, &data_handler)
+ if result.exit_status != 0
+ raise RemoteExecutionFailed.new(hostname, command, result)
+ end
+
+ result
+ end
+
+ private
+
+ # For a given url and set of options, create a config
+ # hash suitable for passing into train.
+ def transport_config(host_url, opts_in)
+ # These baseline opts are not protocol-specific
+ opts = { target: host_url,
+ www_form_encoded_password: true,
+ transport_retries: 2,
+ transport_retry_sleep: 1,
+ backend: opts_in[:backend],
+ logger: opts_in[:logger] }
+
+ # Accepts options provided by caller if they're not already configured,
+ # but note that they will be constrained to valid options for the backend protocol
+ opts.merge!(opts_from_caller(opts, opts_in))
+
+ # WinRM has some additional computed options
+ opts.merge!(opts_inferred_from_winrm(opts, opts_in))
+
+ # Now that everything is populated, fill in anything missing
+ # that may be found in user ssh config
+ opts.merge!(missing_opts_from_ssh_config(opts, opts_in))
+
+ Train.target_config(opts)
+ end
+
+ # Some winrm options are inferred based on other options.
+ # Return a hash of winrm options based on configuration already built.
+ def opts_inferred_from_winrm(config, opts_in)
+ return {} unless config[:backend] == "winrm"
+
+ opts_out = {}
+
+ if opts_in[:ssl]
+ opts_out[:ssl] = true
+ opts_out[:self_signed] = opts_in[:self_signed] || false
+ end
+
+ # See note here: https://github.com/mwrock/WinRM#example
+ if %w{ssl plaintext}.include?(opts_in[:winrm_auth_method])
+ opts_out[:winrm_disable_sspi] = true
+ end
+ opts_out
+ end
+
+ # Returns a hash containing valid options for the current
+ # transport protocol that are not already present in config
+ def opts_from_caller(config, opts_in)
+ # Train.options gives us the supported config options for the
+ # backend provider (ssh, winrm). We'll use that
+ # to filter out options that don't belong
+ # to the transport type we're using.
+ valid_opts = Train.options(config[:backend])
+ opts_in.select do |key, _v|
+ valid_opts.key?(key) && !config.key?(key)
+ end
+ end
+
+ # Extract any of username/password/host/port/transport
+ # that are in the URI and return them as a config has
+ def opts_from_uri(uri, default_protocol)
+ # Train.unpack_target_from_uri only works for complete URIs in
+ # form of proto://[user[:pass]@]host[:port]/
+ # So we'll add the protocol prefix if it's not supplied.
+ uri_to_check = if URI::DEFAULT_PARSER.make_regexp.match(uri)
+ uri
+ else
+ "#{default_protocol}://#{uri}"
+ end
+
+ Train.unpack_target_from_uri(uri_to_check)
+ end
+
+ # This returns a hash that consists of settings
+ # populated from SSH configuration that are not already present
+ # in the configuration passed in.
+ # This is necessary because train will default these values
+ # itself - causing SSH config data to be ignored
+ def missing_opts_from_ssh_config(config, opts_in)
+ return {} unless config[:backend] == "ssh"
+
+ host_cfg = ssh_config_for_host(config[:host])
+ opts_out = {}
+ opts_in.each do |key, _value|
+ if SSH_CONFIG_OVERRIDE_KEYS.include?(key) && !config.key?(key)
+ opts_out[key] = host_cfg[key]
+ end
+ end
+ opts_out
+ end
+
+ # Having this as a method makes it easier to mock
+ # SSH Config for testing.
+ def ssh_config_for_host(host)
+ require "net/ssh" unless defined?(Net::SSH)
+ Net::SSH::Config.for(host)
+ end
+ end
+
+ class RemoteExecutionFailed < StandardError
+ attr_reader :exit_status, :command, :hostname, :stdout, :stderr
+
+ def initialize(hostname, command, result)
+ @hostname = hostname
+ @exit_status = result.exit_status
+ @stderr = result.stderr
+ @stdout = result.stdout
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/knife/client_bulk_delete.rb b/lib/chef/knife/client_bulk_delete.rb
index a7fa7142c8..38d25583b3 100644
--- a/lib/chef/knife/client_bulk_delete.rb
+++ b/lib/chef/knife/client_bulk_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,21 +16,20 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class ClientBulkDelete < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
option :delete_validators,
- :short => "-D",
- :long => "--delete-validators",
- :description => "Force deletion of clients if they're validators"
+ short: "-D",
+ long: "--delete-validators",
+ description: "Force deletion of clients if they're validators."
banner "knife client bulk delete REGEX (options)"
@@ -45,7 +44,8 @@ class Chef
clients_to_delete = {}
validators_to_delete = {}
all_clients.each do |name, client|
- next unless name =~ matcher
+ next unless name&.match?(matcher)
+
if client.validator
validators_to_delete[client.name] = client
else
diff --git a/lib/chef/knife/client_create.rb b/lib/chef/knife/client_create.rb
index e28378cd4a..d6e0eab63b 100644
--- a/lib/chef/knife/client_create.rb
+++ b/lib/chef/knife/client_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,43 +16,37 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
class ClientCreate < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file if the server generated one."
-
- option :admin,
- :short => "-a",
- :long => "--admin",
- :description => "Open Source Chef Server 11 only. Create the client as an admin.",
- :boolean => true
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the private key to a file if the #{ChefUtils::Dist::Server::PRODUCT} generated one."
option :validator,
- :long => "--validator",
- :description => "Create the client as a validator.",
- :boolean => true
+ long: "--validator",
+ description: "Create the client as a validator.",
+ boolean: true
option :public_key,
- :short => "-p FILE",
- :long => "--public-key",
- :description => "Set the initial default key for the client from a file on disk (cannot pass with --prevent-keygen)."
+ short: "-p FILE",
+ long: "--public-key",
+ description: "Set the initial default key for the client from a file on disk (cannot pass with --prevent-keygen)."
option :prevent_keygen,
- :short => "-k",
- :long => "--prevent-keygen",
- :description => "API V1 (Chef Server 12.1+) only. Prevent server from generating a default key pair for you. Cannot be passed with --public-key.",
- :boolean => true
+ short: "-k",
+ long: "--prevent-keygen",
+ description: "Prevent #{ChefUtils::Dist::Server::PRODUCT} from generating a default key pair for you. Cannot be passed with --public-key.",
+ boolean: true
banner "knife client create CLIENTNAME (options)"
@@ -79,10 +73,6 @@ class Chef
client.create_key(true)
end
- if config[:admin]
- client.admin(true)
- end
-
if config[:validator]
client.validator(true)
end
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index 08cdf6c7dd..3ecfa38242 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,45 +16,47 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class ClientDelete < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
option :delete_validators,
- :short => "-D",
- :long => "--delete-validators",
- :description => "Force deletion of client if it's a validator"
+ short: "-D",
+ 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") do
- 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_edit.rb b/lib/chef/knife/client_edit.rb
index 948d43cc67..f89f5e38ec 100644
--- a/lib/chef/knife/client_edit.rb
+++ b/lib/chef/knife/client_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class ClientEdit < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
banner "knife client edit CLIENT (options)"
@@ -38,7 +37,7 @@ class Chef
exit 1
end
- original_data = Chef::ApiClientV1.load(@client_name).to_hash
+ original_data = Chef::ApiClientV1.load(@client_name).to_h
edited_client = edit_hash(original_data)
if original_data != edited_client
client = Chef::ApiClientV1.from_hash(edited_client)
diff --git a/lib/chef/knife/client_key_create.rb b/lib/chef/knife/client_key_create.rb
index 68ad4d16d2..192d724473 100644
--- a/lib/chef/knife/client_key_create.rb
+++ b/lib/chef/knife/client_key_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_create_base"
+require_relative "../knife"
+require_relative "key_create_base"
class Chef
class Knife
@@ -30,6 +30,12 @@ class Chef
class ClientKeyCreate < Knife
include Chef::Knife::KeyCreateBase
+ banner "knife client key create CLIENT (options)"
+
+ deps do
+ require_relative "key_create"
+ end
+
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..2d486ffcbd 100644
--- a/lib/chef/knife/client_key_delete.rb
+++ b/lib/chef/knife/client_key_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -29,6 +29,10 @@ class Chef
class ClientKeyDelete < Knife
banner "knife client key delete CLIENT KEYNAME (options)"
+ deps do
+ require_relative "key_delete"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/client_key_edit.rb b/lib/chef/knife/client_key_edit.rb
index 1dbd3c487b..d178aafc17 100644
--- a/lib/chef/knife/client_key_edit.rb
+++ b/lib/chef/knife/client_key_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_edit_base"
+require_relative "../knife"
+require_relative "key_edit_base"
class Chef
class Knife
@@ -32,6 +32,10 @@ class Chef
banner "knife client key edit CLIENT KEYNAME (options)"
+ deps do
+ require_relative "key_edit"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/client_key_list.rb b/lib/chef/knife/client_key_list.rb
index 194ad42931..afc04335d9 100644
--- a/lib/chef/knife/client_key_list.rb
+++ b/lib/chef/knife/client_key_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_list_base"
+require_relative "../knife"
+require_relative "key_list_base"
class Chef
class Knife
@@ -32,6 +32,10 @@ class Chef
banner "knife client key list CLIENT (options)"
+ deps do
+ require_relative "key_list"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/client_key_show.rb b/lib/chef/knife/client_key_show.rb
index 77f9e96c5a..14e1f0ca7a 100644
--- a/lib/chef/knife/client_key_show.rb
+++ b/lib/chef/knife/client_key_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -29,6 +29,10 @@ class Chef
class ClientKeyShow < Knife
banner "knife client key show CLIENT KEYNAME (options)"
+ deps do
+ require_relative "key_show"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/client_list.rb b/lib/chef/knife/client_list.rb
index b17de0f3ad..b4fc46767b 100644
--- a/lib/chef/knife/client_list.rb
+++ b/lib/chef/knife/client_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class ClientList < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
banner "knife client list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
output(format_list_for_display(Chef::ApiClientV1.list))
diff --git a/lib/chef/knife/client_reregister.rb b/lib/chef/knife/client_reregister.rb
index 5d9b2c0962..6741895b23 100644
--- a/lib/chef/knife/client_reregister.rb
+++ b/lib/chef/knife/client_reregister.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class ClientReregister < Knife
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
banner "knife client reregister CLIENT (options)"
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the key to a file"
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the key to a file."
def run
@client_name = @name_args[0]
@@ -44,7 +43,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/client_show.rb b/lib/chef/knife/client_show.rb
index ce3bf458b2..9170c73085 100644
--- a/lib/chef/knife/client_show.rb
+++ b/lib/chef/knife/client_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -25,8 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/api_client_v1"
- require "chef/json_compat"
+ require_relative "../api_client_v1"
end
banner "knife client show CLIENT (options)"
diff --git a/lib/chef/knife/config_get.rb b/lib/chef/knife/config_get.rb
new file mode 100644
index 0000000000..91e6b7affd
--- /dev/null
+++ b/lib/chef/knife/config_get.rb
@@ -0,0 +1,39 @@
+#
+# Author:: Joshua Timberman <opensource@housepub.org>
+# Copyright:: Copyright (c) 2012, Joshua Timberman
+# Copyright:: Copyright (c) 2018, 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_relative "../knife"
+require_relative "./config_show"
+
+class Chef
+ class Knife
+ class ConfigGet < ConfigShow
+
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+
+ banner "knife config get [OPTION...] (options)\nDisplays the value of Chef::Config[OPTION] (or all config values)"
+ category "deprecated"
+
+ def run
+ Chef::Log.warn("knife config get has been deprecated in favor of knife config show. This will be removed in the major release version!")
+ super
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/config_get_profile.rb b/lib/chef/knife/config_get_profile.rb
new file mode 100644
index 0000000000..a355c531fe
--- /dev/null
+++ b/lib/chef/knife/config_get_profile.rb
@@ -0,0 +1,37 @@
+#
+# Copyright:: Copyright (c) 2018, 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_relative "../knife"
+require_relative "./config_use"
+
+class Chef
+ class Knife
+ class ConfigGetProfile < ConfigUse
+
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+
+ banner "knife config get-profile"
+ category "deprecated"
+
+ def run
+ Chef::Log.warn("knife config get-profiles has been deprecated in favor of knife config use. This will be removed in the major release version!")
+ super
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/config_list.rb b/lib/chef/knife/config_list.rb
new file mode 100644
index 0000000000..c9f821e2a8
--- /dev/null
+++ b/lib/chef/knife/config_list.rb
@@ -0,0 +1,139 @@
+#
+# Copyright:: Copyright (c) 2018, 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_relative "../knife"
+
+class Chef
+ class Knife
+ class ConfigList < Knife
+ banner "knife config list (options)"
+
+ TABLE_HEADER ||= [" Profile", "Client", "Key", "Server"].freeze
+
+ deps do
+ require_relative "../workstation_config_loader"
+ require "tty-screen" unless defined?(TTY::Screen)
+ require "tty-table" unless defined?(TTY::Table)
+ end
+
+ option :ignore_knife_rb,
+ short: "-i",
+ long: "--ignore-knife-rb",
+ description: "Ignore the current config.rb/knife.rb configuration.",
+ default: false
+
+ def configure_chef
+ apply_computed_config
+ end
+
+ def run
+ credentials_data = self.class.config_loader.parse_credentials_file
+ if credentials_data.nil? || credentials_data.empty?
+ # Should this just show the ambient knife.rb config as "default" instead?
+ ui.fatal("No profiles found, #{self.class.config_loader.credentials_file_path} does not exist or is empty")
+ exit 1
+ end
+
+ current_profile = self.class.config_loader.credentials_profile(config[:profile])
+ profiles = credentials_data.keys.map do |profile|
+ if config[:ignore_knife_rb]
+ # Don't do any fancy loading nonsense, just the raw data.
+ profile_data = credentials_data[profile]
+ {
+ profile: profile,
+ active: profile == current_profile,
+ client_name: profile_data["client_name"] || profile_data["node_name"],
+ client_key: profile_data["client_key"],
+ server_url: profile_data["chef_server_url"],
+ }
+ else
+ # Fancy loading nonsense so we get what the actual config would be.
+ # Note that this modifies the global config, after this, all bets are
+ # off as to whats in the config.
+ Chef::Config.reset
+ wcl = Chef::WorkstationConfigLoader.new(nil, Chef::Log, profile: profile)
+ wcl.load
+ {
+ profile: profile,
+ active: profile == current_profile,
+ client_name: Chef::Config[:node_name],
+ client_key: Chef::Config[:client_key],
+ server_url: Chef::Config[:chef_server_url],
+ }
+ end
+ end
+
+ # Try to reset the config.
+ unless config[:ignore_knife_rb]
+ Chef::Config.reset
+ apply_computed_config
+ end
+
+ if ui.interchange?
+ # Machine-readable output.
+ ui.output(profiles)
+ else
+ # Table output.
+ ui.output(render_table(profiles))
+ end
+ end
+
+ private
+
+ def render_table(profiles, padding: 1)
+ rows = []
+ # Render the data to a 2D array that will be used for the table.
+ profiles.each do |profile|
+ # Replace the home dir in the client key path with ~.
+ profile[:client_key] = profile[:client_key].to_s.gsub(/^#{Regexp.escape(Dir.home)}/, "~") if profile[:client_key]
+ profile[:profile] = "#{profile[:active] ? "*" : " "}#{profile[:profile]}"
+ rows << profile.values_at(:profile, :client_name, :client_key, :server_url)
+ end
+
+ table = TTY::Table.new(header: TABLE_HEADER, rows: rows)
+
+ # Rotate the table to vertical if the screen width is less than table width.
+ if table.width > TTY::Screen.width
+ table.orientation = :vertical
+ table.rotate
+ # Add a new line after each profile record.
+ table.render do |renderer|
+ renderer.border do
+ separator ->(row) { (row + 1) % TABLE_HEADER.size == 0 }
+ end
+ # Remove the leading space added of the first column.
+ renderer.filter = Proc.new do |val, row_index, col_index|
+ if col_index == 1 || (row_index) % TABLE_HEADER.size == 0
+ val.strip
+ else
+ val
+ end
+ end
+ end
+ else
+ table.render do |renderer|
+ renderer.border do
+ mid "-"
+ end
+ renderer.padding = [0, padding, 0, 0] # pad right with 2 characters
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/knife/config_list_profiles.rb b/lib/chef/knife/config_list_profiles.rb
new file mode 100644
index 0000000000..c037b0de53
--- /dev/null
+++ b/lib/chef/knife/config_list_profiles.rb
@@ -0,0 +1,37 @@
+#
+# Copyright:: Copyright (c) 2018, 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_relative "../knife"
+require_relative "./config_list"
+
+class Chef
+ class Knife
+ class ConfigListProfiles < ConfigList
+
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+
+ banner "knife config list-profiles (options)"
+ category "deprecated"
+
+ def run
+ Chef::Log.warn("knife config list-profiles has been deprecated in favor of knife config list. This will be removed in the major release version!")
+ super
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/config_show.rb b/lib/chef/knife/config_show.rb
new file mode 100644
index 0000000000..7f28891885
--- /dev/null
+++ b/lib/chef/knife/config_show.rb
@@ -0,0 +1,127 @@
+#
+# Author:: Vivek Singh (<vsingh@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class ConfigShow < Knife
+ banner "knife config show [OPTION...] (options)\nDisplays the value of Chef::Config[OPTION] (or all config values)"
+
+ option :all,
+ short: "-a",
+ long: "--all",
+ description: "Include options that are not set in the configuration.",
+ default: false
+
+ option :raw,
+ short: "-r",
+ long: "--raw",
+ description: "Display a each value with no formatting.",
+ default: false
+
+ def run
+ if config[:format] == "summary" && !config[:raw]
+ # If using the default, human-readable output, also show which config files are being loaded.
+ # Some of this is a bit hacky since it duplicates
+ wcl = self.class.config_loader
+ if wcl.credentials_found
+ loading_from("credentials", ChefConfig::PathHelper.home(".chef", "credentials"))
+ end
+ if wcl.config_location
+ loading_from("configuration", wcl.config_location)
+ end
+
+ if Chef::Config[:config_d_dir]
+ wcl.find_dot_d(Chef::Config[:config_d_dir]).each do |path|
+ loading_from(".d/ configuration", path)
+ end
+ end
+ end
+
+ # Dump the whole config, including defaults is --all was given.
+ config_data = Chef::Config.save(config[:all])
+ # Two special cases, these are set during knife startup but we don't usually care about them.
+ unless config[:all]
+ config_data.delete(:color)
+ # Only keep these if true, false is much less important because it's the default.
+ config_data.delete(:local_mode) unless config_data[:local_mode]
+ config_data.delete(:enforce_default_paths) unless config_data[:enforce_default_paths]
+ config_data.delete(:enforce_path_sanity) unless config_data[:enforce_path_sanity]
+ end
+
+ # Extract the data to show.
+ output_data = {}
+ if @name_args.empty?
+ output_data = config_data
+ else
+ @name_args.each do |filter|
+ if filter =~ %r{^/(.*)/(i?)$}
+ # It's a regex.
+ filter_re = Regexp.new($1, $2 ? Regexp::IGNORECASE : 0)
+ config_data.each do |key, value|
+ output_data[key] = value if key.to_s&.match?(filter_re)
+ end
+ else
+ # It's a dotted path string.
+ filter_parts = filter.split(".")
+ extract = lambda do |memo, filter_part|
+ memo.is_a?(Hash) ? memo[filter_part.to_sym] : nil
+ end
+ # Check against both config_data and all of the data, so that even
+ # in non-all mode, if you ask for a key that isn't in the non-all
+ # data, it will check against the broader set.
+ output_data[filter] = filter_parts.inject(config_data, &extract) || filter_parts.inject(Chef::Config.save(true), &extract)
+ end
+ end
+ end
+
+ # Fix up some values.
+ output_data.each do |key, value|
+ if value == STDOUT
+ output_data[key] = "STDOUT"
+ elsif value == STDERR
+ output_data[key] = "STDERR"
+ end
+ end
+
+ # Show the data.
+ if config[:raw]
+ output_data.each_value do |value|
+ ui.msg(value)
+ end
+ else
+ ui.output(output_data)
+ end
+ end
+
+ private
+
+ # Display a banner about loading from a config file.
+ #
+ # @api private
+ # @param type_of_file [String] Description of the file for the banner.
+ # @param path [String] Path of the file.
+ # @return [nil]
+ def loading_from(type_of_file, path)
+ path = Pathname.new(path).realpath
+ ui.msg(ui.color("Loading from #{type_of_file} file #{path}", :yellow))
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/config_use.rb b/lib/chef/knife/config_use.rb
new file mode 100644
index 0000000000..e944dc210b
--- /dev/null
+++ b/lib/chef/knife/config_use.rb
@@ -0,0 +1,61 @@
+#
+# Author:: Vivek Singh (<vsingh@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class ConfigUse < Knife
+ banner "knife config use [PROFILE]"
+
+ deps do
+ require "fileutils" unless defined?(FileUtils)
+ end
+
+ # Disable normal config loading since this shouldn't fail if the profile
+ # doesn't exist of the config is otherwise corrupted.
+ def configure_chef
+ apply_computed_config
+ end
+
+ def run
+ profile = @name_args[0]&.strip
+ if profile.nil? || profile.empty?
+ ui.msg(self.class.config_loader.credentials_profile(config[:profile]))
+ else
+ credentials_data = self.class.config_loader.parse_credentials_file
+ context_file = ChefConfig::PathHelper.home(".chef", "context").freeze
+
+ if credentials_data.nil? || credentials_data.empty?
+ ui.fatal("No profiles found, #{self.class.config_loader.credentials_file_path} does not exist or is empty")
+ exit 1
+ end
+
+ if credentials_data[profile].nil?
+ raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{self.class.config_loader.credentials_file_path} and if it is profile with DNS name check that you are not missing single quotes around it as per docs https://docs.chef.io/workstation/knife_setup/#knife-profiles."
+ else
+ # Ensure the .chef/ folder exists.
+ FileUtils.mkdir_p(File.dirname(context_file))
+ IO.write(context_file, "#{profile}\n")
+ ui.msg("Set default profile to #{profile}")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/config_use_profile.rb b/lib/chef/knife/config_use_profile.rb
new file mode 100644
index 0000000000..169bdbef30
--- /dev/null
+++ b/lib/chef/knife/config_use_profile.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright (c) 2018, 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_relative "../knife"
+require_relative "./config_use"
+
+class Chef
+ class Knife
+ class ConfigUseProfile < ConfigUse
+
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+
+ banner "knife config use-profile PROFILE"
+ category "deprecated"
+
+ def run
+ Chef::Log.warn("knife config use-profile has been deprecated in favor of knife config use. This will be removed in the major release version!")
+
+ credentials_data = self.class.config_loader.parse_credentials_file
+ context_file = ChefConfig::PathHelper.home(".chef", "context").freeze
+ profile = @name_args[0]&.strip
+ if profile.nil? || profile.empty?
+ show_usage
+ ui.fatal("You must specify a profile")
+ exit 1
+ end
+
+ super
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/configure.rb b/lib/chef/knife/configure.rb
index e726e32684..2a27fd5d88 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
@@ -25,7 +26,10 @@ class Chef
attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key
deps do
- require "ohai"
+ require_relative "../util/path_helper"
+ require_relative "client_create"
+ require_relative "user_create"
+ require "ohai" unless defined?(Ohai::System)
Chef::Knife::ClientCreate.load_deps
Chef::Knife::UserCreate.load_deps
end
@@ -33,31 +37,31 @@ class Chef
banner "knife configure (options)"
option :repository,
- :short => "-r REPO",
- :long => "--repository REPO",
- :description => "The path to the chef-repo"
+ short: "-r REPO",
+ long: "--repository REPO",
+ description: "The path to the chef-repo."
option :initial,
- :short => "-i",
- :long => "--initial",
- :boolean => true,
- :description => "Use to create a API client, typically an administrator client on a freshly-installed server"
+ short: "-i",
+ long: "--initial",
+ boolean: true,
+ description: "Use to create a API client, typically an administrator client on a freshly-installed server."
option :admin_client_name,
- :long => "--admin-client-name NAME",
- :description => "The name of the client, typically the name of the admin client"
+ long: "--admin-client-name NAME",
+ description: "The name of the client, typically the name of the admin client."
option :admin_client_key,
- :long => "--admin-client-key PATH",
- :description => "The path to the private key used by the client, typically a file named admin.pem"
+ long: "--admin-client-key PATH",
+ description: "The path to the private key used by the client, typically a file named admin.pem."
option :validation_client_name,
- :long => "--validation-client-name NAME",
- :description => "The name of the validation client, typically a client named chef-validator"
+ long: "--validation-client-name NAME",
+ description: "The name of the validation client, typically a client named chef-validator."
option :validation_key,
- :long => "--validation-key PATH",
- :description => "The path to the validation key used by the client, typically a file named validation.pem"
+ long: "--validation-key PATH",
+ description: "The path to the validation key used by the client, typically a file named validation.pem."
def configure_chef
# We are just faking out the system so that you can do this without a key specified
@@ -67,26 +71,19 @@ class Chef
end
def run
- ask_user_for_config_path
-
FileUtils.mkdir_p(chef_config_path)
ask_user_for_config
- ::File.open(config[: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")}'
-EOH
- unless chef_repo.empty?
- f.puts "cookbook_path [ '#{chef_repo}/cookbooks' ]"
- end
+ confirm("Overwrite #{config_file_path}") if ::File.exist?(config_file_path)
+
+ ::File.open(config_file_path, "w") do |f|
+ f.puts <<~EOH
+ [default]
+ client_name = '#{new_client_name}'
+ client_key = '#{new_client_key}'
+ chef_server_url = '#{chef_server}'
+ EOH
end
if config[:initial]
@@ -97,7 +94,7 @@ EOH
user_create = Chef::Knife::UserCreate.new
user_create.name_args = [ new_client_name ]
user_create.config[:user_password] = config[:user_password] ||
- ui.ask("Please enter a password for the new user: ") { |q| q.echo = false }
+ ui.ask("Please enter a password for the new user: ", echo: false)
user_create.config[:admin] = true
user_create.config[:file] = new_client_key
user_create.config[:yes] = true
@@ -111,60 +108,42 @@ 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
+ ui.msg("Knife configuration file written to #{config_file_path}")
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")
- @admin_client_key = config[:admin_client_key] || ask_question("Please enter the location of the existing admin's private key: ", :default => "/etc/chef-server/admin.pem")
+ @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")
+ @admin_client_key = config[:admin_client_key] || ask_question("Please enter the location of the existing admin's private key: ", default: "#{ChefUtils::Dist::Server::CONF_DIR}/admin.pem")
@admin_client_key = File.expand_path(@admin_client_key)
else
- @new_client_name = config[:node_name] || ask_question("Please enter an existing username or clientname for the API: ", :default => Etc.getlogin)
+ @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)
end
+ # @return [String] our best guess at what the servername should be using Ohai data and falling back to localhost
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]
+ # @return [String] the path to the user's .chef directory
+ def chef_config_path
+ @chef_config_path ||= Chef::Util::PathHelper.home(".chef")
end
- def chef_config_path
- File.dirname(config_file)
+ # @return [String] the full path to the config file (credential file)
+ def config_file_path
+ @config_file_path ||= ::File.expand_path(::File.join(chef_config_path, "credentials"))
end
end
end
diff --git a/lib/chef/knife/configure_client.rb b/lib/chef/knife/configure_client.rb
index 7d0b3d260d..c6f159ec8f 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 (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -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 cdd1584e36..d6657ccb4f 100644
--- a/lib/chef/knife/cookbook_bulk_delete.rb
+++ b/lib/chef/knife/cookbook_bulk_delete.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class CookbookBulkDelete < Knife
deps do
- require "chef/knife/cookbook_delete"
- require "chef/cookbook_version"
+ require_relative "cookbook_delete"
+ require_relative "../cookbook_version"
end
- option :purge, :short => "-p", :long => "--purge", :boolean => true, :description => "Permanently remove files from backing data store"
+ option :purge, short: "-p", long: "--purge", boolean: true, description: "Permanently remove files from backing data store."
banner "knife cookbook bulk delete REGEX (options)"
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
deleted file mode 100644
index ccb78bb7a6..0000000000
--- a/lib/chef/knife/cookbook_create.rb
+++ /dev/null
@@ -1,462 +0,0 @@
-#
-# Author:: Nuo Yan (<nuo@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 "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
- Chef::Log.deprecation <<EOF
-This command is being deprecated in favor of `chef generate cookbook` and will soon return an error.
-Please use `chef generate cookbook` instead of this command.
-EOF
- 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
- 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_delete.rb b/lib/chef/knife/cookbook_delete.rb
index b1bb88b388..04ecb95cf4 100644
--- a/lib/chef/knife/cookbook_delete.rb
+++ b/lib/chef/knife/cookbook_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -25,12 +25,12 @@ class Chef
attr_accessor :cookbook_name, :version
deps do
- require "chef/cookbook_version"
+ require_relative "../cookbook_version"
end
- option :all, :short => "-a", :long => "--all", :boolean => true, :description => "delete all versions"
+ option :all, short: "-a", long: "--all", boolean: true, description: "Delete all versions of the cookbook."
- option :purge, :short => "-p", :long => "--purge", :boolean => true, :description => "Permanently remove files from backing data store"
+ option :purge, short: "-p", long: "--purge", boolean: true, description: "Permanently remove files from backing data store."
banner "knife cookbook delete COOKBOOK VERSION (options)"
@@ -45,7 +45,7 @@ class Chef
delete_without_explicit_version
elsif @cookbook_name.nil?
show_usage
- ui.fatal("You must provide the name of the cookbook to delete")
+ ui.fatal("You must provide the name of the cookbook to delete.")
exit(1)
end
end
@@ -88,9 +88,9 @@ class Chef
@available_versions ||= rest.get("cookbooks/#{@cookbook_name}").map do |name, url_and_version|
url_and_version["versions"].map { |url_by_version| url_by_version["version"] }
end.flatten
- rescue Net::HTTPServerException => e
- if e.to_s =~ /^404/
- ui.error("Cannot find a cookbook named #{@cookbook_name} to delete")
+ rescue Net::HTTPClientException => e
+ if /^404/.match?(e.to_s)
+ ui.error("Cannot find a cookbook named #{@cookbook_name} to delete.")
nil
else
raise
@@ -106,7 +106,7 @@ class Chef
end
valid_responses[(available_versions.size + 1).to_s] = :all
question << "#{available_versions.size + 1}. All versions\n\n"
- responses = ask_question(question).split(",").map { |response| response.strip }
+ responses = ask_question(question).split(",").map(&:strip)
if responses.empty?
ui.error("No versions specified, exiting")
diff --git a/lib/chef/knife/cookbook_download.rb b/lib/chef/knife/cookbook_download.rb
index 741f444093..a07b519511 100644
--- a/lib/chef/knife/cookbook_download.rb
+++ b/lib/chef/knife/cookbook_download.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -27,27 +27,27 @@ class Chef
attr_accessor :cookbook_name
deps do
- require "chef/cookbook_version"
+ require_relative "../cookbook_version"
end
banner "knife cookbook download COOKBOOK [VERSION] (options)"
option :latest,
- :short => "-N",
- :long => "--latest",
- :description => "The version of the cookbook to download",
- :boolean => true
+ short: "-N",
+ long: "--latest",
+ description: "The version of the cookbook to download.",
+ boolean: true
option :download_directory,
- :short => "-d DOWNLOAD_DIRECTORY",
- :long => "--dir DOWNLOAD_DIRECTORY",
- :description => "The directory to download the cookbook into",
- :default => Dir.pwd
+ short: "-d DOWNLOAD_DIRECTORY",
+ long: "--dir DOWNLOAD_DIRECTORY",
+ description: "The directory to download the cookbook into.",
+ default: Dir.pwd
option :force,
- :short => "-f",
- :long => "--force",
- :description => "Force download over the download directory if it exists"
+ short: "-f",
+ long: "--force",
+ description: "Force download over the download directory if it exists."
# TODO: tim/cw: 5-23-2010: need to implement knife-side
# specificity for downloads - need to implement --platform and
@@ -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 File.exist?(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_list.rb b/lib/chef/knife/cookbook_list.rb
index ea81f5d286..719c10f893 100644
--- a/lib/chef/knife/cookbook_list.rb
+++ b/lib/chef/knife/cookbook_list.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -26,14 +26,14 @@ class Chef
banner "knife cookbook list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
option :all_versions,
- :short => "-a",
- :long => "--all",
- :description => "Show all available versions."
+ short: "-a",
+ long: "--all",
+ description: "Show all available versions."
def run
env = config[:environment]
diff --git a/lib/chef/knife/cookbook_metadata.rb b/lib/chef/knife/cookbook_metadata.rb
index 29eba6a36a..8d8970b1c1 100644
--- a/lib/chef/knife/cookbook_metadata.rb
+++ b/lib/chef/knife/cookbook_metadata.rb
@@ -1,7 +1,6 @@
#
-#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,29 +16,29 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class CookbookMetadata < Knife
deps do
- require "chef/cookbook_loader"
- require "chef/cookbook/metadata"
+ require_relative "../cookbook_loader"
+ require_relative "../cookbook/metadata"
end
banner "knife cookbook metadata COOKBOOK (options)"
option :cookbook_path,
- :short => "-o PATH:PATH",
- :long => "--cookbook-path PATH:PATH",
- :description => "A colon-separated path to look for cookbooks in",
- :proc => lambda { |o| o.split(":") }
+ short: "-o PATH:PATH",
+ long: "--cookbook-path PATH:PATH",
+ description: "A colon-separated path to look for cookbooks in.",
+ proc: lambda { |o| o.split(":") }
option :all,
- :short => "-a",
- :long => "--all",
- :description => "Generate metadata for all cookbooks, rather than just a single cookbook"
+ short: "-a",
+ long: "--all",
+ description: "Generate metadata for all cookbooks, rather than just a single cookbook."
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
@@ -47,7 +46,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
@@ -63,7 +62,7 @@ class Chef
def generate_metadata(cookbook)
Array(config[:cookbook_path]).reverse_each do |path|
file = File.expand_path(File.join(path, cookbook, "metadata.rb"))
- if File.exists?(file)
+ if File.exist?(file)
generate_metadata_from_file(cookbook, file)
else
validate_metadata_json(path, cookbook)
@@ -80,7 +79,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_metadata_from_file.rb b/lib/chef/knife/cookbook_metadata_from_file.rb
index ec46379da7..d768213384 100644
--- a/lib/chef/knife/cookbook_metadata_from_file.rb
+++ b/lib/chef/knife/cookbook_metadata_from_file.rb
@@ -1,8 +1,7 @@
#
-#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2010-2016, Matthew Kent
# License:: Apache License, Version 2.0
#
@@ -19,19 +18,25 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class CookbookMetadataFromFile < Knife
deps do
- require "chef/cookbook/metadata"
+ require_relative "../cookbook/metadata"
end
- banner "knife cookbook metadata from FILE (options)"
+ banner "knife cookbook metadata from file FILE (options)"
def run
+ if @name_args.length < 1
+ show_usage
+ ui.fatal("You must specify the FILE.")
+ exit(1)
+ end
+
file = @name_args[0]
cookbook = File.basename(File.dirname(file))
diff --git a/lib/chef/knife/cookbook_show.rb b/lib/chef/knife/cookbook_show.rb
index d0c930de0a..0b97fba139 100644
--- a/lib/chef/knife/cookbook_show.rb
+++ b/lib/chef/knife/cookbook_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,39 +16,39 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class CookbookShow < Knife
deps do
- require "chef/json_compat"
- require "uri"
- require "chef/cookbook_version"
+ require_relative "../json_compat"
+ require "uri" unless defined?(URI)
+ require_relative "../cookbook_version"
end
banner "knife cookbook show COOKBOOK [VERSION] [PART] [FILENAME] (options)"
option :fqdn,
- :short => "-f FQDN",
- :long => "--fqdn FQDN",
- :description => "The FQDN of the host to see the file for"
+ short: "-f FQDN",
+ long: "--fqdn FQDN",
+ description: "The FQDN of the host to see the file for."
option :platform,
- :short => "-p PLATFORM",
- :long => "--platform PLATFORM",
- :description => "The platform to see the file for"
+ short: "-p PLATFORM",
+ long: "--platform PLATFORM",
+ description: "The platform to see the file for."
option :platform_version,
- :short => "-V VERSION",
- :long => "--platform-version VERSION",
- :description => "The platform version to see the file for"
+ short: "-V VERSION",
+ long: "--platform-version VERSION",
+ description: "The platform version to see the file for."
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
cookbook_name, cookbook_version, segment, filename = @name_args
@@ -57,14 +57,14 @@ class Chef
case @name_args.length
when 4 # We are showing a specific file
- node = Hash.new
- node[:fqdn] = config[:fqdn] if config.has_key?(:fqdn)
- node[:platform] = config[:platform] if config.has_key?(:platform)
- node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version)
+ node = {}
+ node[:fqdn] = config[:fqdn] if config.key?(:fqdn)
+ node[:platform] = config[:platform] if config.key?(:platform)
+ node[:platform_version] = config[:platform_version] if config.key?(:platform_version)
class << node
def attribute?(name) # rubocop:disable Lint/NestedMethodDefinition
- has_key?(name)
+ key?(name)
end
end
@@ -76,9 +76,13 @@ class Chef
pretty_print(temp_file.read)
when 3 # We are showing a specific part of the cookbook
- output(cookbook.manifest[segment])
- when 2 # We are showing the whole cookbook data
- output(cookbook)
+ 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)
env = config[:environment]
api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook_name}" : "cookbooks/#{cookbook_name}"
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
deleted file mode 100644
index 43677cfa78..0000000000
--- a/lib/chef/knife/cookbook_site_download.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-# Author:: Adam Jacob (<adam@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 "chef/knife"
-
-class Chef
- class Knife
- class CookbookSiteDownload < Knife
-
- deps do
- require "fileutils"
- end
-
- banner "knife cookbook site download COOKBOOK [VERSION] (options)"
- category "cookbook site"
-
- option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "The filename to write to"
-
- option :force,
- :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}."
- ui.warn message
-
- unless config[:force]
- ui.warn "Use --force to force download deprecated cookbook."
- return
- end
- end
-
- download_cookbook
- end
-
- def version
- @version = desired_cookbook_data["version"]
- end
-
- private
-
- def cookbooks_api_url
- "#{config[:supermarket_site]}/api/v1/cookbooks"
- end
-
- def current_cookbook_data
- @current_cookbook_data ||= begin
- noauth_rest.get "#{cookbooks_api_url}/#{@name_args[0]}"
- end
- end
-
- def current_cookbook_deprecated?
- current_cookbook_data["deprecated"] == true
- end
-
- def desired_cookbook_data
- @desired_cookbook_data ||= begin
- uri = if @name_args.length == 1
- current_cookbook_data["latest_version"]
- else
- specific_cookbook_version_url
- end
-
- noauth_rest.get uri
- end
- end
-
- def download_cookbook
- ui.info "Downloading #{@name_args[0]} from Supermarket at version #{version} to #{download_location}"
- tf = noauth_rest.streaming_request(desired_cookbook_data["file"])
-
- ::FileUtils.cp tf.path, download_location
- ui.info "Cookbook saved: #{download_location}"
- end
-
- def download_location
- config[:file] ||= File.join Dir.pwd, "#{@name_args[0]}-#{version}.tar.gz"
- config[:file]
- end
-
- def replacement_cookbook
- File.basename(current_cookbook_data["replacement"])
- end
-
- def specific_cookbook_version_url
- "#{cookbooks_api_url}/#{@name_args[0]}/versions/#{@name_args[1].tr('.', '_')}"
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
deleted file mode 100644
index 43d015dcc4..0000000000
--- a/lib/chef/knife/cookbook_site_install.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# 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/knife"
-require "chef/exceptions"
-require "shellwords"
-require "mixlib/archive"
-
-class Chef
- class Knife
- class CookbookSiteInstall < Knife
-
- deps do
- require "chef/mixin/shell_out"
- require "chef/knife/core/cookbook_scm_repo"
- require "chef/cookbook/metadata"
- end
-
- banner "knife cookbook site install COOKBOOK [VERSION] (options)"
- category "cookbook site"
-
- option :no_deps,
- :short => "-D",
- :long => "--skip-dependencies",
- :boolean => true,
- :default => false,
- :description => "Skips automatic dependency installation."
-
- option :cookbook_path,
- :short => "-o PATH:PATH",
- :long => "--cookbook-path PATH:PATH",
- :description => "A colon-separated path to look for cookbooks in",
- :proc => lambda { |o| o.split(":") }
-
- option :default_branch,
- :short => "-B BRANCH",
- :long => "--branch BRANCH",
- :description => "Default branch to work with",
- :default => "master"
-
- option :use_current_branch,
- :short => "-b",
- :long => "--use-current-branch",
- :description => "Use the current branch",
- :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
-
- def run
- extend Chef::Mixin::ShellOut
-
- if config[:cookbook_path]
- Chef::Config[:cookbook_path] = config[:cookbook_path]
- else
- config[:cookbook_path] = Chef::Config[:cookbook_path]
- end
-
- @cookbook_name = parse_name_args!
- # 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}"
-
- @repo = CookbookSCMRepo.new(@install_path, ui, config)
- #cookbook_path = File.join(vendor_path, name_args[0])
- upstream_file = File.join(@install_path, "#{@cookbook_name}.tar.gz")
-
- @repo.sanity_check
- unless config[:use_current_branch]
- @repo.reset_to_default_state
- @repo.prepare_to_import(@cookbook_name)
- end
-
- downloader = download_cookbook_to(upstream_file)
- clear_existing_files(File.join(@install_path, @cookbook_name))
- extract_cookbook(upstream_file, downloader.version)
-
- # TODO: it'd be better to store these outside the cookbook repo and
- # keep them around, e.g., in ~/Library/Caches on OS X.
- ui.info("Removing downloaded tarball")
- File.unlink(upstream_file)
-
- if @repo.finalize_updates_to(@cookbook_name, downloader.version)
- unless config[:use_current_branch]
- @repo.reset_to_default_state
- end
- @repo.merge_updates_from(@cookbook_name, downloader.version)
- else
- unless config[:use_current_branch]
- @repo.reset_to_default_state
- end
- end
-
- unless config[:no_deps]
- preferred_metadata.dependencies.each do |cookbook, version_list|
- # Doesn't do versions.. yet
- nv = self.class.new
- nv.config = config
- nv.name_args = [ cookbook ]
- nv.run
- end
- end
- end
-
- def parse_name_args!
- if name_args.empty?
- ui.error("Please specify a cookbook to download and install.")
- exit 1
- elsif name_args.size >= 2
- unless name_args.last.match(/^(\d+)(\.\d+){1,2}$/) && name_args.size == 2
- ui.error("Installing multiple cookbooks at once is not supported.")
- exit 1
- end
- end
- name_args.first
- end
-
- 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
- end
-
- def extract_cookbook(upstream_file, version)
- ui.info("Uncompressing #{@cookbook_name} version #{version}.")
- Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false)
- end
-
- def clear_existing_files(cookbook_path)
- ui.info("Removing pre-existing version.")
- FileUtils.rmtree(cookbook_path) if File.directory?(cookbook_path)
- end
-
- 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')
- else
- return Shellwords.escape upstream_file
- end
- end
-
- # Get the preferred metadata path on disk. Chef prefers the metadata.rb
- # over the metadata.json.
- #
- # @raise if there is no metadata in the cookbook
- #
- # @return [Chef::Cookbook::Metadata]
- def preferred_metadata
- md = Chef::Cookbook::Metadata.new
-
- rb = File.join(@install_path, @cookbook_name, "metadata.rb")
- if File.exist?(rb)
- md.from_file(rb)
- return md
- end
-
- json = File.join(@install_path, @cookbook_name, "metadata.json")
- if File.exist?(json)
- json = IO.read(json)
- md.from_json(json)
- return md
- end
-
- raise Chef::Exceptions::MetadataNotFound.new(@install_path, @cookbook_name)
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_list.rb b/lib/chef/knife/cookbook_site_list.rb
deleted file mode 100644
index 3bdef8abe5..0000000000
--- a/lib/chef/knife/cookbook_site_list.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@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 "chef/knife"
-
-class Chef
- class Knife
- class CookbookSiteList < Knife
-
- banner "knife cookbook site list (options)"
- category "cookbook site"
-
- option :with_uri,
- :short => "-w",
- :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
- get_cookbook_list.each { |k, v| cookbooks[k] = v["cookbook"] }
- ui.output(format_for_display(cookbooks))
- else
- ui.msg(ui.list(get_cookbook_list.keys.sort, :columns_down))
- end
- end
-
- def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
- 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
- end
- new_start = start + cr["items"].length
- if new_start < cr["total"]
- get_cookbook_list(items, new_start, cookbook_collection)
- else
- cookbook_collection
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_search.rb b/lib/chef/knife/cookbook_site_search.rb
deleted file mode 100644
index d401844217..0000000000
--- a/lib/chef/knife/cookbook_site_search.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# Author:: Adam Jacob (<adam@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 "chef/knife"
-
-class Chef
- class Knife
- class CookbookSiteSearch < Knife
-
- 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 = "#{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
- end
- new_start = start + cr["items"].length
- if new_start < cr["total"]
- search_cookbook(query, items, new_start, cookbook_collection)
- else
- cookbook_collection
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
deleted file mode 100644
index d55d6c123a..0000000000
--- a/lib/chef/knife/cookbook_site_share.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Author:: Tim Hinderliter (<tim@chef.io>)
-# 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/knife"
-require "chef/mixin/shell_out"
-
-class Chef
- class Knife
- class CookbookSiteShare < Knife
-
- include Chef::Mixin::ShellOut
-
- deps do
- require "chef/cookbook_loader"
- require "chef/cookbook_uploader"
- require "chef/cookbook_site_streaming_uploader"
- require "mixlib/shellout"
- end
-
- include Chef::Mixin::ShellOut
-
- banner "knife cookbook site share COOKBOOK [CATEGORY] (options)"
- category "cookbook site"
-
- option :cookbook_path,
- :short => "-o PATH:PATH",
- :long => "--cookbook-path PATH:PATH",
- :description => "A colon-separated path to look for cookbooks in",
- :proc => lambda { |o| Chef::Config.cookbook_path = o.split(":") }
-
- option :dry_run,
- :long => "--dry-run",
- :short => "-n",
- :boolean => true,
- :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]
-
- if @name_args.length < 1
- show_usage
- ui.fatal("You must specify the cookbook name.")
- exit(1)
- elsif @name_args.length < 2
- cookbook_name = @name_args[0]
- category = get_category(cookbook_name)
- else
- cookbook_name = @name_args[0]
- category = @name_args[1]
- end
-
- cl = Chef::CookbookLoader.new(config[:cookbook_path])
- if cl.cookbook_exists?(cookbook_name)
- cookbook = cl[cookbook_name]
- 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}")
- 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")}")
- exit(1)
- end
-
- if config[:dry_run]
- ui.info("Not uploading #{cookbook_name}.tgz due to --dry-run flag.")
- result = shell_out!("#{tar_cmd} -tzf #{cookbook_name}.tgz", :cwd => tmp_cookbook_dir)
- ui.info(result.stdout)
- FileUtils.rm_rf tmp_cookbook_dir
- return
- end
-
- 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}")
- 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")}")
- exit(1)
- end
-
- else
- ui.error("Could not find cookbook #{cookbook_name} in your cookbook path.")
- exit(1)
- end
- end
-
- def get_category(cookbook_name)
- 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.debug("\n#{e.backtrace.join("\n")}")
- exit(1)
- end
-
- def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
- uri = "#{config[:supermarket_site]}/api/v1/cookbooks"
-
- category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category })
-
- http_resp = Chef::CookbookSiteStreamingUploader.post(uri, user_id, user_secret_filename, {
- :tarball => File.open(cookbook_filename),
- :cookbook => category_string,
- })
-
- res = Chef::JSONCompat.from_json(http_resp.body)
- if http_resp.code.to_i != 201
- if res["error_messages"]
- if res["error_messages"][0] =~ /Version already exists/
- ui.error "The same version of this cookbook already exists on Supermarket."
- exit(1)
- else
- ui.error "#{res['error_messages'][0]}"
- exit(1)
- end
- else
- ui.error "Unknown error while sharing cookbook"
- ui.error "Server response: #{http_resp.body}"
- exit(1)
- end
- end
- res
- end
-
- def tar_cmd
- if !@tar_cmd
- @tar_cmd = "tar"
- begin
- # Unix and Mac only - prefer gnutar
- if shell_out("which gnutar").exitstatus.equal?(0)
- @tar_cmd = "gnutar"
- end
- rescue Errno::ENOENT
- end
- end
- @tar_cmd
- end
- end
-
- end
-end
diff --git a/lib/chef/knife/cookbook_site_show.rb b/lib/chef/knife/cookbook_site_show.rb
deleted file mode 100644
index ce153ca5a1..0000000000
--- a/lib/chef/knife/cookbook_site_show.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# Author:: Adam Jacob (<adam@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 "chef/knife"
-
-class Chef
- class Knife
- class CookbookSiteShow < Knife
-
- 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("#{supermarket_uri}/cookbooks/#{@name_args[0]}")
- when 2
- 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 = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}"
- cr = noauth_rest.get(cookbooks_url)
- cr["items"].each do |cookbook|
- cookbook_collection[cookbook["cookbook_name"]] = cookbook
- end
- new_start = start + cr["items"].length
- if new_start < cr["total"]
- get_cookbook_list(items, new_start, cookbook_collection)
- else
- cookbook_collection
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
deleted file mode 100644
index bdabff0b94..0000000000
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# Author:: Stephen Delano (<stephen@chef.io>)
-# Author:: Tim Hinderliter (<tim@chef.io>)
-# 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/knife"
-
-class Chef
- class Knife
- class CookbookSiteUnshare < Knife
-
- deps do
- require "chef/json_compat"
- end
-
- 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?
- show_usage
- ui.fatal "You must provide the name of the cookbook to unshare"
- exit 1
- end
-
- confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
-
- begin
- 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."
- exit 1
- end
-
- ui.info "Unshared all versions of the cookbook #{@cookbook_name}"
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_site_vendor.rb b/lib/chef/knife/cookbook_site_vendor.rb
deleted file mode 100644
index 291715cc0b..0000000000
--- a/lib/chef/knife/cookbook_site_vendor.rb
+++ /dev/null
@@ -1,46 +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.
-#
-
-require "chef/knife"
-require "chef/knife/cookbook_site_install"
-
-class Chef::Knife::CookbookSiteVendor < Chef::Knife::CookbookSiteInstall
-
- def self.load_deps
- superclass.load_deps
- end
-
- def self.options=(new_opts)
- superclass.options = new_opts
- end
-
- def self.options
- superclass.options
- end
-
- banner(<<-B)
-*************************************************
-DEPRECATED: please use knife cookbook site install
-*************************************************
-
-#{superclass.banner}
-B
-
- category "deprecated"
-
-end
diff --git a/lib/chef/knife/cookbook_test.rb b/lib/chef/knife/cookbook_test.rb
deleted file mode 100644
index 1a5c9df74e..0000000000
--- a/lib/chef/knife/cookbook_test.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
-# Copyright:: Copyright 2010-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.
-#
-require "chef/knife"
-
-class Chef
- class Knife
- class CookbookTest < Knife
-
- deps do
- require "chef/cookbook_loader"
- require "chef/cookbook/syntax_check"
- end
-
- banner "knife cookbook test [COOKBOOKS...] (options)"
-
- option :cookbook_path,
- :short => "-o PATH:PATH",
- :long => "--cookbook-path PATH:PATH",
- :description => "A colon-separated path to look for cookbooks in",
- :proc => lambda { |o| o.split(":") }
-
- option :all,
- :short => "-a",
- :long => "--all",
- :description => "Test all cookbooks, rather than just a single cookbook"
-
- def run
- ui.warn("DEPRECATED: Please use ChefSpec or Rubocop 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|
- checked_a_cookbook = true
- test_cookbook(key)
- end
- else
- @name_args.each do |cb|
- ui.info "checking #{cb}"
- next unless cookbook_loader.cookbook_exists?(cb)
- checked_a_cookbook = true
- test_cookbook(cb)
- end
- end
- unless checked_a_cookbook
- ui.warn("No cookbooks to test in #{Array(config[:cookbook_path]).join(',')} - is your cookbook path misconfigured?")
- end
- end
-
- def test_cookbook(cookbook)
- ui.info("Running syntax check on #{cookbook}")
- Array(config[:cookbook_path]).reverse_each do |path|
- syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook, path)
- test_ruby(syntax_checker)
- test_templates(syntax_checker)
- end
- end
-
- def test_ruby(syntax_checker)
- ui.info("Validating ruby files")
- exit(1) unless syntax_checker.validate_ruby_files
- end
-
- def test_templates(syntax_checker)
- ui.info("Validating templates")
- exit(1) unless syntax_checker.validate_templates
- end
-
- def cookbook_loader
- @cookbook_loader ||= Chef::CookbookLoader.new(config[:cookbook_path])
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/cookbook_upload.rb b/lib/chef/knife/cookbook_upload.rb
index 6938ac280d..9f6f3c4cb2 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,82 +18,70 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/cookbook_uploader"
+require_relative "../knife"
class Chef
class Knife
class CookbookUpload < Knife
-
- CHECKSUM = "checksum"
- MATCH_CHECKSUM = /[0-9a-f]{32,}/
-
deps do
- require "chef/exceptions"
- require "chef/cookbook_loader"
- require "chef/cookbook_uploader"
+ require_relative "../mixin/file_class"
+ include Chef::Mixin::FileClass
+ require_relative "../exceptions"
+ require_relative "../cookbook_loader"
+ require_relative "../cookbook_uploader"
end
banner "knife cookbook upload [COOKBOOKS...] (options)"
option :cookbook_path,
- :short => "-o PATH:PATH",
- :long => "--cookbook-path PATH:PATH",
- :description => "A colon-separated path to look for cookbooks in",
- :proc => lambda { |o| o.split(":") }
+ short: "-o 'PATH:PATH'",
+ long: "--cookbook-path 'PATH:PATH'",
+ description: "A delimited path to search for cookbooks. On Unix the delimiter is ':', on Windows it is ';'.",
+ proc: lambda { |o| o.split(File::PATH_SEPARATOR) }
option :freeze,
- :long => "--freeze",
- :description => "Freeze this version of the cookbook so that it cannot be overwritten",
- :boolean => true
+ long: "--freeze",
+ description: "Freeze this version of the cookbook so that it cannot be overwritten.",
+ boolean: true
option :all,
- :short => "-a",
- :long => "--all",
- :description => "Upload all cookbooks, rather than just a single cookbook"
+ short: "-a",
+ long: "--all",
+ description: "Upload all cookbooks, rather than just a single cookbook."
option :force,
- :long => "--force",
- :boolean => true,
- :description => "Update cookbook versions even if they have been frozen"
+ long: "--force",
+ boolean: true,
+ description: "Update cookbook versions even if they have been frozen."
option :concurrency,
- :long => "--concurrency NUMBER_OF_THREADS",
- :description => "How many concurrent threads will be used",
- :default => 10,
- :proc => lambda { |o| o.to_i }
+ long: "--concurrency NUMBER_OF_THREADS",
+ description: "How many concurrent threads will be used.",
+ default: 10,
+ proc: lambda { |o| o.to_i }
option :environment,
- :short => "-E",
- :long => "--environment ENVIRONMENT",
- :description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
- :default => nil
+ short: "-E",
+ long: "--environment ENVIRONMENT",
+ description: "Set ENVIRONMENT's version dependency match the version you're uploading.",
+ default: nil
option :depends,
- :short => "-d",
- :long => "--include-dependencies",
- :description => "Also upload cookbook dependencies"
+ short: "-d",
+ long: "--include-dependencies",
+ description: "Also upload cookbook dependencies."
def run
# Sanity check before we load anything from the server
- unless config[:all]
- if @name_args.empty?
- show_usage
- ui.fatal("You must specify the --all flag or at least one cookbook name")
- exit 1
- end
- end
-
- config[:cookbook_path] ||= Chef::Config[:cookbook_path]
-
- if @name_args.empty? && ! config[:all]
+ if ! config[:all] && @name_args.empty?
show_usage
ui.fatal("You must specify the --all flag or at least one cookbook name")
exit 1
end
+ config[:cookbook_path] ||= Chef::Config[:cookbook_path]
+
assert_environment_valid!
- warn_about_cookbook_shadowing
version_constraints_to_update = {}
upload_failures = 0
upload_ok = 0
@@ -101,84 +89,100 @@ class Chef
# Get a list of cookbooks and their versions from the server
# to check for the existence of a cookbook's dependencies.
@server_side_cookbooks = Chef::CookbookVersion.list_all_versions
- justify_width = @server_side_cookbooks.map { |name| name.size }.max.to_i + 2
- if config[:all]
- cookbook_repo.load_cookbooks_without_shadow_warning
- cookbooks_for_upload = []
- cookbook_repo.each do |cookbook_name, cookbook|
- cookbooks_for_upload << cookbook
- cookbook.freeze_version if config[:freeze]
- version_constraints_to_update[cookbook_name] = cookbook.version
- end
- if cookbooks_for_upload.any?
- begin
- upload(cookbooks_for_upload, justify_width)
- rescue Exceptions::CookbookFrozen
- ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.")
- end
- ui.info("Uploaded all cookbooks.")
- else
- cookbook_path = config[:cookbook_path].respond_to?(:join) ? config[:cookbook_path].join(", ") : config[:cookbook_path]
- ui.warn("Could not find any cookbooks in your cookbook path: #{cookbook_path}. Use --cookbook-path to specify the desired path.")
- end
- else
- if @name_args.empty?
- show_usage
- ui.error("You must specify the --all flag or at least one cookbook name")
- exit 1
+ justify_width = @server_side_cookbooks.map(&:size).max.to_i + 2
+
+ cookbooks = []
+ cookbooks_to_upload.each do |cookbook_name, cookbook|
+ raise Chef::Exceptions::MetadataNotFound.new(cookbook.root_paths[0], cookbook_name) unless cookbook.has_metadata_file?
+
+ if cookbook.metadata.name.nil?
+ message = "Cookbook loaded at path [#{cookbook.root_paths[0]}] has invalid metadata: #{cookbook.metadata.errors.join("; ")}"
+ raise Chef::Exceptions::MetadataNotValid, message
end
- cookbooks_to_upload.each do |cookbook_name, cookbook|
- cookbook.freeze_version if config[:freeze]
- begin
- upload([cookbook], justify_width)
- upload_ok += 1
+ cookbooks << cookbook
+ end
+
+ if cookbooks.empty?
+ cookbook_path = config[:cookbook_path].respond_to?(:join) ? config[:cookbook_path].join(", ") : config[:cookbook_path]
+ ui.warn("Could not find any cookbooks in your cookbook path: '#{File.expand_path(cookbook_path)}'. Use --cookbook-path to specify the desired path.")
+ else
+ Chef::CookbookLoader.copy_to_tmp_dir_from_array(cookbooks) do |tmp_cl|
+ tmp_cl.load_cookbooks
+ tmp_cl.compile_metadata
+ tmp_cl.freeze_versions if config[:freeze]
+
+ cookbooks_for_upload = []
+ tmp_cl.each do |cookbook_name, cookbook|
+ cookbooks_for_upload << cookbook
version_constraints_to_update[cookbook_name] = cookbook.version
- rescue Exceptions::CookbookNotFoundInRepo => e
- upload_failures += 1
- ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
- Log.debug(e)
- upload_failures += 1
- rescue Exceptions::CookbookFrozen
- ui.warn("Not updating version constraints for #{cookbook_name} in the environment as the cookbook is frozen.")
- upload_failures += 1
end
- end
+ if config[:all]
+ if cookbooks_for_upload.any?
+ begin
+ upload(cookbooks_for_upload, justify_width)
+ rescue Chef::Exceptions::CookbookFrozen
+ ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.")
+ ui.error("Uploading of some of the cookbooks must be failed. Remove cookbook whose version is frozen from your cookbooks repo OR use --force option.")
+ upload_failures += 1
+ rescue SystemExit => e
+ raise exit e.status
+ end
+ ui.info("Uploaded all cookbooks.") if upload_failures == 0
+ end
+ else
+ tmp_cl.each do |cookbook_name, cookbook|
- if upload_failures == 0
- 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."
- elsif upload_failures > 0 && upload_ok == 0
- ui.error "Failed to upload #{upload_failures} cookbook#{upload_failures > 1 ? "s" : ""}."
- exit 1
- end
- end
+ upload([cookbook], justify_width)
+ upload_ok += 1
+ rescue Exceptions::CookbookNotFoundInRepo => e
+ upload_failures += 1
+ ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
+ Log.debug(e)
+ upload_failures += 1
+ rescue Exceptions::CookbookFrozen
+ ui.warn("Not updating version constraints for #{cookbook_name} in the environment as the cookbook is frozen.")
+ upload_failures += 1
+ rescue SystemExit => e
+ raise exit e.status
- unless version_constraints_to_update.empty?
- update_version_constraints(version_constraints_to_update) if config[:environment]
+ end
+
+ if upload_failures == 0
+ 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."
+ elsif upload_failures > 0 && upload_ok == 0
+ ui.error "Failed to upload #{upload_failures} cookbook#{upload_failures == 1 ? "" : "s"}."
+ exit 1
+ end
+ end
+ unless version_constraints_to_update.empty?
+ update_version_constraints(version_constraints_to_update) if config[:environment]
+ end
+ end
end
end
def cookbooks_to_upload
@cookbooks_to_upload ||=
if config[:all]
- cookbook_repo.load_cookbooks_without_shadow_warning
+ cookbook_repo.load_cookbooks
else
upload_set = {}
@name_args.each do |cookbook_name|
- begin
- 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 }
- end
+
+ unless upload_set.key?(cookbook_name)
+ upload_set[cookbook_name] = cookbook_repo[cookbook_name]
+ if config[:depends]
+ upload_set[cookbook_name].metadata.dependencies.each_key { |dep| @name_args << dep }
end
- rescue Exceptions::CookbookNotFoundInRepo => e
- ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
- Log.debug(e)
end
+ rescue Exceptions::CookbookNotFoundInRepo => e
+ ui.error(e.message)
+ Log.debug(e)
+
end
upload_set
end
@@ -202,31 +206,11 @@ class Chef
@environment ||= config[:environment] ? Environment.load(config[:environment]) : nil
end
- def warn_about_cookbook_shadowing
- # because cookbooks are lazy-loaded, we have to force the loader
- # to load the cookbooks the user intends to upload here:
- cookbooks_to_upload
-
- unless cookbook_repo.merged_cookbooks.empty?
- ui.warn "* " * 40
- ui.warn(<<-WARNING)
-The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path.
-A composite version of these cookbooks has been compiled for uploading.
-
-#{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer
-be able to have the same version of a cookbook in multiple places in your cookbook_path.
-WARNING
- ui.warn "The affected cookbooks are located:"
- ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths)
- ui.warn "* " * 40
- end
- end
-
private
def assert_environment_valid!
environment
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code.to_s == "404"
ui.error "The environment #{config[:environment]} does not exist on the server, aborting."
Log.debug(e)
@@ -242,7 +226,7 @@ WARNING
check_for_broken_links!(cb)
check_for_dependencies!(cb)
end
- Chef::CookbookUploader.new(cookbooks, :force => config[:force], :concurrency => config[:concurrency]).upload_cookbooks
+ Chef::CookbookUploader.new(cookbooks, force: config[:force], concurrency: config[:concurrency]).upload_cookbooks
rescue Chef::Exceptions::CookbookFrozen => e
ui.error e
raise
@@ -253,13 +237,13 @@ WARNING
# manifest object, but the manifest becomes invalid when you
# regenerate the metadata
broken_files = cookbook.dup.manifest_records_by_path.select do |path, info|
- info[CHECKSUM].nil? || info[CHECKSUM] !~ MATCH_CHECKSUM
+ !/[0-9a-f]{32,}/.match?(info["checksum"])
end
unless broken_files.empty?
broken_filenames = Array(broken_files).map { |path, info| path }
ui.error "The cookbook #{cookbook.name} has one or more broken files"
ui.error "This is probably caused by broken symlinks in the cookbook directory"
- ui.error "The broken file(s) are: #{broken_filenames.join(' ')}"
+ ui.error "The broken file(s) are: #{broken_filenames.join(" ")}"
exit 1
end
end
@@ -275,7 +259,7 @@ WARNING
missing_cookbook_names = missing_dependencies.map { |cookbook_name, version| "'#{cookbook_name}' version '#{version}'" }
ui.error "Cookbook #{cookbook.name} depends on cookbooks which are not currently"
ui.error "being uploaded and cannot be found on the server."
- ui.error "The missing cookbook(s) are: #{missing_cookbook_names.join(', ')}"
+ ui.error "The missing cookbook(s) are: #{missing_cookbook_names.join(", ")}"
exit 1
end
end
@@ -288,7 +272,7 @@ WARNING
Log.debug "Versions of cookbook '#{cookbook_name}' returned by the server: #{versions.join(", ")}"
@server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
if Chef::VersionConstraint.new(version).include?(versions_hash["version"])
- Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to cookbook version '#{versions_hash['version']}' on the server"
+ Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to cookbook version '#{versions_hash["version"]}' on the server"
return true
end
end
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index b2670f196b..9aa81da82f 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,10 @@
# limitations under the License.
#
-require "chef/run_list"
-require "chef/util/path_helper"
-require "pathname"
+require_relative "../../run_list"
+require_relative "../../util/path_helper"
+require "pathname" unless defined?(Pathname)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
@@ -27,11 +28,13 @@ class Chef
# bootstrap templates. For backwards compatibility, they +must+ set the
# following instance variables:
# * @config - a hash of knife's config values
- # * @run_list - the run list for the node to boostrap
+ # * @run_list - the run list for the node to bootstrap
#
class BootstrapContext
attr_accessor :client_pem
+ attr_accessor :config
+ attr_accessor :chef_config
def initialize(config, run_list, chef_config, secret = nil)
@config = config
@@ -41,13 +44,13 @@ class Chef
end
def bootstrap_environment
- @config[:environment]
+ config[:environment]
end
def validation_key
- if @chef_config.has_key?(:validation_key) &&
- File.exist?(File.expand_path(@chef_config[:validation_key]))
- IO.read(File.expand_path(@chef_config[:validation_key]))
+ if chef_config[:validation_key] &&
+ File.exist?(File.expand_path(chef_config[:validation_key]))
+ IO.read(File.expand_path(chef_config[:validation_key]))
else
false
end
@@ -62,40 +65,68 @@ class Chef
end
# Contains commands and content, see trusted_certs_content
- # TODO: Rename to trusted_certs_script
+ # @todo Rename to trusted_certs_script
def trusted_certs
@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]}"
+ client_rb = <<~CONFIG
+ chef_server_url "#{chef_config[:chef_server_url]}"
+ validation_client_name "#{chef_config[:validation_client_name]}"
CONFIG
- if @config[:chef_node_name]
- client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
+
+ unless chef_config[:chef_license].nil?
+ client_rb << "chef_license \"#{chef_config[:chef_license]}\"\n"
+ end
+
+ unless 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
client_rb << "# Using default node name (fqdn)\n"
end
# We configure :verify_api_cert only when it's overridden on the CLI
# or when specified in the knife config.
- if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
- value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
+ if !config[:node_verify_api_cert].nil? || config.key?(:verify_api_cert)
+ value = config[:node_verify_api_cert].nil? ? config[:verify_api_cert] : config[:node_verify_api_cert]
client_rb << %Q{verify_api_cert #{value}\n}
end
# We configure :ssl_verify_mode only when it's overridden on the CLI
# or when specified in the knife config.
- if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
- value = case @config[:node_ssl_verify_mode]
+ if config[:node_ssl_verify_mode] || config.key?(:ssl_verify_mode)
+ value = case config[:node_ssl_verify_mode]
when "peer"
:verify_peer
when "none"
:verify_none
when nil
- knife_config[:ssl_verify_mode]
+ config[:ssl_verify_mode]
else
nil
end
@@ -105,27 +136,27 @@ validation_client_name "#{@chef_config[:validation_client_name]}"
end
end
- if @config[:ssl_verify_mode]
- client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
+ if config[:ssl_verify_mode]
+ client_rb << %Q{ssl_verify_mode :#{config[:ssl_verify_mode]}\n}
end
- if knife_config[:bootstrap_proxy]
- client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
- client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
+ if config[:bootstrap_proxy]
+ client_rb << %Q{http_proxy "#{config[:bootstrap_proxy]}"\n}
+ client_rb << %Q{https_proxy "#{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}
+ if config[:bootstrap_proxy_user]
+ client_rb << %Q{http_proxy_user "#{config[:bootstrap_proxy_user]}"\n}
+ client_rb << %Q{https_proxy_user "#{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}
+ if config[:bootstrap_proxy_pass]
+ client_rb << %Q{http_proxy_pass "#{config[:bootstrap_proxy_pass]}"\n}
+ client_rb << %Q{https_proxy_pass "#{config[:bootstrap_proxy_pass]}"\n}
end
- if knife_config[:bootstrap_no_proxy]
- client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
+ if config[:bootstrap_no_proxy]
+ client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n}
end
if encrypted_data_bag_secret
@@ -136,14 +167,16 @@ validation_client_name "#{@chef_config[:validation_client_name]}"
client_rb << %Q{trusted_certs_dir "/etc/chef/trusted_certs"\n}
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
+ if chef_config[:fips]
+ client_rb << "fips true\n"
+ end
+
+ unless chef_config[:file_cache_path].nil?
+ client_rb << "file_cache_path \"#{chef_config[:file_cache_path]}\"\n"
+ end
+
+ unless chef_config[:file_backup_path].nil?
+ client_rb << "file_backup_path \"#{chef_config[:file_backup_path]}\"\n"
end
client_rb
@@ -151,54 +184,42 @@ CONFIG
def start_chef
# 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"
+ client_path = chef_config[:chef_client_path] || ChefUtils::Dist::Infra::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 << " --no-color" unless config[:color]
s
end
- def knife_config
- @chef_config.key?(:knife) ? @chef_config[:knife] : {}
- end
-
#
- # chef version string to fetch the latest current version from omnitruck
- # If user is on X.Y.Z bootstrap will use the latest X release
- # X here can be 10 or 11
- def latest_current_chef_version_string
- installer_version_string = nil
- if @config[:prerelease]
- installer_version_string = ["-p"]
- else
- chef_version_string = if knife_config[:bootstrap_version]
- knife_config[:bootstrap_version]
- else
- Chef::VERSION.split(".").first
- end
-
- installer_version_string = ["-v", chef_version_string]
+ # Returns the version of Chef to install (as recognized by the Omnitruck API)
+ #
+ # @return [String] download version string
+ def version_to_install
+ return config[:bootstrap_version] if config[:bootstrap_version]
- # If bootstrapping a pre-release version add -p to the installer string
- if chef_version_string.split(".").length > 3
- installer_version_string << "-p"
- end
+ if config[:channel] == "stable"
+ Chef::VERSION.split(".").first
+ else
+ "latest"
end
-
- installer_version_string.join(" ")
end
def first_boot
- (@config[:first_boot_attributes] || {}).tap do |attributes|
- if @config[:policy_name] && @config[:policy_group]
- attributes[:policy_name] = @config[:policy_name]
- attributes[:policy_group] = @config[:policy_group]
+ (config[:first_boot_attributes] = Mash.new(config[:first_boot_attributes]) || Mash.new).tap do |attributes|
+ if config[:policy_name] && config[:policy_group]
+ attributes[:policy_name] = config[:policy_name]
+ attributes[:policy_group] = config[:policy_group]
else
attributes[:run_list] = @run_list
end
-
- attributes.merge!(:tags => @config[:tags]) if @config[:tags] && !@config[:tags].empty?
+ 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
@@ -208,8 +229,8 @@ CONFIG
# This string should contain both the commands necessary to both create the files, as well as their content
def trusted_certs_content
content = ""
- if @chef_config[:trusted_certs_dir]
- Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
+ if chef_config[:trusted_certs_dir]
+ Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
content << "cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'\n" +
IO.read(File.expand_path(cert)) + "\nEOP\n"
end
@@ -219,8 +240,8 @@ CONFIG
def client_d_content
content = ""
- if @chef_config[:client_d_dir] && File.exist?(@chef_config[:client_d_dir])
- root = Pathname(@chef_config[:client_d_dir])
+ if chef_config[:client_d_dir] && File.exist?(chef_config[:client_d_dir])
+ root = Pathname(chef_config[:client_d_dir])
root.find do |f|
relative = f.relative_path_from(root)
if f != root
@@ -229,7 +250,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..ba194a8a6d 100644
--- a/lib/chef/knife/core/cookbook_scm_repo.rb
+++ b/lib/chef/knife/core/cookbook_scm_repo.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/mixin/shell_out"
+require_relative "../../mixin/shell_out"
class Chef
class Knife
class CookbookSCMRepo
- DIRTY_REPO = /^[\s]+M/
+ DIRTY_REPO = /^\s+M/.freeze
include Chef::Mixin::ShellOut
@@ -50,7 +50,7 @@ class Chef
exit 1
end
if use_current_branch
- @default_branch = get_current_branch()
+ @default_branch = get_current_branch
end
unless branch_exists?(default_branch)
ui.error "The default branch '#{default_branch}' does not exist"
@@ -58,7 +58,7 @@ class Chef
exit 1
end
cmd = git("status --porcelain")
- if cmd.stdout =~ DIRTY_REPO
+ if DIRTY_REPO.match?(cmd.stdout)
ui.error "You have uncommitted changes to your cookbook repo (#{repo_path}):"
ui.msg cmd.stdout
ui.info "Commit or stash your changes before importing cookbooks"
@@ -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
@@ -151,7 +151,7 @@ class Chef
end
def git(command)
- shell_out!("git #{command}", :cwd => repo_path)
+ shell_out!("git #{command}", cwd: repo_path)
end
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/formatting_options.rb b/lib/chef/knife/core/formatting_options.rb
new file mode 100644
index 0000000000..cdee2c5989
--- /dev/null
+++ b/lib/chef/knife/core/formatting_options.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Nicolas DUPEUX (<nicolas.dupeux@arkea.com>)
+# Copyright:: Copyright (c) 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
+ module Core
+
+ # This module may be included into a knife subcommand class to automatically
+ # add configuration options used by the StatusPresenter and NodePresenter.
+ module FormattingOptions
+ # @private
+ # Would prefer to do this in a rational way, but can't be done b/c of
+ # Mixlib::CLI's design :(
+ def self.included(includer)
+ includer.class_eval do
+ option :medium_output,
+ short: "-m",
+ long: "--medium",
+ boolean: true,
+ default: false,
+ description: "Include normal attributes in the output"
+
+ option :long_output,
+ short: "-l",
+ long: "--long",
+ boolean: true,
+ default: false,
+ description: "Include all attributes in the output"
+ end
+ 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 34c5c53d75..d058379e71 100644
--- a/lib/chef/knife/core/gem_glob_loader.rb
+++ b/lib/chef/knife/core/gem_glob_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "chef/version"
-require "chef/util/path_helper"
+require_relative "../../version"
+require_relative "../../util/path_helper"
class Chef
class Knife
class SubcommandLoader
class GemGlobLoader < Chef::Knife::SubcommandLoader
- MATCHES_CHEF_GEM = %r{/chef-[\d]+\.[\d]+\.[\d]+}
- MATCHES_THIS_CHEF_GEM = %r{/chef-#{Chef::VERSION}(-\w+)?(-\w+)?/}
+ MATCHES_CHEF_GEM ||= %r{/chef-\d+\.\d+\.\d+}.freeze
+ MATCHES_THIS_CHEF_GEM ||= %r{/chef-#{Chef::VERSION}(-\w+)?(-\w+)?/}.freeze
def subcommand_files
@subcommand_files ||= (gem_and_builtin_subcommands.values + site_subcommands).flatten.uniq
@@ -39,7 +39,7 @@ class Chef
# subcommand loader has been modified to load the plugins by using Kernel.load
# with the absolute path.
def gem_and_builtin_subcommands
- require "rubygems"
+ require "rubygems" unless defined?(Gem)
find_subcommands_via_rubygems
rescue LoadError
find_subcommands_via_dirglob
@@ -47,7 +47,7 @@ class Chef
def find_subcommands_via_dirglob
# The "require paths" of the core knife subcommands bundled with chef
- files = Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path("../../../knife", __FILE__)), "*.rb")]
+ files = Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path("../../knife", __dir__)), "*.rb")]
subcommand_files = {}
files.each do |knife_file|
rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/, 1]
@@ -98,7 +98,7 @@ class Chef
files.concat gem_files
files.uniq! if check_load_path
- return files
+ files
end
def latest_gem_specs
@@ -111,14 +111,14 @@ class Chef
def check_spec_for_glob(spec, glob)
dirs = if spec.require_paths.size > 1
- "{#{spec.require_paths.join(',')}}"
+ "{#{spec.require_paths.join(",")}}"
else
spec.require_paths.first
end
glob = File.join(Chef::Util::PathHelper.escape_glob_dir(spec.full_gem_path, dirs), glob)
- Dir[glob].map { |f| f.untaint }
+ Dir[glob].map(&:untaint)
end
def from_different_chef_version?(path)
diff --git a/lib/chef/knife/core/generic_presenter.rb b/lib/chef/knife/core/generic_presenter.rb
index f273cb5bca..850bfa8b3d 100644
--- a/lib/chef/knife/core/generic_presenter.rb
+++ b/lib/chef/knife/core/generic_presenter.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife/core/text_formatter"
+require_relative "text_formatter"
class Chef
class Knife
@@ -25,20 +25,27 @@ class Chef
# Allows includer knife commands to return multiple attributes
# @brief knife node show NAME -a ATTR1 -a ATTR2
module MultiAttributeReturnOption
- # :nodoc:
+ # @private
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"
+ short: "-a ATTR1 [-a ATTR2]",
+ long: "--attribute ATTR1 [--attribute ATTR2] ",
+ description: "Show one or more attributes",
+ proc: Proc.new { |arg, accumulator|
+ accumulator ||= []
+ accumulator << arg
+ accumulator
+ }
end
end
end
- #==Chef::Knife::Core::GenericPresenter
# The base presenter class for displaying structured data in knife commands.
# This is not an abstract base class, and it is suitable for displaying
# most kinds of objects that knife needs to display.
@@ -47,7 +54,7 @@ class Chef
attr_reader :ui
attr_reader :config
- # Instaniates a new GenericPresenter. This is generally handled by the
+ # Instantiates a new GenericPresenter. This is generally handled by the
# Chef::Knife::UI object, though you need to match the signature of this
# method if you intend to use your own presenter instead.
def initialize(ui, config)
@@ -80,10 +87,10 @@ class Chef
when :json
Chef::JSONCompat.to_json_pretty(data)
when :yaml
- require "yaml"
+ require "yaml" unless defined?(YAML)
YAML.dump(data)
when :pp
- require "stringio"
+ require "stringio" unless defined?(StringIO)
# If you were looking for some attribute and there is only one match
# just dump the attribute value
if config[:attribute] && data.length == 1
@@ -173,32 +180,35 @@ 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.respond_to?(:key?) && 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
- ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
+ # necessary (?) for coercing objects (the run_list object?) to hashes
+ ( !data.is_a?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
end
def format_cookbook_list_for_display(item)
if config[:with_uri]
item.inject({}) do |collected, (cookbook, versions)|
- collected[cookbook] = Hash.new
+ collected[cookbook] = {}
versions["versions"].each do |ver|
collected[cookbook][ver["version"]] = ver["url"]
end
@@ -209,9 +219,9 @@ class Chef
collected[cookbook] = versions["versions"].map { |v| v["version"] }
collected
end
- key_length = versions_by_cookbook.empty? ? 0 : versions_by_cookbook.keys.map { |name| name.size }.max + 2
+ key_length = versions_by_cookbook.empty? ? 0 : versions_by_cookbook.keys.map(&:size).max + 2
versions_by_cookbook.sort.map do |cookbook, versions|
- "#{cookbook.ljust(key_length)} #{versions.join(' ')}"
+ "#{cookbook.ljust(key_length)} #{versions.join(" ")}"
end
end
end
diff --git a/lib/chef/knife/core/hashed_command_loader.rb b/lib/chef/knife/core/hashed_command_loader.rb
index 8423c01812..c1d71f3edf 100644
--- a/lib/chef/knife/core/hashed_command_loader.rb
+++ b/lib/chef/knife/core/hashed_command_loader.rb
@@ -1,5 +1,5 @@
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +15,7 @@
# limitations under the License.
#
-require "chef/version"
+require_relative "../../version"
class Chef
class Knife
class SubcommandLoader
@@ -24,9 +24,10 @@ class Chef
# for the given command.
#
class HashedCommandLoader < Chef::Knife::SubcommandLoader
- KEY = "_autogenerated_command_paths"
+ KEY = "_autogenerated_command_paths".freeze
attr_accessor :manifest
+
def initialize(chef_config_dir, plugin_manifest)
super(chef_config_dir)
@manifest = plugin_manifest
@@ -44,7 +45,7 @@ class Chef
else
commands = manifest[KEY]["plugins_by_category"]
end
- # If any of the specified plugins in the manifest dont have a valid path we will
+ # If any of the specified plugins in the manifest don't have a valid path we will
# eventually get an error and the user will need to rehash - instead, lets just
# print out 1 error here telling them to rehash
errors = {}
@@ -52,7 +53,7 @@ class Chef
paths = manifest[KEY]["plugins_paths"][command]
if paths && paths.is_a?(Array)
# It is only an error if all the paths don't exist
- if paths.all? { |sc| !File.exists?(sc) }
+ if paths.all? { |sc| !File.exist?(sc) }
errors[command] = paths
end
end
@@ -60,7 +61,7 @@ class Chef
if errors.empty?
commands
else
- Chef::Log.error "There are files specified in the manifest that are missing. Please rehash to update the subcommands cache. If you see this error after rehashing delete the cache at #{Chef::Knife::SubcommandLoader.plugin_manifest_path}"
+ Chef::Log.error "There are plugin files specified in the knife cache that cannot be found. Please run knife rehash to update the subcommands cache. If you see this error after rehashing delete the cache at #{Chef::Knife::SubcommandLoader.plugin_manifest_path}"
Chef::Log.error "Missing files:\n\t#{errors.values.flatten.join("\n\t")}"
{}
end
@@ -76,7 +77,7 @@ class Chef
false
else
paths.each do |sc|
- if File.exists?(sc)
+ if File.exist?(sc)
Kernel.load sc
else
return false
diff --git a/lib/chef/knife/core/node_editor.rb b/lib/chef/knife/core/node_editor.rb
index b009bf8652..2f9b326d16 100644
--- a/lib/chef/knife/core/node_editor.rb
+++ b/lib/chef/knife/core/node_editor.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Jordan Running (<jr@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/json_compat"
-require "chef/node"
+require_relative "../../json_compat"
+require_relative "../../node"
class Chef
class Knife
diff --git a/lib/chef/knife/core/node_presenter.rb b/lib/chef/knife/core/node_presenter.rb
index cdb664ec33..8c948cf76c 100644
--- a/lib/chef/knife/core/node_presenter.rb
+++ b/lib/chef/knife/core/node_presenter.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,39 +16,13 @@
# limitations under the License.
#
-require "chef/knife/core/text_formatter"
-require "chef/knife/core/generic_presenter"
+require_relative "text_formatter"
+require_relative "generic_presenter"
class Chef
class Knife
module Core
- # This module may be included into a knife subcommand class to automatically
- # add configuration options used by the NodePresenter
- module NodeFormattingOptions
- # :nodoc:
- # Would prefer to do this in a rational way, but can't be done b/c of
- # Mixlib::CLI's design :(
- def self.included(includer)
- includer.class_eval do
- option :medium_output,
- :short => "-m",
- :long => "--medium",
- :boolean => true,
- :default => false,
- :description => "Include normal attributes in the output"
-
- option :long_output,
- :short => "-l",
- :long => "--long",
- :boolean => true,
- :default => false,
- :description => "Include all attributes in the output"
- end
- end
- end
-
- #==Chef::Knife::Core::NodePresenter
# A customized presenter for Chef::Node objects. Supports variable-length
# output formats for displaying node data
class NodePresenter < GenericPresenter
@@ -62,7 +36,7 @@ class Chef
end
def summarize_json(data)
- if data.kind_of?(Chef::Node)
+ if data.is_a?(Chef::Node)
node = data
result = {}
@@ -93,55 +67,55 @@ class Chef
# the volume of output is adjusted accordingly. Uses colors if enabled
# in the ui object.
def summarize(data)
- if data.kind_of?(Chef::Node)
+ if data.is_a?(Chef::Node)
node = data
- # special case ec2 with their split horizon whatsis.
- ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress]
+ # special case clouds with their split horizon thing.
+ ip = (node[:cloud] && node[:cloud][:public_ipv4_addrs] && node[:cloud][:public_ipv4_addrs].first) || node[:ipaddress]
- summarized = <<-SUMMARY
-#{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
-SUMMARY
+ summarized = <<~SUMMARY
+ #{ui.color("Node Name:", :bold)} #{ui.color(node.name, :bold)}
+ SUMMARY
show_policy = !(node.policy_name.nil? && node.policy_group.nil?)
if show_policy
- summarized << <<-POLICY
-#{key('Policy Name:')} #{node.policy_name}
-#{key('Policy Group:')} #{node.policy_group}
-POLICY
+ summarized << <<~POLICY
+ #{key("Policy Name:")} #{node.policy_name}
+ #{key("Policy Group:")} #{node.policy_group}
+ POLICY
else
- summarized << <<-ENV
-#{key('Environment:')} #{node.chef_environment}
-ENV
+ summarized << <<~ENV
+ #{key("Environment:")} #{node.chef_environment}
+ ENV
end
- summarized << <<-SUMMARY
-#{key('FQDN:')} #{node[:fqdn]}
-#{key('IP:')} #{ip}
-#{key('Run List:')} #{node.run_list}
-SUMMARY
+ summarized << <<~SUMMARY
+ #{key("FQDN:")} #{node[:fqdn]}
+ #{key("IP:")} #{ip}
+ #{key("Run List:")} #{node.run_list}
+ SUMMARY
unless show_policy
- summarized << <<-ROLES
-#{key('Roles:')} #{Array(node[:roles]).join(', ')}
-ROLES
+ summarized << <<~ROLES
+ #{key("Roles:")} #{Array(node[:roles]).join(", ")}
+ ROLES
end
- summarized << <<-SUMMARY
-#{key('Recipes:')} #{Array(node[:recipes]).join(', ')}
-#{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
-#{key('Tags:')} #{node.tags.join(', ')}
-SUMMARY
+ summarized << <<~SUMMARY
+ #{key("Recipes:")} #{Array(node[:recipes]).join(", ")}
+ #{key("Platform:")} #{node[:platform]} #{node[:platform_version]}
+ #{key("Tags:")} #{node.tags.join(", ")}
+ SUMMARY
if config[:medium_output] || config[:long_output]
- summarized += <<-MORE
-#{key('Attributes:')}
-#{text_format(node.normal_attrs)}
-MORE
+ summarized += <<~MORE
+ #{key("Attributes:")}
+ #{text_format(node.normal_attrs)}
+ MORE
end
if config[:long_output]
- summarized += <<-MOST
-#{key('Default Attributes:')}
-#{text_format(node.default_attrs)}
-#{key('Override Attributes:')}
-#{text_format(node.override_attrs)}
-#{key('Automatic Attributes (Ohai Data):')}
-#{text_format(node.automatic_attrs)}
-MOST
+ summarized += <<~MOST
+ #{key("Default Attributes:")}
+ #{text_format(node.default_attrs)}
+ #{key("Override Attributes:")}
+ #{text_format(node.override_attrs)}
+ #{key("Automatic Attributes (Ohai Data):")}
+ #{text_format(node.automatic_attrs)}
+ MOST
end
summarized
else
diff --git a/lib/chef/knife/core/object_loader.rb b/lib/chef/knife/core/object_loader.rb
index b08483f9a2..5421fc46ce 100644
--- a/lib/chef/knife/core/object_loader.rb
+++ b/lib/chef/knife/core/object_loader.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "ffi_yajl"
-require "chef/util/path_helper"
-require "chef/data_bag_item"
+autoload :FFI_Yajl, "ffi_yajl"
+require_relative "../../util/path_helper"
+require_relative "../../data_bag_item"
class Chef
class Knife
@@ -40,7 +40,7 @@ class Chef
def load_from(repo_location, *components)
unless object_file = find_file(repo_location, *components)
- ui.error "Could not find or open file '#{components.last}' in current directory or in '#{repo_location}/#{components.join('/')}'"
+ ui.error "Could not find or open file '#{components.last}' in current directory or in '#{repo_location}/#{components.join("/")}'"
exit 1
end
object_from_file(object_file)
diff --git a/lib/chef/knife/core/status_presenter.rb b/lib/chef/knife/core/status_presenter.rb
index 68c1acf4f1..271c71d618 100644
--- a/lib/chef/knife/core/status_presenter.rb
+++ b/lib/chef/knife/core/status_presenter.rb
@@ -1,6 +1,6 @@
#
# Author:: Nicolas DUPEUX (<nicolas.dupeux@arkea.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,39 +16,13 @@
# limitations under the License.
#
-require "chef/knife/core/text_formatter"
-require "chef/knife/core/generic_presenter"
+require_relative "text_formatter"
+require_relative "generic_presenter"
class Chef
class Knife
module Core
- # This module may be included into a knife subcommand class to automatically
- # add configuration options used by the StatusPresenter
- module StatusFormattingOptions
- # :nodoc:
- # Would prefer to do this in a rational way, but can't be done b/c of
- # Mixlib::CLI's design :(
- def self.included(includer)
- includer.class_eval do
- option :medium_output,
- :short => "-m",
- :long => "--medium",
- :boolean => true,
- :default => false,
- :description => "Include normal attributes in the output"
-
- option :long_output,
- :short => "-l",
- :long => "--long",
- :boolean => true,
- :default => false,
- :description => "Include all attributes in the output"
- end
- end
- end
-
- #==Chef::Knife::Core::StatusPresenter
# A customized presenter for Chef::Node objects. Supports variable-length
# output formats for displaying node data
class StatusPresenter < GenericPresenter
@@ -68,8 +42,8 @@ class Chef
result["name"] = node["name"] || node.name
result["chef_environment"] = node["chef_environment"]
- ip = (node["ec2"] && node["ec2"]["public_ipv4"]) || node["ipaddress"]
- fqdn = (node["ec2"] && node["ec2"]["public_hostname"]) || node["fqdn"]
+ ip = (node["cloud"] && node["cloud"]["public_ipv4_addrs"]&.first) || node["ipaddress"]
+ fqdn = (node["cloud"] && node["cloud"]["public_hostname"]) || node["fqdn"]
result["ip"] = ip if ip
result["fqdn"] = fqdn if fqdn
result["run_list"] = node.run_list if config["run_list"]
@@ -96,36 +70,52 @@ class Chef
summarized = ""
list.each do |data|
node = data
- # special case ec2 with their split horizon whatsis.
- ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress]
- fqdn = (node[:ec2] && node[:ec2][:public_hostname]) || node[:fqdn]
+ # special case clouds with their split horizon thing.
+ ip = (node[:cloud] && node[:cloud][:public_ipv4_addrs] && node[:cloud][:public_ipv4_addrs].first) || node[:ipaddress]
+ fqdn = (node[:cloud] && node[:cloud][: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
+ if config[:run_list]
+ if config[:long_output]
+ run_list = node.run_list.map { |rl| "#{rl.type}[#{rl.name}]" }
+ else
+ run_list = node["run_list"]
+ end
+ end
+
+ line_parts = []
+
+ 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
+ line_parts << run_list.to_s if run_list
if node["platform"]
- platform = node["platform"]
+ platform = node["platform"].dup
if node["platform_version"]
- platform << " #{node['platform_version']}"
+ platform << " #{node["platform_version"]}"
end
line_parts << platform
end
@@ -139,8 +129,8 @@ class Chef
ui.color(key_text, :cyan)
end
- # :nodoc:
- # TODO: this is duplicated from StatusHelper in the Webui. dedup.
+ # @private
+ # @todo this is duplicated from StatusHelper in the Webui. dedup.
def time_difference_in_hms(unix_time)
now = Time.now.to_i
difference = now - unix_time.to_i
@@ -148,7 +138,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..26d7e0277c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,10 @@
# limitations under the License.
#
-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"
+require_relative "../../version"
+require_relative "../../util/path_helper"
+require_relative "gem_glob_loader"
+require_relative "hashed_command_loader"
class Chef
class Knife
@@ -33,12 +32,11 @@ class Chef
# optionally filtering by category
# subcommand_files - returns an array of all subcommand files
# that could be loaded
- # commnad_class_from(args) - returns the subcommand class for the
+ # command_class_from(args) - returns the subcommand class for the
# user-requested command
#
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,19 +75,33 @@ class Chef
Chef::Util::PathHelper.home(".chef", "plugin_manifest.json")
end
- def initialize(chef_config_dir, env = nil)
- @chef_config_dir = chef_config_dir
+ def self.generate_hash
+ output = if plugin_manifest?
+ plugin_manifest
+ else
+ { Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY => {} }
+ end
+ output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_paths"] = Chef::Knife.subcommand_files
+ output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_by_category"] = Chef::Knife.subcommands_by_category
+ output
+ end
- # 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.")
+ def self.write_hash(data)
+ plugin_manifest_dir = File.expand_path("..", plugin_manifest_path)
+ FileUtils.mkdir_p(plugin_manifest_dir) unless File.directory?(plugin_manifest_dir)
+ File.open(plugin_manifest_path, "w") do |f|
+ f.write(Chef::JSONCompat.to_json_pretty(data))
end
end
+ def initialize(chef_config_dir)
+ @chef_config_dir = chef_config_dir
+ end
+
# Load all the sub-commands
def load_commands
return true if @loaded
+
subcommand_files.each { |subcommand| Kernel.load subcommand }
@loaded = true
end
@@ -123,7 +128,7 @@ class Chef
cmd_words = positional_arguments(args)
load_command(cmd_words)
result = Chef::Knife.subcommands[find_longest_key(Chef::Knife.subcommands,
- cmd_words, "_")]
+ cmd_words, "_")]
result || Chef::Knife.subcommands[args.first.tr("-", "_")]
end
@@ -131,7 +136,7 @@ class Chef
category_words = positional_arguments(args)
category_words.map! { |w| w.split("-") }.flatten!
find_longest_key(Chef::Knife.subcommands_by_category,
- category_words, " ")
+ category_words, " ")
end
#
@@ -139,7 +144,7 @@ class Chef
#
def find_subcommands_via_dirglob
# The "require paths" of the core knife subcommands bundled with chef
- files = Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path("../../../knife", __FILE__)), "*.rb")]
+ files = Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(File.expand_path("../../knife", __dir__)), "*.rb")]
subcommand_files = {}
files.each do |knife_file|
rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/, 1]
@@ -149,29 +154,15 @@ 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.
#
def find_longest_key(hash, words, sep = "_")
+ words = words.dup
match = nil
until match || words.empty?
- candidate = words.join(sep)
+ candidate = words.join(sep).tr("-", "_")
if hash.key?(candidate)
match = candidate
else
diff --git a/lib/chef/knife/core/text_formatter.rb b/lib/chef/knife/core/text_formatter.rb
index 8775e2e76f..ec97748afb 100644
--- a/lib/chef/knife/core/text_formatter.rb
+++ b/lib/chef/knife/core/text_formatter.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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
@ui = ui
@data = if data.respond_to?(:display_hash)
data.display_hash
- elsif data.kind_of?(Array)
+ elsif data.is_a?(Array)
data
elsif data.respond_to?(:to_hash)
data.to_hash
@@ -48,7 +48,7 @@ class Chef
justify_width = data.keys.map { |k| k.to_s.size }.max.to_i + 1
data.sort.each do |key, value|
# key: ['value'] should be printed as key: value
- if value.kind_of?(Array) && value.size == 1 && is_singleton(value[0])
+ if value.is_a?(Array) && value.size == 1 && is_singleton(value[0])
value = value[0]
end
if is_singleton(value)
@@ -62,7 +62,7 @@ class Chef
lines.each { |line| buffer << " #{line}\n" }
end
end
- elsif data.kind_of?(Array)
+ elsif data.is_a?(Array)
data.each_index do |index|
item = data[index]
buffer << text_format(data[index])
@@ -77,7 +77,7 @@ class Chef
end
def is_singleton(value)
- !(value.kind_of?(Array) || value.respond_to?(:keys))
+ !(value.is_a?(Array) || value.respond_to?(:keys))
end
end
end
diff --git a/lib/chef/knife/core/ui.rb b/lib/chef/knife/core/ui.rb
index 67e431f1a7..aa84537064 100644
--- a/lib/chef/knife/core/ui.rb
+++ b/lib/chef/knife/core/ui.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +18,14 @@
# limitations under the License.
#
-require "forwardable"
-require "chef/platform/query_helpers"
-require "chef/knife/core/generic_presenter"
-require "tempfile"
+require "forwardable" unless defined?(Forwardable)
+require_relative "../../platform/query_helpers"
+require_relative "generic_presenter"
+require "tempfile" unless defined?(Tempfile)
class Chef
class Knife
- #==Chef::Knife::UI
# The User Interaction class used by knife.
class UI
@@ -62,48 +61,105 @@ class Chef
end
end
+ # Creates a new object of class TTY::Prompt
+ # with interrupt as exit so that it can be terminated with status code.
+ def prompt
+ @prompt ||= begin
+ require "tty-prompt"
+ TTY::Prompt.new(interrupt: :exit)
+ end
+ end
+
+ # pastel.decorate is a lightweight replacement for highline.color
+ def pastel
+ @pastel ||= begin
+ require "pastel" unless defined?(Pastel)
+ Pastel.new
+ end
+ end
+
# Prints a message to stdout. Aliased as +info+ for compatibility with
# the logger API.
+ #
+ # @param message [String] the text string
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.
+ #
+ # @param message [String] the text string
def log(message)
- begin
- stderr.puts message
- rescue Errno::EPIPE => e
- raise e if @config[:verbosity] >= 2
- exit 0
+ lines = message.split("\n")
+ first_line = lines.shift
+ stderr.puts first_line
+ # If the message is multiple lines,
+ # indent subsequent lines to align with the
+ # log type prefix ("ERROR: ", etc)
+ unless lines.empty?
+ prefix, = first_line.split(":", 2)
+ return if prefix.nil?
+
+ prefix_len = prefix.length
+ prefix_len -= 9 if color? # prefix includes 9 bytes of color escape sequences
+ prefix_len += 2 # include room to align to the ": " following PREFIX
+ padding = " " * prefix_len
+ lines.each do |line|
+ stderr.puts "#{padding}#{line}"
+ end
end
+ rescue Errno::EPIPE => e
+ raise e if @config[:verbosity] >= 2
+
+ exit 0
end
alias :info :log
alias :err :log
+ # Print a Debug
+ #
+ # @param message [String] the text string
+ def debug(message)
+ log("#{color("DEBUG:", :blue, :bold)} #{message}")
+ end
+
# Print a warning message
+ #
+ # @param message [String] the text string
def warn(message)
- log("#{color('WARNING:', :yellow, :bold)} #{message}")
+ log("#{color("WARNING:", :yellow, :bold)} #{message}")
end
# Print an error message
+ #
+ # @param message [String] the text string
def error(message)
- log("#{color('ERROR:', :red, :bold)} #{message}")
+ log("#{color("ERROR:", :red, :bold)} #{message}")
end
# Print a message describing a fatal error.
+ #
+ # @param message [String] the text string
def fatal(message)
- log("#{color('FATAL:', :red, :bold)} #{message}")
+ log("#{color("FATAL:", :red, :bold)} #{message}")
+ end
+
+ # Print a message describing a fatal error and exit 1
+ #
+ # @param message [String] the text string
+ def fatal!(message)
+ fatal(message)
+ exit 1
end
def color(string, *colors)
if color?
- highline.color(string, *colors)
+ pastel.decorate(string, *colors)
else
string
end
@@ -116,8 +172,8 @@ class Chef
Chef::Config[:color] && stdout.tty?
end
- def ask(*args, &block)
- highline.ask(*args, &block)
+ def ask(*args, **options, &block)
+ prompt.ask(*args, **options, &block)
end
def list(*args)
@@ -138,7 +194,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 +211,11 @@ 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
@@ -173,12 +228,12 @@ class Chef
def edit_data(data, parse_output = true, object_class: nil)
output = Chef::JSONCompat.to_json_pretty(data)
- if !config[:disable_editing]
+ unless config[:disable_editing]
Tempfile.open([ "knife-edit-", ".json" ]) do |tf|
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/ for details." unless system("#{config[:editor]} #{tf.path}")
output = IO.read(tf.path)
end
@@ -186,8 +241,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 +269,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 +301,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/core/windows_bootstrap_context.rb b/lib/chef/knife/core/windows_bootstrap_context.rb
new file mode 100644
index 0000000000..fa8b43f383
--- /dev/null
+++ b/lib/chef/knife/core/windows_bootstrap_context.rb
@@ -0,0 +1,406 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+# Copyright:: Copyright (c) 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 "bootstrap_context"
+require_relative "../../util/path_helper"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Knife
+ module Core
+ # Instances of BootstrapContext are the context objects (i.e., +self+) for
+ # bootstrap templates. For backwards compatibility, they +must+ set the
+ # following instance variables:
+ # * @config - a hash of knife's config values
+ # * @run_list - the run list for the node to bootstrap
+ #
+ class WindowsBootstrapContext < BootstrapContext
+ attr_accessor :config
+ attr_accessor :chef_config
+ attr_accessor :secret
+
+ def initialize(config, run_list, chef_config, secret = nil)
+ @config = config
+ @run_list = run_list
+ @chef_config = chef_config
+ @secret = secret
+ super(config, run_list, chef_config, secret)
+ end
+
+ def validation_key
+ if File.exist?(File.expand_path(chef_config[:validation_key]))
+ IO.read(File.expand_path(chef_config[:validation_key]))
+ else
+ false
+ end
+ end
+
+ def encrypted_data_bag_secret
+ escape_and_echo(@secret)
+ end
+
+ def trusted_certs_script
+ @trusted_certs_script ||= trusted_certs_content
+ end
+
+ def config_content
+ # The windows: true / windows: false in the block that follows is more than a bit weird. The way to read this is that we need
+ # the e.g. var_chef_dir to be rendered for the windows value ("C:\chef"), but then we are rendering into a file to be read by
+ # ruby, so we don't actually care about forward-vs-backslashes and by rendering into unix we avoid having to deal with the
+ # double-backwhacking of everything. So we expect to see:
+ #
+ # file_cache_path "C:/chef"
+ #
+ # Which is mildly odd, but should be entirely correct as far as ruby cares.
+ #
+ client_rb = <<~CONFIG
+ chef_server_url "#{chef_config[:chef_server_url]}"
+ validation_client_name "#{chef_config[:validation_client_name]}"
+ file_cache_path "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.var_chef_dir(windows: true))}\\\\cache"
+ file_backup_path "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.var_chef_dir(windows: true))}\\\\backup"
+ cache_options ({:path => "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\cache\\\\checksums", :skip_expires => true})
+ CONFIG
+
+ unless chef_config[:chef_license].nil?
+ client_rb << "chef_license \"#{chef_config[:chef_license]}\"\n"
+ end
+
+ if config[:chef_node_name]
+ client_rb << %Q{node_name "#{config[:chef_node_name]}"\n}
+ else
+ client_rb << "# Using default node name (fqdn)\n"
+ end
+
+ if config[:config_log_level]
+ client_rb << %Q{log_level :#{config[:config_log_level]}\n}
+ else
+ client_rb << "log_level :auto\n"
+ end
+
+ client_rb << "log_location #{get_log_location}"
+
+ # We configure :verify_api_cert only when it's overridden on the CLI
+ # or when specified in the knife config.
+ if !config[:node_verify_api_cert].nil? || config.key?(:verify_api_cert)
+ value = config[:node_verify_api_cert].nil? ? config[:verify_api_cert] : config[:node_verify_api_cert]
+ client_rb << %Q{verify_api_cert #{value}\n}
+ end
+
+ # We configure :ssl_verify_mode only when it's overridden on the CLI
+ # or when specified in the knife config.
+ if config[:node_ssl_verify_mode] || config.key?(:ssl_verify_mode)
+ value = case config[:node_ssl_verify_mode]
+ when "peer"
+ :verify_peer
+ when "none"
+ :verify_none
+ when nil
+ config[:ssl_verify_mode]
+ else
+ nil
+ end
+
+ if value
+ client_rb << %Q{ssl_verify_mode :#{value}\n}
+ end
+ end
+
+ if config[:ssl_verify_mode]
+ client_rb << %Q{ssl_verify_mode :#{config[:ssl_verify_mode]}\n}
+ end
+
+ if config[:bootstrap_proxy]
+ client_rb << "\n"
+ client_rb << %Q{http_proxy "#{config[:bootstrap_proxy]}"\n}
+ client_rb << %Q{https_proxy "#{config[:bootstrap_proxy]}"\n}
+ client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n} if config[:bootstrap_no_proxy]
+ end
+
+ if config[:bootstrap_no_proxy]
+ client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n}
+ end
+
+ if secret
+ client_rb << %Q{encrypted_data_bag_secret "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\encrypted_data_bag_secret"\n}
+ end
+
+ unless trusted_certs_script.empty?
+ client_rb << %Q{trusted_certs_dir "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\trusted_certs"\n}
+ end
+
+ if chef_config[:fips]
+ client_rb << "fips true\n"
+ end
+
+ escape_and_echo(client_rb)
+ end
+
+ def get_log_location
+ if chef_config[:config_log_location].equal?(:win_evt)
+ %Q{:#{chef_config[:config_log_location]}\n}
+ elsif chef_config[:config_log_location].equal?(:syslog)
+ raise "syslog is not supported for log_location on Windows OS\n"
+ elsif chef_config[:config_log_location].equal?(STDOUT)
+ "STDOUT\n"
+ elsif chef_config[:config_log_location].equal?(STDERR)
+ "STDERR\n"
+ elsif chef_config[:config_log_location].nil? || chef_config[:config_log_location].empty?
+ "STDOUT\n"
+ elsif chef_config[:config_log_location]
+ %Q{"#{chef_config[:config_log_location]}"\n}
+ else
+ "STDOUT\n"
+ end
+ end
+
+ def start_chef
+ c_opscode_dir = ChefConfig::PathHelper.cleanpath(ChefConfig::Config.c_opscode_dir, windows: true)
+ client_rb = clean_etc_chef_file("client.rb")
+ first_boot = clean_etc_chef_file("first-boot.json")
+
+ bootstrap_environment_option = bootstrap_environment.nil? ? "" : " -E #{bootstrap_environment}"
+
+ start_chef = "SET \"PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;#{c_opscode_dir}\\bin;#{c_opscode_dir}\\embedded\\bin\;%PATH%\"\n"
+ start_chef << "#{ChefUtils::Dist::Infra::CLIENT} -c #{client_rb} -j #{first_boot}#{bootstrap_environment_option}\n"
+ end
+
+ def win_wget
+ # I tried my best to figure out how to properly url decode and switch / to \
+ # but this is VBScript - so I don't really care that badly.
+ win_wget = <<~WGET
+ url = WScript.Arguments.Named("url")
+ path = WScript.Arguments.Named("path")
+ proxy = null
+ '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
+ '* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
+ '* is possibly a network file path.
+ If InStr(url, "file://") = 1 Then
+ url = Unescape(url)
+ If InStr(url, "file:///") = 1 Then
+ sourcePath = Mid(url, Len("file:///") + 1)
+ Else
+ sourcePath = Mid(url, Len("file:") + 1)
+ End If
+ sourcePath = Replace(sourcePath, "/", "\\")
+
+ Set objFSO = CreateObject("Scripting.FileSystemObject")
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
+ objFSO.CopyFile sourcePath, path, true
+ Set objFSO = Nothing
+
+ Else
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
+ Set wshShell = CreateObject( "WScript.Shell" )
+ Set objUserVariables = wshShell.Environment("USER")
+
+ rem http proxy is optional
+ rem attempt to read from HTTP_PROXY env var first
+ On Error Resume Next
+
+ If NOT (objUserVariables("HTTP_PROXY") = "") Then
+ proxy = objUserVariables("HTTP_PROXY")
+
+ rem fall back to named arg
+ ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
+ proxy = WScript.Arguments.Named("proxy")
+ End If
+
+ If NOT isNull(proxy) Then
+ rem setProxy method is only available on ServerXMLHTTP 6.0+
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
+ objXMLHTTP.setProxy 2, proxy
+ End If
+
+ On Error Goto 0
+
+ objXMLHTTP.open "GET", url, false
+ objXMLHTTP.send()
+ If objXMLHTTP.Status = 200 Then
+ Set objADOStream = CreateObject("ADODB.Stream")
+ objADOStream.Open
+ objADOStream.Type = 1
+ objADOStream.Write objXMLHTTP.ResponseBody
+ objADOStream.Position = 0
+ Set objFSO = Createobject("Scripting.FileSystemObject")
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
+ Set objFSO = Nothing
+ objADOStream.SaveToFile path
+ objADOStream.Close
+ Set objADOStream = Nothing
+ End If
+ Set objXMLHTTP = Nothing
+ End If
+ WGET
+ escape_and_echo(win_wget)
+ end
+
+ def win_wget_ps
+ win_wget_ps = <<~WGET_PS
+ param(
+ [String] $remoteUrl,
+ [String] $localPath
+ )
+
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+
+ $ProxyUrl = $env:http_proxy;
+ $webClient = new-object System.Net.WebClient;
+
+ if ($ProxyUrl -ne '') {
+ $WebProxy = New-Object System.Net.WebProxy($ProxyUrl,$true)
+ $WebClient.Proxy = $WebProxy
+ }
+
+ $webClient.DownloadFile($remoteUrl, $localPath);
+ WGET_PS
+
+ escape_and_echo(win_wget_ps)
+ end
+
+ def install_chef
+ # The normal install command uses regular double quotes in
+ # the install command, so request such a string from install_command
+ install_command('"') + "\n" + fallback_install_task_command
+ end
+
+ def clean_etc_chef_file(path)
+ ChefConfig::PathHelper.cleanpath(etc_chef_file(path), windows: true)
+ end
+
+ def etc_chef_file(path)
+ "#{bootstrap_directory}/#{path}"
+ end
+
+ def bootstrap_directory
+ ChefConfig::Config.etc_chef_dir(windows: true)
+ end
+
+ def local_download_path
+ "%TEMP%\\#{ChefUtils::Dist::Infra::CLIENT}-latest.msi"
+ end
+
+ # Build a URL to query www.chef.io that will redirect to the correct
+ # Chef Infra msi download.
+ def msi_url(machine_os = nil, machine_arch = nil, download_context = nil)
+ if config[:msi_url].nil? || config[:msi_url].empty?
+ url = "https://www.chef.io/chef/download?p=windows"
+ url += "&pv=#{machine_os}" unless machine_os.nil?
+ url += "&m=#{machine_arch}" unless machine_arch.nil?
+ url += "&DownloadContext=#{download_context}" unless download_context.nil?
+ url += "&channel=#{config[:channel]}"
+ url += "&v=#{version_to_install}"
+ else
+ config[:msi_url]
+ end
+ end
+
+ def first_boot
+ escape_and_echo(super.to_json)
+ end
+
+ # escape WIN BATCH special chars
+ # and prefixes each line with an
+ # echo
+ def escape_and_echo(file_contents)
+ file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
+ end
+
+ private
+
+ def install_command(executor_quote)
+ "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
+ end
+
+ # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
+ # This string should contain both the commands necessary to both create the files, as well as their content
+ def trusted_certs_content
+ content = ""
+ if chef_config[:trusted_certs_dir]
+ Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
+ content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
+ escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
+ end
+ end
+ content
+ end
+
+ def client_d_content
+ content = ""
+ if chef_config[:client_d_dir] && File.exist?(chef_config[:client_d_dir])
+ root = Pathname(chef_config[:client_d_dir])
+ root.find do |f|
+ relative = f.relative_path_from(root)
+ if f != root
+ file_on_node = "#{bootstrap_directory}/client.d/#{relative}".tr("/", "\\")
+ if f.directory?
+ content << "mkdir #{file_on_node}\n"
+ else
+ content << "> #{file_on_node} (\n" +
+ escape_and_echo(IO.read(File.expand_path(f))) + "\n)\n"
+ end
+ end
+ end
+ end
+ content
+ end
+
+ def fallback_install_task_command
+ # This command will be executed by schtasks.exe in the batch
+ # code below. To handle tasks that contain arguments that
+ # need to be double quoted, schtasks allows the use of single
+ # quotes that will later be converted to double quotes
+ command = install_command("'")
+ <<~EOH
+ @set MSIERRORCODE=!ERRORLEVEL!
+ @if ERRORLEVEL 1 (
+ @echo WARNING: Failed to install #{ChefUtils::Dist::Infra::PRODUCT} MSI package in remote context with status code !MSIERRORCODE!.
+ @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
+ @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
+ @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
+ @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
+ @echo WARNING: Retrying installation with local context...
+ @schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
+
+ @if ERRORLEVEL 1 (
+ @echo ERROR: Failed to create #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL! > "&2"
+ ) else (
+ @echo Successfully created scheduled task to install #{ChefUtils::Dist::Infra::PRODUCT}.
+ @schtasks /run /tn chefclientbootstraptask
+ @if ERRORLEVEL 1 (
+ @echo ERROR: Failed to execute #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL!. > "&2"
+ ) else (
+ @echo Successfully started #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task.
+ @echo Waiting for installation to complete -- this may take a few minutes...
+ waitfor chefclientinstalldone /t 600
+ if ERRORLEVEL 1 (
+ @echo ERROR: Timed out waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install
+ ) else (
+ @echo Finished waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install.
+ )
+ @schtasks /delete /f /tn chefclientbootstraptask > NUL
+ )
+ )
+ ) else (
+ @echo Successfully installed #{ChefUtils::Dist::Infra::PRODUCT} package.
+ )
+ EOH
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/data_bag_create.rb b/lib/chef/knife/data_bag_create.rb
index 196278bb80..11448c69b7 100644
--- a/lib/chef/knife/data_bag_create.rb
+++ b/lib/chef/knife/data_bag_create.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/data_bag_secret_options"
+require_relative "../knife"
+require_relative "data_bag_secret_options"
class Chef
class Knife
@@ -26,8 +26,8 @@ class Chef
include DataBagSecretOptions
deps do
- require "chef/data_bag"
- require "chef/encrypted_data_bag_item"
+ require_relative "../data_bag"
+ require_relative "../encrypted_data_bag_item"
end
banner "knife data bag create BAG [ITEM] (options)"
@@ -49,13 +49,16 @@ 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::HTTPClientException => e
+ raise unless /^404/.match?(e.to_s)
+
+ # 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_delete.rb b/lib/chef/knife/data_bag_delete.rb
index c1ea2013c8..ab38244e91 100644
--- a/lib/chef/knife/data_bag_delete.rb
+++ b/lib/chef/knife/data_bag_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class DataBagDelete < Knife
deps do
- require "chef/data_bag"
+ require_relative "../data_bag"
end
banner "knife data bag delete BAG [ITEM] (options)"
diff --git a/lib/chef/knife/data_bag_edit.rb b/lib/chef/knife/data_bag_edit.rb
index 5d76762058..1935f2149e 100644
--- a/lib/chef/knife/data_bag_edit.rb
+++ b/lib/chef/knife/data_bag_edit.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/data_bag_secret_options"
+require_relative "../knife"
+require_relative "data_bag_secret_options"
class Chef
class Knife
@@ -26,8 +26,8 @@ class Chef
include DataBagSecretOptions
deps do
- require "chef/data_bag_item"
- require "chef/encrypted_data_bag_item"
+ require_relative "../data_bag_item"
+ require_relative "../encrypted_data_bag_item"
end
banner "knife data bag edit BAG ITEM (options)"
@@ -37,13 +37,13 @@ class Chef
item = Chef::DataBagItem.load(bag, item_name)
if encrypted?(item.raw_data)
if encryption_secret_provided_ignore_encrypt_flag?
- return Chef::EncryptedDataBagItem.new(item, read_secret).to_hash, true
+ [Chef::EncryptedDataBagItem.new(item, read_secret).to_hash, true]
else
ui.fatal("You cannot edit an encrypted data bag without providing the secret.")
exit(1)
end
else
- return item, false
+ [item.raw_data, false]
end
end
diff --git a/lib/chef/knife/data_bag_from_file.rb b/lib/chef/knife/data_bag_from_file.rb
index 30b9de3386..5f48b0a934 100644
--- a/lib/chef/knife/data_bag_from_file.rb
+++ b/lib/chef/knife/data_bag_from_file.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/util/path_helper"
-require "chef/knife/data_bag_secret_options"
+require_relative "../knife"
+require_relative "data_bag_secret_options"
class Chef
class Knife
@@ -27,20 +26,20 @@ class Chef
include DataBagSecretOptions
deps do
- require "chef/data_bag"
- require "chef/data_bag_item"
- require "chef/knife/core/object_loader"
- require "chef/json_compat"
- require "chef/encrypted_data_bag_item"
+ require_relative "../util/path_helper"
+ require_relative "../data_bag"
+ require_relative "../data_bag_item"
+ require_relative "core/object_loader"
+ require_relative "../encrypted_data_bag_item"
end
banner "knife data bag from file BAG FILE|FOLDER [FILE|FOLDER..] (options)"
category "data bag"
option :all,
- :short => "-a",
- :long => "--all",
- :description => "Upload all data bags or all items for specified data bags"
+ short: "-a",
+ long: "--all",
+ description: "Upload all data bags or all items for specified data bags."
def loader
@loader ||= Knife::Core::ObjectLoader.new(DataBagItem, ui)
@@ -84,7 +83,7 @@ class Chef
items ||= find_all_data_bag_items(data_bag)
item_paths = normalize_item_paths(items)
item_paths.each do |item_path|
- item = loader.load_from("#{data_bags_path}", data_bag, item_path)
+ item = loader.load_from((data_bags_path).to_s, data_bag, item_path)
item = if encryption_secret_provided?
Chef::EncryptedDataBagItem.encrypt_data_bag_item(item, read_secret)
else
@@ -99,7 +98,7 @@ class Chef
end
def normalize_item_paths(args)
- paths = Array.new
+ paths = []
args.each do |path|
if File.directory?(path)
paths.concat(Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(path), "*.json")))
diff --git a/lib/chef/knife/data_bag_list.rb b/lib/chef/knife/data_bag_list.rb
index d507925ec8..801bf588b4 100644
--- a/lib/chef/knife/data_bag_list.rb
+++ b/lib/chef/knife/data_bag_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class DataBagList < Knife
deps do
- require "chef/data_bag"
+ require_relative "../data_bag"
end
banner "knife data bag list (options)"
category "data bag"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
output(format_list_for_display(Chef::DataBag.list))
diff --git a/lib/chef/knife/data_bag_secret_options.rb b/lib/chef/knife/data_bag_secret_options.rb
index b426cd442c..8f9f96502f 100644
--- a/lib/chef/knife/data_bag_secret_options.rb
+++ b/lib/chef/knife/data_bag_secret_options.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "mixlib/cli"
-require "chef/config"
-require "chef/encrypted_data_bag_item/check_encrypted"
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require_relative "../config"
+require_relative "../encrypted_data_bag_item/check_encrypted"
class Chef
class Knife
@@ -34,24 +34,19 @@ class Chef
# are provided.
def self.included(base)
- base.option :secret,
- :short => "-s SECRET",
- :long => "--secret ",
- :description => "The secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret'",
- # Need to store value from command line in separate variable - knife#merge_configs populates same keys
- # on config object from
- :proc => Proc.new { |s| set_cl_secret(s) }
-
- base.option :secret_file,
- :long => "--secret-file SECRET_FILE",
- :description => "A file containing the secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret_file'",
- :proc => Proc.new { |sf| set_cl_secret_file(sf) }
+ base.option :cl_secret,
+ long: "--secret SECRET",
+ description: "The secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret'."
+
+ base.option :cl_secret_file,
+ long: "--secret-file SECRET_FILE",
+ description: "A file containing the secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret_file'."
base.option :encrypt,
- :long => "--encrypt",
- :description => "If 'secret' or 'secret_file' is present in your config, then encrypt data bags using it",
- :boolean => true,
- :default => false
+ long: "--encrypt",
+ description: "If 'secret' or 'secret_file' is present in your config, then encrypt data bags using it.",
+ boolean: true,
+ default: false
end
def encryption_secret_provided?
@@ -65,27 +60,27 @@ class Chef
def read_secret
# Moving the non 'compile-time' requires into here to speed up knife command loading
# IE, if we are not running 'knife data bag *' we don't need to load 'chef/encrypted_data_bag_item'
- require "chef/encrypted_data_bag_item"
+ require_relative "../encrypted_data_bag_item"
- if has_cl_secret?
- config[:secret]
- elsif has_cl_secret_file?
- Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
- elsif secret = knife_config[:secret]
+ if config[:cl_secret]
+ config[:cl_secret]
+ elsif config[:cl_secret_file]
+ Chef::EncryptedDataBagItem.load_secret(config[:cl_secret_file])
+ elsif secret = config[:secret]
secret
else
- secret_file = knife_config[:secret_file]
+ secret_file = config[:secret_file]
Chef::EncryptedDataBagItem.load_secret(secret_file)
end
end
def validate_secrets
- if has_cl_secret? && has_cl_secret_file?
+ if config[:cl_secret] && config[:cl_secret_file]
ui.fatal("Please specify only one of --secret, --secret-file")
exit(1)
end
- if knife_config[:secret] && knife_config[:secret_file]
+ if config[:secret] && config[:secret_file]
ui.fatal("Please specify only one of 'secret' or 'secret_file' in your config file")
exit(1)
end
@@ -95,45 +90,30 @@ 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
- return true if has_cl_secret? || has_cl_secret_file?
+ return true if config[:cl_secret] || config[:cl_secret_file]
if need_encrypt_flag
if config[:encrypt]
- unless knife_config[:secret] || knife_config[:secret_file]
+ unless config[:secret] || config[:secret_file]
ui.fatal("No secret or secret_file specified in config, unable to encrypt item.")
exit(1)
end
return true
end
return false
- elsif knife_config[:secret] || knife_config[:secret_file]
+ elsif config[:secret] || config[:secret_file]
# Certain situations (show and bootstrap) don't need a --encrypt flag to use the config file secret
return true
end
- return false
- end
-
- def has_cl_secret?
- Chef::Config[:knife].has_key?(:cl_secret)
- end
-
- def self.set_cl_secret(s)
- Chef::Config[:knife][:cl_secret] = s
- end
-
- def has_cl_secret_file?
- Chef::Config[:knife].has_key?(:cl_secret_file)
- end
-
- def self.set_cl_secret_file(sf)
- Chef::Config[:knife][:cl_secret_file] = sf
+ false
end
def knife_config
+ Chef.deprecated(:knife_bootstrap_apis, "The `knife_config` bootstrap helper has been deprecated, use the properly merged `config` helper instead")
Chef::Config.key?(:knife) ? Chef::Config[:knife] : {}
end
diff --git a/lib/chef/knife/data_bag_show.rb b/lib/chef/knife/data_bag_show.rb
index ea9c390f19..cb7b56c333 100644
--- a/lib/chef/knife/data_bag_show.rb
+++ b/lib/chef/knife/data_bag_show.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/data_bag_secret_options"
+require_relative "../knife"
+require_relative "data_bag_secret_options"
class Chef
class Knife
@@ -26,8 +26,8 @@ class Chef
include DataBagSecretOptions
deps do
- require "chef/data_bag"
- require "chef/encrypted_data_bag_item"
+ require_relative "../data_bag"
+ require_relative "../encrypted_data_bag_item"
end
banner "knife data bag show BAG [ITEM] (options)"
@@ -44,14 +44,14 @@ class Chef
# Users do not need to pass --encrypt to read data, we simply try to use the provided secret
ui.info("Encrypted data bag detected, decrypting with provided secret.")
raw = Chef::EncryptedDataBagItem.load(@name_args[0],
- @name_args[1],
- secret)
- format_for_display(raw.to_hash)
+ @name_args[1],
+ secret)
+ format_for_display(raw.to_h)
elsif encrypted && !secret
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/delete.rb b/lib/chef/knife/delete.rb
index cf6ca09878..3e5c545017 100644
--- a/lib/chef/knife/delete.rb
+++ b/lib/chef/knife/delete.rb
@@ -1,4 +1,20 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
@@ -8,25 +24,27 @@ class Chef
category "path-based"
deps do
- require "chef/chef_fs/file_system"
+ require_relative "../chef_fs/file_system"
end
option :recurse,
- :short => "-r",
- :long => "--[no-]recurse",
- :boolean => true,
- :default => false,
- :description => "Delete directories recursively."
+ short: "-r",
+ long: "--[no-]recurse",
+ boolean: true,
+ default: false,
+ description: "Delete directories recursively."
+
option :both,
- :long => "--both",
- :boolean => true,
- :default => false,
- :description => "Delete both the local and remote copies."
+ long: "--both",
+ boolean: true,
+ default: false,
+ description: "Delete both the local and remote copies."
+
option :local,
- :long => "--local",
- :boolean => true,
- :default => false,
- :description => "Delete the local copy (leave the remote copy)."
+ long: "--local",
+ boolean: true,
+ default: false,
+ description: "Delete the local copy (leave the remote copy)."
def run
if name_args.length == 0
@@ -78,21 +96,21 @@ class Chef
found_any = false
error = false
results.each do |result|
- begin
- result.delete(config[:recurse])
- deleted_any = true
- found_any = true
- rescue Chef::ChefFS::FileSystem::NotFoundError
- # This is not an error unless *all* of them were not found
- rescue Chef::ChefFS::FileSystem::MustDeleteRecursivelyError => e
- ui.error "#{format_path_with_root(e.entry)} must be deleted recursively! Pass -r to knife delete."
- found_any = true
- error = true
- rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
- ui.error "#{format_path_with_root(e.entry)} #{e.reason}."
- found_any = true
- error = true
- end
+
+ result.delete(config[:recurse])
+ deleted_any = true
+ found_any = true
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ # This is not an error unless *all* of them were not found
+ rescue Chef::ChefFS::FileSystem::MustDeleteRecursivelyError => e
+ ui.error "#{format_path_with_root(e.entry)} must be deleted recursively! Pass -r to knife delete."
+ found_any = true
+ error = true
+ rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
+ ui.error "#{format_path_with_root(e.entry)} #{e.reason}."
+ found_any = true
+ error = true
+
end
if deleted_any
output("Deleted #{format_path(results[0])}")
diff --git a/lib/chef/knife/deps.rb b/lib/chef/knife/deps.rb
index e773f65106..f620b53bfa 100644
--- a/lib/chef/knife/deps.rb
+++ b/lib/chef/knife/deps.rb
@@ -1,4 +1,20 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
@@ -8,22 +24,24 @@ class Chef
category "path-based"
deps do
- require "chef/chef_fs/file_system"
- require "chef/run_list"
+ require_relative "../chef_fs/file_system"
+ require_relative "../run_list"
end
option :recurse,
- :long => "--[no-]recurse",
- :boolean => true,
- :description => "List dependencies recursively (default: true). Only works with --tree."
+ long: "--[no-]recurse",
+ boolean: true,
+ description: "List dependencies recursively (default: true). Only works with --tree."
+
option :tree,
- :long => "--tree",
- :boolean => true,
- :description => "Show dependencies in a visual tree. May show duplicates."
+ long: "--tree",
+ boolean: true,
+ description: "Show dependencies in a visual tree. May show duplicates."
+
option :remote,
- :long => "--remote",
- :boolean => true,
- :description => "List dependencies on the server instead of the local filesystem"
+ long: "--remote",
+ boolean: true,
+ description: "List dependencies on the server instead of the local filesystem."
attr_accessor :exit_code
@@ -49,7 +67,7 @@ class Chef
end
def print_flattened_dependencies(entry, dependencies)
- if !dependencies[entry.path]
+ unless dependencies[entry.path]
dependencies[entry.path] = get_dependencies(entry)
dependencies[entry.path].each do |child|
child_entry = Chef::ChefFS::FileSystem.resolve_path(@root, child)
@@ -60,8 +78,8 @@ class Chef
end
def print_dependencies_tree(entry, dependencies, printed = {}, depth = 0)
- dependencies[entry.path] = get_dependencies(entry) if !dependencies[entry.path]
- output "#{' ' * depth}#{format_path(entry)}"
+ dependencies[entry.path] = get_dependencies(entry) unless dependencies[entry.path]
+ output "#{" " * depth}#{format_path(entry)}"
if !printed[entry.path] && (config[:recurse] || depth == 0)
printed[entry.path] = true
dependencies[entry.path].each do |child|
@@ -72,49 +90,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}" }
-
- 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
+ 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 == "/roles"
+ role = Chef::JSONCompat.parse(entry.read)
+ result = []
+ if role["run_list"]
+ dependencies_from_runlist(role["run_list"]).each do |dependency|
+ result << dependency unless 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 unless 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/diff.rb b/lib/chef/knife/diff.rb
index d965490f0a..3e9336aacc 100644
--- a/lib/chef/knife/diff.rb
+++ b/lib/chef/knife/diff.rb
@@ -1,4 +1,20 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
@@ -8,33 +24,32 @@ class Chef
category "path-based"
deps do
- require "chef/chef_fs/command_line"
+ require_relative "../chef_fs/command_line"
end
option :recurse,
- :long => "--[no-]recurse",
- :boolean => true,
- :default => true,
- :description => "List directories recursively."
+ long: "--[no-]recurse",
+ boolean: true,
+ default: true,
+ description: "List directories recursively."
option :name_only,
- :long => "--name-only",
- :boolean => true,
- :description => "Only show names of modified files."
+ long: "--name-only",
+ boolean: true,
+ description: "Only show names of modified files."
option :name_status,
- :long => "--name-status",
- :boolean => true,
- :description => "Only show names and statuses of modified files: Added, Deleted, Modified, and Type Changed."
+ long: "--name-status",
+ boolean: true,
+ description: "Only show names and statuses of modified files: Added, Deleted, Modified, and Type Changed."
option :diff_filter,
- :long => "--diff-filter=[(A|D|M|T)...[*]]",
- :description => "Select only files that are Added (A), Deleted (D), Modified (M), or have their type (i.e. regular file, directory) changed (T). Any combination of the filter characters (including none) can be used. When * (All-or-none) is added to the combination, all paths are selected if
- there is any file that matches other criteria in the comparison; if there is no file that matches other criteria, nothing is selected."
+ long: "--diff-filter=[(A|D|M|T)...[*]]",
+ description: "Select only files that are Added (A), Deleted (D), Modified (M), or have their type (i.e. regular file, directory) changed (T). Any combination of the filter characters (including none) can be used. When * (All-or-none) is added to the combination, all paths are selected if there is any file that matches other criteria in the comparison; if there is no file that matches other criteria, nothing is selected."
option :cookbook_version,
- :long => "--cookbook-version VERSION",
- :description => "Version of cookbook to download (if there are multiple versions and cookbook_versions is false)"
+ long: "--cookbook-version VERSION",
+ description: "Version of cookbook to download (if there are multiple versions and cookbook_versions is false)."
def run
if config[:name_only]
diff --git a/lib/chef/knife/download.rb b/lib/chef/knife/download.rb
index ac8420d468..ab8c92a1c0 100644
--- a/lib/chef/knife/download.rb
+++ b/lib/chef/knife/download.rb
@@ -1,4 +1,20 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
@@ -8,43 +24,43 @@ class Chef
category "path-based"
deps do
- require "chef/chef_fs/command_line"
+ require_relative "../chef_fs/command_line"
end
option :recurse,
- :long => "--[no-]recurse",
- :boolean => true,
- :default => true,
- :description => "List directories recursively."
+ long: "--[no-]recurse",
+ boolean: true,
+ default: true,
+ description: "List directories recursively."
option :purge,
- :long => "--[no-]purge",
- :boolean => true,
- :default => false,
- :description => "Delete matching local files and directories that do not exist remotely."
+ long: "--[no-]purge",
+ boolean: true,
+ default: false,
+ description: "Delete matching local files and directories that do not exist remotely."
option :force,
- :long => "--[no-]force",
- :boolean => true,
- :default => false,
- :description => "Force upload of files even if they match (quicker and harmless, but doesn't print out what it changed)"
+ long: "--[no-]force",
+ boolean: true,
+ default: false,
+ description: "Force download of files even if they match (quicker and harmless, but doesn't print out what it changed)."
option :dry_run,
- :long => "--dry-run",
- :short => "-n",
- :boolean => true,
- :default => false,
- :description => "Don't take action, only print what would happen"
+ long: "--dry-run",
+ short: "-n",
+ boolean: true,
+ default: false,
+ description: "Don't take action, only print what would happen."
option :diff,
- :long => "--[no-]diff",
- :boolean => true,
- :default => true,
- :description => "Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff"
+ long: "--[no-]diff",
+ boolean: true,
+ default: true,
+ description: "Turn off to avoid downloading existing files; only new (and possibly deleted) files with --no-diff."
option :cookbook_version,
- :long => "--cookbook-version VERSION",
- :description => "Version of cookbook to download (if there are multiple versions and cookbook_versions is false)"
+ long: "--cookbook-version VERSION",
+ description: "Version of cookbook to download (if there are multiple versions and cookbook_versions is false)."
def run
if name_args.length == 0
diff --git a/lib/chef/knife/edit.rb b/lib/chef/knife/edit.rb
index 8489e4e179..caca201566 100644
--- a/lib/chef/knife/edit.rb
+++ b/lib/chef/knife/edit.rb
@@ -1,4 +1,20 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
@@ -8,14 +24,14 @@ class Chef
category "path-based"
deps do
- require "chef/chef_fs/file_system"
- require "chef/chef_fs/file_system/not_found_error"
+ require_relative "../chef_fs/file_system"
+ require_relative "../chef_fs/file_system/exceptions"
end
option :local,
- :long => "--local",
- :boolean => true,
- :description => "Show local files instead of remote"
+ long: "--local",
+ boolean: true,
+ description: "Show local files instead of remote."
def run
# Get the matches (recursively)
@@ -50,15 +66,15 @@ class Chef
end
def edit_text(text, extension)
- if !config[:disable_editing]
+ unless config[:disable_editing]
Tempfile.open([ "knife-edit-", extension ]) do |file|
# Write the text to a temporary file
file.write(text)
file.close
# 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."
+ unless system("#{config[:editor]} #{file.path}")
+ raise "Please set EDITOR environment variable. See https://docs.chef.io/knife_setup/ 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..22abee59c8 100644
--- a/lib/chef/knife/environment_compare.rb
+++ b/lib/chef/knife/environment_compare.rb
@@ -16,29 +16,29 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class EnvironmentCompare < Knife
deps do
- require "chef/environment"
+ require_relative "../environment"
end
banner "knife environment compare [ENVIRONMENT..] (options)"
option :all,
- :short => "-a",
- :long => "--all",
- :description => "Show all cookbooks",
- :boolean => true
+ short: "-a",
+ long: "--all",
+ description: "Show all cookbooks.",
+ boolean: true
option :mismatch,
- :short => "-m",
- :long => "--mismatch",
- :description => "Only show mismatching versions",
- :boolean => true
+ short: "-m",
+ long: "--mismatch",
+ description: "Only show mismatching versions.",
+ boolean: true
def run
# Get the commandline environments or all if none are provided.
@@ -81,7 +81,7 @@ class Chef
def constraint_list(environments)
constraints = {}
- environments.each do |env, url|
+ environments.each do |env, url| # rubocop:disable Style/HashEachMethods
# Because you cannot modify the default environment I filter it out here.
unless env == "_default"
envdata = Chef::Environment.load(env)
@@ -94,21 +94,22 @@ 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
next if config[:mismatch]
+
color = :white
else
color = :yellow
diff --git a/lib/chef/knife/environment_create.rb b/lib/chef/knife/environment_create.rb
index cfc1bc268c..a724f72d4f 100644
--- a/lib/chef/knife/environment_create.rb
+++ b/lib/chef/knife/environment_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class EnvironmentCreate < Knife
deps do
- require "chef/environment"
- require "chef/json_compat"
+ require_relative "../environment"
end
banner "knife environment create ENVIRONMENT (options)"
option :description,
- :short => "-d DESCRIPTION",
- :long => "--description DESCRIPTION",
- :description => "The environment description"
+ short: "-d DESCRIPTION",
+ long: "--description DESCRIPTION",
+ description: "The environment description."
def run
env_name = @name_args[0]
diff --git a/lib/chef/knife/environment_delete.rb b/lib/chef/knife/environment_delete.rb
index 869d1c74fe..ec1b7cb8d8 100644
--- a/lib/chef/knife/environment_delete.rb
+++ b/lib/chef/knife/environment_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class EnvironmentDelete < Knife
deps do
- require "chef/environment"
- require "chef/json_compat"
+ require_relative "../environment"
end
banner "knife environment delete ENVIRONMENT (options)"
diff --git a/lib/chef/knife/environment_edit.rb b/lib/chef/knife/environment_edit.rb
index 43f0b067ae..7c6105a6c0 100644
--- a/lib/chef/knife/environment_edit.rb
+++ b/lib/chef/knife/environment_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class EnvironmentEdit < Knife
deps do
- require "chef/environment"
- require "chef/json_compat"
+ require_relative "../environment"
end
banner "knife environment edit ENVIRONMENT (options)"
diff --git a/lib/chef/knife/environment_from_file.rb b/lib/chef/knife/environment_from_file.rb
index 5272c8934a..a5011a3abf 100644
--- a/lib/chef/knife/environment_from_file.rb
+++ b/lib/chef/knife/environment_from_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,21 +16,23 @@
# limitations under the License.
#
+require_relative "../knife"
+
class Chef
class Knife
class EnvironmentFromFile < Knife
deps do
- require "chef/environment"
- require "chef/knife/core/object_loader"
+ require_relative "../environment"
+ require_relative "core/object_loader"
end
banner "knife environment from file FILE [FILE..] (options)"
option :all,
- :short => "-a",
- :long => "--all",
- :description => "Upload all environments"
+ short: "-a",
+ long: "--all",
+ description: "Upload all environments."
def loader
@loader ||= Knife::Core::ObjectLoader.new(Chef::Environment, ui)
diff --git a/lib/chef/knife/environment_list.rb b/lib/chef/knife/environment_list.rb
index f278046bf9..7bcdeb6084 100644
--- a/lib/chef/knife/environment_list.rb
+++ b/lib/chef/knife/environment_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class EnvironmentList < Knife
deps do
- require "chef/environment"
- require "chef/json_compat"
+ require_relative "../environment"
end
banner "knife environment list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
output(format_list_for_display(Chef::Environment.list))
diff --git a/lib/chef/knife/environment_show.rb b/lib/chef/knife/environment_show.rb
index 6d260adbd6..e336b2d392 100644
--- a/lib/chef/knife/environment_show.rb
+++ b/lib/chef/knife/environment_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -25,8 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/environment"
- require "chef/json_compat"
+ require_relative "../environment"
end
banner "knife environment show ENVIRONMENT (options)"
diff --git a/lib/chef/knife/exec.rb b/lib/chef/knife/exec.rb
index 0aa8ea2ba8..d3ce2cee24 100644
--- a/lib/chef/knife/exec.rb
+++ b/lib/chef/knife/exec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,30 +16,34 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/util/path_helper"
+require_relative "../knife"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef::Knife::Exec < Chef::Knife
banner "knife exec [SCRIPT] (options)"
+ deps do
+ require_relative "../util/path_helper"
+ end
+
option :exec,
- :short => "-E CODE",
- :long => "--exec CODE",
- :description => "a string of Chef code to execute"
+ short: "-E CODE",
+ long: "--exec CODE",
+ description: "A string of #{ChefUtils::Dist::Infra::PRODUCT} code to execute."
option :script_path,
- :short => "-p PATH:PATH",
- :long => "--script-path PATH:PATH",
- :description => "A colon-separated path to look for scripts in",
- :proc => lambda { |o| o.split(":") }
+ short: "-p PATH:PATH",
+ long: "--script-path PATH:PATH",
+ description: "A colon-separated path to look for scripts in.",
+ proc: lambda { |o| o.split(":") }
deps do
- require "chef/shell/ext"
+ require_relative "../shell/ext"
end
def run
- config[:script_path] ||= Array(Chef::Config[:script_path])
+ config[:script_path] = Array(config[:script_path] || Chef::Config[:script_path])
# Default script paths are chef-repo/.chef/scripts and ~/.chef/scripts
config[:script_path] << File.join(Chef::Knife.chef_config_dir, "scripts") if Chef::Knife.chef_config_dir
@@ -56,6 +60,14 @@ class Chef::Knife::Exec < Chef::Knife
context.instance_eval(IO.read(file), file, 0)
end
else
+ puts "An interactive shell is opened"
+ puts
+ puts "Type your script and do:"
+ puts
+ puts "1. To run the script, use 'Ctrl D'"
+ puts "2. To exit, use 'Ctrl/Shift C'"
+ puts
+ puts "Type here a script..."
script = STDIN.read
context.instance_eval(script, "STDIN", 0)
end
@@ -64,19 +76,19 @@ class Chef::Knife::Exec < Chef::Knife
def find_script(x)
# Try to find a script. First try expanding the path given.
script = File.expand_path(x)
- return script if File.exists?(script)
+ return script if File.exist?(script)
# 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}")
- if File.exists?(test)
+ Chef::Log.trace("Testing: #{test}")
+ if File.exist?(test)
script = test
- Chef::Log.debug("Found: #{test}")
+ Chef::Log.trace("Found: #{test}")
return script
end
end
diff --git a/lib/chef/knife/group_add.rb b/lib/chef/knife/group_add.rb
new file mode 100644
index 0000000000..eccb7dd10c
--- /dev/null
+++ b/lib/chef/knife/group_add.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupAdd < Chef::Knife
+ category "group"
+ banner "knife group add MEMBER_TYPE MEMBER_NAME GROUP_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, group_name = name_args
+
+ if name_args.length != 3
+ show_usage
+ ui.fatal "You must specify member type [client|group|user], member name and group name"
+ exit 1
+ end
+
+ validate_member_name!(group_name)
+ validate_member_type!(member_type)
+ validate_member_name!(member_name)
+
+ if group_name.downcase == "users"
+ ui.fatal "knife group can not manage members of Chef Infra Server's 'users' group, which contains all users."
+ exit 1
+ end
+
+ add_to_group!(member_type, member_name, group_name)
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/group_create.rb b/lib/chef/knife/group_create.rb
new file mode 100644
index 0000000000..4219188951
--- /dev/null
+++ b/lib/chef/knife/group_create.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupCreate < Chef::Knife
+ category "group"
+ banner "knife group create GROUP_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ group_name = name_args[0]
+
+ if name_args.length != 1
+ show_usage
+ ui.fatal "You must specify group name"
+ exit 1
+ end
+
+ validate_member_name!(group_name)
+
+ ui.msg "Creating '#{group_name}' group"
+ rest.post_rest("groups", { groupname: group_name })
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/group_destroy.rb b/lib/chef/knife/group_destroy.rb
new file mode 100644
index 0000000000..433a5cc627
--- /dev/null
+++ b/lib/chef/knife/group_destroy.rb
@@ -0,0 +1,53 @@
+#
+# Author:: Christopher Maier (<cm@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupDestroy < Chef::Knife
+ category "group"
+ banner "knife group destroy GROUP_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ group_name = name_args[0]
+
+ if name_args.length != 1
+ show_usage
+ ui.fatal "You must specify group name"
+ exit 1
+ end
+
+ validate_member_name!(group_name)
+
+ if %w{admins billing-admins clients users}.include?(group_name.downcase)
+ ui.fatal "The '#{group_name}' group is a special group that cannot not be destroyed"
+ exit 1
+ end
+ ui.msg "Destroying '#{group_name}' group"
+ rest.delete_rest("groups/#{group_name}")
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/group_list.rb b/lib/chef/knife/group_list.rb
new file mode 100644
index 0000000000..fc8f00ad6d
--- /dev/null
+++ b/lib/chef/knife/group_list.rb
@@ -0,0 +1,43 @@
+#
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupList < Chef::Knife
+ category "group"
+ banner "knife group list"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ groups = rest.get_rest("groups").keys.sort
+ ui.output(remove_usags(groups))
+ end
+
+ def remove_usags(groups)
+ groups.select { |gname| !is_usag?(gname) }
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/group_remove.rb b/lib/chef/knife/group_remove.rb
new file mode 100644
index 0000000000..07ab19693f
--- /dev/null
+++ b/lib/chef/knife/group_remove.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupRemove < Chef::Knife
+ category "group"
+ banner "knife group remove MEMBER_TYPE MEMBER_NAME GROUP_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ member_type, member_name, group_name = name_args
+
+ if name_args.length != 3
+ show_usage
+ ui.fatal "You must specify member type [client|group|user], member name and group name"
+ exit 1
+ end
+
+ validate_member_name!(group_name)
+ validate_member_type!(member_type)
+ validate_member_name!(member_name)
+
+ if group_name.downcase == "users"
+ ui.fatal "knife-acl can not manage members of the Users group"
+ ui.fatal "please read knife-acl's README.md for more information"
+ exit 1
+ end
+
+ remove_from_group!(member_type, member_name, group_name)
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/group_show.rb b/lib/chef/knife/group_show.rb
new file mode 100644
index 0000000000..6ac53f6b6e
--- /dev/null
+++ b/lib/chef/knife/group_show.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Jeremiah Snapp (<jeremiah@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class GroupShow < Chef::Knife
+ category "group"
+ banner "knife group show GROUP_NAME"
+
+ deps do
+ require_relative "acl_base"
+ include Chef::Knife::AclBase
+ end
+
+ def run
+ group_name = name_args[0]
+
+ if name_args.length != 1
+ show_usage
+ ui.fatal "You must specify group name"
+ exit 1
+ end
+
+ validate_member_name!(group_name)
+
+ group = rest.get_rest("groups/#{group_name}")
+ ui.output group
+ end
+ end
+ 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/key_create.rb b/lib/chef/knife/key_create.rb
index a9f9da97a7..6129cab683 100644
--- a/lib/chef/knife/key_create.rb
+++ b/lib/chef/knife/key_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/key"
-require "chef/json_compat"
-require "chef/exceptions"
+require_relative "../key"
+require_relative "../json_compat"
+require_relative "../exceptions"
class Chef
class Knife
@@ -40,11 +40,11 @@ class Chef
end
def public_key_or_key_name_error_msg
- <<EOS
-You must pass either --public-key or --key-name, or both.
-If you only pass --public-key, a key name will be generated from the fingerprint of your key.
-If you only pass --key-name, a key pair will be generated by the server.
-EOS
+ <<~EOS
+ You must pass either --public-key or --key-name, or both.
+ If you only pass --public-key, a key name will be generated from the fingerprint of your key.
+ If you only pass --key-name, a key pair will be generated by the server.
+ EOS
end
def edit_data(key)
diff --git a/lib/chef/knife/key_create_base.rb b/lib/chef/knife/key_create_base.rb
index d02d5ee180..a1d658e43c 100644
--- a/lib/chef/knife/key_create_base.rb
+++ b/lib/chef/knife/key_create_base.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,24 +25,24 @@ class Chef
def self.included(includer)
includer.class_eval do
option :public_key,
- :short => "-p FILENAME",
- :long => "--public-key FILENAME",
- :description => "Public key for newly created key. If not passed, the server will create a key pair for you, but you must pass --key-name NAME in that case."
+ short: "-p FILENAME",
+ long: "--public-key FILENAME",
+ description: "Public key for newly created key. If not passed, the server will create a key pair for you, but you must pass --key-name NAME in that case."
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file, if you requested the server to create one."
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the private key to a file, if you requested the server to create one."
option :key_name,
- :short => "-k NAME",
- :long => "--key-name NAME",
- :description => "The name for your key. If you do not pass a name, you must pass --public-key, and the name will default to the fingerprint of the public key passed."
+ short: "-k NAME",
+ long: "--key-name NAME",
+ description: "The name for your key. If you do not pass a name, you must pass --public-key, and the name will default to the fingerprint of the public key passed."
option :expiration_date,
- :short => "-e DATE",
- :long => "--expiration-date DATE",
- :description => "Optionally pass the expiration date for the key in ISO 8601 fomatted string: YYYY-MM-DDTHH:MM:SSZ e.g. 2013-12-24T21:00:00Z. Defaults to infinity if not passed. UTC timezone assumed."
+ short: "-e DATE",
+ long: "--expiration-date DATE",
+ description: "Optionally pass the expiration date for the key in ISO 8601 formatted string: YYYY-MM-DDTHH:MM:SSZ e.g. 2013-12-24T21:00:00Z. Defaults to infinity if not passed. UTC timezone assumed."
end
end
end
diff --git a/lib/chef/knife/key_delete.rb b/lib/chef/knife/key_delete.rb
index a798e06475..10f1235924 100644
--- a/lib/chef/knife/key_delete.rb
+++ b/lib/chef/knife/key_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/key"
+require_relative "../key"
class Chef
class Knife
diff --git a/lib/chef/knife/key_edit.rb b/lib/chef/knife/key_edit.rb
index 8490d10fa5..3f8918f1a9 100644
--- a/lib/chef/knife/key_edit.rb
+++ b/lib/chef/knife/key_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/key"
-require "chef/json_compat"
-require "chef/exceptions"
+require_relative "../key"
+require_relative "../json_compat"
+require_relative "../exceptions"
class Chef
class Knife
@@ -41,12 +41,12 @@ class Chef
end
def public_key_and_create_key_error_msg
- <<EOS
-You passed both --public-key and --create-key. Only pass one, or the other, or neither.
-Do not pass either if you do not want to change the public_key field of your key.
-Pass --public-key if you want to update the public_key field of your key from a specific public key.
-Pass --create-key if you want the server to generate a new key and use that to update the public_key field of your key.
-EOS
+ <<~EOS
+ You passed both --public-key and --create-key. Only pass one, or the other, or neither.
+ Do not pass either if you do not want to change the public_key field of your key.
+ Pass --public-key if you want to update the public_key field of your key from a specific public key.
+ Pass --create-key if you want the server to generate a new key and use that to update the public_key field of your key.
+ EOS
end
def edit_data(key)
diff --git a/lib/chef/knife/key_edit_base.rb b/lib/chef/knife/key_edit_base.rb
index 1a613ef1bc..b094877190 100644
--- a/lib/chef/knife/key_edit_base.rb
+++ b/lib/chef/knife/key_edit_base.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,29 +25,29 @@ class Chef
def self.included(includer)
includer.class_eval do
option :public_key,
- :short => "-p FILENAME",
- :long => "--public-key FILENAME",
- :description => "Replace the public_key field from a file on disk. If not passed, the public_key field will not change."
+ short: "-p FILENAME",
+ long: "--public-key FILENAME",
+ description: "Replace the public_key field from a file on disk. If not passed, the public_key field will not change."
option :create_key,
- :short => "-c",
- :long => "--create-key",
- :description => "Replace the public_key field with a key generated by the server. The private key will be returned."
+ short: "-c",
+ long: "--create-key",
+ description: "Replace the public_key field with a key generated by the server. The private key will be returned."
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file, if you requested the server to create one via --create-key."
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the private key to a file, if you requested the server to create one via --create-key."
option :key_name,
- :short => "-k NAME",
- :long => "--key-name NAME",
- :description => "The new name for your key. Pass if you wish to update the name field of your key."
+ short: "-k NAME",
+ long: "--key-name NAME",
+ description: "The new name for your key. Pass if you wish to update the name field of your key."
option :expiration_date,
- :short => "-e DATE",
- :long => "--expiration-date DATE",
- :description => "Updates the expiration_date field of your key if passed. Pass in ISO 8601 fomatted string: YYYY-MM-DDTHH:MM:SSZ e.g. 2013-12-24T21:00:00Z or infinity. UTC timezone assumed."
+ short: "-e DATE",
+ long: "--expiration-date DATE",
+ description: "Updates the expiration_date field of your key if passed. Pass in ISO 8601 formatted string: YYYY-MM-DDTHH:MM:SSZ e.g. 2013-12-24T21:00:00Z or infinity. UTC timezone assumed."
end
end
end
diff --git a/lib/chef/knife/key_list.rb b/lib/chef/knife/key_list.rb
index 9a820b7a50..076b39d251 100644
--- a/lib/chef/knife/key_list.rb
+++ b/lib/chef/knife/key_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/key"
-require "chef/json_compat"
-require "chef/exceptions"
+require_relative "../key"
+require_relative "../json_compat"
+require_relative "../exceptions"
class Chef
class Knife
@@ -40,10 +40,10 @@ class Chef
end
def expired_and_non_expired_msg
- <<EOS
-You cannot pass both --only-expired and --only-non-expired.
-Please pass one or none.
-EOS
+ <<~EOS
+ You cannot pass both --only-expired and --only-non-expired.
+ Please pass one or none.
+ EOS
end
def display_info(string)
@@ -70,7 +70,8 @@ EOS
keys.each do |key|
next if !key["expired"] && @config[:only_expired]
next if key["expired"] && @config[:only_non_expired]
- display = "#{colorize(key['name'].ljust(max_length))} #{key['uri']}"
+
+ display = "#{colorize(key["name"].ljust(max_length))} #{key["uri"]}"
display = "#{display} (expired)" if key["expired"]
display_info(display)
end
@@ -78,6 +79,7 @@ EOS
keys.each do |key|
next if !key["expired"] && @config[:only_expired]
next if key["expired"] && @config[:only_non_expired]
+
display_info(key["name"])
end
end
diff --git a/lib/chef/knife/key_list_base.rb b/lib/chef/knife/key_list_base.rb
index 95858e9ba1..e06e908b69 100644
--- a/lib/chef/knife/key_list_base.rb
+++ b/lib/chef/knife/key_list_base.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,19 +25,19 @@ class Chef
def self.included(includer)
includer.class_eval do
option :with_details,
- :short => "-w",
- :long => "--with-details",
- :description => "Show corresponding URIs and whether the key has expired or not."
+ short: "-w",
+ long: "--with-details",
+ description: "Show corresponding URIs and whether the key has expired or not."
option :only_expired,
- :short => "-e",
- :long => "--only-expired",
- :description => "Only show expired keys."
+ short: "-e",
+ long: "--only-expired",
+ description: "Only show expired keys."
option :only_non_expired,
- :short => "-n",
- :long => "--only-non-expired",
- :description => "Only show non-expired keys."
+ short: "-n",
+ long: "--only-non-expired",
+ description: "Only show non-expired keys."
end
end
end
diff --git a/lib/chef/knife/key_show.rb b/lib/chef/knife/key_show.rb
index 851db7208b..8b3d980004 100644
--- a/lib/chef/knife/key_show.rb
+++ b/lib/chef/knife/key_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/key"
-require "chef/json_compat"
-require "chef/exceptions"
+require_relative "../key"
+require_relative "../json_compat"
+require_relative "../exceptions"
class Chef
class Knife
diff --git a/lib/chef/knife/list.rb b/lib/chef/knife/list.rb
index fcfde0eb45..1cc398e01a 100644
--- a/lib/chef/knife/list.rb
+++ b/lib/chef/knife/list.rb
@@ -1,42 +1,63 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
class List < Chef::ChefFS::Knife
- banner "knife list [-dfR1p] [PATTERN1 ... PATTERNn]"
+ banner "knife list [-dfR1p] [PATTERN1 ... PATTERNn] (options)"
category "path-based"
deps do
- require "chef/chef_fs/file_system"
- require "highline"
+ require_relative "../chef_fs/file_system"
+ require "tty-screen"
end
option :recursive,
- :short => "-R",
- :boolean => true,
- :description => "List directories recursively"
+ short: "-R",
+ boolean: true,
+ description: "List directories recursively."
+
option :bare_directories,
- :short => "-d",
- :boolean => true,
- :description => "When directories match the pattern, do not show the directories' children"
+ short: "-d",
+ boolean: true,
+ description: "When directories match the pattern, do not show the directories' children."
+
option :local,
- :long => "--local",
- :boolean => true,
- :description => "List local directory instead of remote"
+ long: "--local",
+ boolean: true,
+ description: "List local directory instead of remote."
+
option :flat,
- :short => "-f",
- :long => "--flat",
- :boolean => true,
- :description => "Show a list of filenames rather than the prettified ls-like output normally produced"
+ short: "-f",
+ long: "--flat",
+ boolean: true,
+ description: "Show a list of filenames rather than the prettified ls-like output normally produced."
+
option :one_column,
- :short => "-1",
- :boolean => true,
- :description => "Show only one column of results"
+ short: "-1",
+ boolean: true,
+ description: "Show only one column of results."
+
option :trailing_slashes,
- :short => "-p",
- :boolean => true,
- :description => "Show trailing slashes after directories"
+ short: "-p",
+ boolean: true,
+ description: "Show trailing slashes after directories."
attr_accessor :exit_code
@@ -56,7 +77,7 @@ class Chef
# Process directories
if !config[:bare_directories]
- dir_results = parallelize(all_results.select { |result| result.dir? }) do |result|
+ dir_results = parallelize(all_results.select(&:dir?)) do |result|
add_dir_result(result)
end.flatten(1)
@@ -69,14 +90,14 @@ 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 Style/HashEachMethods
results += children
end
dir_results = []
end
# Sort by path for happy output
- results = results.sort_by { |result| result.path }
+ results = results.sort_by(&:path)
dir_results = dir_results.sort_by { |result| result[0].path }
# Print!
@@ -97,12 +118,12 @@ class Chef
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)
begin
- children = result.children.sort_by { |child| child.name }
+ children = result.children.sort_by(&:name)
rescue Chef::ChefFS::FileSystem::NotFoundError => e
ui.error "#{format_path(e.entry)}: No such file or directory"
return []
@@ -110,7 +131,7 @@ class Chef
result = [ [ result, children ] ]
if config[:recursive]
- child_dirs = children.select { |child| child.dir? }
+ child_dirs = children.select(&:dir?)
result += parallelize(child_dirs) { |child| add_dir_result(child) }.flatten(1).to_a
end
result
@@ -123,11 +144,11 @@ class Chef
def print_results(results, indent)
return if results.length == 0
- print_space = results.map { |result| result.length }.max + 2
+ print_space = results.map(&:length).max + 2
if config[:one_column] || !stdout.isatty
columns = 0
else
- columns = HighLine::SystemExtensions.terminal_size[0]
+ columns = TTY::Screen.columns
end
current_line = ""
results.each do |result|
diff --git a/lib/chef/knife/node_bulk_delete.rb b/lib/chef/knife/node_bulk_delete.rb
index 2ca63da512..874509b730 100644
--- a/lib/chef/knife/node_bulk_delete.rb
+++ b/lib/chef/knife/node_bulk_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeBulkDelete < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife node bulk delete REGEX (options)"
@@ -39,7 +39,8 @@ class Chef
matcher = /#{name_args[0]}/
all_nodes.each do |name, node|
- next unless name =~ matcher
+ next unless name&.match?(matcher)
+
nodes_to_delete[name] = node
end
diff --git a/lib/chef/knife/node_create.rb b/lib/chef/knife/node_create.rb
index f80ac9e87c..c0db667b25 100644
--- a/lib/chef/knife/node_create.rb
+++ b/lib/chef/knife/node_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeCreate < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife node create NODE (options)"
diff --git a/lib/chef/knife/node_delete.rb b/lib/chef/knife/node_delete.rb
index 4dd7d764a1..7c0c6f0a21 100644
--- a/lib/chef/knife/node_delete.rb
+++ b/lib/chef/knife/node_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,29 +16,29 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeDelete < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../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_edit.rb b/lib/chef/knife/node_edit.rb
index 4632c0a5b4..a2585391ea 100644
--- a/lib/chef/knife/node_edit.rb
+++ b/lib/chef/knife/node_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -24,18 +24,18 @@ class Chef
class NodeEdit < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
- require "chef/knife/core/node_editor"
+ require_relative "../node"
+ require_relative "../json_compat"
+ require_relative "core/node_editor"
end
banner "knife node edit NODE (options)"
option :all_attributes,
- :short => "-a",
- :long => "--all",
- :boolean => true,
- :description => "Display all attributes when editing"
+ short: "-a",
+ long: "--all",
+ boolean: true,
+ description: "Display all attributes when editing."
def run
if node_name.nil?
@@ -46,7 +46,7 @@ class Chef
updated_node = node_editor.edit_node
if updated_values = node_editor.updated?
- ui.info "Saving updated #{updated_values.join(', ')} on node #{node.name}"
+ ui.info "Saving updated #{updated_values.join(", ")} on node #{node.name}"
updated_node.save
else
ui.info "Node not updated, skipping node save"
diff --git a/lib/chef/knife/node_environment_set.rb b/lib/chef/knife/node_environment_set.rb
index ecba01be29..644b6138b6 100644
--- a/lib/chef/knife/node_environment_set.rb
+++ b/lib/chef/knife/node_environment_set.rb
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeEnvironmentSet < Knife
deps do
- require "chef/node"
+ require_relative "../node"
end
banner "knife node environment set NODE ENVIRONMENT"
@@ -44,8 +44,7 @@ class Chef
node.save
- config[:attribute] = "chef_environment"
-
+ config[:environment] = @environment
output(format_for_display(node))
end
diff --git a/lib/chef/knife/node_from_file.rb b/lib/chef/knife/node_from_file.rb
index 61b83edd92..86d602ae7c 100644
--- a/lib/chef/knife/node_from_file.rb
+++ b/lib/chef/knife/node_from_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,16 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeFromFile < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
- require "chef/knife/core/object_loader"
+ require_relative "../node"
+ require_relative "../json_compat"
+ require_relative "core/object_loader"
end
banner "knife node from file FILE (options)"
diff --git a/lib/chef/knife/node_list.rb b/lib/chef/knife/node_list.rb
index 4885208136..a8b57aedc5 100644
--- a/lib/chef/knife/node_list.rb
+++ b/lib/chef/knife/node_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeList < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife node list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
env = Chef::Config[:environment]
diff --git a/lib/chef/knife/node_policy_set.rb b/lib/chef/knife/node_policy_set.rb
new file mode 100644
index 0000000000..d34ebd9478
--- /dev/null
+++ b/lib/chef/knife/node_policy_set.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Piyush Awasthi (<piyush.awasthi@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class NodePolicySet < Knife
+
+ deps do
+ require_relative "../node"
+ require_relative "../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..]
+ 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..]
+ (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..40476371eb 100644
--- a/lib/chef/knife/node_run_list_add.rb
+++ b/lib/chef/knife/node_run_list_add.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,39 +16,39 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeRunListAdd < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../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",
- :long => "--after ITEM",
- :description => "Place the ENTRY in the run list after ITEM"
+ short: "-a ITEM",
+ long: "--after ITEM",
+ description: "Place the ENTRY in the run list after ITEM."
option :before,
- :short => "-b ITEM",
- :long => "--before ITEM",
- :description => "Place the ENTRY in the run list before ITEM"
+ short: "-b ITEM",
+ long: "--before ITEM",
+ description: "Place the ENTRY in the run list before ITEM."
def run
node = Chef::Node.load(@name_args[0])
if @name_args.size > 2
# Check for nested lists and create a single plain one
- entries = @name_args[1..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[1..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map { |e| e.strip }
+ entries = @name_args[1].split(",").map(&:strip)
end
if config[:after] && config[:before]
diff --git a/lib/chef/knife/node_run_list_remove.rb b/lib/chef/knife/node_run_list_remove.rb
index 3f9cdabfff..484e575475 100644
--- a/lib/chef/knife/node_run_list_remove.rb
+++ b/lib/chef/knife/node_run_list_remove.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,30 +16,30 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeRunListRemove < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../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])
if @name_args.size > 2
# Check for nested lists and create a single plain one
- entries = @name_args[1..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[1..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map { |e| e.strip }
+ entries = @name_args[1].split(",").map(&:strip)
end
# iterate over the list of things to remove,
@@ -49,7 +49,7 @@ class Chef
node.run_list.remove(e)
else
ui.warn "#{e} is not in the run list"
- unless e =~ /^(recipe|role)\[/
+ unless /^(recipe|role)\[/.match?(e)
ui.warn "(did you forget recipe[] or role[] around it?)"
end
end
diff --git a/lib/chef/knife/node_run_list_set.rb b/lib/chef/knife/node_run_list_set.rb
index 2d68c88415..f356b39d95 100644
--- a/lib/chef/knife/node_run_list_set.rb
+++ b/lib/chef/knife/node_run_list_set.rb
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class NodeRunListSet < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife node run_list set NODE ENTRIES (options)"
@@ -36,12 +36,12 @@ class Chef
exit 1
elsif @name_args.size > 2
# Check for nested lists and create a single plain one
- entries = @name_args[1..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[1..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map { |e| e.strip }
+ entries = @name_args[1].split(",").map(&:strip)
end
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..173348dc41 100644
--- a/lib/chef/knife/node_show.rb
+++ b/lib/chef/knife/node_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,32 +16,34 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/core/node_presenter"
+require_relative "../knife"
+require_relative "core/node_presenter"
+require_relative "core/formatting_options"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
class NodeShow < Knife
- include Knife::Core::NodeFormattingOptions
+ include Knife::Core::FormattingOptions
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife node show NODE (options)"
option :run_list,
- :short => "-r",
- :long => "--run-list",
- :description => "Show only the run list"
+ short: "-r",
+ long: "--run-list",
+ description: "Show only the run list."
option :environment,
- :short => "-E",
- :long => "--environment",
- :description => "Show only the Chef environment"
+ short: "-E",
+ long: "--environment",
+ description: "Show only the #{ChefUtils::Dist::Infra::PRODUCT} environment."
def run
ui.use_presenter Knife::Core::NodePresenter
@@ -55,11 +57,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/null.rb b/lib/chef/knife/null.rb
index 0b5058e8ea..7221eee9f5 100644
--- a/lib/chef/knife/null.rb
+++ b/lib/chef/knife/null.rb
@@ -3,8 +3,10 @@ class Chef
class Null < Chef::Knife
banner "knife null"
- def run
- end
+ # setting the category to deprecated keeps it out of help
+ category "deprecated"
+
+ def run; end
end
end
end
diff --git a/lib/chef/knife/osc_user_create.rb b/lib/chef/knife/osc_user_create.rb
deleted file mode 100644
index 74b50a4ef4..0000000000
--- a/lib/chef/knife/osc_user_create.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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/knife"
-
-# DEPRECATION NOTE
-# 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_create.rb.
-class Chef
- class Knife
- class OscUserCreate < Knife
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file"
-
- option :admin,
- :short => "-a",
- :long => "--admin",
- :description => "Create the user as an admin",
- :boolean => true
-
- option :user_password,
- :short => "-p PASSWORD",
- :long => "--password PASSWORD",
- :description => "Password for newly created user",
- :default => ""
-
- option :user_key,
- :long => "--user-key FILENAME",
- :description => "Public key for newly created user. By default a key will be created for you."
-
- banner "knife osc_user create USER (options)"
-
- def run
- @user_name = @name_args[0]
-
- if @user_name.nil?
- show_usage
- ui.fatal("You must specify a user name")
- exit 1
- end
-
- if config[:user_password].length == 0
- show_usage
- ui.fatal("You must specify a non-blank password")
- exit 1
- end
-
- user = Chef::User.new
- user.name(@user_name)
- user.admin(config[:admin])
- user.password config[:user_password]
-
- if config[:user_key]
- user.public_key File.read(File.expand_path(config[:user_key]))
- end
-
- output = edit_hash(user)
- user = Chef::User.from_hash(output).create
-
- ui.info("Created #{user}")
- if user.private_key
- if config[:file]
- File.open(config[:file], "w") do |f|
- f.print(user.private_key)
- end
- else
- ui.msg user.private_key
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/osc_user_delete.rb b/lib/chef/knife/osc_user_delete.rb
deleted file mode 100644
index 51abc1c668..0000000000
--- a/lib/chef/knife/osc_user_delete.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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/knife"
-
-# DEPRECATION NOTE
-# 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 the user_delete.rb.
-
-class Chef
- class Knife
- class OscUserDelete < Knife
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- banner "knife osc_user delete USER (options)"
-
- def run
- @user_name = @name_args[0]
-
- if @user_name.nil?
- show_usage
- ui.fatal("You must specify a user name")
- exit 1
- end
-
- delete_object(Chef::User, @user_name)
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/osc_user_edit.rb b/lib/chef/knife/osc_user_edit.rb
deleted file mode 100644
index 89986c6f04..0000000000
--- a/lib/chef/knife/osc_user_edit.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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/knife"
-
-# DEPRECATION NOTE
-# 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_edit.rb.
-
-class Chef
- class Knife
- class OscUserEdit < Knife
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- banner "knife osc_user edit USER (options)"
-
- def run
- @user_name = @name_args[0]
-
- if @user_name.nil?
- show_usage
- ui.fatal("You must specify a user name")
- exit 1
- end
-
- original_user = Chef::User.load(@user_name).to_hash
- edited_user = edit_hash(original_user)
- if original_user != edited_user
- user = Chef::User.from_hash(edited_user)
- user.update
- ui.msg("Saved #{user}.")
- else
- ui.msg("User unchanged, not saving.")
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/osc_user_list.rb b/lib/chef/knife/osc_user_list.rb
deleted file mode 100644
index f1002c4f54..0000000000
--- a/lib/chef/knife/osc_user_list.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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/knife"
-
-# DEPRECATION NOTE
-# 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_list.rb.
-
-class Chef
- class Knife
- class OscUserList < Knife
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- banner "knife osc_user list (options)"
-
- option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
-
- def run
- output(format_list_for_display(Chef::User.list))
- end
- end
- end
-end
diff --git a/lib/chef/knife/osc_user_reregister.rb b/lib/chef/knife/osc_user_reregister.rb
deleted file mode 100644
index b513f31328..0000000000
--- a/lib/chef/knife/osc_user_reregister.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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/knife"
-
-# DEPRECATION NOTE
-# 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.rb.
-
-class Chef
- class Knife
- class OscUserReregister < Knife
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- banner "knife osc_user reregister USER (options)"
-
- option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file"
-
- def run
- @user_name = @name_args[0]
-
- if @user_name.nil?
- show_usage
- ui.fatal("You must specify a user name")
- exit 1
- end
-
- user = Chef::User.load(@user_name).reregister
- Chef::Log.debug("Updated user data: #{user.inspect}")
- key = user.private_key
- if config[:file]
- File.open(config[:file], "w") do |f|
- f.print(key)
- end
- else
- ui.msg key
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/osc_user_show.rb b/lib/chef/knife/osc_user_show.rb
deleted file mode 100644
index 22e9bf4dcd..0000000000
--- a/lib/chef/knife/osc_user_show.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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 "chef/knife"
-
-# DEPRECATION NOTE
-# 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_show.rb.
-
-class Chef
- class Knife
- class OscUserShow < Knife
-
- include Knife::Core::MultiAttributeReturnOption
-
- deps do
- require "chef/user"
- require "chef/json_compat"
- end
-
- banner "knife osc_user show USER (options)"
-
- def run
- @user_name = @name_args[0]
-
- if @user_name.nil?
- show_usage
- ui.fatal("You must specify a user name")
- exit 1
- end
-
- user = Chef::User.load(@user_name)
- output(format_for_display(user))
- end
-
- end
- end
-end
diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb
index 76b83d2212..5adb36ea70 100644
--- a/lib/chef/knife/raw.rb
+++ b/lib/chef/knife/raw.rb
@@ -1,48 +1,68 @@
-require "chef/knife"
-require "chef/http"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../knife"
class Chef
class Knife
class Raw < Chef::Knife
- banner "knife raw REQUEST_PATH"
+ banner "knife raw REQUEST_PATH (options)"
deps do
- require "chef/json_compat"
- require "chef/config"
- require "chef/http"
- require "chef/http/authenticator"
- require "chef/http/cookie_manager"
- require "chef/http/decompressor"
- require "chef/http/json_output"
+ require_relative "../json_compat"
+ require_relative "../config"
+ require_relative "../http"
+ require_relative "../http/authenticator"
+ require_relative "../http/cookie_manager"
+ require_relative "../http/decompressor"
+ require_relative "../http/json_output"
end
option :method,
- :long => "--method METHOD",
- :short => "-m METHOD",
- :default => "GET",
- :description => "Request method (GET, POST, PUT or DELETE). Default: GET"
+ long: "--method METHOD",
+ short: "-m METHOD",
+ default: "GET",
+ description: "Request method (GET, POST, PUT or DELETE). Default: GET."
option :pretty,
- :long => "--[no-]pretty",
- :boolean => true,
- :default => true,
- :description => "Pretty-print JSON output. Default: true"
+ long: "--[no-]pretty",
+ boolean: true,
+ default: true,
+ description: "Pretty-print JSON output. Default: true."
option :input,
- :long => "--input FILE",
- :short => "-i FILE",
- :description => "Name of file to use for PUT or POST"
+ long: "--input FILE",
+ short: "-i FILE",
+ description: "Name of file to use for PUT or POST."
option :proxy_auth,
- :long => "--proxy-auth",
- :boolean => true,
- :default => false,
- :description => "Use webui proxy authentication. Client key must be the webui key."
+ long: "--proxy-auth",
+ boolean: true,
+ default: false,
+ description: "Use webui proxy authentication. Client key must be the webui key."
+ # We need a custom HTTP client class here because we don't want to even
+ # try to decode the body, in case we get back corrupted JSON or whatnot.
class RawInputServerAPI < Chef::HTTP
def initialize(options = {})
+ # If making a change here, also update Chef::ServerAPI.
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[:ssh_agent_signing] ||= Chef::Config[:ssh_agent_signing]
super(Chef::Config[:chef_server_url], options)
end
use Chef::HTTP::JSONOutput
@@ -84,14 +104,14 @@ class Chef
result = Chef::JSONCompat.to_json_pretty(result)
end
else
- chef_rest = RawInputServerAPI.new(:raw_output => true)
+ chef_rest = RawInputServerAPI.new(raw_output: true)
result = chef_rest.request(method, name_args[0], headers, data)
end
output result
rescue Timeout::Error => e
ui.error "Server timeout"
exit 1
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
ui.error "Server responded with error #{e.response.code} \"#{e.response.message}\""
ui.error "Error Body: #{e.response.body}" if e.response.body && e.response.body != ""
exit 1
diff --git a/lib/chef/knife/recipe_list.rb b/lib/chef/knife/recipe_list.rb
index 8f76e494ad..39e040a2f4 100644
--- a/lib/chef/knife/recipe_list.rb
+++ b/lib/chef/knife/recipe_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef::Knife::RecipeList < Chef::Knife
banner "knife recipe list [PATTERN]"
diff --git a/lib/chef/knife/rehash.rb b/lib/chef/knife/rehash.rb
index 79286368f8..69ee19229a 100644
--- a/lib/chef/knife/rehash.rb
+++ b/lib/chef/knife/rehash.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna <steve@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,17 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/core/subcommand_loader"
+require_relative "../knife"
class Chef
class Knife
class Rehash < Chef::Knife
banner "knife rehash"
+ deps do
+ require_relative "core/subcommand_loader"
+ end
+
def run
if ! Chef::Knife::SubcommandLoader.autogenerated_manifest?
ui.msg "Using knife-rehash will speed up knife's load time by caching the location of subcommands on disk."
@@ -31,7 +34,9 @@ class Chef
else
reload_plugins
end
- write_hash(generate_hash)
+
+ ui.msg "Knife subcommands are cached in #{Chef::Knife::SubcommandLoader.plugin_manifest_path}. Delete this file to disable the caching."
+ Chef::Knife::SubcommandLoader.write_hash(Chef::Knife::SubcommandLoader.generate_hash)
end
def reload_plugins
@@ -40,26 +45,6 @@ class Chef
# loaded plugins and `load_commands` shouldn't have an effect.
Chef::Knife.subcommand_loader.load_commands
end
-
- def generate_hash
- output = if Chef::Knife::SubcommandLoader.plugin_manifest?
- Chef::Knife::SubcommandLoader.plugin_manifest
- else
- { Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY => {} }
- end
- output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_paths"] = Chef::Knife.subcommand_files
- output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_by_category"] = Chef::Knife.subcommands_by_category
- output
- end
-
- def write_hash(data)
- plugin_manifest_dir = File.expand_path("..", Chef::Knife::SubcommandLoader.plugin_manifest_path)
- FileUtils.mkdir_p(plugin_manifest_dir) unless File.directory?(plugin_manifest_dir)
- File.open(Chef::Knife::SubcommandLoader.plugin_manifest_path, "w") do |f|
- f.write(Chef::JSONCompat.to_json_pretty(data))
- ui.msg "Knife subcommands are cached in #{Chef::Knife::SubcommandLoader.plugin_manifest_path}. Delete this file to disable the caching."
- end
- end
end
end
end
diff --git a/lib/chef/knife/role_bulk_delete.rb b/lib/chef/knife/role_bulk_delete.rb
index 0726454da3..f57ac79619 100644
--- a/lib/chef/knife/role_bulk_delete.rb
+++ b/lib/chef/knife/role_bulk_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleBulkDelete < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
banner "knife role bulk delete REGEX (options)"
@@ -40,7 +40,8 @@ class Chef
matcher = /#{@name_args[0]}/
roles_to_delete = {}
all_roles.each do |name, role|
- next unless name =~ matcher
+ next unless name&.match?(matcher)
+
roles_to_delete[role.name] = role
end
diff --git a/lib/chef/knife/role_create.rb b/lib/chef/knife/role_create.rb
index a389d849f7..295445554d 100644
--- a/lib/chef/knife/role_create.rb
+++ b/lib/chef/knife/role_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleCreate < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
banner "knife role create ROLE (options)"
option :description,
- :short => "-d DESC",
- :long => "--description DESC",
- :description => "The role description"
+ short: "-d DESC",
+ long: "--description DESC",
+ description: "The role description."
def run
@role_name = @name_args[0]
diff --git a/lib/chef/knife/role_delete.rb b/lib/chef/knife/role_delete.rb
index 5c10a05d85..c46e265c5e 100644
--- a/lib/chef/knife/role_delete.rb
+++ b/lib/chef/knife/role_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleDelete < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
banner "knife role delete ROLE (options)"
diff --git a/lib/chef/knife/role_edit.rb b/lib/chef/knife/role_edit.rb
index d3697849ad..1925336646 100644
--- a/lib/chef/knife/role_edit.rb
+++ b/lib/chef/knife/role_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,15 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEdit < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
banner "knife role edit ROLE (options)"
diff --git a/lib/chef/knife/role_env_run_list_add.rb b/lib/chef/knife/role_env_run_list_add.rb
index 61aec506a9..b5753b46fc 100644
--- a/lib/chef/knife/role_env_run_list_add.rb
+++ b/lib/chef/knife/role_env_run_list_add.rb
@@ -1,6 +1,7 @@
+#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +17,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEnvRunListAdd < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../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",
- :long => "--after ITEM",
- :description => "Place the ENTRY in the run list after ITEM"
+ short: "-a ITEM",
+ long: "--after ITEM",
+ description: "Place the ENTRY in the run list after ITEM."
def add_to_env_run_list(role, environment, entries, after = nil)
if after
@@ -67,12 +68,12 @@ class Chef
if @name_args.size > 2
# Check for nested lists and create a single plain one
- entries = @name_args[2..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[2..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[2].split(",").map { |e| e.strip }
+ entries = @name_args[2].split(",").map(&:strip)
end
add_to_env_run_list(role, environment, entries, config[:after])
diff --git a/lib/chef/knife/role_env_run_list_clear.rb b/lib/chef/knife/role_env_run_list_clear.rb
index d9dc96c87d..dda523e809 100644
--- a/lib/chef/knife/role_env_run_list_clear.rb
+++ b/lib/chef/knife/role_env_run_list_clear.rb
@@ -17,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEnvRunListClear < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role env_run_list clear [ROLE] [ENVIRONMENT]"
+ banner "knife role env_run_list clear [ROLE] [ENVIRONMENT] (options)"
def clear_env_run_list(role, environment)
nlist = []
role.env_run_lists_add(environment => nlist)
diff --git a/lib/chef/knife/role_env_run_list_remove.rb b/lib/chef/knife/role_env_run_list_remove.rb
index 576e32e2a9..57363610ce 100644
--- a/lib/chef/knife/role_env_run_list_remove.rb
+++ b/lib/chef/knife/role_env_run_list_remove.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,26 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEnvRunListRemove < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role env_run_list remove [ROLE] [ENVIRONMENT] [ENTRIES]"
+ banner "knife role env_run_list remove [ROLE] [ENVIRONMENT] [ENTRIES] (options)"
def remove_from_env_run_list(role, environment, item_to_remove)
nlist = []
role.run_list_for(environment).each do |entry|
nlist << entry unless entry == item_to_remove
- #unless entry == @name_args[2]
+ # unless entry == @name_args[2]
# nlist << entry
- #end
+ # end
end
role.env_run_lists_add(environment => nlist)
end
diff --git a/lib/chef/knife/role_env_run_list_replace.rb b/lib/chef/knife/role_env_run_list_replace.rb
index e84e351c1e..e76680661e 100644
--- a/lib/chef/knife/role_env_run_list_replace.rb
+++ b/lib/chef/knife/role_env_run_list_replace.rb
@@ -1,6 +1,7 @@
+#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEnvRunListReplace < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role env_run_list replace [ROLE] [ENVIRONMENT] [OLD_ENTRY] [NEW_ENTRY] "
+ banner "knife role env_run_list replace [ROLE] [ENVIRONMENT] [OLD_ENTRY] [NEW_ENTRY] (options)"
def replace_in_env_run_list(role, environment, old_entry, new_entry)
nlist = []
diff --git a/lib/chef/knife/role_env_run_list_set.rb b/lib/chef/knife/role_env_run_list_set.rb
index f53616e151..0f1ce62a5d 100644
--- a/lib/chef/knife/role_env_run_list_set.rb
+++ b/lib/chef/knife/role_env_run_list_set.rb
@@ -17,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleEnvRunListSet < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role env_run_list set [ROLE] [ENVIRONMENT] [ENTRIES]"
+ banner "knife role env_run_list set [ROLE] [ENVIRONMENT] [ENTRIES] (options)"
# Clears out any existing env_run_list_items and sets them to the
# specified entries
@@ -51,12 +51,12 @@ class Chef
exit 1
elsif @name_args.size > 2
# Check for nested lists and create a single plain one
- entries = @name_args[2..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[2..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[2].split(",").map { |e| e.strip }
+ entries = @name_args[2].split(",").map(&:strip)
end
set_env_run_list(role, environment, entries )
diff --git a/lib/chef/knife/role_from_file.rb b/lib/chef/knife/role_from_file.rb
index bf21a38fd7..16e38eeb63 100644
--- a/lib/chef/knife/role_from_file.rb
+++ b/lib/chef/knife/role_from_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,16 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleFromFile < Knife
deps do
- require "chef/role"
- require "chef/knife/core/object_loader"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "core/object_loader"
+ require_relative "../json_compat"
end
banner "knife role from file FILE [FILE..] (options)"
diff --git a/lib/chef/knife/role_list.rb b/lib/chef/knife/role_list.rb
index 1247478ef5..d6aad053c1 100644
--- a/lib/chef/knife/role_list.rb
+++ b/lib/chef/knife/role_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleList < Knife
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../node"
+ require_relative "../json_compat"
end
banner "knife role list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
output(format_list_for_display(Chef::Role.list))
diff --git a/lib/chef/knife/role_run_list_add.rb b/lib/chef/knife/role_run_list_add.rb
index 6aa92d37ba..76633ff5f6 100644
--- a/lib/chef/knife/role_run_list_add.rb
+++ b/lib/chef/knife/role_run_list_add.rb
@@ -1,6 +1,7 @@
+#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +17,23 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleRunListAdd < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../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",
- :long => "--after ITEM",
- :description => "Place the ENTRY in the run list after ITEM"
+ short: "-a ITEM",
+ long: "--after ITEM",
+ description: "Place the ENTRY in the run list after ITEM."
def add_to_env_run_list(role, environment, entries, after = nil)
if after
@@ -67,12 +68,12 @@ class Chef
if @name_args.size > 1
# Check for nested lists and create a single plain one
- entries = @name_args[1..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[1..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map { |e| e.strip }
+ entries = @name_args[1].split(",").map(&:strip)
end
add_to_env_run_list(role, environment, entries, config[:after])
diff --git a/lib/chef/knife/role_run_list_clear.rb b/lib/chef/knife/role_run_list_clear.rb
index 81678d39ef..b7106233f0 100644
--- a/lib/chef/knife/role_run_list_clear.rb
+++ b/lib/chef/knife/role_run_list_clear.rb
@@ -17,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleRunListClear < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role run_list clear [ROLE]"
+ banner "knife role run_list clear [ROLE] (options)"
def clear_env_run_list(role, environment)
nlist = []
role.env_run_lists_add(environment => nlist)
diff --git a/lib/chef/knife/role_run_list_remove.rb b/lib/chef/knife/role_run_list_remove.rb
index 0dacfee051..884f3bc28d 100644
--- a/lib/chef/knife/role_run_list_remove.rb
+++ b/lib/chef/knife/role_run_list_remove.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,25 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleRunListRemove < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
end
- banner "knife role run_list remove [ROLE] [ENTRY]"
+ banner "knife role run_list remove [ROLE] [ENTRY] (options)"
def remove_from_env_run_list(role, environment, item_to_remove)
nlist = []
role.run_list_for(environment).each do |entry|
nlist << entry unless entry == item_to_remove
- #unless entry == @name_args[2]
+ # unless entry == @name_args[2]
# nlist << entry
- #end
+ # end
end
role.env_run_lists_add(environment => nlist)
end
diff --git a/lib/chef/knife/role_run_list_replace.rb b/lib/chef/knife/role_run_list_replace.rb
index 3e7bc2d5ec..16f789fbef 100644
--- a/lib/chef/knife/role_run_list_replace.rb
+++ b/lib/chef/knife/role_run_list_replace.rb
@@ -1,6 +1,7 @@
+#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: William Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +17,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleRunListReplace < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
+ require_relative "../json_compat"
end
- banner "knife role run_list replace [ROLE] [OLD_ENTRY] [NEW_ENTRY] "
+ banner "knife role run_list replace [ROLE] [OLD_ENTRY] [NEW_ENTRY] (options)"
def replace_in_env_run_list(role, environment, old_entry, new_entry)
nlist = []
diff --git a/lib/chef/knife/role_run_list_set.rb b/lib/chef/knife/role_run_list_set.rb
index 644af1c381..ad1a5e2923 100644
--- a/lib/chef/knife/role_run_list_set.rb
+++ b/lib/chef/knife/role_run_list_set.rb
@@ -17,18 +17,17 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class RoleRunListSet < Knife
deps do
- require "chef/role"
- require "chef/json_compat"
+ require_relative "../role"
end
- banner "knife role run_list set [ROLE] [ENTRIES]"
+ banner "knife role run_list set [ROLE] [ENTRIES] (options)"
# Clears out any existing env_run_list_items and sets them to the
# specified entries
@@ -51,12 +50,12 @@ class Chef
exit 1
elsif @name_args.size > 1
# Check for nested lists and create a single plain one
- entries = @name_args[1..-1].map do |entry|
- entry.split(",").map { |e| e.strip }
+ entries = @name_args[1..].map do |entry|
+ entry.split(",").map(&:strip)
end.flatten
else
# Convert to array and remove the extra spaces
- entries = @name_args[1].split(",").map { |e| e.strip }
+ entries = @name_args[1].split(",").map(&:strip)
end
set_env_run_list(role, environment, entries )
diff --git a/lib/chef/knife/role_show.rb b/lib/chef/knife/role_show.rb
index 99684768bb..ee90352e50 100644
--- a/lib/chef/knife/role_show.rb
+++ b/lib/chef/knife/role_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -25,8 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/node"
- require "chef/json_compat"
+ require_relative "../role"
end
banner "knife role show ROLE (options)"
@@ -36,7 +35,7 @@ class Chef
if @role_name.nil?
show_usage
- ui.fatal("You must specify a role name")
+ ui.fatal("You must specify a role name.")
exit 1
end
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
index d102c1e955..620cfb971d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/core/node_presenter"
-require "addressable/uri"
+require_relative "../knife"
+require_relative "core/node_presenter"
+require_relative "core/formatting_options"
class Chef
class Knife
@@ -27,84 +27,80 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/node"
- require "chef/environment"
- require "chef/api_client"
- require "chef/search/query"
+ require_relative "../node"
+ require_relative "../environment"
+ require_relative "../api_client"
+ require_relative "../search/query"
end
- include Knife::Core::NodeFormattingOptions
+ include Knife::Core::FormattingOptions
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",
- :description => "The row to start returning results at",
- :default => 0,
- :proc => lambda { |i| i.to_i }
+ short: "-b ROW",
+ long: "--start ROW",
+ description: "The row to start returning results at.",
+ default: 0,
+ proc: lambda { |i| i.to_i }
option :rows,
- :short => "-R INT",
- :long => "--rows INT",
- :description => "The number of rows to return",
- :default => nil,
- :proc => lambda { |i| i.to_i }
+ short: "-R INT",
+ long: "--rows INT",
+ description: "The number of rows to return.",
+ default: nil,
+ proc: lambda { |i| i.to_i }
option :run_list,
- :short => "-r",
- :long => "--run-list",
- :description => "Show only the run list"
+ short: "-r",
+ long: "--run-list",
+ description: "Show only the run list."
option :id_only,
- :short => "-i",
- :long => "--id-only",
- :description => "Show only the ID of matching objects"
+ short: "-i",
+ long: "--id-only",
+ description: "Show only the ID of matching objects."
option :query,
- :short => "-q QUERY",
- :long => "--query QUERY",
- :description => "The search query; useful to protect queries starting with -"
+ short: "-q QUERY",
+ long: "--query QUERY",
+ description: "The search query; useful to protect queries starting with -."
option :filter_result,
- :short => "-f FILTER",
- :long => "--filter-result FILTER",
- :description => "Only return specific attributes of the matching objects; for example: \"ServerName=name, Kernel=kernel.version\""
+ short: "-f FILTER",
+ long: "--filter-result FILTER",
+ description: "Only return specific attributes of the matching objects; for example: \"ServerName=name, Kernel=kernel.version\"."
def run
read_cli_args
- fuzzify_query
if @type == "node"
ui.use_presenter Knife::Core::NodePresenter
end
q = Chef::Search::Query.new
- escaped_query = Addressable::URI.encode_component(@query, Addressable::URI::CharacterClasses::QUERY)
result_items = []
result_count = 0
- search_args = Hash.new
- search_args[:sort] = config[:sort] if config[:sort]
+ search_args = {}
+ 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|
- formatted_item = Hash.new
- if item.is_a?(Hash)
+ q.search(@type, @query, search_args) do |item|
+ formatted_item = {}
+ 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
@@ -113,14 +109,14 @@ class Chef
result_items << formatted_item
result_count += 1
end
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => 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?
- output({ :results => result_count, :rows => result_items })
+ output({ results: result_count, rows: result_items })
else
ui.log "#{result_count} items found"
ui.log("\n")
@@ -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:
@@ -176,24 +169,24 @@ class Chef
# and the path is an array with the path elements as strings (in order)
# See lib/chef/search/query.rb for more examples of this.
def create_result_filter(filter_string)
- final_filter = Hash.new
+ final_filter = {}
filter_string.delete!(" ")
filters = filter_string.split(",")
filters.each do |f|
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)
- final_filter = Hash.new
+ final_filter = {}
filter_array.each do |f|
final_filter[f] = f.split(".")
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/serve.rb b/lib/chef/knife/serve.rb
index 95996e6d1e..d79e05aa85 100644
--- a/lib/chef/knife/serve.rb
+++ b/lib/chef/knife/serve.rb
@@ -1,5 +1,22 @@
-require "chef/knife"
-require "chef/local_mode"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../knife"
+require_relative "../local_mode"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
@@ -8,16 +25,16 @@ class Chef
banner "knife serve (options)"
option :repo_mode,
- :long => "--repo-mode MODE",
- :description => "Specifies the local repository layout. Values: static (only environments/roles/data_bags/cookbooks), everything (includes nodes/clients/users), hosted_everything (includes acls/groups/etc. for Enterprise/Hosted Chef). Default: everything/hosted_everything"
+ long: "--repo-mode MODE",
+ description: "Specifies the local repository layout. Values: static (only environments/roles/data_bags/cookbooks), everything (includes nodes/clients/users), hosted_everything (includes acls/groups/etc. for Enterprise/Hosted Chef). Default: everything/hosted_everything."
option :chef_repo_path,
- :long => "--chef-repo-path PATH",
- :description => "Overrides the location of chef repo. Default is specified by chef_repo_path in the config"
+ long: "--chef-repo-path PATH",
+ description: "Overrides the location of #{ChefUtils::Dist::Infra::PRODUCT} repo. Default is specified by chef_repo_path in the config."
option :chef_zero_host,
- :long => "--chef-zero-host IP",
- :description => "Overrides the host upon which chef-zero listens. Default is 127.0.0.1."
+ long: "--chef-zero-host IP",
+ description: "Overrides the host upon which #{ChefUtils::Dist::Zero::PRODUCT} listens. Default is 127.0.0.1."
def configure_chef
super
diff --git a/lib/chef/knife/show.rb b/lib/chef/knife/show.rb
index 4c1c882815..0e5ab9d0fe 100644
--- a/lib/chef/knife/show.rb
+++ b/lib/chef/knife/show.rb
@@ -1,21 +1,37 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
class Show < Chef::ChefFS::Knife
- banner "knife show [PATTERN1 ... PATTERNn]"
+ banner "knife show [PATTERN1 ... PATTERNn] (options)"
category "path-based"
deps do
- require "chef/chef_fs/file_system"
- require "chef/chef_fs/file_system/exceptions"
+ require_relative "../chef_fs/file_system"
+ require_relative "../chef_fs/file_system/exceptions"
end
option :local,
- :long => "--local",
- :boolean => true,
- :description => "Show local files instead of remote"
+ long: "--local",
+ boolean: true,
+ description: "Show local files instead of remote."
def run
# Get the matches (recursively)
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 6f266b2719..8681fdfd02 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,139 +16,143 @@
# limitations under the License.
#
-require "chef/mixin/shell_out"
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class Ssh < Knife
deps do
- require "net/ssh"
+ require_relative "../mixin/shell_out"
+ require "net/ssh" unless defined?(Net::SSH)
require "net/ssh/multi"
- require "chef/monkey_patches/net-ssh-multi"
require "readline"
- require "chef/exceptions"
- require "chef/search/query"
- require "chef/util/path_helper"
- require "mixlib/shellout"
- end
+ require_relative "../exceptions"
+ require_relative "../search/query"
+ require_relative "../util/path_helper"
- include Chef::Mixin::ShellOut
+ include Chef::Mixin::ShellOut
+ end
attr_writer :password
banner "knife ssh QUERY COMMAND (options)"
option :concurrency,
- :short => "-C NUM",
- :long => "--concurrency NUM",
- :description => "The number of concurrent connections",
- :default => nil,
- :proc => lambda { |o| o.to_i }
-
- option :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 }
+ short: "-C NUM",
+ long: "--concurrency NUM",
+ description: "The number of concurrent connections.",
+ default: nil,
+ proc: lambda { |o| o.to_i }
+
+ option :ssh_attribute,
+ short: "-a ATTR",
+ long: "--attribute ATTR",
+ description: "The attribute to use for opening the connection - default depends on the context."
option :manual,
- :short => "-m",
- :long => "--manual-list",
- :boolean => true,
- :description => "QUERY is a space separated list of servers",
- :default => false
+ short: "-m",
+ long: "--manual-list",
+ boolean: true,
+ 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 output - default depends on the context."
option :ssh_user,
- :short => "-x USERNAME",
- :long => "--ssh-user USERNAME",
- :description => "The ssh username"
-
- option :ssh_password_ng,
- :short => "-P [PASSWORD]",
- :long => "--ssh-password [PASSWORD]",
- :description => "The ssh password - will prompt if flag is specified but no password is given",
+ short: "-x USERNAME",
+ long: "--ssh-user USERNAME",
+ description: "The ssh username."
+
+ option :ssh_password,
+ short: "-P [PASSWORD]",
+ long: "--ssh-password [PASSWORD]",
+ description: "The ssh password - will prompt if flag is specified but no password is given.",
# default to a value that can not be a password (boolean)
# so we can effectively test if this parameter was specified
# without a value
- :default => false
+ default: false
option :ssh_port,
- :short => "-p PORT",
- :long => "--ssh-port PORT",
- :description => "The ssh port",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip }
+ short: "-p PORT",
+ long: "--ssh-port PORT",
+ description: "The ssh port.",
+ proc: Proc.new { |key| key.strip }
option :ssh_timeout,
- :short => "-t SECONDS",
- :long => "--ssh-timeout SECONDS",
- :description => "The ssh connection timeout",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_timeout] = key.strip.to_i },
- :default => 120
+ short: "-t SECONDS",
+ long: "--ssh-timeout SECONDS",
+ description: "The ssh connection timeout.",
+ proc: Proc.new { |key| key.strip.to_i },
+ default: 120
option :ssh_gateway,
- :short => "-G GATEWAY",
- :long => "--ssh-gateway GATEWAY",
- :description => "The ssh gateway",
- :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key.strip }
+ short: "-G GATEWAY",
+ long: "--ssh-gateway GATEWAY",
+ description: "The ssh gateway.",
+ proc: Proc.new { |key| key.strip }
- option :forward_agent,
- :short => "-A",
- :long => "--forward-agent",
- :description => "Enable SSH agent forwarding",
- :boolean => true
+ option :ssh_gateway_identity,
+ long: "--ssh-gateway-identity SSH_GATEWAY_IDENTITY",
+ description: "The SSH identity file used for gateway authentication."
- option :identity_file,
- :long => "--identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication. [DEPRECATED] Use --ssh-identity-file instead."
+ option :forward_agent,
+ short: "-A",
+ long: "--forward-agent",
+ description: "Enable SSH agent forwarding.",
+ boolean: true
option :ssh_identity_file,
- :short => "-i IDENTITY_FILE",
- :long => "--ssh-identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication"
+ short: "-i IDENTITY_FILE",
+ long: "--ssh-identity-file IDENTITY_FILE",
+ description: "The SSH identity file used for authentication."
option :host_key_verify,
- :long => "--[no-]host-key-verify",
- :description => "Verify host key, enabled by default.",
- :boolean => true,
- :default => true
+ long: "--[no-]host-key-verify",
+ description: "Verify host key, enabled by default.",
+ boolean: true,
+ default: true
option :on_error,
- :short => "-e",
- :long => "--exit-on-error",
- :description => "Immediately exit if an error is encountered",
- :boolean => true,
- :proc => Proc.new { :raise }
+ short: "-e",
+ long: "--exit-on-error",
+ description: "Immediately exit if an error is encountered.",
+ boolean: true,
+ default: false
+
+ option :duplicated_fqdns,
+ long: "--duplicated-fqdns",
+ description: "Behavior if FQDNs are duplicated, ignored by default.",
+ proc: Proc.new { |key| key.strip.to_sym },
+ default: :ignore
option :tmux_split,
- :long => "--tmux-split",
- :description => "Split tmux window.",
- :boolean => true,
- :default => false
+ long: "--tmux-split",
+ description: "Split tmux window.",
+ boolean: true,
+ default: false
def session
- config[:on_error] ||= :skip
ssh_error_handler = Proc.new do |server|
- case config[:on_error]
- when :skip
+ 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) }
- when :raise
- #Net::SSH::Multi magic to force exception to be re-raised.
- throw :go, :raise
end
end
- @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency], :on_error => ssh_error_handler)
+ @session ||= Net::SSH::Multi.start(concurrent_connections: config[:concurrency], on_error: ssh_error_handler)
end
def configure_gateway
- config[:ssh_gateway] ||= Chef::Config[:knife][:ssh_gateway]
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
@@ -166,59 +170,92 @@ class Chef
def configure_session
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
+ list = []
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
@@ -230,15 +267,18 @@ 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
@@ -247,38 +287,47 @@ 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
- if !config[:host_key_verify]
- opts[:paranoid] = false
+ opts[:logger] = Chef::Log.with_child(subsystem: "net/ssh") if Chef::Log.level == :trace
+ unless config[:host_key_verify]
+ opts[:verify_host_key] = :never
opts[:user_known_hosts_file] = "/dev/null"
end
+ if ssh_config[:keepalive]
+ opts[:keepalive] = true
+ opts[:keepalive_interval] = ssh_config[:keepalive_interval]
+ end
+ # maintain support for legacy key types / ciphers / key exchange algorithms.
+ # most importantly this adds back support for DSS host keys
+ # See https://github.com/net-ssh/net-ssh/pull/709
+ opts[:append_all_supported_algorithms] = true
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)
@@ -309,19 +358,41 @@ 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")
+ begin
+ open_session(subsession, command)
+ rescue => e
+ open_session(subsession, command, true)
+ end
+ end
+
+ def open_session(subsession, command, pty = false)
+ stderr = ""
+ exit_status = 0
+ subsession.open_channel do |chan|
+ if config[:on_error] && exit_status != 0
+ chan.close
+ else
+ chan.request_pty if 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 /^knife sudo password: /.match?(data)
+ print_data(ichannel.connection[:prefix], "\n")
+ ichannel.send_data("#{get_password}\n")
+ end
+ end
+
+ ch.on_extended_data do |_, _type, data|
+ raise ArgumentError if data.eql?("sudo: no tty present and no askpass program specified\n")
+
+ stderr += data
+ 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
@@ -334,7 +405,7 @@ class Chef
end
def prompt_for_password(prompt = "Enter your password: ")
- ui.ask(prompt) { |q| q.echo = false }
+ ui.ask(prompt, echo: false)
end
# Present the prompt and read a single line from the console. It also
@@ -343,7 +414,7 @@ class Chef
# line is input.
def read_line
loop do
- command = reader.readline("#{ui.color('knife-ssh>', :bold)} ", true)
+ command = reader.readline("#{ui.color("knife-ssh>", :bold)} ", true)
if command.nil?
command = "exit"
@@ -379,7 +450,7 @@ class Chef
break
when /^on (.+?); (.+)$/
raw_list = $1.split(" ")
- server_list = Array.new
+ server_list = []
session.servers.each do |session_server|
server_list << session_server if raw_list.include?(session_server.host)
end
@@ -420,7 +491,7 @@ class Chef
new_window_cmds = lambda do
if session.servers_for.size > 1
- [""] + session.servers_for[1..-1].map do |server|
+ [""] + session.servers_for[1..].map do |server|
if config[:tmux_split]
"split-window #{ssh_dest.call(server)}; tmux select-layout tiled"
else
@@ -432,7 +503,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}",
@@ -446,37 +517,37 @@ class Chef
def macterm
begin
- require "appscript"
+ require "appscript" unless defined?(Appscript)
rescue LoadError
STDERR.puts "You need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
raise
end
Appscript.app("/Applications/Utilities/Terminal.app").windows.first.activate
- Appscript.app("System Events").application_processes["Terminal.app"].keystroke("n", :using => :command_down)
+ Appscript.app("System Events").application_processes["Terminal.app"].keystroke("n", using: :command_down)
term = Appscript.app("Terminal")
window = term.windows.first.get
(session.servers_for.size - 1).times do |i|
window.activate
- Appscript.app("System Events").application_processes["Terminal.app"].keystroke("t", :using => :command_down)
+ Appscript.app("System Events").application_processes["Terminal.app"].keystroke("t", using: :command_down)
end
session.servers_for.each_with_index do |server, tab_number|
cmd = "unset PROMPT_COMMAND; echo -e \"\\033]0;#{server.host}\\007\"; ssh #{server.user ? "#{server.user}@#{server.host}" : server.host}"
- Appscript.app("Terminal").do_script(cmd, :in => window.tabs[tab_number + 1].get)
+ Appscript.app("Terminal").do_script(cmd, in: window.tabs[tab_number + 1].get)
end
end
def cssh
cssh_cmd = nil
%w{csshX cssh}.each do |cmd|
- begin
- # Unix and Mac only
- cssh_cmd = shell_out!("which #{cmd}").stdout.strip
- break
- rescue Mixlib::ShellOut::ShellCommandFailed
- end
+
+ # Unix and Mac only
+ cssh_cmd = shell_out!("which #{cmd}").stdout.strip
+ break
+ rescue Mixlib::ShellOut::ShellCommandFailed
+
end
raise Chef::Exceptions::Exec, "no command found for cssh" unless cssh_cmd
@@ -493,7 +564,8 @@ class Chef
end
def get_stripped_unfrozen_value(value)
- return nil if value.nil?
+ return nil unless value
+
value.strip
end
@@ -502,46 +574,43 @@ class Chef
Chef::Config[:knife][:ssh_user])
end
- # This is a bit overly complicated because of the way we want knife ssh to work with -P causing a password prompt for
- # the user, but we have to be conscious that this code gets included in knife bootstrap and knife * server create as
- # well. We want to change the semantics so that the default is false and 'nil' means -P without an argument on the
- # command line. But the other utilities expect nil to be the default and we can't prompt in that case. So we effectively
- # use ssh_password_ng to determine if we're coming from knife ssh or from the other utilities. The other utilties can
- # also be patched to use ssh_password_ng easily as long they follow the convention that the default is false.
def configure_password
- if config.has_key?(:ssh_password_ng) && config[:ssh_password_ng].nil?
- # If the parameter is called on the command line with no value
- # it will set :ssh_password_ng = nil
- # This is where we want to trigger a prompt for password
+ if config.key?(:ssh_password) && config[:ssh_password].nil?
+ # if we have an actual nil that means someone called "--ssh-password" with no value, so we prompt for a password
config[:ssh_password] = get_password
else
- # if ssh_password_ng is false then it has not been set at all, and we may be in knife ec2 and still
- # using an old config[:ssh_password]. this is backwards compatibility. all knife cloud plugins should
- # be updated to use ssh_password_ng with a default of false and ssh_password should be retired, (but
- # we'll still need to use the ssh_password out of knife.rb if we find that).
- ssh_password = config.has_key?(:ssh_password_ng) ? config[:ssh_password_ng] : config[:ssh_password]
- # Otherwise, the password has either been specified on the command line,
- # in knife.rb, or key based auth will be attempted
- config[:ssh_password] = get_stripped_unfrozen_value(ssh_password ||
- Chef::Config[:knife][:ssh_password])
+ # the false default of ssh_password results in a nil here
+ config[:ssh_password] = get_stripped_unfrozen_value(config[:ssh_password])
end
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])
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])
end
def run
@longest = 0
+ if @name_args.length < 1
+ show_usage
+ ui.fatal("You must specify the SEARCH QUERY.")
+ exit(1)
+ end
+
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.
+ unless @password
+ configure_ssh_identity_file
+ configure_ssh_gateway_identity
+ end
+
configure_gateway
configure_session
@@ -557,16 +626,12 @@ class Chef
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(" "))
+ ssh_command(@name_args[1..].join(" "))
end
session.close
- if exit_status != 0
+ if exit_status && exit_status != 0
exit exit_status
else
exit_status
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
index 82ccb76ad7..0cc4141d42 100644
--- a/lib/chef/knife/ssl_check.rb
+++ b/lib/chef/knife/ssl_check.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +16,21 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/config"
+require_relative "../knife"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
class SslCheck < Chef::Knife
deps do
- require "pp"
- require "socket"
- require "uri"
- require "chef/http/ssl_policies"
- require "openssl"
- require "chef/mixin/proxified_socket"
+ require_relative "../config"
+ require "pp" unless defined?(PP)
+ require "socket" unless defined?(Socket)
+ require "uri" unless defined?(URI)
+ require_relative "../http/ssl_policies"
+ require "openssl" unless defined?(OpenSSL)
+ require_relative "../mixin/proxified_socket"
include Chef::Mixin::ProxifiedSocket
end
@@ -44,7 +45,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 +132,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 +142,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
@@ -151,27 +152,27 @@ class Chef
debug_ssl_settings
debug_chef_ssl_config
- ui.warn(<<-BAD_CERTS)
-There are invalid certificates in your trusted_certs_dir.
-OpenSSL will not use the following certificates when verifying SSL connections:
+ ui.warn(<<~BAD_CERTS)
+ There are invalid certificates in your trusted_certs_dir.
+ OpenSSL will not use the following certificates when verifying SSL connections:
-#{cert_debug_msg}
+ #{cert_debug_msg}
-#{ui.color("TO FIX THESE WARNINGS:", :bold)}
+ #{ui.color("TO FIX THESE WARNINGS:", :bold)}
-We are working on documentation for resolving common issues uncovered here.
+ We are working on documentation for resolving common issues uncovered here.
-* If the certificate is generated by the server, you may try redownloading the
-server's certificate. By default, the certificate is stored in the following
-location on the host where your chef-server runs:
+ * If the certificate is generated by the server, you may try redownloading the
+ server's certificate. By default, the certificate is stored in the following
+ location on the host where your chef-server runs:
- /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt
+ /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt
-Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
-using SSH/SCP or some other secure method, then re-run this command to confirm
-that the server's certificate is now trusted.
+ Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
+ using SSH/SCP or some other secure method, then re-run this command to confirm
+ that the server's certificate is now trusted.
-BAD_CERTS
+ BAD_CERTS
# @TODO: ^ needs URL once documentation is posted.
end
@@ -184,23 +185,23 @@ BAD_CERTS
debug_ssl_settings
debug_chef_ssl_config
- ui.err(<<-ADVICE)
+ ui.err(<<~ADVICE)
-#{ui.color("TO FIX THIS ERROR:", :bold)}
+ #{ui.color("TO FIX THIS ERROR:", :bold)}
-If the server you are connecting to uses a self-signed certificate, you must
-configure chef to trust that server's certificate.
+ If the server you are connecting to uses a self-signed certificate, you must
+ configure #{ChefUtils::Dist::Infra::PRODUCT} to trust that server's certificate.
-By default, the certificate is stored in the following location on the host
-where your chef-server runs:
+ By default, the certificate is stored in the following location on the host
+ where your chef-server runs:
- /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt
+ /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt
-Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
-using SSH/SCP or some other secure method, then re-run this command to confirm
-that the server's certificate is now trusted.
+ Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
+ using SSH/SCP or some other secure method, then re-run this command to confirm
+ that the server's certificate is now trusted.
-ADVICE
+ ADVICE
end
def debug_invalid_host
@@ -211,18 +212,18 @@ ADVICE
ui.error("You are attempting to connect to: '#{host}'")
ui.error("The server's certificate belongs to '#{cn}'")
- ui.err(<<-ADVICE)
+ ui.err(<<~ADVICE)
-#{ui.color("TO FIX THIS ERROR:", :bold)}
+ #{ui.color("TO FIX THIS ERROR:", :bold)}
-The solution for this issue depends on your networking configuration. If you
-are able to connect to this server using the hostname #{cn}
-instead of #{host}, then you can resolve this issue by updating chef_server_url
-in your configuration file.
+ The solution for this issue depends on your networking configuration. If you
+ are able to connect to this server using the hostname #{cn}
+ instead of #{host}, then you can resolve this issue by updating chef_server_url
+ in your configuration file.
-If you are not able to connect to the server using the hostname #{cn}
-you will have to update the certificate on the server to use the correct hostname.
-ADVICE
+ If you are not able to connect to the server using the hostname #{cn}
+ you will have to update the certificate on the server to use the correct hostname.
+ ADVICE
end
def debug_ssl_settings
@@ -233,7 +234,7 @@ ADVICE
end
def debug_chef_ssl_config
- ui.err "Chef SSL Configuration:"
+ ui.err "#{ChefUtils::Dist::Infra::PRODUCT} SSL Configuration:"
ui.err "* ssl_ca_path: #{configuration.ssl_ca_path.inspect}"
ui.err "* ssl_ca_file: #{configuration.ssl_ca_file.inspect}"
ui.err "* trusted_certs_dir: #{configuration.trusted_certs_dir.inspect}"
@@ -276,7 +277,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..cfbbc823b2 100644
--- a/lib/chef/knife/ssl_fetch.rb
+++ b/lib/chef/knife/ssl_fetch.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,19 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/config"
+require_relative "../knife"
class Chef
class Knife
class SslFetch < Chef::Knife
deps do
- require "pp"
- require "socket"
- require "uri"
- require "openssl"
- require "chef/mixin/proxified_socket"
+ require_relative "../config"
+ require "pp" unless defined?(PP)
+ require "socket" unless defined?(Socket)
+ require "uri" unless defined?(URI)
+ require "openssl" unless defined?(OpenSSL)
+ require_relative "../mixin/proxified_socket"
include Chef::Mixin::ProxifiedSocket
end
@@ -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,15 +89,18 @@ 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
# filename. To do so, all `*` characters are converted to the string
- # "wildcard" and then all characters other than alphanumeric and hypen
+ # "wildcard" and then all characters other than alphanumeric and hyphen
# characters are converted to underscores.
- # NOTE: There is some confustion about what the CN will contain when
+ # NOTE: There is some confusion about what the CN will contain when
# using internationalized domain names. RFC 6125 mandates that the ascii
# representation be used, but it is not clear whether this is followed in
# practice.
@@ -117,23 +120,24 @@ 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
def run
validate_uri
- ui.warn(<<-TRUST_TRUST)
-Certificates from #{host} will be fetched and placed in your trusted_cert
-directory (#{trusted_certs_dir}).
+ ui.warn(<<~TRUST_TRUST)
+ Certificates from #{host} will be fetched and placed in your trusted_cert
+ directory (#{trusted_certs_dir}).
-Knife has no means to verify these are the correct certificates. You should
-verify the authenticity of these certificates after downloading.
+ Knife has no means to verify these are the correct certificates. You should
+ verify the authenticity of these certificates after downloading.
-TRUST_TRUST
+ TRUST_TRUST
remote_cert_chain.each do |cert|
write_cert(cert)
end
diff --git a/lib/chef/knife/status.rb b/lib/chef/knife/status.rb
index 0e3cd7e7d6..34692d6da7 100644
--- a/lib/chef/knife/status.rb
+++ b/lib/chef/knife/status.rb
@@ -1,6 +1,6 @@
#
# Author:: Ian Meyer (<ianmmeyer@gmail.com>)
-# Copyright:: Copyright 2010-2016, Ian Meyer
+# Copyright:: Copyright 2010-2020, Ian Meyer
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,39 +16,35 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/core/status_presenter"
-require "chef/knife/core/node_presenter"
+require_relative "../knife"
+require_relative "core/status_presenter"
+require_relative "core/formatting_options"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
class Status < Knife
- include Knife::Core::NodeFormattingOptions
+ include Knife::Core::FormattingOptions
deps do
- require "chef/search/query"
+ require_relative "../search/query"
end
banner "knife status QUERY (options)"
option :run_list,
- :short => "-r",
- :long => "--run-list",
- :description => "Show the run list"
+ short: "-r",
+ long: "--run-list",
+ description: "Show the run list"
option :sort_reverse,
- :short => "-s",
- :long => "--sort-reverse",
- :description => "Sort the status list by last run time descending"
-
- option :hide_healthy,
- :short => "-H",
- :long => "--hide-healthy",
- :description => "Hide nodes that have run chef in the last hour. [DEPRECATED] Use --hide-by-mins MINS instead"
+ short: "-s",
+ long: "--sort-reverse",
+ description: "Sort the status list by last run time descending"
option :hide_by_mins,
- :long => "--hide-by-mins MINS",
- :description => "Hide nodes that have run chef in the last MINS minutes"
+ long: "--hide-by-mins MINS",
+ description: "Hide nodes that have run #{ChefUtils::Dist::Infra::CLIENT} in the last MINS minutes"
def append_to_query(term)
@query << " AND " unless @query.empty?
@@ -63,7 +59,7 @@ class Chef
else
opts = { filter_result:
{ name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"],
- ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"],
+ cloud: ["cloud"], run_list: ["run_list"], platform: ["platform"],
platform_version: ["platform_version"], chef_environment: ["chef_environment"] } }
end
@@ -71,20 +67,12 @@ class Chef
append_to_query(@name_args[0]) if @name_args[0]
append_to_query("chef_environment:#{config[:environment]}") if config[:environment]
- if config[:hide_healthy]
- ui.warn("-H / --hide-healthy is deprecated. Use --hide-by-mins MINS instead")
- time = Time.now.to_i
- # AND NOT is not valid lucene syntax, so don't use append_to_query
- @query << " " unless @query.empty?
- @query << "NOT ohai_time:[#{(time - 60 * 60)} TO #{time}]"
- end
-
if config[:hide_by_mins]
- hidemins = config[:hide_by_mins].to_i
+ hide_by_mins = config[:hide_by_mins].to_i
time = Time.now.to_i
# AND NOT is not valid lucene syntax, so don't use append_to_query
@query << " " unless @query.empty?
- @query << "NOT ohai_time:[#{(time - hidemins * 60)} TO #{time}]"
+ @query << "NOT ohai_time:[#{(time - hide_by_mins * 60)} TO #{time}]"
end
@query = @query.empty? ? "*:*" : @query
@@ -96,13 +84,10 @@ class Chef
all_nodes << node
end
- 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)
+ all_nodes.sort_by! { |n| n["ohai_time"] || 0 }
+ all_nodes.reverse! if config[:sort_reverse] || config[:sort_status_reverse]
+
+ output(all_nodes)
end
end
diff --git a/lib/chef/knife/supermarket_download.rb b/lib/chef/knife/supermarket_download.rb
index 5657558591..5acd733b78 100644
--- a/lib/chef/knife/supermarket_download.rb
+++ b/lib/chef/knife/supermarket_download.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,106 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_download"
+require_relative "../knife"
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)
+ class SupermarketDownload < Knife
banner "knife supermarket download COOKBOOK [VERSION] (options)"
category "supermarket"
+
+ deps do
+ require "fileutils" unless defined?(FileUtils)
+ end
+
+ option :file,
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "The filename to write to."
+
+ option :force,
+ long: "--force",
+ description: "Force download deprecated version."
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ def run
+ if current_cookbook_deprecated?
+ message = "DEPRECATION: This cookbook has been deprecated. "
+ 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]
+ ui.warn "Use --force to force download deprecated cookbook."
+ return
+ end
+ end
+
+ download_cookbook
+ end
+
+ def version
+ @version = desired_cookbook_data["version"]
+ end
+
+ private
+
+ def cookbooks_api_url
+ "#{config[:supermarket_site]}/api/v1/cookbooks"
+ end
+
+ def current_cookbook_data
+ @current_cookbook_data ||= begin
+ noauth_rest.get "#{cookbooks_api_url}/#{@name_args[0]}"
+ end
+ end
+
+ def current_cookbook_deprecated?
+ current_cookbook_data["deprecated"] == true
+ end
+
+ def desired_cookbook_data
+ @desired_cookbook_data ||= begin
+ uri = if @name_args.length == 1
+ current_cookbook_data["latest_version"]
+ else
+ specific_cookbook_version_url
+ end
+
+ noauth_rest.get uri
+ end
+ end
+
+ def download_cookbook
+ ui.info "Downloading #{@name_args[0]} from Supermarket at version #{version} to #{download_location}"
+ tf = noauth_rest.streaming_request(desired_cookbook_data["file"])
+
+ ::FileUtils.cp tf.path, download_location
+ ui.info "Cookbook saved: #{download_location}"
+ end
+
+ def download_location
+ config[:file] ||= File.join Dir.pwd, "#{@name_args[0]}-#{version}.tar.gz"
+ config[:file]
+ end
+
+ def replacement_cookbook
+ File.basename(current_cookbook_data["replacement"] || "")
+ end
+
+ def specific_cookbook_version_url
+ "#{cookbooks_api_url}/#{@name_args[0]}/versions/#{@name_args[1].tr(".", "_")}"
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_install.rb b/lib/chef/knife/supermarket_install.rb
index 7642e68181..a3d3aa7a5d 100644
--- a/lib/chef/knife/supermarket_install.rb
+++ b/lib/chef/knife/supermarket_install.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,177 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_install"
+require_relative "../knife"
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)
+ class SupermarketInstall < Knife
+
+ deps do
+ require_relative "../exceptions"
+ require "shellwords" unless defined?(Shellwords)
+ require "mixlib/archive" unless defined?(Mixlib::Archive)
+ require_relative "core/cookbook_scm_repo"
+ require_relative "../cookbook/metadata"
+ end
banner "knife supermarket install COOKBOOK [VERSION] (options)"
category "supermarket"
+
+ option :no_deps,
+ short: "-D",
+ long: "--skip-dependencies",
+ boolean: true,
+ default: false,
+ description: "Skips automatic dependency installation."
+
+ option :cookbook_path,
+ short: "-o PATH:PATH",
+ long: "--cookbook-path PATH:PATH",
+ description: "A colon-separated path to look for cookbooks in.",
+ proc: lambda { |o| o.split(":") }
+
+ option :default_branch,
+ short: "-B BRANCH",
+ long: "--branch BRANCH",
+ description: "Default branch to work with.",
+ default: "master"
+
+ option :use_current_branch,
+ short: "-b",
+ long: "--use-current-branch",
+ description: "Use the current branch.",
+ boolean: true,
+ default: false
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ attr_reader :cookbook_name
+ attr_reader :vendor_path
+
+ def run
+ if config[:cookbook_path]
+ Chef::Config[:cookbook_path] = config[:cookbook_path]
+ else
+ config[:cookbook_path] = Chef::Config[:cookbook_path]
+ end
+
+ @cookbook_name = parse_name_args!
+ # 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}"
+
+ @repo = CookbookSCMRepo.new(@install_path, ui, config)
+ # cookbook_path = File.join(vendor_path, name_args[0])
+ upstream_file = File.join(@install_path, "#{@cookbook_name}.tar.gz")
+
+ @repo.sanity_check
+ unless config[:use_current_branch]
+ @repo.reset_to_default_state
+ @repo.prepare_to_import(@cookbook_name)
+ end
+
+ downloader = download_cookbook_to(upstream_file)
+ clear_existing_files(File.join(@install_path, @cookbook_name))
+ extract_cookbook(upstream_file, downloader.version)
+
+ # TODO: it'd be better to store these outside the cookbook repo and
+ # keep them around, e.g., in ~/Library/Caches on macOS.
+ ui.info("Removing downloaded tarball")
+ File.unlink(upstream_file)
+
+ if @repo.finalize_updates_to(@cookbook_name, downloader.version)
+ unless config[:use_current_branch]
+ @repo.reset_to_default_state
+ end
+ @repo.merge_updates_from(@cookbook_name, downloader.version)
+ else
+ unless config[:use_current_branch]
+ @repo.reset_to_default_state
+ end
+ end
+
+ unless config[:no_deps]
+ preferred_metadata.dependencies.each_key do |cookbook|
+ # Doesn't do versions.. yet
+ nv = self.class.new
+ nv.config = config
+ nv.name_args = [ cookbook ]
+ nv.run
+ end
+ end
+ end
+
+ def parse_name_args!
+ if name_args.empty?
+ ui.error("Please specify a cookbook to download and install.")
+ exit 1
+ elsif name_args.size >= 2
+ unless name_args.last.match(/^(\d+)(\.\d+){1,2}$/) && name_args.size == 2
+ ui.error("Installing multiple cookbooks at once is not supported.")
+ exit 1
+ end
+ end
+ name_args.first
+ end
+
+ def download_cookbook_to(download_path)
+ downloader = Chef::Knife::SupermarketDownload.new
+ downloader.config[:file] = download_path
+ downloader.config[:supermarket_site] = config[:supermarket_site]
+ downloader.name_args = name_args
+ downloader.run
+ downloader
+ end
+
+ def extract_cookbook(upstream_file, version)
+ ui.info("Uncompressing #{@cookbook_name} version #{version}.")
+ Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false)
+ end
+
+ def clear_existing_files(cookbook_path)
+ ui.info("Removing pre-existing version.")
+ FileUtils.rmtree(cookbook_path) if File.directory?(cookbook_path)
+ end
+
+ def convert_path(upstream_file)
+ # converts a Windows path (C:\foo) to a mingw path (/c/foo)
+ if ENV["MSYSTEM"] == "MINGW32"
+ upstream_file.sub(/^([[:alpha:]]):/, '/\1')
+ else
+ Shellwords.escape upstream_file
+ end
+ end
+
+ # Get the preferred metadata path on disk. Chef prefers the metadata.rb
+ # over the metadata.json.
+ #
+ # @raise if there is no metadata in the cookbook
+ #
+ # @return [Chef::Cookbook::Metadata]
+ def preferred_metadata
+ md = Chef::Cookbook::Metadata.new
+
+ rb = File.join(@install_path, @cookbook_name, "metadata.rb")
+ if File.exist?(rb)
+ md.from_file(rb)
+ return md
+ end
+
+ json = File.join(@install_path, @cookbook_name, "metadata.json")
+ if File.exist?(json)
+ json = IO.read(json)
+ md.from_json(json)
+ return md
+ end
+
+ raise Chef::Exceptions::MetadataNotFound.new(@install_path, @cookbook_name)
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_list.rb b/lib/chef/knife/supermarket_list.rb
index f2bc98bd0e..7dca8d031b 100644
--- a/lib/chef/knife/supermarket_list.rb
+++ b/lib/chef/knife/supermarket_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,61 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_list"
+require_relative "../knife"
class Chef
class Knife
- class SupermarketList < Knife::CookbookSiteList
- # Handle the subclassing (knife doesn't do this :()
- dependency_loaders.concat(superclass.dependency_loaders)
- options.merge!(superclass.options)
+ class SupermarketList < Knife
banner "knife supermarket list (options)"
category "supermarket"
+
+ option :with_uri,
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ option :sort_by,
+ long: "--sort-by SORT",
+ description: "Use to sort the records",
+ in: %w{recently_updated recently_added most_downloaded most_followed}
+
+ option :owned_by,
+ short: "-o USER",
+ long: "--owned-by USER",
+ description: "Show cookbooks that are owned by the USER"
+
+ def run
+ if config[:with_uri]
+ ui.output(format_for_display(get_cookbook_list))
+ else
+ ui.msg(ui.list(get_cookbook_list.keys, :columns_down))
+ end
+ end
+
+ # In order to avoid pagination items limit set to 9999999
+ def get_cookbook_list(items = 9999999, start = 0, cookbook_collection = {})
+ cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}"
+ cookbooks_url << "&order=#{config[:sort_by]}" if config[:sort_by]
+ cookbooks_url << "&user=#{config[:owned_by]}" if config[:owned_by]
+ cr = noauth_rest.get(cookbooks_url)
+
+ cr["items"].each do |cookbook|
+ cookbook_collection[cookbook["cookbook_name"]] = cookbook["cookbook"]
+ end
+ new_start = start + items
+ if new_start < cr["total"]
+ get_cookbook_list(items, new_start, cookbook_collection)
+ else
+ cookbook_collection
+ end
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_search.rb b/lib/chef/knife/supermarket_search.rb
index 3206b0cb80..57befaed35 100644
--- a/lib/chef/knife/supermarket_search.rb
+++ b/lib/chef/knife/supermarket_search.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,38 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_search"
+require_relative "../knife"
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)
-
+ class SupermarketSearch < Knife
banner "knife supermarket search QUERY (options)"
category "supermarket"
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ def run
+ output(search_cookbook(name_args[0]))
+ end
+
+ # In order to avoid pagination items limit set to 9999999
+ def search_cookbook(query, items = 9999999, start = 0, cookbook_collection = {})
+ 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
+ end
+ new_start = start + items
+ if new_start < cr["total"]
+ search_cookbook(query, items, new_start, cookbook_collection)
+ else
+ cookbook_collection
+ end
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_share.rb b/lib/chef/knife/supermarket_share.rb
index 3109b9e794..49b3474566 100644
--- a/lib/chef/knife/supermarket_share.rb
+++ b/lib/chef/knife/supermarket_share.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,151 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_share"
+require_relative "../knife"
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)
+ class SupermarketShare < Knife
+
+ include Chef::Mixin::ShellOut
+
+ deps do
+ require_relative "../cookbook_loader"
+ require_relative "../cookbook_uploader"
+ require_relative "../cookbook_site_streaming_uploader"
+ require_relative "../mixin/shell_out"
+ end
banner "knife supermarket share COOKBOOK [CATEGORY] (options)"
category "supermarket"
+
+ option :cookbook_path,
+ short: "-o PATH:PATH",
+ long: "--cookbook-path PATH:PATH",
+ description: "A colon-separated path to look for cookbooks in.",
+ proc: lambda { |o| Chef::Config.cookbook_path = o.split(":") }
+
+ option :dry_run,
+ long: "--dry-run",
+ short: "-n",
+ boolean: true,
+ 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: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ def run
+ config[:cookbook_path] ||= Chef::Config[:cookbook_path]
+
+ if @name_args.length < 1
+ show_usage
+ ui.fatal("You must specify the cookbook name.")
+ exit(1)
+ elsif @name_args.length < 2
+ cookbook_name = @name_args[0]
+ category = get_category(cookbook_name)
+ else
+ cookbook_name = @name_args[0]
+ category = @name_args[1]
+ end
+
+ cl = Chef::CookbookLoader.new(config[:cookbook_path])
+ if cl.cookbook_exists?(cookbook_name)
+ cookbook = cl[cookbook_name]
+ Chef::CookbookUploader.new(cookbook).validate_cookbooks
+ tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook)
+ begin
+ 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.trace("\n#{e.backtrace.join("\n")}")
+ exit(1)
+ end
+
+ if config[:dry_run]
+ ui.info("Not uploading #{cookbook_name}.tgz due to --dry-run flag.")
+ result = shell_out!("#{tar_cmd} -tzf #{cookbook_name}.tgz", cwd: tmp_cookbook_dir)
+ ui.info(result.stdout)
+ FileUtils.rm_rf tmp_cookbook_dir
+ return
+ end
+
+ begin
+ do_upload("#{tmp_cookbook_dir}/#{cookbook_name}.tgz", category, Chef::Config[:node_name], Chef::Config[:client_key])
+ ui.info("Upload complete")
+ 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.trace("\n#{e.backtrace.join("\n")}")
+ exit(1)
+ end
+
+ else
+ ui.error("Could not find cookbook #{cookbook_name} in your cookbook path.")
+ exit(1)
+ end
+ end
+
+ def get_category(cookbook_name)
+ data = noauth_rest.get("#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}")
+ data["category"]
+ rescue => e
+ return "Other" if e.is_a?(Net::HTTPClientException) && 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 = "#{config[:supermarket_site]}/api/v1/cookbooks"
+
+ category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category })
+
+ http_resp = Chef::CookbookSiteStreamingUploader.post(uri, user_id, user_secret_filename, {
+ tarball: File.open(cookbook_filename),
+ cookbook: category_string,
+ })
+
+ res = Chef::JSONCompat.from_json(http_resp.body)
+ if http_resp.code.to_i != 201
+ if res["error_messages"]
+ if /Version already exists/.match?(res["error_messages"][0])
+ ui.error "The same version of this cookbook already exists on Supermarket."
+ exit(1)
+ else
+ ui.error (res["error_messages"][0]).to_s
+ exit(1)
+ end
+ else
+ ui.error "Unknown error while sharing cookbook"
+ ui.error "Server response: #{http_resp.body}"
+ exit(1)
+ end
+ end
+ res
+ end
+
+ def tar_cmd
+ unless @tar_cmd
+ @tar_cmd = "tar"
+ begin
+ # Unix and Mac only - prefer gnutar
+ if shell_out("which gnutar").exitstatus.equal?(0)
+ @tar_cmd = "gnutar"
+ end
+ rescue Errno::ENOENT
+ end
+ end
+ @tar_cmd
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_show.rb b/lib/chef/knife/supermarket_show.rb
index 2ad122143f..7237cf0bc7 100644
--- a/lib/chef/knife/supermarket_show.rb
+++ b/lib/chef/knife/supermarket_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,51 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_show"
+require_relative "../knife"
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)
+ class SupermarketShow < Knife
banner "knife supermarket show COOKBOOK [VERSION] (options)"
category "supermarket"
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ 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("#{supermarket_uri}/cookbooks/#{@name_args[0]}")
+ when 2
+ 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 = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}"
+ cr = noauth_rest.get(cookbooks_url)
+ cr["items"].each do |cookbook|
+ cookbook_collection[cookbook["cookbook_name"]] = cookbook
+ end
+ new_start = start + cr["items"].length
+ if new_start < cr["total"]
+ get_cookbook_list(items, new_start, cookbook_collection)
+ else
+ cookbook_collection
+ end
+ end
end
end
end
diff --git a/lib/chef/knife/supermarket_unshare.rb b/lib/chef/knife/supermarket_unshare.rb
index fd48e172ce..686d95f47a 100644
--- a/lib/chef/knife/supermarket_unshare.rb
+++ b/lib/chef/knife/supermarket_unshare.rb
@@ -1,6 +1,6 @@
#
# Author:: Christopher Webber (<cwebber@chef.io>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,46 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/cookbook_site_unshare"
+require_relative "../knife"
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)
+ class SupermarketUnshare < Knife
- banner "knife supermarket unshare COOKBOOK (options)"
+ deps do
+ require_relative "../json_compat"
+ end
+
+ banner "knife supermarket unshare COOKBOOK"
category "supermarket"
+
+ option :supermarket_site,
+ short: "-m SUPERMARKET_SITE",
+ long: "--supermarket-site SUPERMARKET_SITE",
+ description: "The URL of the Supermarket site.",
+ default: "https://supermarket.chef.io"
+
+ def run
+ @cookbook_name = @name_args[0]
+ if @cookbook_name.nil?
+ show_usage
+ ui.fatal "You must provide the name of the cookbook to unshare"
+ exit 1
+ end
+
+ confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
+
+ begin
+ rest.delete "#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}"
+ rescue Net::HTTPClientException => e
+ raise e unless /Forbidden/.match?(e.message)
+
+ ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it."
+ exit 1
+ end
+
+ ui.info "Unshared all versions of the cookbook #{@cookbook_name}"
+ end
end
end
end
diff --git a/lib/chef/knife/tag_create.rb b/lib/chef/knife/tag_create.rb
index e8cfe11303..2f0d302e74 100644
--- a/lib/chef/knife/tag_create.rb
+++ b/lib/chef/knife/tag_create.rb
@@ -18,21 +18,21 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class TagCreate < Knife
deps do
- require "chef/node"
+ require_relative "../node"
end
banner "knife tag create NODE TAG ..."
def run
name = @name_args[0]
- tags = @name_args[1..-1]
+ tags = @name_args[1..]
if name.nil? || tags.nil? || tags.empty?
show_usage
diff --git a/lib/chef/knife/tag_delete.rb b/lib/chef/knife/tag_delete.rb
index d00ec2f60b..85fa6a9e27 100644
--- a/lib/chef/knife/tag_delete.rb
+++ b/lib/chef/knife/tag_delete.rb
@@ -18,21 +18,21 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class TagDelete < Knife
deps do
- require "chef/node"
+ require_relative "../node"
end
banner "knife tag delete NODE TAG ..."
def run
name = @name_args[0]
- tags = @name_args[1..-1]
+ tags = @name_args[1..]
if name.nil? || tags.nil? || tags.empty?
show_usage
@@ -41,7 +41,7 @@ class Chef
end
node = Chef::Node.load name
- deleted_tags = Array.new
+ deleted_tags = []
tags.each do |tag|
unless node.tags.delete(tag).nil?
deleted_tags << tag
diff --git a/lib/chef/knife/tag_list.rb b/lib/chef/knife/tag_list.rb
index 2665e53bf4..8b91034609 100644
--- a/lib/chef/knife/tag_list.rb
+++ b/lib/chef/knife/tag_list.rb
@@ -18,14 +18,14 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class TagList < Knife
deps do
- require "chef/node"
+ require_relative "../node"
end
banner "knife tag list NODE"
diff --git a/lib/chef/knife/upload.rb b/lib/chef/knife/upload.rb
index f0ecaaae47..190549d86a 100644
--- a/lib/chef/knife/upload.rb
+++ b/lib/chef/knife/upload.rb
@@ -1,52 +1,68 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
class Upload < Chef::ChefFS::Knife
- banner "knife upload PATTERNS"
+ banner "knife upload PATTERNS (options)"
category "path-based"
deps do
- require "chef/chef_fs/command_line"
+ require_relative "../chef_fs/command_line"
end
option :recurse,
- :long => "--[no-]recurse",
- :boolean => true,
- :default => true,
- :description => "List directories recursively."
+ long: "--[no-]recurse",
+ boolean: true,
+ default: true,
+ description: "List directories recursively."
option :purge,
- :long => "--[no-]purge",
- :boolean => true,
- :default => false,
- :description => "Delete matching local files and directories that do not exist remotely."
+ long: "--[no-]purge",
+ boolean: true,
+ default: false,
+ description: "Delete matching local files and directories that do not exist remotely."
option :force,
- :long => "--[no-]force",
- :boolean => true,
- :default => false,
- :description => "Force upload of files even if they match (quicker for many files). Will overwrite frozen cookbooks."
+ long: "--[no-]force",
+ boolean: true,
+ default: false,
+ description: "Force upload of files even if they match (quicker for many files). Will overwrite frozen cookbooks."
option :freeze,
- :long => "--[no-]freeze",
- :boolean => true,
- :default => false,
- :description => "Freeze cookbooks that get uploaded."
+ long: "--[no-]freeze",
+ boolean: true,
+ default: false,
+ description: "Freeze cookbooks that get uploaded."
option :dry_run,
- :long => "--dry-run",
- :short => "-n",
- :boolean => true,
- :default => false,
- :description => "Don't take action, only print what would happen"
+ long: "--dry-run",
+ short: "-n",
+ boolean: true,
+ default: false,
+ description: "Don't take action, only print what would happen."
option :diff,
- :long => "--[no-]diff",
- :boolean => true,
- :default => true,
- :description => "Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff"
+ long: "--[no-]diff",
+ boolean: true,
+ default: true,
+ description: "Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff."
def run
if name_args.length == 0
diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb
index d21afb1059..6d68f3ebbb 100644
--- a/lib/chef/knife/user_create.rb
+++ b/lib/chef/knife/user_create.rb
@@ -1,7 +1,7 @@
#
# Author:: Steven Danna (<steve@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/osc_user_create"
+require_relative "../knife"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Knife
@@ -27,36 +27,23 @@ class Chef
attr_accessor :user_field
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file if the server generated one."
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the private key to a file if the server generated one."
option :user_key,
- :long => "--user-key FILENAME",
- :description => "Set the initial default key for the user from a file on disk (cannot pass with --prevent-keygen)."
+ long: "--user-key FILENAME",
+ description: "Set the initial default key for the user from a file on disk (cannot pass with --prevent-keygen)."
option :prevent_keygen,
- :short => "-k",
- :long => "--prevent-keygen",
- :description => "API V1 (Chef Server 12.1+) only. Prevent server from generating a default key pair for you. Cannot be passed with --user-key.",
- :boolean => true
-
- option :admin,
- :short => "-a",
- :long => "--admin",
- :description => "DEPRECATED: Open Source Chef 11 only. Create the user as an admin.",
- :boolean => true
-
- option :user_password,
- :short => "-p PASSWORD",
- :long => "--password PASSWORD",
- :description => "DEPRECATED: Open Source Chef 11 only. Password for newly created user.",
- :default => ""
+ short: "-k",
+ long: "--prevent-keygen",
+ description: "API V1 (#{ChefUtils::Dist::Server::PRODUCT} 12.1+) only. Prevent server from generating a default key pair for you. Cannot be passed with --user-key.",
+ boolean: true
banner "knife user create USERNAME DISPLAY_NAME FIRST_NAME LAST_NAME EMAIL PASSWORD (options)"
@@ -68,80 +55,50 @@ class Chef
Chef::UserV1.from_hash(hash).create
end
- def osc_11_warning
- <<-EOF
-IF YOU ARE USING CHEF SERVER 12+, PLEASE FOLLOW THE INSTRUCTIONS UNDER knife user create --help.
-You only passed a single argument to knife user create.
-For backwards compatibility, when only a single argument is passed,
-knife user create assumes you want Open Source 11 Server user creation.
-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.
-EOF
- end
-
- def run_osc_11_user_create
- # run osc_user_create with our input
- ARGV.delete("user")
- ARGV.unshift("osc_user")
- Chef::Knife.run(ARGV, Chef::Application::Knife.options)
- end
-
def run
- # DEPRECATION NOTE
- # Remove this if statement and corrosponding code post OSC 11 support.
- #
- # If only 1 arg is passed, assume OSC 11 case.
- if @name_args.length == 1
- ui.warn(osc_11_warning)
- run_osc_11_user_create
- else # EC / CS 12 user create
+ test_mandatory_field(@name_args[0], "username")
+ user.username @name_args[0]
- test_mandatory_field(@name_args[0], "username")
- user.username @name_args[0]
+ test_mandatory_field(@name_args[1], "display name")
+ user.display_name @name_args[1]
- test_mandatory_field(@name_args[1], "display name")
- user.display_name @name_args[1]
+ test_mandatory_field(@name_args[2], "first name")
+ user.first_name @name_args[2]
- test_mandatory_field(@name_args[2], "first name")
- user.first_name @name_args[2]
+ test_mandatory_field(@name_args[3], "last name")
+ user.last_name @name_args[3]
- test_mandatory_field(@name_args[3], "last name")
- user.last_name @name_args[3]
+ test_mandatory_field(@name_args[4], "email")
+ user.email @name_args[4]
- test_mandatory_field(@name_args[4], "email")
- user.email @name_args[4]
+ test_mandatory_field(@name_args[5], "password")
+ user.password @name_args[5]
- test_mandatory_field(@name_args[5], "password")
- user.password @name_args[5]
+ if config[:user_key] && config[:prevent_keygen]
+ show_usage
+ ui.fatal("You cannot pass --user-key and --prevent-keygen")
+ exit 1
+ end
- if config[:user_key] && config[:prevent_keygen]
- show_usage
- ui.fatal("You cannot pass --user-key and --prevent-keygen")
- exit 1
- end
+ if !config[:prevent_keygen] && !config[:user_key]
+ user.create_key(true)
+ end
- if !config[:prevent_keygen] && !config[:user_key]
- user.create_key(true)
- end
+ if config[:user_key]
+ user.public_key File.read(File.expand_path(config[:user_key]))
+ end
- if config[:user_key]
- user.public_key File.read(File.expand_path(config[:user_key]))
- end
+ output = edit_hash(user)
+ final_user = create_user_from_hash(output)
- output = edit_hash(user)
- final_user = create_user_from_hash(output)
-
- ui.info("Created #{user}")
- if final_user.private_key
- if config[:file]
- File.open(config[:file], "w") do |f|
- f.print(final_user.private_key)
- end
- else
- ui.msg final_user.private_key
+ ui.info("Created #{user}")
+ if final_user.private_key
+ if config[:file]
+ File.open(config[:file], "w") do |f|
+ f.print(final_user.private_key)
end
+ else
+ ui.msg final_user.private_key
end
end
end
diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb
index ce4575ceab..87c1f734bb 100644
--- a/lib/chef/knife/user_delete.rb
+++ b/lib/chef/knife/user_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,53 +16,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class UserDelete < Knife
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
banner "knife user delete USER (options)"
- def osc_11_warning
- <<-EOF
-The Chef Server you are using does not support the username field.
-This means it is an Open Source 11 Server.
-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.
-EOF
- end
-
- def run_osc_11_user_delete
- # run osc_user_delete with our input
- ARGV.delete("user")
- ARGV.unshift("osc_user")
- Chef::Knife.run(ARGV, Chef::Application::Knife.options)
- end
-
- # DEPRECATION NOTE
- # Delete this override method after OSC 11 support is dropped
- def delete_object(user_name)
- confirm("Do you really want to delete #{user_name}")
-
- if Kernel.block_given?
- object = block.call
- else
- object = Chef::UserV1.load(user_name)
- object.destroy
- end
-
- output(format_for_display(object)) if config[:print_after]
- self.msg("Deleted #{user_name}")
- end
-
def run
@user_name = @name_args[0]
@@ -72,23 +37,7 @@ EOF
exit 1
end
- # DEPRECATION NOTE
- #
- # Below is modification of Chef::Knife.delete_object to detect OSC 11 server.
- # When OSC 11 is deprecated, simply delete all this and go back to:
- #
- # delete_object(Chef::UserV1, @user_name)
- #
- # Also delete our override of delete_object above
- object = Chef::UserV1.load(@user_name)
-
- # OSC 11 case
- if object.username.nil?
- ui.warn(osc_11_warning)
- run_osc_11_user_delete
- else # proceed with EC / CS delete
- delete_object(@user_name)
- end
+ delete_object(Chef::UserV1, @user_name)
end
end
end
diff --git a/lib/chef/knife/user_dissociate.rb b/lib/chef/knife/user_dissociate.rb
new file mode 100644
index 0000000000..6af1559608
--- /dev/null
+++ b/lib/chef/knife/user_dissociate.rb
@@ -0,0 +1,42 @@
+#
+# Author:: Steven Danna (<steve@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class UserDissociate < Chef::Knife
+ category "user"
+ banner "knife user dissociate USERNAMES"
+
+ def run
+ if name_args.length < 1
+ show_usage
+ ui.fatal("You must specify a username.")
+ exit 1
+ end
+ users = name_args
+ ui.confirm("Are you sure you want to dissociate the following users: #{users.join(", ")}")
+ users.each do |u|
+ api_endpoint = "users/#{u}"
+ rest.delete_rest(api_endpoint)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/user_edit.rb b/lib/chef/knife/user_edit.rb
index bb80ede267..ad9dfac079 100644
--- a/lib/chef/knife/user_edit.rb
+++ b/lib/chef/knife/user_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,37 +16,18 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class UserEdit < Knife
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
banner "knife user edit USER (options)"
- def osc_11_warning
- <<-EOF
-The Chef Server you are using does not support the username field.
-This means it is an Open Source 11 Server.
-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.
-EOF
- end
-
- def run_osc_11_user_edit
- # run osc_user_create with our input
- ARGV.delete("user")
- ARGV.unshift("osc_user")
- Chef::Knife.run(ARGV, Chef::Application::Knife.options)
- end
-
def run
@user_name = @name_args[0]
@@ -57,23 +38,13 @@ EOF
end
original_user = Chef::UserV1.load(@user_name).to_hash
- # DEPRECATION NOTE
- # Remove this if statement and corrosponding code post OSC 11 support.
- #
- # if username is nil, we are in the OSC 11 case,
- # forward to deprecated command
- if original_user["username"].nil?
- ui.warn(osc_11_warning)
- run_osc_11_user_edit
- else # EC / CS 12 user create
- edited_user = edit_hash(original_user)
- if original_user != edited_user
- user = Chef::UserV1.from_hash(edited_user)
- user.update
- ui.msg("Saved #{user}.")
- else
- ui.msg("User unchanged, not saving.")
- end
+ edited_user = edit_hash(original_user)
+ if original_user != edited_user
+ user = Chef::UserV1.from_hash(edited_user)
+ user.update
+ ui.msg("Saved #{user}.")
+ else
+ ui.msg("User unchanged, not saving.")
end
end
end
diff --git a/lib/chef/knife/user_invite_add.rb b/lib/chef/knife/user_invite_add.rb
new file mode 100644
index 0000000000..1690147535
--- /dev/null
+++ b/lib/chef/knife/user_invite_add.rb
@@ -0,0 +1,43 @@
+#
+# Author:: Steven Danna (<steve@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class UserInviteAdd < Chef::Knife
+ category "user"
+ banner "knife user invite add USERNAMES"
+
+ def run
+ if name_args.length < 1
+ show_usage
+ ui.fatal("You must specify a username.")
+ exit 1
+ end
+
+ users = name_args
+ api_endpoint = "association_requests/"
+ users.each do |u|
+ body = { user: u }
+ rest.post_rest(api_endpoint, body)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/user_invite_list.rb b/lib/chef/knife/user_invite_list.rb
new file mode 100644
index 0000000000..831774d1bf
--- /dev/null
+++ b/lib/chef/knife/user_invite_list.rb
@@ -0,0 +1,34 @@
+#
+# Author:: Steven Danna (<steve@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class UserInviteList < Chef::Knife
+ category "user"
+ banner "knife user invite list"
+
+ def run
+ api_endpoint = "association_requests/"
+ invited_users = rest.get_rest(api_endpoint).map { |i| i["username"] }
+ ui.output(invited_users)
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/user_invite_rescind.rb b/lib/chef/knife/user_invite_rescind.rb
new file mode 100644
index 0000000000..fd5804e10a
--- /dev/null
+++ b/lib/chef/knife/user_invite_rescind.rb
@@ -0,0 +1,63 @@
+#
+# Author:: Steven Danna (<steve@chef.io>)
+# Copyright:: Copyright (c) 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 "../knife"
+
+class Chef
+ class Knife
+ class UserInviteRescind < Chef::Knife
+ category "user"
+ banner "knife user invite rescind [USERNAMES] (options)"
+
+ option :all,
+ short: "-a",
+ long: "--all",
+ description: "Rescind all invites!"
+
+ def run
+ if (name_args.length < 1) && ! config.key?(:all)
+ show_usage
+ ui.fatal("You must specify a username.")
+ exit 1
+ end
+
+ # To rescind we need to send a DELETE to association_requests/INVITE_ID
+ # For user friendliness we look up the invite ID based on username.
+ @invites = {}
+ usernames = name_args
+ rest.get_rest("association_requests").each { |i| @invites[i["username"]] = i["id"] }
+ if config[:all]
+ ui.confirm("Are you sure you want to rescind all association requests")
+ @invites.each do |u, i|
+ rest.delete_rest("association_requests/#{i}")
+ end
+ else
+ ui.confirm("Are you sure you want to rescind the association requests for: #{usernames.join(", ")}")
+ usernames.each do |u|
+ if @invites.key?(u)
+ rest.delete_rest("association_requests/#{@invites[u]}")
+ else
+ ui.fatal("No association request for #{u}.")
+ exit 1
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/knife/user_key_create.rb b/lib/chef/knife/user_key_create.rb
index 95a98a2f4f..efc783dd7f 100644
--- a/lib/chef/knife/user_key_create.rb
+++ b/lib/chef/knife/user_key_create.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_create_base"
+require_relative "../knife"
+require_relative "key_create_base"
class Chef
class Knife
@@ -32,6 +32,10 @@ class Chef
banner "knife user key create USER (options)"
+ deps do
+ require_relative "key_create"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/user_key_delete.rb b/lib/chef/knife/user_key_delete.rb
index 1c559f2ef0..b4f84fdb7b 100644
--- a/lib/chef/knife/user_key_delete.rb
+++ b/lib/chef/knife/user_key_delete.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -29,6 +29,10 @@ class Chef
class UserKeyDelete < Knife
banner "knife user key delete USER KEYNAME (options)"
+ deps do
+ require_relative "key_delete"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/user_key_edit.rb b/lib/chef/knife/user_key_edit.rb
index 561af8edd0..15ef2ada1e 100644
--- a/lib/chef/knife/user_key_edit.rb
+++ b/lib/chef/knife/user_key_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_edit_base"
+require_relative "../knife"
+require_relative "key_edit_base"
class Chef
class Knife
@@ -32,6 +32,10 @@ class Chef
banner "knife user key edit USER KEYNAME (options)"
+ deps do
+ require_relative "key_edit"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/user_key_list.rb b/lib/chef/knife/user_key_list.rb
index 799c069182..781998b301 100644
--- a/lib/chef/knife/user_key_list.rb
+++ b/lib/chef/knife/user_key_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/knife"
-require "chef/knife/key_list_base"
+require_relative "../knife"
+require_relative "key_list_base"
class Chef
class Knife
@@ -32,6 +32,10 @@ class Chef
banner "knife user key list USER (options)"
+ deps do
+ require_relative "key_list"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/user_key_show.rb b/lib/chef/knife/user_key_show.rb
index e09d5e04ed..2bf535c792 100644
--- a/lib/chef/knife/user_key_show.rb
+++ b/lib/chef/knife/user_key_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -29,6 +29,10 @@ class Chef
class UserKeyShow < Knife
banner "knife user key show USER KEYNAME (options)"
+ deps do
+ require_relative "key_show"
+ end
+
attr_reader :actor
def initialize(argv = [])
diff --git a/lib/chef/knife/user_list.rb b/lib/chef/knife/user_list.rb
index 88e834025d..f6aa7bcfc4 100644
--- a/lib/chef/knife/user_list.rb
+++ b/lib/chef/knife/user_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,25 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
-# NOTE: only knife user command that is backwards compatible with OSC 11,
-# so no deprecation warnings are necessary.
class Chef
class Knife
class UserList < Knife
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
banner "knife user list (options)"
option :with_uri,
- :short => "-w",
- :long => "--with-uri",
- :description => "Show corresponding URIs"
+ short: "-w",
+ long: "--with-uri",
+ description: "Show corresponding URIs."
def run
output(format_list_for_display(Chef::UserV1.list))
diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb
index 8d2f2c1e73..ee58c19d9f 100644
--- a/lib/chef/knife/user_reregister.rb
+++ b/lib/chef/knife/user_reregister.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,41 +16,22 @@
# limitations under the License.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
class UserReregister < Knife
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
banner "knife user reregister USER (options)"
- def osc_11_warning
- <<-EOF
-The Chef Server you are using does not support the username field.
-This means it is an Open Source 11 Server.
-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.
-EOF
- end
-
- def run_osc_11_user_reregister
- # run osc_user_edit with our input
- ARGV.delete("user")
- ARGV.unshift("osc_user")
- Chef::Knife.run(ARGV, Chef::Application::Knife.options)
- end
-
option :file,
- :short => "-f FILE",
- :long => "--file FILE",
- :description => "Write the private key to a file"
+ short: "-f FILE",
+ long: "--file FILE",
+ description: "Write the private key to a file."
def run
@user_name = @name_args[0]
@@ -62,26 +43,15 @@ EOF
end
user = Chef::UserV1.load(@user_name)
-
- # DEPRECATION NOTE
- # Remove this if statement and corrosponding code post OSC 11 support.
- #
- # if username is nil, we are in the OSC 11 case,
- # forward to deprecated command
- if user.username.nil?
- ui.warn(osc_11_warning)
- run_osc_11_user_reregister
- else # EC / CS 12 case
- user.reregister
- Chef::Log.debug("Updated user data: #{user.inspect}")
- key = user.private_key
- if config[:file]
- File.open(config[:file], "w") do |f|
- f.print(key)
- end
- else
- ui.msg key
+ user.reregister
+ Chef::Log.trace("Updated user data: #{user.inspect}")
+ key = user.private_key
+ if config[:file]
+ File.open(config[:file], "w") do |f|
+ f.print(key)
end
+ else
+ ui.msg key
end
end
end
diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb
index 04251c0863..e59f969e9a 100644
--- a/lib/chef/knife/user_show.rb
+++ b/lib/chef/knife/user_show.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/knife"
+require_relative "../knife"
class Chef
class Knife
@@ -25,30 +25,11 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require "chef/user_v1"
- require "chef/json_compat"
+ require_relative "../user_v1"
end
banner "knife user show USER (options)"
- def osc_11_warning
- <<-EOF
-The Chef Server you are using does not support the username field.
-This means it is an Open Source 11 Server.
-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.
-EOF
- end
-
- def run_osc_11_user_show
- # run osc_user_edit with our input
- ARGV.delete("user")
- ARGV.unshift("osc_user")
- Chef::Knife.run(ARGV, Chef::Application::Knife.options)
- end
-
def run
@user_name = @name_args[0]
@@ -59,18 +40,7 @@ EOF
end
user = Chef::UserV1.load(@user_name)
-
- # DEPRECATION NOTE
- # Remove this if statement and corrosponding code post OSC 11 support.
- #
- # if username is nil, we are in the OSC 11 case,
- # forward to deprecated command
- if user.username.nil?
- ui.warn(osc_11_warning)
- run_osc_11_user_show
- else
- output(format_for_display(user))
- end
+ output(format_for_display(user))
end
end
diff --git a/lib/chef/knife/xargs.rb b/lib/chef/knife/xargs.rb
index 6559ca2e74..9dcc724d38 100644
--- a/lib/chef/knife/xargs.rb
+++ b/lib/chef/knife/xargs.rb
@@ -1,74 +1,90 @@
-require "chef/chef_fs/knife"
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../chef_fs/knife"
class Chef
class Knife
class Xargs < Chef::ChefFS::Knife
- banner "knife xargs [COMMAND]"
+ banner "knife xargs [COMMAND] (options)"
category "path-based"
deps do
- require "chef/chef_fs/file_system"
- require "chef/chef_fs/file_system/not_found_error"
+ require_relative "../chef_fs/file_system"
+ require_relative "../chef_fs/file_system/exceptions"
end
# TODO modify to remote-only / local-only pattern (more like delete)
option :local,
- :long => "--local",
- :boolean => true,
- :description => "Xargs local files instead of remote"
+ long: "--local",
+ boolean: true,
+ description: "Xargs local files instead of remote."
option :patterns,
- :long => "--pattern [PATTERN]",
- :short => "-p [PATTERN]",
- :description => "Pattern on command line (if these are not specified, a list of patterns is expected on standard input). Multiple patterns may be passed in this way.",
- :arg_arity => [1, -1]
+ long: "--pattern [PATTERN]",
+ short: "-p [PATTERN]",
+ description: "Pattern on command line (if these are not specified, a list of patterns is expected on standard input). Multiple patterns may be passed in this way.",
+ arg_arity: [1, -1]
option :diff,
- :long => "--[no-]diff",
- :default => true,
- :boolean => true,
- :description => "Whether to show a diff when files change (default: true)"
+ long: "--[no-]diff",
+ default: true,
+ boolean: true,
+ description: "Whether to show a diff when files change (default: true)."
option :dry_run,
- :long => "--dry-run",
- :boolean => true,
- :description => "Prevents changes from actually being uploaded to the server."
+ long: "--dry-run",
+ boolean: true,
+ description: "Prevents changes from actually being uploaded to the server."
option :force,
- :long => "--[no-]force",
- :boolean => true,
- :default => false,
- :description => "Force upload of files even if they are not changed (quicker and harmless, but doesn't print out what it changed)"
+ long: "--[no-]force",
+ boolean: true,
+ default: false,
+ description: "Force upload of files even if they are not changed (quicker and harmless, but doesn't print out what it changed)."
option :replace_first,
- :long => "--replace-first REPLACESTR",
- :short => "-J REPLACESTR",
- :description => "String to replace with filenames. -J will only replace the FIRST occurrence of the replacement string."
+ long: "--replace-first REPLACESTR",
+ short: "-J REPLACESTR",
+ description: "String to replace with filenames. -J will only replace the FIRST occurrence of the replacement string."
option :replace_all,
- :long => "--replace REPLACESTR",
- :short => "-I REPLACESTR",
- :description => "String to replace with filenames. -I will replace ALL occurrence of the replacement string."
+ long: "--replace REPLACESTR",
+ short: "-I REPLACESTR",
+ description: "String to replace with filenames. -I will replace ALL occurrence of the replacement string."
option :max_arguments_per_command,
- :long => "--max-args MAXARGS",
- :short => "-n MAXARGS",
- :description => "Maximum number of arguments per command line."
+ long: "--max-args MAXARGS",
+ short: "-n MAXARGS",
+ description: "Maximum number of arguments per command line."
option :max_command_line,
- :long => "--max-chars LENGTH",
- :short => "-s LENGTH",
- :description => "Maximum size of command line, in characters"
+ long: "--max-chars LENGTH",
+ short: "-s LENGTH",
+ description: "Maximum size of command line, in characters."
option :verbose_commands,
- :short => "-t",
- :description => "Print command to be run on the command line"
+ short: "-t",
+ description: "Print command to be run on the command line."
option :null_separator,
- :short => "-0",
- :boolean => true,
- :description => "Use the NULL character (\0) as a separator, instead of whitespace"
+ short: "-0",
+ boolean: true,
+ description: "Use the NULL character (\0) as a separator, instead of whitespace."
def run
error = false
@@ -159,7 +175,7 @@ class Chef
# Create the temporary files
files.each do |file|
tempfile = Tempfile.new(file.name)
- tempfiles[tempfile] = { :file => file }
+ tempfiles[tempfile] = { file: file }
end
rescue
destroy_tempfiles(files)
@@ -167,7 +183,7 @@ class Chef
end
# Create the command
- paths = tempfiles.keys.map { |tempfile| tempfile.path }.join(" ")
+ paths = tempfiles.keys.map(&:path).join(" ")
if config[:replace_all]
final_command = command.gsub(config[:replace_all], paths)
elsif config[:replace_first]
@@ -181,32 +197,32 @@ 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(&:close!)
end
def xargs_files(command, tempfiles)
error = false
# Create the temporary files
tempfiles.each_pair do |tempfile, file|
- begin
- value = file[:file].read
- file[:value] = value
- tempfile.open
- tempfile.write(value)
- tempfile.close
- rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
- ui.error "#{format_path(e.entry)}: #{e.reason}."
- error = true
- tempfile.close!
- tempfiles.delete(tempfile)
- next
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- error = true
- tempfile.close!
- tempfiles.delete(tempfile)
- next
- end
+
+ value = file[:file].read
+ file[:value] = value
+ tempfile.open
+ tempfile.write(value)
+ tempfile.close
+ rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
+ ui.error "#{format_path(e.entry)}: #{e.reason}."
+ error = true
+ tempfile.close!
+ tempfiles.delete(tempfile)
+ next
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ ui.error "#{format_path(e.entry)}: No such file or directory"
+ error = true
+ tempfile.close!
+ tempfiles.delete(tempfile)
+ next
+
end
return error if error && tempfiles.size == 0
diff --git a/lib/chef/knife/yaml_convert.rb b/lib/chef/knife/yaml_convert.rb
new file mode 100644
index 0000000000..6bd2d1c0ea
--- /dev/null
+++ b/lib/chef/knife/yaml_convert.rb
@@ -0,0 +1,91 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 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.
+#
+
+autoload :YAML, "yaml"
+require_relative "../knife"
+class Chef::Knife::YamlConvert < Chef::Knife
+
+ banner "knife yaml convert YAML_FILENAME [RUBY_FILENAME]"
+
+ def run
+ if name_args.empty?
+ ui.fatal!("Please specify the file name of a YAML recipe to convert to Ruby")
+ elsif name_args.size >= 3
+ ui.fatal!("knife yaml convert YAML_FILENAME [RUBY_FILENAME]")
+ end
+
+ yaml_file = @name_args[0]
+ unless ::File.exist?(yaml_file) && ::File.readable?(yaml_file)
+ ui.fatal("Input YAML file '#{yaml_file}' does not exist or is unreadable")
+ end
+
+ ruby_file = if @name_args[1]
+ @name_args[1] # use the specified output filename if provided
+ else
+ if ::File.extname(yaml_file) == ".yml" || ::File.extname(yaml_file) == ".yaml"
+ yaml_file.gsub(/\.(yml|yaml)$/, ".rb")
+ else
+ yaml_file + ".rb" # fall back to putting .rb on the end of whatever the yaml file was named
+ end
+ end
+
+ if ::File.exist?(ruby_file)
+ ui.fatal!("Output Ruby file '#{ruby_file}' already exists")
+ end
+
+ yaml_contents = IO.read(yaml_file)
+
+ # YAML can contain multiple documents (--- is the separator), let's not support that.
+ if ::YAML.load_stream(yaml_contents).length > 1
+ ui.fatal!("YAML recipe '#{yaml_file}' contains multiple documents, only one is supported")
+ end
+
+ # Unfortunately, per the YAML spec, comments are stripped when we load, so we lose them on conversion
+ yaml_hash = ::YAML.safe_load(yaml_contents, permitted_classes: [Symbol])
+ unless yaml_hash.is_a?(Hash) && yaml_hash.key?("resources")
+ ui.fatal!("YAML recipe '#{source_file}' must contain a top-level 'resources' hash (YAML sequence), i.e. 'resources:'")
+ end
+
+ ui.warn("No resources found in '#{yaml_file}'") if yaml_hash["resources"].size == 0
+
+ ::File.open(ruby_file, "w") do |file|
+ file.write(resource_hash_to_string(yaml_hash["resources"], yaml_file))
+ end
+ ui.info("Converted '#{yaml_file}' to '#{ruby_file}'")
+ end
+
+ # Converts a Hash of resources to a Ruby recipe
+ # returns a string ready to be written to a file or stdout
+ def resource_hash_to_string(resource_hash, filename)
+ ruby_contents = []
+ ruby_contents << "# Autoconverted recipe from #{filename}\n"
+
+ resource_hash.each do |r|
+ type = r.delete("type")
+ name = r.delete("name")
+
+ ruby_contents << "#{type} \"#{name}\" do"
+ r.each do |k, v|
+ ruby_contents << " #{k} #{v.inspect}"
+ end
+ ruby_contents << "end\n"
+ end
+
+ ruby_contents.join("\n")
+ end
+end
diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb
index 5ce17e6fb3..e7346322d2 100644
--- a/lib/chef/local_mode.rb
+++ b/lib/chef/local_mode.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,12 +14,10 @@
# WITHOUT WARRANTIES 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"
-if Chef::Platform.windows?
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1")
- require "chef/monkey_patches/webrick-utils"
- end
-end
+
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require_relative "config"
+require_relative "monkey_patches/webrick-utils" if ChefUtils.windows?
class Chef
module LocalMode
@@ -55,8 +53,8 @@ class Chef
destroy_server_connectivity
require "chef_zero/server"
- require "chef/chef_fs/chef_fs_data_store"
- require "chef/chef_fs/config"
+ require_relative "chef_fs/chef_fs_data_store"
+ require_relative "chef_fs/config"
@chef_fs = Chef::ChefFS::Config.new.local_fs
@chef_fs.write_pretty_json = true
@@ -73,6 +71,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
@@ -80,7 +79,7 @@ class Chef
local_mode_url = @chef_zero_server.local_mode_url
- Chef::Log.info("Started chef-zero at #{local_mode_url} with #{@chef_fs.fs_description}")
+ Chef::Log.info("Started #{ChefUtils::Dist::Zero::PRODUCT} at #{local_mode_url} with #{@chef_fs.fs_description}")
Chef::Config.chef_server_url = local_mode_url
end
end
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
index ac2baeb9d1..8cf2fba9bd 100644
--- a/lib/chef/log.rb
+++ b/lib/chef/log.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<@aj@opscode.com>)
# Author:: Christopher Brown (<cb@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,18 +18,23 @@
# limitations under the License.
require "logger"
-require "chef/monologger"
-require "chef/exceptions"
+require_relative "monologger"
+require_relative "exceptions"
require "mixlib/log"
-require "chef/log/syslog" unless RUBY_PLATFORM =~ /mswin|mingw|windows/
-require "chef/log/winevt"
+require_relative "log/syslog" unless RUBY_PLATFORM.match?(/mswin|mingw|windows/)
+require_relative "log/winevt"
class Chef
class Log
extend Mixlib::Log
+ def self.setup!
+ init(MonoLogger.new(STDOUT))
+ nil
+ end
+
# Force initialization of the primary log device (@logger)
- init(MonoLogger.new(STDOUT))
+ setup!
class Formatter
def self.show_time=(*args)
@@ -46,16 +51,18 @@ class Chef
#
def self.caller_location
# Pick the first caller that is *not* part of the Chef gem, that's the
- # thing the user wrote.
- chef_gem_path = File.expand_path("../..", __FILE__)
- caller(0..20).find { |c| !c.start_with?(chef_gem_path) }
+ # thing the user wrote. Or failing that, the most recent caller.
+ chef_gem_path = File.expand_path("..", __dir__)
+ caller(0..20).find { |c| !c.start_with?(chef_gem_path) } || caller(0..1)[0]
end
- def self.deprecation(msg = nil, location = caller(2..2)[0], &block)
- if msg
- msg << " at #{Array(location).join("\n")}"
- msg = msg.join("") if msg.respond_to?(:join)
- end
+ # Log a deprecation warning.
+ #
+ # If the treat_deprecation_warnings_as_errors config option is set, this
+ # will raise an exception instead.
+ #
+ # @param msg [String] Deprecation message to display.
+ def self.deprecation(msg, &block)
if Chef::Config[:treat_deprecation_warnings_as_errors]
error(msg, &block)
raise Chef::Exceptions::DeprecatedFeatureError.new(msg)
diff --git a/lib/chef/log/syslog.rb b/lib/chef/log/syslog.rb
index 58d6671095..4e6a6dd0b5 100644
--- a/lib/chef/log/syslog.rb
+++ b/lib/chef/log/syslog.rb
@@ -1,7 +1,7 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
# Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,8 @@
require "logger"
require "syslog-logger"
-require "chef/mixin/unformatter"
+require_relative "../mixin/unformatter"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Log
@@ -32,14 +33,14 @@ class Chef
attr_accessor :sync, :formatter
- def initialize(program_name = "chef-client", facility = ::Syslog::LOG_DAEMON, logopts = nil)
+ def initialize(program_name = ChefUtils::Dist::Infra::CLIENT, facility = ::Syslog::LOG_DAEMON, logopts = nil)
super
return if defined? ::Logger::Syslog::SYSLOG
+
::Logger::Syslog.const_set :SYSLOG, SYSLOG
end
- def close
- end
+ def close; end
end
end
end
diff --git a/lib/chef/log/winevt.rb b/lib/chef/log/winevt.rb
index 506d4c9a6c..f060ecfde6 100644
--- a/lib/chef/log/winevt.rb
+++ b/lib/chef/log/winevt.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,9 +16,10 @@
# limitations under the License.
#
-require "chef/event_loggers/base"
-require "chef/platform/query_helpers"
-require "chef/mixin/unformatter"
+require_relative "../event_loggers/base"
+require_relative "../platform/query_helpers"
+require_relative "../mixin/unformatter"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Log
@@ -36,7 +37,7 @@ class Chef
FATAL_EVENT_ID = 10104
# Since we must install the event logger, this is not really configurable
- SOURCE = "Chef"
+ SOURCE = ChefUtils::Dist::Infra::SHORT.freeze
include Chef::Mixin::Unformatter
@@ -46,51 +47,50 @@ class Chef
@eventlog = eventlog || ::Win32::EventLog.open("Application")
end
- def close
- end
+ def close; end
def info(msg)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::INFO_TYPE,
- :source => SOURCE,
- :event_id => INFO_EVENT_ID,
- :data => [msg]
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: SOURCE,
+ event_id: INFO_EVENT_ID,
+ data: [msg]
)
end
def warn(msg)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::WARN_TYPE,
- :source => SOURCE,
- :event_id => WARN_EVENT_ID,
- :data => [msg]
+ event_type: ::Win32::EventLog::WARN_TYPE,
+ source: SOURCE,
+ event_id: WARN_EVENT_ID,
+ data: [msg]
)
end
def debug(msg)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::INFO_TYPE,
- :source => SOURCE,
- :event_id => DEBUG_EVENT_ID,
- :data => [msg]
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: SOURCE,
+ event_id: DEBUG_EVENT_ID,
+ data: [msg]
)
end
def error(msg)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::ERROR_TYPE,
- :source => SOURCE,
- :event_id => ERROR_EVENT_ID,
- :data => [msg]
+ event_type: ::Win32::EventLog::ERROR_TYPE,
+ source: SOURCE,
+ event_id: ERROR_EVENT_ID,
+ data: [msg]
)
end
def fatal(msg)
@eventlog.report_event(
- :event_type => ::Win32::EventLog::ERROR_TYPE,
- :source => SOURCE,
- :event_id => FATAL_EVENT_ID,
- :data => [msg]
+ event_type: ::Win32::EventLog::ERROR_TYPE,
+ source: SOURCE,
+ event_id: FATAL_EVENT_ID,
+ data: [msg]
)
end
diff --git a/lib/chef/mash.rb b/lib/chef/mash.rb
index 3858ff09dd..d386af2591 100644
--- a/lib/chef/mash.rb
+++ b/lib/chef/mash.rb
@@ -1,226 +1,21 @@
-# Copyright 2009-2016, Dan Kubb
-
-# 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.
-
-# ---
-# ---
-
-# Some portions of blank.rb and mash.rb are verbatim copies of software
-# licensed under the MIT license. That license is included below:
-
-# Copyright 2005-2016, David Heinemeier Hansson
-
-# 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.
-
-# This class has dubious semantics and we only have it so that people can write
-# params[:key] instead of params['key'].
-class Mash < Hash
-
- # @param constructor<Object>
- # The default value for the mash. Defaults to an empty hash.
- #
- # @details [Alternatives]
- # If constructor is a Hash, a new mash will be created based on the keys of
- # the hash and no default value will be set.
- def initialize(constructor = {})
- if constructor.is_a?(Hash)
- super()
- update(constructor)
- else
- super(constructor)
- end
- end
-
- # @param orig<Object> Mash being copied
- #
- # @return [Object] A new copied Mash
- def initialize_copy(orig)
- super
- # Handle nested values
- each do |k, v|
- if v.kind_of?(Mash) || v.is_a?(Array)
- self[k] = v.dup
- end
- end
- self
- end
-
- # @param key<Object> The default value for the mash. Defaults to nil.
- #
- # @details [Alternatives]
- # If key is a Symbol and it is a key in the mash, then the default value will
- # be set to the value matching the key.
- def default(key = nil)
- if key.is_a?(Symbol) && include?(key = key.to_s)
- self[key]
- else
- super
- end
- end
-
- alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
- alias_method :regular_update, :update unless method_defined?(:regular_update)
-
- # @param key<Object> The key to set.
- # @param value<Object>
- # The value to set the key to.
- #
- # @see Mash#convert_key
- # @see Mash#convert_value
- def []=(key, value)
- regular_writer(convert_key(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.
- #
- # @return [Mash] The updated mash.
- def update(other_hash)
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
- self
- end
-
- alias_method :merge!, :update
-
- # @param key<Object> The key to check for. This will be run through convert_key.
- #
- # @return [Boolean] True if the key exists in the mash.
- def key?(key)
- super(convert_key(key))
- end
-
- # def include? def has_key? def member?
- alias_method :include?, :key?
- alias_method :has_key?, :key?
- alias_method :member?, :key?
-
- # @param key<Object> The key to fetch. This will be run through convert_key.
- # @param *extras<Array> Default value.
- #
- # @return [Object] The value at key or the default value.
- def fetch(key, *extras)
- super(convert_key(key), *extras)
- end
-
- # @param *indices<Array>
- # The keys to retrieve values for. These will be run through +convert_key+.
- #
- # @return [Array] The values at each of the provided keys
- def values_at(*indices)
- indices.collect { |key| self[convert_key(key)] }
- end
-
- # @param hash<Hash> The hash to merge with the mash.
- #
- # @return [Mash] A new mash with the hash values merged in.
- def merge(hash)
- self.dup.update(hash)
- end
-
- # @param key<Object>
- # The key to delete from the mash.\
- def delete(key)
- super(convert_key(key))
- end
-
- # @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
- #
- # @return [Mash] A new mash without the selected keys.
- #
- # @example
- # { :one => 1, :two => 2, :three => 3 }.except(:one)
- # #=> { "two" => 2, "three" => 3 }
- def except(*keys)
- super(*keys.map { |k| convert_key(k) })
- end
-
- # Used to provide the same interface as Hash.
- #
- # @return [Mash] This mash unchanged.
- def stringify_keys!; self end
-
- # @return [Hash] The mash as a Hash with symbolized keys.
- def symbolize_keys
- h = Hash.new(default)
- each { |key, val| h[key.to_sym] = val }
- h
- end
-
- # @return [Hash] The mash as a Hash with string keys.
- def to_hash
- Hash.new(default).merge(self)
- end
-
- # @return [Mash] Convert a Hash into a Mash
- # The input Hash's default value is maintained
- def self.from_hash(hash)
- mash = Mash.new(hash)
- mash.default = hash.default
- mash
- end
-
- protected
-
- # @param key<Object> The key to convert.
- #
- # @param [Object]
- # The converted key. If the key was a symbol, it will be converted to a
- # string.
- #
- # @api private
- def convert_key(key)
- key.kind_of?(Symbol) ? key.to_s : key
- end
-
- # @param value<Object> The value to convert.
- #
- # @return [Object]
- # The converted value. A Hash or an Array of hashes, will be converted to
- # their Mash equivalents.
- #
- # @api private
- def convert_value(value)
- if value.class == Hash
- Mash.from_hash(value)
- elsif value.is_a?(Array)
- value.collect { |e| convert_value(e) }
- else
- value
- end
- end
-end
+#
+# Copyright:: Copyright (c) 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-utils/mash" unless defined?(ChefUtils::Mash)
+
+# For historical reasons we inject Mash directly into the top level class namespace
+Mash = ChefUtils::Mash unless defined?(Mash)
diff --git a/lib/chef/mixin/api_version_request_handling.rb b/lib/chef/mixin/api_version_request_handling.rb
index b91a1dfe0a..a46e001dd8 100644
--- a/lib/chef/mixin/api_version_request_handling.rb
+++ b/lib/chef/mixin/api_version_request_handling.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,25 +19,23 @@
class Chef
module Mixin
module ApiVersionRequestHandling
- # Input:
- # exeception:
- # Net::HTTPServerException that may or may not contain the x-ops-server-api-version header
+ # @param exception [Net::HTTPClientException] 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:
- # If the execption was not a 406 or the server does not support versioning
+ # If the exception was not a 406 or the server does not support versioning
# Array of length zero:
# If there was no intersection between supported client versions and supported server versions
- # Arrary of Integers:
+ # Array of Integers:
# If there was an intersection of supported versions, the array returns will contain that intersection
def server_client_api_version_intersection(exception, supported_client_versions)
# return empty array unless 406 Unacceptable with proper header
return nil if exception.response.code != "406" || exception.response["x-ops-server-api-version"].nil?
# intersection of versions the server and client support, will be of length zero if no intersection
- server_supported_client_versions = Array.new
+ server_supported_client_versions = []
header = Chef::JSONCompat.from_json(exception.response["x-ops-server-api-version"])
min_server_version = Integer(header["min_version"])
@@ -52,13 +50,13 @@ class Chef
end
def reregister_only_v0_supported_error_msg(max_version, min_version)
- <<-EOH
-The reregister command only supports server API version 0.
-The server that received the request supports a min version of #{min_version} and a max version of #{max_version}.
-User keys are now managed via the key rotation commmands.
-Please refer to the documentation on how to manage your keys via the key rotation commands:
-https://docs.chef.io/server_security.html#key-rotation
-EOH
+ <<~EOH
+ The reregister command only supports server API version 0.
+ The server that received the request supports a min version of #{min_version} and a max version of #{max_version}.
+ User keys are now managed via the key rotation commands.
+ Please refer to the documentation on how to manage your keys via the key rotation commands:
+ https://docs.chef.io/ctl_chef_server/#key-rotation
+ EOH
end
end
diff --git a/lib/chef/mixin/checksum.rb b/lib/chef/mixin/checksum.rb
index f223894c39..083e524d63 100644
--- a/lib/chef/mixin/checksum.rb
+++ b/lib/chef/mixin/checksum.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,7 @@
# limitations under the License.
#
-require "digest/sha2"
-require "chef/digester"
+require_relative "../digester"
class Chef
module Mixin
@@ -27,6 +26,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/chef_utils_wiring.rb b/lib/chef/mixin/chef_utils_wiring.rb
new file mode 100644
index 0000000000..938520059c
--- /dev/null
+++ b/lib/chef/mixin/chef_utils_wiring.rb
@@ -0,0 +1,40 @@
+#--
+# Copyright:: Copyright (c) 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 "../log"
+require_relative "../config"
+require_relative "../chef_class"
+
+class Chef
+ module Mixin
+ # Common Dependency Injection wiring for ChefUtils-related modules
+ module ChefUtilsWiring
+ private
+
+ def __config
+ Chef::Config
+ end
+
+ def __log
+ Chef::Log
+ end
+
+ def __transport_connection
+ Chef.run_context&.transport_connection
+ 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 aa541c3637..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 do
- 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!
- end
- 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/convert_to_class_name.rb b/lib/chef/mixin/convert_to_class_name.rb
index d6bd8a4ea7..de11110574 100644
--- a/lib/chef/mixin/convert_to_class_name.rb
+++ b/lib/chef/mixin/convert_to_class_name.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ class Chef
def convert_to_class_name(str)
str = normalize_snake_case_name(str)
rname = nil
- regexp = %r{^(.+?)(_(.+))?$}
+ regexp = /^(.+?)(_(.+))?$/
mn = str.match(regexp)
if mn
@@ -66,61 +66,6 @@ class Chef
str = base.to_s + (file_base == "default" ? "" : "_#{file_base}")
normalize_snake_case_name(str)
end
-
- # Copied from rails activesupport. In ruby >= 2.0 const_get will just do this, so this can
- # be deprecated and removed.
- #
- # MIT LICENSE is here: https://github.com/rails/rails/blob/master/activesupport/MIT-LICENSE
-
- # Tries to find a constant with the name specified in the argument string.
- #
- # 'Module'.constantize # => Module
- # 'Test::Unit'.constantize # => Test::Unit
- #
- # The name is assumed to be the one of a top-level constant, no matter
- # whether it starts with "::" or not. No lexical context is taken into
- # account:
- #
- # C = 'outside'
- # module M
- # C = 'inside'
- # C # => 'inside'
- # 'C'.constantize # => 'outside', same as ::C
- # end
- #
- # NameError is raised when the name is not in CamelCase or the constant is
- # unknown.
- def constantize(camel_cased_word)
- names = camel_cased_word.split("::")
-
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
- Object.const_get(camel_cased_word) if names.empty?
-
- # Remove the first blank element in case of '::ClassName' notation.
- names.shift if names.size > 1 && names.first.empty?
-
- names.inject(Object) do |constant, name|
- if constant == Object
- constant.const_get(name)
- else
- candidate = constant.const_get(name)
- next candidate if constant.const_defined?(name, false)
- next candidate unless Object.const_defined?(name)
-
- # Go down the ancestors to check if it is owned directly. The check
- # stops when we reach Object or the end of ancestors tree.
- constant = constant.ancestors.inject do |const, ancestor|
- break const if ancestor == Object
- break ancestor if ancestor.const_defined?(name, false)
- const
- end
-
- # owner is in Object, so raise
- constant.const_get(name, false)
- end
- end
- end
-
end
end
end
diff --git a/lib/chef/mixin/create_path.rb b/lib/chef/mixin/create_path.rb
index 233f7b9521..26988556aa 100644
--- a/lib/chef/mixin/create_path.rb
+++ b/lib/chef/mixin/create_path.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,11 +29,11 @@ class Chef
# === Returns
# The created file_path.
def create_path(file_path)
- unless file_path.kind_of?(String) || file_path.kind_of?(Array)
+ unless file_path.is_a?(String) || file_path.is_a?(Array)
raise ArgumentError, "file_path must be a string or an array!"
end
- if file_path.kind_of?(String)
+ if file_path.is_a?(String)
file_path = File.expand_path(file_path).split(File::SEPARATOR)
file_path.shift if file_path[0] == ""
# Check if path starts with a separator or drive letter (Windows)
@@ -53,19 +53,17 @@ class Chef
private
def create_dir(path)
- begin
- # When doing multithreaded downloads into the file cache, the following
- # interleaving raises an error here:
- #
- # thread1 thread2
- # File.directory?(create_path) <- false
- # 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
+ # When doing multithreaded downloads into the file cache, the following
+ # interleaving raises an error here:
+ #
+ # thread1 thread2
+ # File.directory?(create_path) <- false
+ # File.directory?(create_path) <- false
+ # Dir.mkdir(create_path)
+ # Dir.mkdir(create_path) <- raises Errno::EEXIST
+ 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..ad9c9e89dd 100644
--- a/lib/chef/mixin/deep_merge.rb
+++ b/lib/chef/mixin/deep_merge.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Steve Midgley (http://www.misuse.org/science)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2008-2016, Steve Midgley
# License:: Apache License, Version 2.0
#
@@ -19,52 +19,67 @@
class Chef
module Mixin
- # == Chef::Mixin::DeepMerge
# Implements a deep merging algorithm for nested data structures.
- # ==== Notice:
- # This code was originally imported from deep_merge by Steve Midgley.
- # deep_merge is available under the MIT license from
- # http://trac.misuse.org/science/wiki/DeepMerge
+ #
+ # This code was originally imported from deep_merge by Steve Midgley.
+ # deep_merge is available under the MIT license from
+ # http://trac.misuse.org/science/wiki/DeepMerge
+ #
+ # Note that this is not considered a public interface. It is technically
+ # public and has been used and we cannot break the API, but continued
+ # external use is discouraged. We are unlikely to change the shape of
+ # the API and break anyone, but this code does not serve the purposes of
+ # cookbook authors and customers. It is intended only for the purposes
+ # of the internal use in the chef-client codebase. We do not accept
+ # pull requests to extend the functionality of this algorithm. Users
+ # who find this does nearly what they want, should copy and paste the
+ # algorithm and tune to their needs. We will not maintain any additional
+ # use cases.
+ #
+ # "It is what it is, and if it isn't what you want, you need to build
+ # that yourself"
+ #
+ # @api private
+ #
module DeepMerge
extend self
+ # @api private
def merge(first, second)
- first = Mash.new(first) unless first.kind_of?(Mash)
- second = Mash.new(second) unless second.kind_of?(Mash)
+ first = Mash.new(first) unless first.is_a?(Mash)
+ second = Mash.new(second) unless second.is_a?(Mash)
DeepMerge.deep_merge(second, first)
end
class InvalidParameter < StandardError; end
- # Deep Merge core documentation.
# deep_merge! method permits merging of arbitrary child elements. The two top level
# elements must be hashes. These hashes can contain unlimited (to stack limit) levels
# of child elements. These child elements to not have to be of the same types.
# Where child elements are of the same type, deep_merge will attempt to merge them together.
# Where child elements are not of the same type, deep_merge will skip or optionally overwrite
# the destination element with the contents of the source element at that level.
+ #
# So if you have two hashes like this:
+ #
# source = {:x => [1,2,3], :y => 2}
# dest = {:x => [4,5,'6'], :y => [7,8,9]}
# dest.deep_merge!(source)
# Results: {:x => [1,2,3,4,5,'6'], :y => 2}
+ #
# By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
# To avoid this, use "deep_merge" (no bang/exclamation mark)
+ #
+ # @api private
+ #
def deep_merge!(source, dest)
- # if dest doesn't exist, then simply copy source to it
- if dest.nil?
- dest = source; return dest
- end
-
case source
- when nil
- dest
when Hash
- if dest.kind_of?(Hash)
+ if dest.is_a?(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
@@ -74,8 +89,8 @@ class Chef
dest = source
end
when Array
- if dest.kind_of?(Array)
- dest = dest | source
+ if dest.is_a?(Array)
+ dest |= source
else
dest = source
end
@@ -87,10 +102,12 @@ class Chef
dest
end # deep_merge!
+ # @api private
def hash_only_merge(merge_onto, merge_with)
hash_only_merge!(safe_dup(merge_onto), safe_dup(merge_with))
end
+ # @api private
def safe_dup(thing)
thing.dup
rescue TypeError
@@ -101,12 +118,15 @@ class Chef
# `merge_onto` is the object that will "lose" in case of conflict.
# `merge_with` is the object whose values will replace `merge_onto`s
# values when there is a conflict.
+ #
+ # @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)
+ if merge_onto.is_a?(Hash) && merge_with.is_a?(Hash)
merge_with.each do |key, merge_with_value|
value =
- if merge_onto.has_key?(key)
+ if merge_onto.key?(key)
hash_only_merge(merge_onto[key], merge_with_value)
else
merge_with_value
@@ -120,17 +140,14 @@ class Chef
end
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
merge_with
end
end
+ # @api private
+ #
def deep_merge(source, dest)
deep_merge!(safe_dup(source), safe_dup(dest))
end
diff --git a/lib/chef/mixin/default_paths.rb b/lib/chef/mixin/default_paths.rb
new file mode 100644
index 0000000000..95f444c13f
--- /dev/null
+++ b/lib/chef/mixin/default_paths.rb
@@ -0,0 +1,32 @@
+#
+# Copyright:: Copyright (c) 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-utils/dsl/default_paths" unless defined?(ChefUtils::DSL::DefaultPaths)
+
+class Chef
+ module Mixin
+ module DefaultPaths
+ include ChefUtils::DSL::DefaultPaths
+
+ def enforce_default_paths(env = ENV)
+ if Chef::Config[:enforce_default_paths] || Chef::Config[:enforce_path_sanity]
+ env["PATH"] = default_paths(env)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/deprecation.rb b/lib/chef/mixin/deprecation.rb
index 0f059a215f..638ddfd5ea 100644
--- a/lib/chef/mixin/deprecation.rb
+++ b/lib/chef/mixin/deprecation.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 }
+ 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])
@@ -54,7 +54,7 @@ class Chef
module Deprecation
class DeprecatedObjectProxyBase
- KEEPERS = %w{__id__ __send__ instance_eval == equal? initialize object_id}
+ KEEPERS = %w{__id__ __send__ instance_eval == equal? initialize object_id}.freeze
instance_methods.each { |method_name| undef_method(method_name) unless KEEPERS.include?(method_name.to_s) }
end
@@ -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/enforce_ownership_and_permissions.rb b/lib/chef/mixin/enforce_ownership_and_permissions.rb
index e02c34748f..20ecdbdcf7 100644
--- a/lib/chef/mixin/enforce_ownership_and_permissions.rb
+++ b/lib/chef/mixin/enforce_ownership_and_permissions.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/file_access_control"
+require_relative "../file_access_control"
class Chef
module Mixin
diff --git a/lib/chef/mixin/file_class.rb b/lib/chef/mixin/file_class.rb
index 166dd5796a..6540427e43 100644
--- a/lib/chef/mixin/file_class.rb
+++ b/lib/chef/mixin/file_class.rb
@@ -2,7 +2,7 @@
# Author:: Mark Mzyk <mmzyk@chef.io>
# Author:: Seth Chisamore <schisamo@chef.io>
# Author:: Bryan McLellan <btm@chef.io>
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,8 +23,8 @@ class Chef
module FileClass
def file_class
- @host_os_file ||= if Chef::Platform.windows?
- require "chef/win32/file"
+ @host_os_file ||= if ChefUtils.windows?
+ require_relative "../win32/file"
Chef::ReservedNames::Win32::File
else
::File
diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb
index a6692d798d..9ced026130 100644
--- a/lib/chef/mixin/from_file.rb
+++ b/lib/chef/mixin/from_file.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)
- if File.exists?(filename) && File.readable?(filename)
- self.instance_eval(IO.read(filename), filename, 1)
+ self.source_file = filename
+ if File.file?(filename) && File.readable?(filename)
+ 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)
- if File.exists?(filename) && File.readable?(filename)
- self.class_eval(IO.read(filename), filename, 1)
+ self.source_file = filename
+ if File.file?(filename) && File.readable?(filename)
+ 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..88e09758cb 100644
--- a/lib/chef/mixin/get_source_from_package.rb
+++ b/lib/chef/mixin/get_source_from_package.rb
@@ -1,5 +1,5 @@
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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"
#
@@ -35,11 +35,12 @@ class Chef
def initialize(new_resource, run_context)
super
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.include?(::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..36936f9578 100644
--- a/lib/chef/mixin/homebrew_user.rb
+++ b/lib/chef/mixin/homebrew_user.rb
@@ -2,8 +2,8 @@
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Graeme Mathieson (<mathie@woss.name>)
#
-# Copyright 2011-2016, Chef Software Inc.
-# Copyright 2014-2016, Chef Software, Inc <legal@chef.io>
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: Copyright (c) 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.
@@ -22,8 +22,8 @@
# This lives here in Chef::Mixin because Chef's namespacing makes it
# awkward to use modules elsewhere (e.g., chef/provider/package/homebrew/owner)
-require "chef/mixin/shell_out"
-require "etc"
+require_relative "shell_out"
+require "etc" unless defined?(Etc)
class Chef
module Mixin
@@ -34,15 +34,27 @@ 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
return provided_user if provided_user.is_a? Integer
+
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
@@ -56,7 +68,7 @@ class Chef
owner = ::File.stat(brew_path).uid
else
raise Chef::Exceptions::CannotDetermineHomebrewOwner,
- 'Could not find the "brew" executable in /usr/local/bin or anywhere on the path.'
+ 'Could not find the "brew" executable in /usr/local/bin or anywhere on the path.'
end
Chef::Log.debug "Found Homebrew owner #{Etc.getpwuid(owner).name}; executing `brew` commands as them"
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/language_include_attribute.rb b/lib/chef/mixin/language_include_attribute.rb
deleted file mode 100644
index 7cb66dc272..0000000000
--- a/lib/chef/mixin/language_include_attribute.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 "chef/dsl/include_attribute"
-require "chef/mixin/deprecation"
-
-class Chef
- module Mixin
-
- # DEPRECATED: This is just here for compatibility, use
- # Chef::DSL::IncludeAttribute instead.
-
- deprecate_constant(:LanguageIncludeAttribute, Chef::DSL::IncludeAttribute, <<-EOM)
-Chef::Mixin::LanguageIncludeAttribute is deprecated. Use
-Chef::DSL::IncludeAttribute instead.
-EOM
-
- end
-end
diff --git a/lib/chef/mixin/language_include_recipe.rb b/lib/chef/mixin/language_include_recipe.rb
deleted file mode 100644
index 97e384c7c4..0000000000
--- a/lib/chef/mixin/language_include_recipe.rb
+++ /dev/null
@@ -1,31 +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/include_recipe"
-require "chef/mixin/deprecation"
-
-class Chef
- module Mixin
-
- deprecate_constant(:LanguageIncludeRecipe, Chef::DSL::IncludeRecipe, <<-EOM)
-Chef::Mixin::LanguageIncludeRecipe is deprecated, use Chef::DSL::IncludeRecipe
-instead.
-EOM
-
- end
-end
diff --git a/lib/chef/mixin/lazy_module_include.rb b/lib/chef/mixin/lazy_module_include.rb
index 34e1fce4f1..f7ca9cb14b 100644
--- a/lib/chef/mixin/lazy_module_include.rb
+++ b/lib/chef/mixin/lazy_module_include.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/mixin/notifying_block.rb b/lib/chef/mixin/notifying_block.rb
index 2d6a82f493..341282d563 100644
--- a/lib/chef/mixin/notifying_block.rb
+++ b/lib/chef/mixin/notifying_block.rb
@@ -1,6 +1,6 @@
#--
# Author:: Lamont Granquist <lamont@chef.io>
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..69583bea98
--- /dev/null
+++ b/lib/chef/mixin/openssl_helper.rb
@@ -0,0 +1,448 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+autoload :OpenSSL, "openssl"
+
+class Chef
+ module Mixin
+ # various helpers for use with openssl. Currently used by the openssl_* resources
+ module OpenSSLHelper
+ # 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.read key_content, key_password
+ rescue ::OpenSSL::PKey::PKeyError, ArgumentError
+ return false
+ end
+
+ if key.is_a?(::OpenSSL::PKey::EC)
+ key.private_key?
+ else
+ key.private?
+ end
+ end
+
+ # given a crl file path see if it's actually a crl
+ # @param [String] crl_file the path to the crlfile
+ # @return [Boolean] is the key valid?
+ def crl_file_valid?(crl_file)
+ begin
+ ::OpenSSL::X509::CRL.new ::File.read(crl_file)
+ rescue ::OpenSSL::X509::CRLError, Errno::ENOENT
+ return false
+ end
+ true
+ end
+
+ # check is a serial given is revoked in a crl given
+ # @param [OpenSSL::X509::CRL] crl X509 CRL to check
+ # @param [String, Integer] serial X509 Certificate Serial Number
+ # @return [true, false]
+ def serial_revoked?(crl, serial)
+ raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL)
+ raise TypeError, "serial must be a Ruby String or Integer object" unless serial.is_a?(String) || serial.is_a?(Integer)
+
+ serial_to_verify = if serial.is_a?(String)
+ serial.to_i(16)
+ else
+ serial
+ end
+ status = false
+ crl.revoked.each do |revoked|
+ status = true if revoked.serial == serial_to_verify
+ end
+ status
+ 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
+
+ # generate an ec private key given curve type
+ # @param [String] curve the kind of curve to use
+ # @return [OpenSSL::PKey::DH]
+ def gen_ec_priv_key(curve)
+ raise TypeError, "curve must be a string" unless curve.is_a?(String)
+ raise ArgumentError, "Specified curve is not available on this system" unless %w{prime256v1 secp384r1 secp521r1}.include?(curve)
+
+ ::OpenSSL::PKey::EC.new(curve).generate_key
+ 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_ec_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::EC.new key_content, priv_key_password
+
+ # Get curve type (prime256v1...)
+ group = ::OpenSSL::PKey::EC::Group.new(key.group.curve_name)
+ # Get Generator point & public point (priv * generator)
+ generator = group.generator
+ pub_point = generator.mul(key.private_key)
+ key.public_key = pub_point
+
+ # Public Key in pem
+ public_key = ::OpenSSL::PKey::EC.new
+ public_key.group = group
+ public_key.public_key = pub_point
+ public_key.to_pem
+ end
+
+ # generate a pem file given a cipher, key, an optional key_password
+ # @param [OpenSSL::PKey::EC] ec_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_ec_key(ec_key, key_password, key_cipher)
+ raise TypeError, "ec_key must be a Ruby OpenSSL::PKey::EC object" unless ec_key.is_a?(::OpenSSL::PKey::EC)
+ 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)
+ ec_key.to_pem(cipher, key_password)
+ end
+
+ # generate a csr pem file given a subject and a private key
+ # @param [OpenSSL::X509::Name] subject the x509 subject object
+ # @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] key the private key object
+ # @return [OpenSSL::X509::Request]
+ def gen_x509_request(subject, key)
+ raise TypeError, "subject must be a Ruby OpenSSL::X509::Name object" unless subject.is_a?(::OpenSSL::X509::Name)
+ raise TypeError, "key must be a Ruby OpenSSL::PKey::EC or a Ruby OpenSSL::PKey::RSA object" unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA)
+
+ request = ::OpenSSL::X509::Request.new
+ request.version = 0
+ request.subject = subject
+ request.public_key = key
+
+ # Chef 12 backward compatibility
+ ::OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
+
+ request.sign(key, ::OpenSSL::Digest.new("SHA256"))
+ request
+ end
+
+ # generate an array of X509 Extensions given a hash of extensions
+ # @param [Hash] extensions hash of extensions
+ # @return [Array]
+ def gen_x509_extensions(extensions)
+ raise TypeError, "extensions must be a Ruby Hash object" unless extensions.is_a?(Hash)
+
+ exts = []
+ extensions.each do |ext_name, ext_prop|
+ raise TypeError, "#{ext_name} must contain a Ruby Hash" unless ext_prop.is_a?(Hash)
+ raise ArgumentError, "keys in #{ext_name} must be 'values' and 'critical'" unless ext_prop.key?("values") && ext_prop.key?("critical")
+ raise TypeError, "the key 'values' must contain a Ruby Arrays" unless ext_prop["values"].is_a?(Array)
+ raise TypeError, "the key 'critical' must be a Ruby Boolean true/false" unless ext_prop["critical"].is_a?(TrueClass) || ext_prop["critical"].is_a?(FalseClass)
+
+ exts << ::OpenSSL::X509::ExtensionFactory.new.create_extension(ext_name, ext_prop["values"].join(","), ext_prop["critical"])
+ end
+ exts
+ end
+
+ # generate a random Serial
+ # @return [Integer]
+ def gen_serial
+ ::OpenSSL::BN.generate_prime(160)
+ end
+
+ # generate a Certificate given a X509 request
+ # @param [OpenSSL::X509::Request] request X509 Certificate Request
+ # @param [Array] extension Array of X509 Certificate Extension
+ # @param [Hash] info issuer & validity
+ # @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] key private key to sign with
+ # @return [OpenSSL::X509::Certificate]
+ def gen_x509_cert(request, extension, info, key)
+ raise TypeError, "request must be a Ruby OpenSSL::X509::Request" unless request.is_a?(::OpenSSL::X509::Request)
+ raise TypeError, "extension must be a Ruby Array" unless extension.is_a?(Array)
+ raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash)
+ raise TypeError, "key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA)
+
+ raise ArgumentError, "info must contain a validity" unless info.key?("validity")
+ raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer)
+
+ cert = ::OpenSSL::X509::Certificate.new
+ ef = ::OpenSSL::X509::ExtensionFactory.new
+
+ cert.serial = gen_serial
+ cert.version = 2
+ cert.subject = request.subject
+ cert.public_key = request.public_key
+ cert.not_before = Time.now
+ cert.not_after = cert.not_before + info["validity"] * 24 * 60 * 60
+
+ if info["issuer"].nil?
+ cert.issuer = request.subject
+ ef.issuer_certificate = cert
+ extension << ef.create_extension("basicConstraints", "CA:TRUE", true)
+ else
+ raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate)
+
+ cert.issuer = info["issuer"].subject
+ ef.issuer_certificate = info["issuer"]
+ end
+ ef.subject_certificate = cert
+ if openssl_config = __openssl_config
+ ef.config = openssl_config
+ end
+
+ cert.extensions = extension
+ cert.add_extension ef.create_extension("subjectKeyIdentifier", "hash")
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+
+ cert.sign(key, ::OpenSSL::Digest.new("SHA256"))
+ cert
+ end
+
+ # generate a X509 CRL given a CA
+ # @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
+ # @param [Hash] info issuer & validity
+ # @return [OpenSSL::X509::CRL]
+ def gen_x509_crl(ca_private_key, info)
+ raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
+ raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash)
+
+ raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity")
+ raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate)
+ raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer)
+
+ crl = ::OpenSSL::X509::CRL.new
+ ef = ::OpenSSL::X509::ExtensionFactory.new
+
+ crl.version = 1
+ crl.issuer = info["issuer"].subject
+ crl.last_update = Time.now
+ crl.next_update = Time.now + 3600 * 24 * info["validity"]
+
+ if openssl_config = __openssl_config
+ ef.config = openssl_config
+ end
+ ef.issuer_certificate = info["issuer"]
+
+ crl.add_extension ::OpenSSL::X509::Extension.new("crlNumber", ::OpenSSL::ASN1::Integer(1))
+ crl.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ crl.sign(ca_private_key, ::OpenSSL::Digest.new("SHA256"))
+ crl
+ end
+
+ # generate the next CRL number available for a X509 CRL given
+ # @param [OpenSSL::X509::CRL] crl x509 CRL
+ # @return [Integer]
+ def get_next_crl_number(crl)
+ raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL)
+
+ crlnum = 1
+ crl.extensions.each do |e|
+ crlnum = e.value if e.oid == "crlNumber"
+ end
+ crlnum.to_i + 1
+ end
+
+ # add a serial given in the crl given
+ # @param [Hash] revoke_info serial to revoke & revocation reason
+ # @param [OpenSSL::X509::CRL] crl X509 CRL
+ # @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
+ # @param [Hash] info issuer & validity
+ # @return [OpenSSL::X509::CRL]
+ def revoke_x509_crl(revoke_info, crl, ca_private_key, info)
+ raise TypeError, "revoke_info must be a Ruby Hash object" unless revoke_info.is_a?(Hash)
+ raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL)
+ raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
+ raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash)
+
+ raise ArgumentError, "revoke_info must contain a serial and a reason" unless revoke_info.key?("serial") && revoke_info.key?("reason")
+ raise TypeError, "revoke_info['serial'] must be a Ruby String or Integer object" unless revoke_info["serial"].is_a?(String) || revoke_info["serial"].is_a?(Integer)
+ raise TypeError, "revoke_info['reason'] must be a Ruby Integer object" unless revoke_info["reason"].is_a?(Integer)
+
+ raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity")
+ raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate)
+ raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer)
+
+ revoked = ::OpenSSL::X509::Revoked.new
+ revoked.serial = if revoke_info["serial"].is_a?(String)
+ revoke_info["serial"].to_i(16)
+ else
+ revoke_info["serial"]
+ end
+ revoked.time = Time.now
+
+ ext = ::OpenSSL::X509::Extension.new("CRLReason",
+ ::OpenSSL::ASN1::Enumerated(revoke_info["reason"]))
+ revoked.add_extension(ext)
+ crl.add_revoked(revoked)
+
+ renew_x509_crl(crl, ca_private_key, info)
+ end
+
+ # renew a X509 crl given
+ # @param [OpenSSL::X509::CRL] crl CRL to renew
+ # @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
+ # @param [Hash] info issuer & validity
+ # @return [OpenSSL::X509::CRL]
+ def renew_x509_crl(crl, ca_private_key, info)
+ raise TypeError, "crl must be a Ruby OpenSSL::X509::CRL object" unless crl.is_a?(::OpenSSL::X509::CRL)
+ raise TypeError, "ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object" unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
+ raise TypeError, "info must be a Ruby Hash" unless info.is_a?(Hash)
+
+ raise ArgumentError, "info must contain a issuer and a validity" unless info.key?("issuer") && info.key?("validity")
+ raise TypeError, "info['issuer'] must be a Ruby OpenSSL::X509::Certificate object" unless info["issuer"].is_a?(::OpenSSL::X509::Certificate)
+ raise TypeError, "info['validity'] must be a Ruby Integer object" unless info["validity"].is_a?(Integer)
+
+ crl.last_update = Time.now
+ crl.next_update = crl.last_update + 3600 * 24 * info["validity"]
+
+ ef = ::OpenSSL::X509::ExtensionFactory.new
+ if openssl_config = __openssl_config
+ ef.config = openssl_config
+ end
+ ef.issuer_certificate = info["issuer"]
+
+ crl.extensions = [ ::OpenSSL::X509::Extension.new("crlNumber",
+ ::OpenSSL::ASN1::Integer(get_next_crl_number(crl)))]
+ crl.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ crl.sign(ca_private_key, ::OpenSSL::Digest.new("SHA256"))
+ crl
+ end
+
+ # Return true if a certificate need to be renewed (or doesn't exist) according to the number
+ # of days before expiration given
+ # @param [string] cert_file path of the cert file or cert content
+ # @param [integer] renew_before_expiry number of days before expiration
+ # @return [true, false]
+ def cert_need_renewal?(cert_file, renew_before_expiry)
+ resp = true
+ cert_content = ::File.exist?(cert_file) ? File.read(cert_file) : cert_file
+ begin
+ cert = OpenSSL::X509::Certificate.new cert_content
+ rescue ::OpenSSL::X509::CertificateError
+ return resp
+ end
+
+ unless cert.not_after <= Time.now + 3600 * 24 * renew_before_expiry
+ resp = false
+ end
+
+ resp
+ end
+
+ alias_method :cert_need_renewall?, :cert_need_renewal?
+
+ private
+
+ def __openssl_config
+ path = if File.exist?(::OpenSSL::Config::DEFAULT_CONFIG_FILE)
+ OpenSSL::Config::DEFAULT_CONFIG_FILE
+ else
+ Dir[File.join(RbConfig::CONFIG["prefix"], "**", "openssl.cnf")].first
+ end
+
+ if File.exist?(path)
+ ::OpenSSL::Config.load(path)
+ else
+ Chef::Log.warn("Couldn't find OpenSSL config file")
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb
index b16df41c8e..5259bf4449 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,9 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/constants"
-require "chef/property"
-require "chef/delayed_evaluator"
+require_relative "../constants"
+require_relative "../property"
+require_relative "../delayed_evaluator"
+require_relative "../exceptions"
class Chef
module Mixin
@@ -34,12 +35,14 @@ 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.)
# @option opts [Object,Array] :equal_to An object, or list
# of objects, that must be equal to the value using Ruby's `==`
- # operator (`opts[:is].any? { |v| v == value }`) (See #_pv_equal_to.)
+ # operator (`opts[:equal_to].any? { |v| v == value }`) (See #_pv_equal_to.)
# @option opts [Regexp,Array<Regexp>] :regex An object, or
# list of objects, that must match the value with `regex.match(value)`.
# (See #_pv_regex)
@@ -87,23 +90,27 @@ class Chef
# looking for _pv_:symbol as methods. Assuming it find them, it calls the right
# one.
#++
- 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)
+ raise ArgumentError, "Options must be a hash" unless opts.is_a?(Hash)
+ raise ArgumentError, "Validation Map must be a hash" unless map.is_a?(Hash)
+
+ @validation_message ||= {}
map.each do |key, validation|
- unless key.kind_of?(Symbol) || key.kind_of?(String)
+ unless key.is_a?(Symbol) || key.is_a?(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.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,15 +131,15 @@ 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.key?(key) ? @validation_message[key] : default
end
# Return the value of a parameter, or nil if it doesn't exist.
def _pv_opts_lookup(opts, key)
- if opts.has_key?(key.to_s)
+ if opts.key?(key.to_s)
opts[key.to_s]
- elsif opts.has_key?(key.to_sym)
+ elsif opts.key?(key.to_sym)
opts[key.to_sym]
else
nil
@@ -142,9 +149,10 @@ class Chef
# Raise an exception if the parameter is not found.
def _pv_required(opts, key, is_required = true, explicitly_allows_nil = false)
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!"
+ return true if opts.key?(key.to_s) && (explicitly_allows_nil || !opts[key.to_s].nil?)
+ return true if opts.key?(key.to_sym) && (explicitly_allows_nil || !opts[key.to_sym].nil?)
+
+ raise Exceptions::ValidationFailed, _validation_message(key, "Required argument #{key.inspect} is missing!")
end
true
end
@@ -167,7 +175,9 @@ 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}."
+ # Ruby will print :something as something, which confuses users so make sure to print them as symbols
+ # by inspecting the value instead of just printing it
+ raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must be equal to one of: #{to_be.map(&:inspect).join(", ")}! You passed #{value.inspect}.")
end
end
@@ -184,9 +194,9 @@ class Chef
unless value.nil?
to_be = Array(to_be)
to_be.each do |tb|
- return true if value.kind_of?(tb)
+ return true if value.is_a?(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 +211,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
@@ -227,13 +237,13 @@ class Chef
#
def _pv_cannot_be(opts, key, predicate_method_base_name)
value = _pv_opts_lookup(opts, key)
- if !value.nil?
+ unless value.nil?
Array(predicate_method_base_name).each do |method_name|
predicate_method = :"#{method_name}?"
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
@@ -269,7 +279,7 @@ class Chef
def _pv_default(opts, key, default_value)
value = _pv_opts_lookup(opts, key)
if value.nil?
- default_value = default_value.freeze if !default_value.is_a?(DelayedEvaluator)
+ default_value = default_value.freeze unless default_value.is_a?(DelayedEvaluator)
opts[key] = default_value
end
end
@@ -289,11 +299,11 @@ class Chef
#
def _pv_regex(opts, key, regex)
value = _pv_opts_lookup(opts, key)
- if !value.nil?
+ unless value.nil?
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, "Property #{key}'s value #{value} does not match regular expression #{regex.inspect}")
end
end
@@ -310,12 +320,13 @@ class Chef
# ```
#
def _pv_callbacks(opts, key, callbacks)
- raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash)
+ raise ArgumentError, "Callback list must be a hash!" unless callbacks.is_a?(Hash)
+
value = _pv_opts_lookup(opts, key)
- if !value.nil?
+ unless 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 +343,9 @@ 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,15 +408,17 @@ class Chef
# x nil #=> invalid
# ```
#
- def _pv_is(opts, key, to_be, raise_error: true)
- return true if !opts.has_key?(key.to_s) && !opts.has_key?(key.to_sym)
+ def _pv_is(opts, key, to_be)
+ return true if !opts.key?(key.to_s) && !opts.key?(key.to_sym)
+
value = _pv_opts_lookup(opts, key)
to_be = [ to_be ].flatten(1)
errors = []
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
@@ -413,6 +427,7 @@ class Chef
rescue Exceptions::ValidationFailed
# re-raise immediately if there is only one "is" so we get a better stack
raise if to_be.size == 1
+
errors << $!
false
end
@@ -423,11 +438,11 @@ class Chef
if passed
true
else
- message = "Property #{key} must be one of: #{to_be.map { |v| v.inspect }.join(", ")}! You passed #{value.inspect}."
+ message = "Property #{key} must be one of: #{to_be.map(&:inspect).join(", ")}! You passed #{value.inspect}."
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
@@ -447,11 +462,13 @@ 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
+ if opts.key?(key.to_s)
+ 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
+ elsif opts.key?(key.to_sym)
+ 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
@@ -469,7 +486,7 @@ class Chef
def get(resource, nil_set: false)
value = super
# All values are sticky, frozen or not
- if !is_set?(resource)
+ unless is_set?(resource)
set_value(resource, value)
end
value
diff --git a/lib/chef/mixin/path_sanity.rb b/lib/chef/mixin/path_sanity.rb
index 6a8e017bcd..d9ca74bebf 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,53 +16,16 @@
# limitations under the License.
#
+require_relative "default_paths"
+
class Chef
module Mixin
module PathSanity
+ include Chef::Mixin::DefaultPaths
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
- end
+ enforce_default_paths(env)
end
-
- private
-
- def sane_paths
- @sane_paths ||= begin
- if Chef::Platform.windows?
- %w{}
- else
- %w{/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin}
- end
- end
- end
-
- def ruby_bindir
- RbConfig::CONFIG["bindir"]
- end
-
- def gem_bindir
- Gem.bindir
- end
-
end
end
end
diff --git a/lib/chef/mixin/powershell_exec.rb b/lib/chef/mixin/powershell_exec.rb
new file mode 100644
index 0000000000..bbf8ae1a69
--- /dev/null
+++ b/lib/chef/mixin/powershell_exec.rb
@@ -0,0 +1,128 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright (c) 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 "../powershell"
+require_relative "../pwsh"
+
+# The powershell_exec mixin provides in-process access to the PowerShell engine.
+#
+# powershell_exec is initialized with a string that should be set to the script
+# to run and also takes an optional interpreter argument which must be either
+# :powershell (Windows PowerShell which is the default) or :pwsh (PowerShell
+# Core). It will return a Chef::PowerShell object that provides 5 methods:
+#
+# .result - returns a hash representing the results returned by executing the
+# PowerShell script block
+# .verbose - this is an array of string containing any messages written to the
+# PowerShell verbose stream during execution
+# .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
+# .error! - raise Chef::PowerShell::CommandFailed if there was an error
+#
+# 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("$PSVersionTable", :pwsh).result["PSEdition"]
+# => "Core"
+#
+# > 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) API.
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
+ # @return [Chef::PowerShell] output
+ def powershell_exec(script, interpreter = :powershell)
+ case interpreter
+ when :powershell
+ Chef::PowerShell.new(script)
+ when :pwsh
+ Chef::Pwsh.new(script)
+ else
+ raise ArgumentError, "Expected interpreter of :powershell or :pwsh"
+ end
+ end
+
+ # The same as the #powershell_exec method except this will raise
+ # Chef::PowerShell::CommandFailed if the command fails
+ def powershell_exec!(script, interpreter = :powershell)
+ cmd = powershell_exec(script, interpreter)
+ cmd.error!
+ cmd
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/powershell_out.rb b/lib/chef/mixin/powershell_out.rb
index 74de85f86f..6d0989f8e0 100644
--- a/lib/chef/mixin/powershell_out.rb
+++ b/lib/chef/mixin/powershell_out.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/mixin/shell_out"
-require "chef/mixin/windows_architecture_helper"
+require_relative "shell_out"
+require_relative "windows_architecture_helper"
class Chef
module Mixin
@@ -28,19 +28,24 @@ class Chef
# can be set to :i386 or :x86_64 to force the windows architecture.
#
# @param script [String] script to run
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
# @param options [Hash] options hash
# @return [Mixlib::Shellout] mixlib-shellout object
def powershell_out(*command_args)
script = command_args.first
options = command_args.last.is_a?(Hash) ? command_args.last : nil
+ interpreter = command_args[1].is_a?(Symbol) ? command_args[1] : :powershell
- run_command_with_os_architecture(script, options)
+ raise ArgumentError, "Expected interpreter of :powershell or :pwsh" unless %i{powershell pwsh}.include?(interpreter)
+
+ run_command_with_os_architecture(script, interpreter, options)
end
# Run a command under powershell with the same API as shell_out!
# (raises exceptions on errors)
#
# @param script [String] script to run
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
# @param options [Hash] options hash
# @return [Mixlib::Shellout] mixlib-shellout object
def powershell_out!(*command_args)
@@ -56,17 +61,18 @@ class Chef
# because chef-client runs as a 32-bit app on 64-bit windows).
#
# @param script [String] script to run
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
# @param options [Hash] options hash
# @return [Mixlib::Shellout] mixlib-shellout object
- def run_command_with_os_architecture(script, options)
+ def run_command_with_os_architecture(script, interpreter, options)
options ||= {}
options = options.dup
arch = options.delete(:architecture)
with_os_architecture(nil, architecture: arch) do
shell_out(
- build_powershell_command(script),
- options
+ build_powershell_command(script, interpreter),
+ **options
)
end
end
@@ -74,8 +80,9 @@ 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
- def build_powershell_command(script)
+ # @param interpreter [Symbol] the interpreter type, `:powershell` or `:pwsh`
+ # @return [String] powershell command to execute
+ def build_powershell_command(script, interpreter)
flags = [
# Hides the copyright banner at startup.
"-NoLogo",
@@ -86,12 +93,12 @@ class Chef
# always set the ExecutionPolicy flag
# see http://technet.microsoft.com/en-us/library/ee176961.aspx
"-ExecutionPolicy Unrestricted",
- # Powershell will hang if STDIN is redirected
+ # PowerShell will hang if STDIN is redirected
# http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
"-InputFormat None",
]
- "powershell.exe #{flags.join(' ')} -Command \"#{script}\""
+ "#{interpreter}.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 6159c87948..0e9e9f43d4 100644
--- a/lib/chef/mixin/powershell_type_coercions.rb
+++ b/lib/chef/mixin/powershell_type_coercions.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,45 +21,45 @@ 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)
translated = x.inject([]) do |memo, (k, v)|
memo << "#{k}=#{translate_type(v)}"
end
- "@{#{translated.join(';')}}"
+ "@{#{translated.join(";")}}"
end
def translate_array(x)
translated = x.map do |v|
translate_type(v)
end
- "@(#{translated.join(',')})"
+ "@(#{translated.join(",")})"
end
def unsafe?(s)
diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb
index 8ff2cc4501..c42e3889b0 100644
--- a/lib/chef/mixin/properties.rb
+++ b/lib/chef/mixin/properties.rb
@@ -1,6 +1,6 @@
-require "chef/delayed_evaluator"
-require "chef/mixin/params_validate"
-require "chef/property"
+require_relative "../delayed_evaluator"
+require_relative "params_validate"
+require_relative "../property"
class Chef
module Mixin
@@ -75,13 +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`.
+ # in any resource reporting output. Defaults to `false`.
#
# @example Bare property
# property :x
@@ -100,14 +102,14 @@ class Chef
options = options.inject({}) { |memo, (key, value)| memo[key.to_sym] = value; memo }
- options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
+ options[:instance_variable_name] = :"@#{name}" unless options.key?(:instance_variable_name)
options[:name] = name
options[:declared_in] = self
if type == NOT_PASSED
# If a type is not passed, the property derives from the
# superclass property (if any)
- if properties.has_key?(name)
+ if properties.key?(name)
property = properties[name].derive(**options)
else
property = property_type(**options)
@@ -133,6 +135,8 @@ class Chef
property.emit_dsl
end
+ alias :attribute :property
+
#
# Create a reusable property type that can be used in multiple properties
# in different resources.
@@ -147,6 +151,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.
#
@@ -170,9 +178,6 @@ class Chef
# by providing additional options for a package manager to use when
# installing a package.
#
- # This list is used by the Chef client auditing system to extract
- # information from resources to describe changes made to the system.
- #
# This method is unnecessary when declaring properties with `property`;
# properties are added to state_properties by default, and can be turned off
# with `desired_state: false`.
@@ -188,8 +193,8 @@ class Chef
# @return [Array<Property>] All properties in desired state.
#
def state_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }.uniq
+ unless names.empty?
+ names = names.map(&:to_sym).uniq
local_properties = properties(false)
# Add new properties to the list.
@@ -211,7 +216,7 @@ class Chef
end
end
- properties.values.select { |property| property.desired_state? }
+ properties.values.select(&:desired_state?)
end
#
@@ -237,8 +242,8 @@ class Chef
# @return [Array<Property>] All identity properties.
#
def identity_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }
+ unless names.empty?
+ names = names.map(&:to_sym)
# Add or change properties that are not part of the identity.
names.each do |name|
@@ -260,11 +265,23 @@ class Chef
end
end
- result = properties.values.select { |property| property.identity? }
- result = [ properties[:name] ] if result.empty?
+ result = properties.values.select(&:identity?)
+ # if there are no other identity properties set, then the name_property becomes the identity, or
+ # failing that we use the actual name.
+ if result.empty?
+ result = name_property ? [ properties[name_property] ] : [ properties[:name] ]
+ end
result
end
+ # Returns the name of the name property. Returns nil if there is no name property.
+ #
+ # @return [Symbol] the name property for this resource
+ def name_property
+ p = properties.find { |n, p| p.name_property? }
+ p ? p.first : nil
+ end
+
def included(other)
other.extend ClassMethods
end
@@ -285,7 +302,8 @@ class Chef
#
def property_is_set?(name)
property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" unless property
+
property.is_set?(self)
end
@@ -298,9 +316,59 @@ class Chef
#
def reset_property(name)
property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" unless 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}" unless property
+
+ property.description
+ end
+
+ # Copy properties from another property object (resource)
+ #
+ # By default this copies all properties other than the name property (that is required to create the
+ # destination object so it has already been done in advance and this way we do not clobber the name
+ # that was set in that constructor). By default it copies everything, optional arguments can be use
+ # to only select a subset. Or specific excludes can be set (and the default exclude on the name property
+ # can also be overridden). Exclude has priority over include, although the caller is likely better
+ # off doing the set arithmetic themselves for explicitness.
+ #
+ # ```ruby
+ # action :doit do
+ # # use it inside a block
+ # file "/etc/whatever.xyz" do
+ # copy_properties_from new_resource
+ # end
+ #
+ # # or directly call it
+ # r = declare_resource(:file, "etc/whatever.xyz")
+ # r.copy_properties_from(new_resource, :owner, :group, :mode)
+ # end
+ # ```
+ #
+ # @param other [Object] the other object (Chef::Resource) which implements the properties API
+ # @param includes [Array<Symbol>] splat-args list of symbols of the properties to copy.
+ # @param exclude [Array<Symbol>] list of symbols of the properties to exclude.
+ # @return the self object the properties were copied to for method chaining
+ #
+ def copy_properties_from(other, *includes, exclude: [ :name ])
+ includes = other.class.properties.keys if includes.empty?
+ includes -= exclude
+ includes.each do |p|
+ send(p, other.send(p)) if other.property_is_set?(p)
+ end
+ self
+ end
+
end
end
end
diff --git a/lib/chef/mixin/provides.rb b/lib/chef/mixin/provides.rb
index 43a726de8c..d088a72d5d 100644
--- a/lib/chef/mixin/provides.rb
+++ b/lib/chef/mixin/provides.rb
@@ -1,10 +1,10 @@
-require "chef/mixin/descendants_tracker"
+require_relative "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/proxified_socket.rb b/lib/chef/mixin/proxified_socket.rb
index 5c9bc3c7d0..3df2def725 100644
--- a/lib/chef/mixin/proxified_socket.rb
+++ b/lib/chef/mixin/proxified_socket.rb
@@ -1,5 +1,5 @@
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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 55b4e0a6d1..08e92b27b7 100644
--- a/lib/chef/mixin/securable.rb
+++ b/lib/chef/mixin/securable.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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
set_or_return(
:owner,
arg,
- :regex => Chef::Config[:user_valid_regex]
+ regex: Chef::Config[:user_valid_regex]
)
end
@@ -34,7 +34,7 @@ class Chef
set_or_return(
:group,
arg,
- :regex => Chef::Config[:group_valid_regex]
+ regex: Chef::Config[:group_valid_regex]
)
end
@@ -42,14 +42,14 @@ class Chef
set_or_return(
:mode,
arg,
- :callbacks => {
+ callbacks: {
"not in valid numeric range" => lambda do |m|
- if m.kind_of?(String)
+ if m.is_a?(String)
m =~ /^0/ || m = "0#{m}"
end
# Windows does not support the sticky or setuid bits
- if Chef::Platform.windows?
+ if ChefUtils.windows?
Integer(m) <= 0777 && Integer(m) >= 0
else
Integer(m) <= 07777 && Integer(m) >= 0
@@ -59,17 +59,14 @@ class Chef
)
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
@@ -100,7 +97,7 @@ class Chef
#
# * `:permissions`: Integer of Windows permissions flags, 1..2^32
# or one of `[:full_control, :modify, :read_execute, :read, :write]`
- # * `:principals`: String or Array of Strings represnting usernames on
+ # * `:principals`: String or Array of Strings representing usernames on
# the system.
# * `:applies_to_children` (optional): Boolean
# * `:applies_to_self` (optional): Boolean
@@ -110,19 +107,19 @@ 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,
- :principals => principals,
+ permissions: permissions,
+ principals: principals,
}
input.merge!(args_hash) unless args_hash.nil?
- validations = { :permissions => { :required => true },
- :principals => { :required => true, :kind_of => [String, Array] },
- :applies_to_children => { :equal_to => [ true, false, :containers_only, :objects_only ] },
- :applies_to_self => { :kind_of => [ TrueClass, FalseClass ] },
- :one_level_deep => { :kind_of => [ TrueClass, FalseClass ] },
+ validations = { permissions: { required: true },
+ principals: { required: true, kind_of: [String, Array] },
+ applies_to_children: { equal_to: [ true, false, :containers_only, :objects_only ] },
+ applies_to_self: { kind_of: [ TrueClass, FalseClass ] },
+ one_level_deep: { kind_of: [ TrueClass, FalseClass ] },
}
validate(input, validations)
@@ -131,23 +128,23 @@ class Chef
if permission < 0 || permission > 1 << 32
raise ArgumentError, "permissions flags must be positive and <= 32 bits (#{permission})"
end
- elsif !([:full_control, :modify, :read_execute, :read, :write].include?(permission.to_sym))
- raise ArgumentError, "permissions parameter must be :full_control, :modify, :read_execute, :read, :write or an integer representing Windows permission flags"
+ elsif !(%i{full_control modify read_execute read write}.include?(permission.to_sym))
+ raise ArgumentError, "permissions property must be :full_control, :modify, :read_execute, :read, :write or an integer representing Windows permission flags"
end
end
[ principals ].flatten.each do |principal|
- if !principal.is_a?(String)
- raise ArgumentError, "principals parameter must be a string or array of strings representing usernames"
+ unless principal.is_a?(String)
+ raise ArgumentError, "principals property must be a string or array of strings representing usernames"
end
end
if input[:applies_to_children] == false
if input[:applies_to_self] == false
- raise ArgumentError, "'rights' attribute must specify either :applies_to_children or :applies_to_self."
+ raise ArgumentError, "'rights' property must specify either :applies_to_children or :applies_to_self."
end
if input[:one_level_deep] == true
- raise ArgumentError, "'rights' attribute specified :one_level_deep without specifying :applies_to_children."
+ raise ArgumentError, "'rights' property specified :one_level_deep without specifying :applies_to_children."
end
end
rights ||= []
@@ -162,7 +159,6 @@ class Chef
end
end
- #==WindowsSecurableAttributes
# Defines #inherits to describe Windows file security ACLs on the
# including class
module WindowsSecurableAttributes
@@ -171,19 +167,19 @@ class Chef
set_or_return(
:inherits,
arg,
- :kind_of => [ TrueClass, FalseClass ]
+ kind_of: [ TrueClass, FalseClass ]
)
end
end
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
+ if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
include WindowsSecurableAttributes
end
# Callback that fires when included; will extend the including class
# with WindowsMacros and define #rights and #deny_rights on it.
def self.included(including_class)
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
+ if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
including_class.extend(WindowsMacros)
# create a default 'rights' attribute
including_class.rights_attribute(:rights)
diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb
index a258a91075..222cd73a73 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,122 +15,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "mixlib/shellout"
+require "mixlib/shellout/helper" unless defined?(Mixlib::ShellOut::Helper)
+require_relative "chef_utils_wiring" unless defined?(Chef::Mixin::ChefUtilsWiring)
class Chef
module Mixin
module ShellOut
-
- # 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
- # Generally speaking, 'extend Chef::Mixin::ShellOut' in your recipes and include 'Chef::Mixin::ShellOut' in your LWRPs
- # You can also call Mixlib::Shellout.new directly, but you lose all of the above functionality
-
- # we use 'en_US.UTF-8' by default because we parse localized strings in English as an API and
- # generally must support UTF-8 unicode.
- def shell_out(*args, **options)
- options = options.dup
- env_key = options.has_key?(:env) ? :env : :environment
- options[env_key] = {
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- }.update(options[env_key] || {})
- shell_out_command(*args, **options)
- end
-
- # call shell_out (using en_US.UTF-8) and raise errors
- def shell_out!(*command_args)
- cmd = shell_out(*command_args)
- cmd.error!
- cmd
- end
-
- def shell_out_with_systems_locale(*command_args)
- shell_out_command(*command_args)
- end
-
- def shell_out_with_systems_locale!(*command_args)
- cmd = shell_out_with_systems_locale(*command_args)
- cmd.error!
- 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
-
- return my_command_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)
- clean_array(*args).join(" ")
- end
-
- # Helper for sublcasses to reject nil and empty strings 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.reject { |i| i.nil? || i == "" }.map(&:to_s)
- end
-
- private
-
- def shell_out_command(*command_args)
- cmd = Mixlib::ShellOut.new(*run_command_compatible_options(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
- else
- nil
- end
- end
+ include Mixlib::ShellOut::Helper
+ include Chef::Mixin::ChefUtilsWiring
end
end
end
-
-# Break circular dep
-require "chef/config"
diff --git a/lib/chef/mixin/subclass_directive.rb b/lib/chef/mixin/subclass_directive.rb
index 397a37c6b8..2b0831233a 100644
--- a/lib/chef/mixin/subclass_directive.rb
+++ b/lib/chef/mixin/subclass_directive.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index b10e036c4e..dcb728f964 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -1,6 +1,6 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,13 @@
# limitations under the License.
#
-require "tempfile"
-require "erubis"
+autoload :Tempfile, "tempfile"
+autoload :Erubis, "erubis"
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
@@ -104,6 +102,7 @@ class Chef
# by the bare `node` everywhere.
def node
return @node if @node
+
raise "Could not find a value for node. If you are explicitly setting variables in a template, " +
"include a node variable if you plan to use it."
end
@@ -140,11 +139,11 @@ class Chef
partial_context._extend_modules(@_extension_modules)
template_location = @template_finder.find(partial_name, options)
- _render_template(IO.binread(template_location), partial_context)
+ _render_template(IO.binread(template_location), partial_context, filename: template_location)
end
def render_template(template_location)
- _render_template(IO.binread(template_location), self)
+ _render_template(IO.binread(template_location), self, filename: template_location)
end
def render_template_from_string(template)
@@ -155,12 +154,13 @@ class Chef
# INTERNAL PUBLIC API
###
- def _render_template(template, context)
+ def _render_template(template, context, options = {})
begin
- eruby = Erubis::Eruby.new(template)
+ # eruby = Erubis::Eruby.new(template, options)
+ eruby = Erubis::Eruby.new(template, options)
output = eruby.evaluate(context)
rescue Object => e
- raise TemplateError.new(e, template, context)
+ raise TemplateError.new(e, template, context, options)
end
# CHEF-4399
@@ -175,7 +175,7 @@ class Chef
# potential issues for the applications that will consume
# this template.
- if Chef::Platform.windows?
+ if ChefUtils.windows?
output = output.gsub(/\r?\n/, "\r\n")
end
@@ -184,7 +184,7 @@ class Chef
def _extend_modules(module_names)
module_names.each do |mod|
- context_methods = [:node, :render, :render_template, :render_template_from_string]
+ context_methods = %i{node render render_template render_template_from_string}
context_methods.each do |core_method|
if mod.method_defined?(core_method) || mod.private_method_defined?(core_method)
Chef::Log.warn("Core template method `#{core_method}' overridden by extension module #{mod}")
@@ -204,7 +204,7 @@ class Chef
all_ivars.delete(:@_extension_modules)
all_ivars.inject({}) do |ivar_map, ivar_symbol_name|
value = instance_variable_get(ivar_symbol_name)
- name_without_at = ivar_symbol_name.to_s[1..-1].to_sym
+ name_without_at = ivar_symbol_name.to_s[1..].to_sym
ivar_map[name_without_at] = value
ivar_map
end
@@ -212,11 +212,12 @@ class Chef
end
class TemplateError < RuntimeError
- attr_reader :original_exception, :context
+ attr_reader :original_exception, :context, :options
+
SOURCE_CONTEXT_WINDOW = 2
- def initialize(original_exception, template, context)
- @original_exception, @template, @context = original_exception, template, context
+ def initialize(original_exception, template, context, options)
+ @original_exception, @template, @context, @options = original_exception, template, context, options
end
def message
@@ -224,7 +225,11 @@ class Chef
end
def line_number
- @line_number ||= $1.to_i if original_exception.backtrace.find { |line| line =~ /\(erubis\):(\d+)/ }
+ @line_number ||= if options[:filename]
+ $1.to_i if original_exception.backtrace.find { |line| line =~ /#{Regexp.escape(options[:filename])}:(\d+)/ }
+ else
+ $1.to_i if original_exception.backtrace.find { |line| line =~ /\(erubis\):(\d+)/ }
+ end
end
def source_location
diff --git a/lib/chef/mixin/unformatter.rb b/lib/chef/mixin/unformatter.rb
index 7dad56b270..c233549b17 100644
--- a/lib/chef/mixin/unformatter.rb
+++ b/lib/chef/mixin/unformatter.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,10 +21,10 @@ class Chef
module Unformatter
def write(message)
- data = message.match(/(\[.+?\] )?([\w]+):(.*)$/)
- self.send(data[2].downcase.chomp.to_sym, data[3].strip)
+ data = message.match(/(\[.+?\] )?(\w+):(.*)$/)
+ 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 7dc04d662b..13c4ee29b3 100644
--- a/lib/chef/mixin/uris.rb
+++ b/lib/chef/mixin/uris.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,10 @@
# limitations under the License.
#
-require "uri"
-require "addressable/uri"
+autoload :URI, "uri"
+module Addressable
+ autoload :URI, "addressable/uri"
+end
class Chef
module Mixin
@@ -31,12 +33,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(Addressable::URI.encode(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..d40d233f35
--- /dev/null
+++ b/lib/chef/mixin/user_context.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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 "../util/windows/logon_session" if ChefUtils.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 ChefUtils.windows?
+ raise Exceptions::UnsupportedPlatform, "User context impersonation is supported only on the Windows platform"
+ end
+
+ unless 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..b627e0210c
--- /dev/null
+++ b/lib/chef/mixin/versioned_api.rb
@@ -0,0 +1,83 @@
+#--
+# Copyright:: Copyright (c) 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
+ .max_by { |a| a.send(:minimum_api_version) }
+ 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..a8048962ee 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,22 +15,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "chef-utils/dsl/which" unless defined?(ChefUtils::DSL::Which)
+require "chef-utils/dsl/default_paths" unless defined?(ChefUtils::DSL::DefaultPaths)
+require_relative "chef_utils_wiring" unless defined?(Chef::Mixin::ChefUtilsWiring)
+
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
+ include ChefUtils::DSL::Which
+ include ChefUtils::DSL::DefaultPaths
+ include ChefUtilsWiring
+
+ private
+
+ # we dep-inject default paths into this API for historical reasons
+ #
+ # @api private
+ def __extra_path
+ __default_paths
end
end
end
diff --git a/lib/chef/mixin/why_run.rb b/lib/chef/mixin/why_run.rb
index ea62527bdd..efe327168e 100644
--- a/lib/chef/mixin/why_run.rb
+++ b/lib/chef/mixin/why_run.rb
@@ -1,7 +1,7 @@
#
# Author:: Dan DeLeo ( <dan@chef.io> )
# Author:: Marc Paradise ( <marc@chef.io> )
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..dbc9939ec2 100644
--- a/lib/chef/mixin/wide_string.rb
+++ b/lib/chef/mixin/wide_string.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala(<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,40 +31,22 @@ class Chef
def utf8_to_wide(ustring)
# ensure it is actually UTF-8
# Ruby likes to mark binary data as ASCII-8BIT
- ustring = (ustring + "").force_encoding("UTF-8") if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
+ ustring = (ustring + "").force_encoding("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
- if ustring.respond_to?(:encode)
- ustring.encode("UTF-16LE")
- else
- require "iconv"
- Iconv.conv("UTF-16LE", "UTF-8", ustring)
- end
- end
- ustring
+ ustring.encode("UTF-16LE")
end
def wide_to_utf8(wstring)
# ensure it is actually UTF-16LE
# Ruby likes to mark binary data as ASCII-8BIT
- wstring = wstring.force_encoding("UTF-16LE") if wstring.respond_to?(:force_encoding)
+ wstring = wstring.force_encoding("UTF-16LE")
- # encode it all as UTF-8
- wstring = begin
- if wstring.respond_to?(:encode)
- wstring.encode("UTF-8")
- else
- require "iconv"
- Iconv.conv("UTF-8", "UTF-16LE", wstring)
- end
- end
- # remove trailing CRLF and NULL characters
- wstring.strip!
- wstring
+ # encode it all as UTF-8 and remove trailing CRLF and NULL characters
+ wstring.encode("UTF-8").strip
end
end
diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb
index 49252af484..a27a9c7932 100644
--- a/lib/chef/mixin/windows_architecture_helper.rb
+++ b/lib/chef/mixin/windows_architecture_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/platform/query_helpers"
-require "chef/win32/process" if Chef::Platform.windows?
-require "chef/win32/system" if Chef::Platform.windows?
+require_relative "../exceptions"
+require_relative "../platform/query_helpers"
+require_relative "../win32/process" if ChefUtils.windows?
+require_relative "../win32/system" if ChefUtils.windows?
class Chef
module Mixin
@@ -49,8 +49,8 @@ class Chef
node ||= begin
os_arch = ENV["PROCESSOR_ARCHITEW6432"] ||
ENV["PROCESSOR_ARCHITECTURE"]
- Hash.new.tap do |n|
- n[:kernel] = Hash.new
+ {}.tap do |n|
+ n[:kernel] = {}
n[:kernel][:machine] = os_arch == "AMD64" ? :x86_64 : :i386
end
end
@@ -74,22 +74,22 @@ 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 )
+ ( 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)
+ unless valid_windows_architecture?(architecture)
raise Chef::Exceptions::Win32ArchitectureIncorrect,
- "The specified architecture was not valid. It must be one of :i386 or :x86_64"
+ "The specified architecture was not valid. It must be one of :i386 or :x86_64"
end
end
def is_i386_process_on_x86_64_windows?
- if Chef::Platform.windows?
+ if ChefUtils.windows?
Chef::ReservedNames::Win32::Process.is_wow64_process
else
false
@@ -97,13 +97,13 @@ class Chef
end
def disable_wow64_file_redirection( node )
- if ( node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?
+ if ( node_windows_architecture(node) == :x86_64) && ::ChefUtils.windows?
Chef::ReservedNames::Win32::System.wow64_disable_wow64_fs_redirection
end
end
def restore_wow64_file_redirection( node, original_redirection_state )
- if (node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?
+ if (node_windows_architecture(node) == :x86_64) && ::ChefUtils.windows?
Chef::ReservedNames::Win32::System.wow64_revert_wow64_fs_redirection(original_redirection_state)
end
end
diff --git a/lib/chef/mixin/windows_env_helper.rb b/lib/chef/mixin/windows_env_helper.rb
index 65b4b33cf0..dcf8dbddf3 100644
--- a/lib/chef/mixin/windows_env_helper.rb
+++ b/lib/chef/mixin/windows_env_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,23 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/mixin/wide_string"
-require "chef/platform/query_helpers"
-require "chef/win32/error" if Chef::Platform.windows?
-require "chef/win32/api/system" if Chef::Platform.windows?
-require "chef/win32/api/unicode" if Chef::Platform.windows?
+require_relative "../exceptions"
+require_relative "wide_string"
+require_relative "../platform/query_helpers"
+require_relative "../win32/error" if ChefUtils.windows?
+require_relative "../win32/api/system" if ChefUtils.windows?
+require_relative "../win32/api/unicode" if ChefUtils.windows?
class Chef
module Mixin
module WindowsEnvHelper
include Chef::Mixin::WideString
- if Chef::Platform.windows?
+ if ChefUtils.windows?
include Chef::ReservedNames::Win32::API::System
end
- #see: http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx
+ # see: http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx
HWND_BROADCAST = 0xffff
WM_SETTINGCHANGE = 0x001A
SMTO_BLOCK = 0x0001
@@ -46,8 +46,8 @@ class Chef
if SendMessageTimeoutA(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string("Environment").address, flags, 5000, nil) == 0
Chef::ReservedNames::Win32::Error.raise!
end
- if SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string(
- utf8_to_wide("Environment")
+ if SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string(
+ utf8_to_wide("Environment")
).address, flags, 5000, nil) == 0
Chef::ReservedNames::Win32::Error.raise!
end
diff --git a/lib/chef/mixin/xml_escape.rb b/lib/chef/mixin/xml_escape.rb
index f9a41b9fd0..6f2f369e4f 100644
--- a/lib/chef/mixin/xml_escape.rb
+++ b/lib/chef/mixin/xml_escape.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2005-2016, Sam Ruby
# License:: Apache License, Version 2.0
#
@@ -46,7 +46,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-require "chef/log"
+require_relative "../log"
begin
require "fast_xs"
@@ -73,8 +73,8 @@ class Chef
137 => 8240, # per mille sign
138 => 352, # latin capital letter s with caron
139 => 8249, # single left-pointing angle quotation mark
- 140 => 338, # latin capital ligature oe
- 142 => 381, # latin capital letter z with caron
+ 140 => 338, # latin capital ligature oe
+ 142 => 381, # latin capital letter z with caron
145 => 8216, # left single quotation mark
146 => 8217, # right single quotation mark
147 => 8220, # left double quotation mark
@@ -86,28 +86,26 @@ class Chef
153 => 8482, # trade mark sign
154 => 353, # latin small letter s with caron
155 => 8250, # single right-pointing angle quotation mark
- 156 => 339, # latin small ligature oe
- 158 => 382, # latin small letter z with caron
- 159 => 376 # latin capital letter y with diaeresis
- }
+ 156 => 339, # latin small ligature oe
+ 158 => 382, # latin small letter z with caron
+ 159 => 376, # latin capital letter y with diaeresis
+ }.freeze
# http://www.w3.org/TR/REC-xml/#dt-chardata
PREDEFINED = {
38 => "&amp;", # ampersand
60 => "&lt;", # left angle bracket
- 62 => "&gt;" # right angle bracket
- }
+ 62 => "&gt;", # right angle bracket
+ }.freeze
# http://www.w3.org/TR/REC-xml/#charsets
VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF),
- (0xE000..0xFFFD), (0x10000..0x10FFFF)]
+ (0xE000..0xFFFD), (0x10000..0x10FFFF)].freeze
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..7d8ef849fe 100644
--- a/lib/chef/mixins.rb
+++ b/lib/chef/mixins.rb
@@ -1,13 +1,13 @@
-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"
-require "chef/mixin/enforce_ownership_and_permissions"
-require "chef/mixin/from_file"
-require "chef/mixin/params_validate"
-require "chef/mixin/path_sanity"
-require "chef/mixin/template"
-require "chef/mixin/securable"
-require "chef/mixin/xml_escape"
+require_relative "mixin/shell_out"
+require_relative "mixin/checksum"
+require_relative "mixin/convert_to_class_name"
+require_relative "mixin/create_path"
+require_relative "mixin/deep_merge"
+require_relative "mixin/enforce_ownership_and_permissions"
+require_relative "mixin/from_file"
+require_relative "mixin/params_validate"
+require_relative "mixin/default_paths"
+require_relative "mixin/path_sanity"
+require_relative "mixin/template"
+require_relative "mixin/securable"
+require_relative "mixin/xml_escape"
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
deleted file mode 100644
index c1cb87bffd..0000000000
--- a/lib/chef/monkey_patches/net_http.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-
-# Module gets mixed in to Net::HTTP exception classes so we can attach our
-# RESTRequest object to them and get the request parameters back out later.
-module ChefNetHTTPExceptionExtensions
- attr_accessor :chef_rest_request
-end
-
-require "net/http"
-module Net
- class HTTPError
- include ChefNetHTTPExceptionExtensions
- end
- class HTTPRetriableError
- include ChefNetHTTPExceptionExtensions
- end
- class HTTPServerException
- include ChefNetHTTPExceptionExtensions
- end
- class HTTPFatalError
- include ChefNetHTTPExceptionExtensions
- end
-end
-
-if Net::HTTP.instance_methods.map { |m| m.to_s }.include?("proxy_uri")
- begin
- # Ruby 2.0 changes the way proxy support is implemented in Net::HTTP.
- # The implementation does not work correctly with IPv6 literals because it
- # concatenates the address into a URI without adding square brackets for
- # IPv6 addresses.
- #
- # If the bug is present, a call to Net::HTTP#proxy_uri when the host is an
- # IPv6 address will fail by creating an invalid URI, like so:
- #
- # 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>'
- #
- # https://bugs.ruby-lang.org/issues/9129
- #
- # NOTE: This should be fixed in Ruby 2.2.0, and backported to Ruby 2.0 and
- # 2.1 (not yet released so the version/patchlevel required isn't known
- # yet).
- Net::HTTP.new("::1", 80).proxy_uri
- rescue URI::InvalidURIError
- class Net::HTTP
-
- def proxy_uri # :nodoc:
- ipv6_safe_addr = address.to_s.include?(":") ? "[#{address}]" : address
- @proxy_uri ||= URI("http://#{ipv6_safe_addr}:#{port}").find_proxy
- end
-
- end
- end
-end
diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb
index ccca5825e2..6e3748f308 100644
--- a/lib/chef/monkey_patches/webrick-utils.rb
+++ b/lib/chef/monkey_patches/webrick-utils.rb
@@ -24,27 +24,29 @@ module WEBrick
unless port
raise ArgumentError, "must specify port"
end
+
res = Socket.getaddrinfo(address, port,
- Socket::AF_UNSPEC, # address family
- Socket::SOCK_STREAM, # socket type
- 0, # protocol
- Socket::AI_PASSIVE) # flag
+ Socket::AF_UNSPEC, # address family
+ Socket::SOCK_STREAM, # socket type
+ 0, # protocol
+ Socket::AI_PASSIVE) # flag
last_error = nil
sockets = []
res.each do |ai|
- begin
- logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
- sock = TCPServer.new(ai[3], port)
- port = sock.addr[1] if port == 0
- Utils.set_close_on_exec(sock)
- sockets << sock
- rescue => ex
- logger.warn("TCPServer Error: #{ex}") if logger
- last_error = ex
- end
+
+ logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
+ sock = TCPServer.new(ai[3], port)
+ port = sock.addr[1] if port == 0
+ Utils.set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ logger.warn("TCPServer Error: #{ex}") if logger
+ last_error = ex
+
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 a08d67becf..dbcbf0ee1e 100644
--- a/lib/chef/monkey_patches/win32/registry.rb
+++ b/lib/chef/monkey_patches/win32/registry.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,9 +15,9 @@
# limitations under the License.
#
-require "chef/win32/api/registry"
-require "chef/win32/unicode"
-require "win32/registry"
+require_relative "../../win32/api/registry"
+require_relative "../../win32/unicode"
+require "win32/registry" unless defined?(Win32::Registry)
module Win32
class Registry
@@ -56,31 +56,5 @@ module Win32
end
end
-
- if RUBY_VERSION =~ /^2\.1/
- # ::Win32::Registry#write does not correctly handle data in Ruby 2.1
- # This bug is _reportedly_ resolved in Ruby 2.1.7 and 2.2.3
- # but fails in appveyor on 2.1.8 unless we keep applying this monkeypatch
- # https://bugs.ruby-lang.org/issues/11439
- def write(name, type, data)
- case type
- when REG_SZ, REG_EXPAND_SZ
- data = data.to_s.encode(WCHAR) + WCHAR_NUL
- when REG_MULTI_SZ
- data = data.to_a.map { |s| s.encode(WCHAR) }.join(WCHAR_NUL) << WCHAR_NUL << WCHAR_NUL
- when REG_BINARY
- data = data.to_s
- when REG_DWORD
- data = API.packdw(data.to_i)
- when REG_DWORD_BIG_ENDIAN
- data = [data.to_i].pack("N")
- when REG_QWORD
- data = API.packqw(data.to_i)
- else
- raise TypeError, "Unsupported type #{type}"
- end
- API.SetValue(@hkey, name, type, data, data.bytesize)
- end
- end
end
end
diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb
index 82816e887f..b9611feaf2 100644
--- a/lib/chef/monologger.rb
+++ b/lib/chef/monologger.rb
@@ -1,88 +1,4 @@
-require "logger"
-require "pp"
+require "mixlib/log/logger"
+require "pp" unless defined?(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/nil_argument.rb b/lib/chef/nil_argument.rb
deleted file mode 100644
index c7cbc1b39e..0000000000
--- a/lib/chef/nil_argument.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class Chef
- NIL_ARGUMENT = Object.new
-end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 212b1ced14..d569eeda38 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,22 +18,24 @@
# limitations under the License.
#
-require "forwardable"
-require "chef/config"
-require "chef/nil_argument"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/mixin/deep_merge"
-require "chef/dsl/include_attribute"
-require "chef/dsl/universal"
-require "chef/environment"
-require "chef/server_api"
-require "chef/run_list"
-require "chef/node/attribute"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/search/query"
-require "chef/whitelist"
+require "forwardable" unless defined?(Forwardable)
+require "securerandom" unless defined?(SecureRandom)
+require_relative "constants"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mixin/deep_merge"
+require_relative "dsl/include_attribute"
+require_relative "dsl/universal"
+require_relative "environment"
+require_relative "server_api"
+require_relative "run_list"
+require_relative "node/attribute"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "search/query"
+require_relative "attribute_allowlist"
+require_relative "attribute_blocklist"
class Chef
class Node
@@ -46,9 +48,9 @@ class Chef
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,
@@ -64,12 +66,11 @@ class Chef
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
@@ -78,7 +79,7 @@ class Chef
@policy_name = nil
@policy_group = nil
- @attributes = Chef::Node::Attribute.new({}, {}, {}, {})
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, self)
@run_state = {}
end
@@ -86,7 +87,6 @@ class Chef
# after the run_context has been set on the node, go through the cookbook_collection
# and setup the node[:cookbooks] attribute so that it is published in the node object
def set_cookbook_attribute
- return unless run_context.cookbook_collection
run_context.cookbook_collection.each do |cookbook_name, cookbook|
automatic_attrs[:cookbooks][cookbook_name][:version] = cookbook.version
end
@@ -111,13 +111,14 @@ 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,
- :cannot_be => :blank,
- :regex => /^[\-[:alnum:]_:.]+$/ },
- })
+ { name: arg },
+ { name: { kind_of: String,
+ cannot_be: :blank,
+ regex: /^[\-[:alnum:]_:.]+$/ },
+ }
+ )
@name = arg
else
@name
@@ -128,7 +129,7 @@ class Chef
set_or_return(
:chef_environment,
arg,
- { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String }
+ { regex: /^[\-[:alnum:]_]+$/, kind_of: String }
)
end
@@ -147,8 +148,9 @@ class Chef
#
# @param arg [String] the new policy_name value
# @return [String] the current policy_name, or the one you just set
- def policy_name(arg = NULL_ARG)
- return @policy_name if arg.equal?(NULL_ARG)
+ def policy_name(arg = NOT_PASSED)
+ return @policy_name if arg.equal?(NOT_PASSED)
+
validate({ policy_name: arg }, { policy_name: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
@policy_name = arg
end
@@ -169,8 +171,9 @@ class Chef
#
# @param arg [String] the new policy_group value
# @return [String] the current policy_group, or the one you just set
- def policy_group(arg = NULL_ARG)
- return @policy_group if arg.equal?(NULL_ARG)
+ def policy_group(arg = NOT_PASSED)
+ return @policy_group if arg.equal?(NOT_PASSED)
+
validate({ policy_group: arg }, { policy_group: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
@policy_group = arg
end
@@ -197,26 +200,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.normal
end
- def set
- Chef.log_deprecation("node.set is deprecated and will be removed in Chef 14, please use node.default/node.override (or node.normal only if you really need persistence)")
- normal
- end
-
# Set a default of this node, but auto-vivify any Mashes that might
# be missing
def default
- attributes.top_level_breadcrumb = nil
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.override
end
@@ -237,7 +232,6 @@ class Chef
end
def automatic_attrs
- attributes.top_level_breadcrumb = nil
attributes.automatic
end
@@ -295,20 +289,49 @@ 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
+ # This boolean can be useful to determine if an override_runlist is set, it can be true
+ # even if the override_runlist is empty.
+ #
+ # (Mutators can set the override_runlist so any non-empty override_runlist is considered set)
+ #
+ # @return [Boolean] if the override run list has been set
+ def override_runlist_set?
+ !!@override_runlist_set || !override_runlist.empty?
+ end
+
+ # Accessor for override_runlist (this cannot set an empty override run list)
+ #
+ # @params args [Array] override run list to set
+ # @return [Chef::RunList] the override run list
def override_runlist(*args)
- args.length > 0 ? @override_runlist.reset!(args) : @override_runlist
+ return @override_runlist if args.length == 0
+
+ @override_runlist_set = true
+ @override_runlist.reset!(args)
+ end
+
+ # Setter for override_runlist which allows setting an empty override run list and marking it to be used
+ #
+ # @params array [Array] override run list to set
+ # @return [Chef::RunList] the override run list
+ def override_runlist=(array)
+ @override_runlist_set = true
+ @override_runlist.reset!(array)
end
def select_run_list
- @override_runlist.empty? ? @primary_runlist : @override_runlist
+ override_runlist_set? ? @override_runlist : @primary_runlist
end
# Returns an Array of roles and recipes, in the order they will be applied.
@@ -328,26 +351,48 @@ class Chef
run_list.detect { |r| r == item } ? true : false
end
- # Consume data from ohai and Attributes provided as JSON on the command line.
+ # Handles both the consumption of ohai data and possibly JSON attributes from the CLI
+ #
+ # @api private
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
+ fix_automatic_attributes
+ end
+ # This is for ohai plugins to consume ohai data and have it merged, it should probably be renamed
+ #
+ # @api private
+ def consume_ohai_data(ohai_data)
+ self.automatic_attrs = Chef::Mixin::DeepMerge.merge(automatic_attrs, ohai_data)
+ fix_automatic_attributes
+ end
+
+ # Always ensure that certain automatic attributes are populated and constructed correctly
+ #
+ # @api private
+ def fix_automatic_attributes
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] = Chef::VersionString.new(version)
+ automatic[:chef_guid] = Chef::Config[:chef_guid] || ( Chef::Config[:chef_guid] = node_uuid )
+ automatic[:name] = name
+ automatic[:chef_environment] = chef_environment
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
@@ -371,7 +416,8 @@ 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
@@ -398,8 +444,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
@@ -418,7 +464,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
@@ -426,6 +472,7 @@ class Chef
apply_expansion_attributes(expansion)
+ automatic_attrs[:chef_environment] = chef_environment
expansion
end
@@ -447,13 +494,10 @@ class Chef
# Transform the node to a Hash
def to_hash
- index_hash = Hash.new
+ index_hash = attributes.to_hash
index_hash["chef_type"] = "node"
index_hash["name"] = name
index_hash["chef_environment"] = chef_environment
- attribute.each do |key, value|
- index_hash[key] = value
- end
index_hash["recipe"] = run_list.recipe_names if run_list.recipe_names.length > 0
index_hash["role"] = run_list.role_names if run_list.role_names.length > 0
index_hash["run_list"] = run_list.run_list_items
@@ -464,10 +508,10 @@ class Chef
display = {}
display["name"] = name
display["chef_environment"] = chef_environment
- display["automatic"] = automatic_attrs
- display["normal"] = normal_attrs
- display["default"] = attributes.combined_default
- display["override"] = attributes.combined_override
+ display["automatic"] = attributes.automatic.to_hash
+ display["normal"] = attributes.normal.to_hash
+ display["default"] = attributes.combined_default.to_hash
+ display["override"] = attributes.combined_override.to_hash
display["run_list"] = run_list.run_list_items
display
end
@@ -482,13 +526,13 @@ class Chef
"name" => name,
"chef_environment" => chef_environment,
"json_class" => self.class.name,
- "automatic" => attributes.automatic,
- "normal" => attributes.normal,
+ "automatic" => attributes.automatic.to_hash,
+ "normal" => attributes.normal.to_hash,
"chef_type" => "node",
- "default" => attributes.combined_default,
- "override" => attributes.combined_override,
- #Render correctly for run_list items so malformed json does not result
- "run_list" => @primary_runlist.run_list.map { |item| item.to_s },
+ "default" => attributes.combined_default.to_hash,
+ "override" => attributes.combined_override.to_hash,
+ # Render correctly for run_list items so malformed json does not result
+ "run_list" => @primary_runlist.run_list.map(&:to_s),
}
# Chef Server rejects node JSON with extra keys; prior to 12.3,
# "policy_name" and "policy_group" are unknown; after 12.3 they are
@@ -511,40 +555,41 @@ 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
+ return o if o.is_a? Chef::Node
+
node = new
node.name(o["name"])
- node.chef_environment(o["chef_environment"])
- if o.has_key?("attributes")
+
+ node.policy_name = o["policy_name"] if o.key?("policy_name")
+ node.policy_group = o["policy_group"] if o.key?("policy_group")
+
+ unless node.policy_group.nil?
+ node.chef_environment(o["policy_group"])
+ else
+ node.chef_environment(o["chef_environment"])
+ end
+
+ if o.key?("attributes")
node.normal_attrs = o["attributes"]
end
- node.automatic_attrs = Mash.new(o["automatic"]) if o.has_key?("automatic")
- node.normal_attrs = Mash.new(o["normal"]) if o.has_key?("normal")
- node.default_attrs = Mash.new(o["default"]) if o.has_key?("default")
- node.override_attrs = Mash.new(o["override"]) if o.has_key?("override")
+ node.automatic_attrs = Mash.new(o["automatic"]) if o.key?("automatic")
+ node.normal_attrs = Mash.new(o["normal"]) if o.key?("normal")
+ node.default_attrs = Mash.new(o["default"]) if o.key?("default")
+ node.override_attrs = Mash.new(o["override"]) if o.key?("override")
- if o.has_key?("run_list")
+ if o.key?("run_list")
node.run_list.reset!(o["run_list"])
- elsif o.has_key?("recipes")
+ elsif o.key?("recipes")
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
def self.list_by_environment(environment, inflate = false)
if inflate
- response = Hash.new
+ response = {}
Chef::Search::Query.new.search(:node, "chef_environment:#{environment}") { |n| response[n.name] = n unless n.nil? }
response
else
@@ -554,7 +599,7 @@ class Chef
def self.list(inflate = false)
if inflate
- response = Hash.new
+ response = {}
Chef::Search::Query.new.search(:node) do |n|
n = Chef::Node.from_hash(n)
response[n.name] = n unless n.nil?
@@ -567,8 +612,9 @@ class Chef
def self.find_or_create(node_name)
load(node_name)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise unless e.response.code == "404"
+
node = build(node_name)
node.create
end
@@ -596,18 +642,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
+ rescue Net::HTTPClientException => 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
@@ -619,7 +660,7 @@ class Chef
def create
chef_server_rest.post("nodes", data_for_save)
self
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# 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
@@ -635,15 +676,15 @@ class Chef
end
def ==(other)
- if other.kind_of?(self.class)
- self.name == other.name
+ if other.is_a?(self.class)
+ name == other.name
else
false
end
end
def <=>(other)
- self.name <=> other.name
+ name <=> other.name
end
private
@@ -652,8 +693,9 @@ class Chef
trimmed_data = data_for_save_without_policyfile_attrs
chef_server_rest.put("nodes/#{name}", trimmed_data)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise e unless e.response.code == "404"
+
chef_server_rest.post("nodes", trimmed_data)
end
@@ -664,18 +706,68 @@ class Chef
end
end
+ # a method to handle the renamed configuration from whitelist -> allowed
+ # and to throw a deprecation warning when the old configuration is set
+ #
+ # @param [String] level the attribute level
+ def allowlist_or_whitelist_config(level)
+ if Chef::Config["#{level}_attribute_whitelist".to_sym]
+ Chef.deprecated(:attribute_blacklist_configuration, "Attribute whitelist configurations have been deprecated. Use the allowed_LEVEL_attribute configs instead")
+ Chef::Config["#{level}_attribute_whitelist".to_sym]
+ else
+ Chef::Config["allowed_#{level}_attributes".to_sym]
+ end
+ end
+
+ # a method to handle the renamed configuration from blacklist -> blocked
+ # and to throw a deprecation warning when the old configuration is set
+ #
+ # @param [String] level the attribute level
+ def blocklist_or_blacklist_config(level)
+ if Chef::Config["#{level}_attribute_blacklist".to_sym]
+ Chef.deprecated(:attribute_blacklist_configuration, "Attribute blacklist configurations have been deprecated. Use the blocked_LEVEL_attribute configs instead")
+ Chef::Config["#{level}_attribute_blacklist".to_sym]
+ else
+ Chef::Config["blocked_#{level}_attributes".to_sym]
+ end
+ end
+
def data_for_save
data = for_json
%w{automatic default normal override}.each do |level|
- 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.")
- data[level] = Chef::Whitelist.filter(data[level], whitelist)
+ allowlist = allowlist_or_whitelist_config(level)
+ unless allowlist.nil? # nil => save everything
+ logger.info("Allowing #{level} node attributes for save.")
+ data[level] = Chef::AttributeAllowlist.filter(data[level], allowlist)
+ end
+
+ blocklist = blocklist_or_blacklist_config(level)
+ unless blocklist.nil? # nil => remove nothing
+ logger.info("Blocking #{level} node attributes for save")
+ data[level] = Chef::AttributeBlocklist.filter(data[level], blocklist)
end
end
data
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
+ path = File.expand_path(Chef::Config[:chef_guid_path])
+ dir = File.dirname(path)
+
+ unless File.exists?(path)
+ FileUtils.mkdir_p(dir)
+ File.write(path, SecureRandom.uuid)
+ end
+
+ File.open(path).first.chomp
+ end
+
end
end
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 95b3b09f7e..29b60a98d5 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,14 @@
# limitations under the License.
#
-require "chef/node/immutable_collections"
-require "chef/node/attribute_collections"
-require "chef/decorator/unchain"
-require "chef/mixin/deep_merge"
-require "chef/log"
+require_relative "mixin/deep_merge_cache"
+require_relative "mixin/immutablize_hash"
+require_relative "mixin/state_tracking"
+require_relative "immutable_collections"
+require_relative "attribute_collections"
+require_relative "../decorator/unchain"
+require_relative "../mixin/deep_merge"
+require_relative "../log"
class Chef
class Node
@@ -34,284 +37,264 @@ 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 = [
- :@default,
- :@env_default,
- :@role_default,
- :@force_default,
- :@normal,
- :@override,
- :@role_override,
- :@env_override,
- :@force_override,
- :@automatic,
- ].freeze
-
- DEFAULT_COMPONENTS = [
- :@default,
- :@env_default,
- :@role_default,
- :@force_default,
- ]
-
- OVERRIDE_COMPONENTS = [
- :@override,
- :@role_override,
- :@env_override,
- :@force_override,
- ]
-
- [:all?,
- :any?,
- :assoc,
- :chunk,
- :collect,
- :collect_concat,
- :compare_by_identity,
- :compare_by_identity?,
- :count,
- :cycle,
- :detect,
- :drop,
- :drop_while,
- :each,
- :each_cons,
- :each_entry,
- :each_key,
- :each_pair,
- :each_slice,
- :each_value,
- :each_with_index,
- :each_with_object,
- :empty?,
- :entries,
- :except,
- :fetch,
- :find,
- :find_all,
- :find_index,
- :first,
- :flat_map,
- :flatten,
- :grep,
- :group_by,
- :has_value?,
- :include?,
- :index,
- :inject,
- :invert,
- :key,
- :keys,
- :length,
- :map,
- :max,
- :max_by,
- :merge,
- :min,
- :min_by,
- :minmax,
- :minmax_by,
- :none?,
- :one?,
- :partition,
- :rassoc,
- :reduce,
- :reject,
- :reverse_each,
- :select,
- :size,
- :slice_before,
- :sort,
- :sort_by,
- :store,
- :symbolize_keys,
- :take,
- :take_while,
- :to_a,
- :to_h,
- :to_hash,
- :to_set,
- :value?,
- :values,
- :values_at,
- :zip].each do |delegated_method|
- define_method(delegated_method) do |*args, &block|
- merged_attributes.send(delegated_method, *args, &block)
- end
- end
-
- # return the cookbook level default attribute component
+ COMPONENTS = %i{
+ @default
+ @env_default
+ @role_default
+ @force_default
+ @normal
+ @override
+ @role_override
+ @env_override
+ @force_override
+ @automatic
+ }.freeze
+
+ DEFAULT_COMPONENTS = %i{
+ @default
+ @env_default
+ @role_default
+ @force_default
+ }.freeze
+
+ OVERRIDE_COMPONENTS = %i{
+ @override
+ @role_override
+ @env_override
+ @force_override
+ }.freeze
+
+ ENUM_METHODS = %i{
+ all?
+ any?
+ assoc
+ chunk
+ collect
+ collect_concat
+ compare_by_identity
+ compare_by_identity?
+ count
+ cycle
+ detect
+ drop
+ drop_while
+ each
+ each_cons
+ each_entry
+ each_key
+ each_pair
+ each_slice
+ each_value
+ each_with_index
+ each_with_object
+ empty?
+ entries
+ except
+ fetch
+ find
+ find_all
+ find_index
+ first
+ flat_map
+ flatten
+ grep
+ group_by
+ has_value?
+ include?
+ index
+ inject
+ invert
+ key
+ keys
+ length
+ map
+ max
+ max_by
+ merge
+ min
+ min_by
+ minmax
+ minmax_by
+ none?
+ one?
+ partition
+ rassoc
+ reduce
+ reject
+ reverse_each
+ select
+ size
+ slice_before
+ sort
+ sort_by
+ store
+ symbolize_keys
+ take
+ take_while
+ to_a
+ to_h
+ to_hash
+ to_json
+ to_set
+ to_yaml
+ value?
+ values
+ values_at
+ zip
+ }.freeze
+
+ ENUM_METHODS.each do |delegated_method|
+ define_method(delegated_method) do |*args, &block|
+ merged_attributes.send(delegated_method, *args, &block)
+ end
+ end
+
+ # return the cookbook level default attribute component
attr_reader :default
- # return the role level default attribute component
+ # return the role level default attribute component
attr_reader :role_default
- # return the environment level default attribute component
+ # return the environment level default attribute component
attr_reader :env_default
- # return the force_default level attribute component
+ # return the force_default level attribute component
attr_reader :force_default
- # return the "normal" level attribute component
+ # return the "normal" level attribute component
attr_reader :normal
- # return the cookbook level override attribute component
+ # return the cookbook level override attribute component
attr_reader :override
- # return the role level override attribute component
+ # return the role level override attribute component
attr_reader :role_override
- # return the enviroment level override attribute component
+ # return the environment level override attribute component
attr_reader :env_override
- # return the force override level attribute component
+ # return the force override level attribute component
attr_reader :force_override
- # return the automatic level attribute component
+ # 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)
- @default = VividMash.new(self, default)
- @env_default = VividMash.new(self, {})
- @role_default = VividMash.new(self, {})
- @force_default = VividMash.new(self, {})
-
- @normal = VividMash.new(self, normal)
-
- @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 = {}
- 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 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 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)
+
+ @normal = VividMash.new(normal, self, node, :normal)
+
+ @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)
+
+ @automatic = VividMash.new(automatic, self, node, :automatic)
+
+ 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 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.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
+ value =
+ begin
+ instance_variable_get(component).read!(*args)
+ rescue
:not_present
end
- end
[component.to_s.sub(/^@/, ""), value]
end
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+.
+ # 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+
+ # 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+
+ # 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+
+ # 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+
+ # 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+
+ # 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+
+ # 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+
+ # 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
- #
- # Deleting attributes
- #
+ #
+ # Deleting attributes
+ #
- # clears attributes from all precedence levels
+ # clears attributes from all precedence levels
def rm(*args)
with_deep_merged_return_value(self, *args) do
rm_default(*args)
@@ -320,11 +303,11 @@ class Chef
end
end
- # clears attributes from all default precedence levels
- #
- # similar to: force_default!['foo']['bar'].delete('baz')
- # - does not autovivify
- # - does not trainwreck if interior keys do not exist
+ # clears attributes from all default precedence levels
+ #
+ # similar to: force_default!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_default(*args)
with_deep_merged_return_value(combined_default, *args) do
default.unlink(*args)
@@ -334,20 +317,20 @@ class Chef
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
+ # 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)
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
+ # 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)
with_deep_merged_return_value(combined_override, *args) do
override.unlink(*args)
@@ -360,6 +343,7 @@ class Chef
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
@@ -367,143 +351,132 @@ class Chef
private :with_deep_merged_return_value
- #
- # Replacing attributes without merging
- #
+ #
+ # Replacing attributes without merging
+ #
- # sets default attributes without merging
- #
- # - this API autovivifies (and cannot trainwreck)
+ # sets default attributes without merging
+ #
+ # - 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
- #
- # - this API autovivifies (and cannot trainwreck)
+ # sets normal attributes without merging
+ #
+ # - 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
- #
- # - this API autovivifies (and cannot trainwreck)
+ # sets override attributes without merging
+ #
+ # - 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
- #
- # - this API autovivifies (and cannot trainwreck)
+ # clears from all default precedence levels and then sets force_default
+ #
+ # - 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
+ # clears from all override precedence levels and then sets force_override
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
- # method-style access to attributes
-
- def read(*path)
- merged_attributes.read(*path)
+ #
+ # Accessing merged attributes.
+ #
+ # Note that merged_attributes('foo', 'bar', 'baz') can be called to compute only the
+ # deep merge of node['foo']['bar']['baz'], but in practice we currently always compute
+ # all of node['foo'] even if the user only requires node['foo']['bar']['baz'].
+ #
+ def merged_attributes(*path)
+ merge_all(path)
end
- def read!(*path)
- merged_attributes.read!(*path)
+ def combined_override(*path)
+ ret = merge_overrides(path)
+ ret == NIL ? nil : ret
end
- def exist?(*path)
- merged_attributes.exist?(*path)
+ def combined_default(*path)
+ ret = merge_defaults(path)
+ ret == NIL ? nil : ret
end
- def write(level, *args, &block)
- self.send(level).write(*args, &block)
- end
+ def normal_unless(*args)
+ return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0
- def write!(level, *args, &block)
- self.send(level).write!(*args, &block)
+ write(:normal, *args) if normal.read(*args[0...-1]).nil?
end
- def unlink(level, *path)
- self.send(level).unlink(*path)
- end
+ def default_unless(*args)
+ return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
- def unlink!(level, *path)
- self.send(level).unlink!(*path)
+ write(:default, *args) if default.read(*args[0...-1]).nil?
end
- #
- # Accessing merged attributes.
- #
- # Note that merged_attributes('foo', 'bar', 'baz') can be called to compute only the
- # deep merge of node['foo']['bar']['baz'], but in practice we currently always compute
- # all of node['foo'] even if the user only requires node['foo']['bar']['baz'].
- #
+ def override_unless(*args)
+ return Decorator::Unchain.new(self, :override_unless) unless args.length > 0
- def merged_attributes(*path)
- # immutablize(
- merge_all(path)
- # )
+ write(:override, *args) if override.read(*args[0...-1]).nil?
end
- def combined_override(*path)
- immutablize(merge_overrides(path))
+ def has_key?(key)
+ COMPONENTS.any? do |component_ivar|
+ instance_variable_get(component_ivar).key?(key)
+ end
end
- def combined_default(*path)
- immutablize(merge_defaults(path))
- end
+ # method-style access to attributes (has to come after the prepended ImmutablizeHash)
- def normal_unless(*args)
- return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0
- write(:normal, *args) if read(*args[0...-1]).nil?
+ def read(*path)
+ merged_attributes.read(*path)
end
- def default_unless(*args)
- return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
- write(:default, *args) if read(*args[0...-1]).nil?
+ alias :dig :read
+
+ def read!(*path)
+ merged_attributes.read!(*path)
end
- def override_unless(*args)
- return Decorator::Unchain.new(self, :override_unless) unless args.length > 0
- write(:override, *args) if read(*args[0...-1]).nil?
+ def exist?(*path)
+ merged_attributes.exist?(*path)
end
- def set_unless(*args)
- Chef.log_deprecation("node.set_unless is deprecated and will be removed in Chef 14, please use node.default_unless/node.override_unless (or node.normal_unless if you really need persistence)")
- return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
- write(:normal, *args) if read(*args[0...-1]).nil?
+ def write(level, *args, &block)
+ send(level).write(*args, &block)
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 write!(level, *args, &block)
+ send(level).write!(*args, &block)
end
- def []=(key, value)
- raise Exceptions::ImmutableAttributeModification
+ def unlink(level, *path)
+ send(level).unlink(*path)
end
- def has_key?(key)
- COMPONENTS.any? do |component_ivar|
- instance_variable_get(component_ivar).has_key?(key)
- end
+ def unlink!(level, *path)
+ send(level).unlink!(*path)
end
alias :attribute? :has_key?
@@ -513,47 +486,28 @@ class Chef
alias :each_attribute :each
- def method_missing(symbol, *args)
- if symbol == :to_ary
- merged_attributes.send(symbol, *args)
- elsif args.empty?
- Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
- if key?(symbol)
- self[symbol]
- else
- raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
- end
- elsif symbol.to_s =~ /=$/
- Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
- 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 do |iv|
+ "#<#{self.class} " << (COMPONENTS + %i{@merged_attributes @properties}).map do |iv|
"#{iv}=#{instance_variable_get(iv).inspect}"
end.join(", ") << ">"
end
private
- # Helper method for merge_all/merge_defaults/merge_overrides.
- #
- # apply_path(thing, [ "foo", "bar", "baz" ]) = thing["foo"]["bar"]["baz"]
- #
- # The path value can be nil in which case the whole component is returned.
- #
- # It returns nil (does not raise an exception) if it walks off the end of an Mash/Hash/Array, it does not
- # raise any TypeError if it attempts to apply a hash key to an Integer/String/TrueClass, and just returns
- # nil in that case.
- #
+ # Helper method for merge_all/merge_defaults/merge_overrides.
+ #
+ # apply_path(thing, [ "foo", "bar", "baz" ]) = thing["foo"]["bar"]["baz"]
+ #
+ # The path value can be nil in which case the whole component is returned.
+ #
+ # It returns nil (does not raise an exception) if it walks off the end of an Mash/Hash/Array, it does not
+ # raise any TypeError if it attempts to apply a hash key to an Integer/String/TrueClass, and just returns
+ # nil in that case.
+ #
def apply_path(component, path)
path ||= []
path.inject(component) do |val, path_arg|
@@ -562,32 +516,32 @@ class Chef
if !val.respond_to?(:has_key?)
# Have an Array-like thing
val[path_arg]
- elsif val.has_key?(path_arg)
+ elsif val.key?(path_arg)
# Hash-like thing (must check has_key? first to protect against Autovivification)
val[path_arg]
else
- nil
+ NIL
end
else
- nil
+ NIL
end
end
end
- # For elements like Fixnums, true, nil...
+ # For elements like Fixnums, true, nil...
def safe_dup(e)
e.dup
rescue TypeError
e
end
- # Deep merge all attribute levels using hash-only merging between different precidence
- # levels (so override arrays completely replace arrays set at any default level).
- #
- # The path allows for selectively deep-merging a subtree of the node object.
- #
- # @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
+ # Deep merge all attribute levels using hash-only merging between different precedence
+ # levels (so override arrays completely replace arrays set at any default level).
+ #
+ # The path allows for selectively deep-merging a subtree of the node object.
+ #
+ # @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_all(path)
components = [
merge_defaults(path),
@@ -596,44 +550,113 @@ 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.
- #
- # The path allows for selectively deep-merging a subtree of the node object.
- #
- # @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
+ # Deep merge the default attribute levels with array merging.
+ #
+ # The path allows for selectively deep-merging a subtree of the node object.
+ #
+ # @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|
+ 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
end
- # Deep merge the override attribute levels with array merging.
- #
- # The path allows for selectively deep-merging a subtree of the node object.
- #
- # @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
+ # Deep merge the override attribute levels with array merging.
+ #
+ # The path allows for selectively deep-merging a subtree of the node object.
+ #
+ # @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|
+ 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
end
- end
+ # needed for __path__
+ def convert_key(key)
+ key.is_a?(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.is_a?(Hash) && merge_with.is_a?(Hash)
+ merge_with.each do |key, merge_with_value|
+ value =
+ if merge_onto.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.is_a?(Array) && merge_with.is_a?(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.is_a?(Hash)
+ Chef::Node::ImmutableMash.new(merge_with)
+ elsif merge_with.is_a?(Array)
+ Chef::Node::ImmutableArray.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.is_a?(Hash) && merge_with.is_a?(Hash)
+ merge_with.each do |key, merge_with_value|
+ value =
+ if merge_onto.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.is_a?(Hash)
+ Chef::Node::ImmutableMash.new(merge_with)
+ elsif merge_with.is_a?(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 b739ea8490..a420563165 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,11 @@
# limitations under the License.
#
-require "chef/node/common_api"
+require_relative "common_api"
+require_relative "mixin/state_tracking"
+require_relative "mixin/immutablize_array"
+require_relative "mixin/immutablize_hash"
+require_relative "mixin/mashy_array"
class Chef
class Node
@@ -25,37 +29,9 @@ class Chef
# "root" (Chef::Node::Attribute) object, and will trigger a cache
# invalidation on that object when mutated.
class AttrArray < Array
- 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,
- ]
+ include Chef::Node::Mixin::MashyArray
+
+ MUTATOR_METHODS = Chef::Node::Mixin::ImmutablizeArray::DISALLOWED_MUTATOR_METHODS
# For all of the methods that may mutate an Array, we override them to
# also invalidate the cached merged_attributes on the root
@@ -63,16 +39,19 @@ class Chef
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
ret = super(*args, &block)
- root.reset_cache(root.top_level_breadcrumb)
+ 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...
@@ -86,6 +65,31 @@ class Chef
Array.new(map { |e| safe_dup(e) })
end
+ def to_yaml(*opts)
+ to_a.to_yaml(*opts)
+ end
+
+ private
+
+ def convert_value(value)
+ case value
+ when VividMash, 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
@@ -99,46 +103,36 @@ class Chef
# #fetch, 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!,
- :update,
- :reject!,
- :replace,
- :select!,
- :shift,
- ]
+ MUTATOR_METHODS = Chef::Node::Mixin::ImmutablizeHash::DISALLOWED_MUTATOR_METHODS - %i{write write! unlink unlink!}
# For all of the mutating methods on Mash, override them so that they
# also invalidate the cached `merged_attributes` on the root Attribute
# object.
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 delete(key, &block)
+ send_reset_cache(__path__, key)
+ super
+ end
+
+ 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
@@ -146,32 +140,13 @@ class Chef
end
def []=(key, value)
- root.top_level_breadcrumb ||= key
ret = super
- root.reset_cache(root.top_level_breadcrumb)
- ret
+ 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 convert_key(key)
super
end
@@ -182,12 +157,12 @@ class Chef
# attribute tree will have the correct cache invalidation behavior.
def convert_value(value)
case value
- when VividMash
+ when VividMash, 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
@@ -197,6 +172,11 @@ class Chef
Mash.new(self)
end
+ def to_yaml(*opts)
+ to_h.to_yaml(*opts)
+ end
+
+ prepend Chef::Node::Mixin::StateTracking
end
end
end
diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb
index ce2c6b6878..b711d4ee95 100644
--- a/lib/chef/node/common_api.rb
+++ b/lib/chef/node/common_api.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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
# method-style access to attributes
def valid_container?(obj, key)
- return obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Fixnum))
+ obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Integer))
end
private :valid_container?
@@ -32,12 +32,11 @@ class Chef
# - autovivifying / autoreplacing writer
# - non-container-ey intermediate objects are replaced with hashes
def write(*args, &block)
- root.top_level_breadcrumb = nil if respond_to?(:root)
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)
+ unless valid_container?(memo, key)
prev_memo[prev_key] = {}
memo = prev_memo[prev_key]
end
@@ -45,7 +44,7 @@ class Chef
prev_key = key
memo[key]
end
- if !valid_container?(chain, last)
+ unless valid_container?(chain, last)
prev_memo[prev_key] = {}
chain = prev_memo[prev_key]
end
@@ -56,14 +55,15 @@ class Chef
# something that is not a container ("schema violation" issues).
#
def write!(*args, &block)
- root.top_level_breadcrumb = nil if respond_to?(:root)
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
@@ -71,9 +71,9 @@ class Chef
# return true or false based on if the attribute exists
def exist?(*path)
- root.top_level_breadcrumb = nil if respond_to?(:root)
path.inject(self) do |memo, key|
return false unless valid_container?(memo, key)
+
if memo.is_a?(Hash)
if memo.key?(key)
memo[key]
@@ -88,22 +88,22 @@ class Chef
end
end
end
- return true
+ true
end
# this is a safe non-autovivifying reader that returns nil if the attribute does not exist
def read(*path)
- begin
- read!(*path)
- rescue Chef::Exceptions::NoSuchAttribute
- nil
- end
+ read!(*path)
+ rescue Chef::Exceptions::NoSuchAttribute
+ nil
end
+ alias :dig :read
+
# non-autovivifying reader that throws an exception if the attribute does not exist
def read!(*path)
- raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
- root.top_level_breadcrumb = nil if respond_to?(:root)
+ raise Chef::Exceptions::NoSuchAttribute.new(path.join ".") unless exist?(*path)
+
path.inject(self) do |memo, key|
memo[key]
end
@@ -112,15 +112,15 @@ class Chef
# FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior? readers that write? ugh...
def unlink(*path, last)
- root.top_level_breadcrumb = nil if respond_to?(:root)
hash = path.empty? ? self : read(*path)
return nil unless hash.is_a?(Hash) || hash.is_a?(Array)
- root.top_level_breadcrumb ||= last
+
hash.delete(last)
end
def unlink!(*path)
raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+
unlink(*path)
end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index d4623ace2a..49dc0256b9 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -1,5 +1,5 @@
#--
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,22 +15,37 @@
# limitations under the License.
#
-require "chef/node/common_api"
+require_relative "common_api"
+require_relative "mixin/state_tracking"
+require_relative "mixin/immutablize_array"
+require_relative "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
@@ -49,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
@@ -110,21 +82,35 @@ 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
+
+ # As Psych module, not respecting ImmutableArray object
+ # So first convert it to Hash/Array then parse it to `.to_yaml`
+ def to_yaml(*opts)
+ to_a.to_yaml(*opts)
+ end
+
+ private
+
+ # needed for __path__
+ def convert_key(key)
+ key
end
+ prepend Chef::Node::Mixin::StateTracking
+ prepend Chef::Node::Mixin::ImmutablizeArray
end
# == ImmutableMash
@@ -134,84 +120,28 @@ class Chef
# ImmutableMash acts like a Mash (Hash that is indifferent to String or
# Symbol keys), with some important exceptions:
# * Methods that mutate state are overridden to raise an error instead.
- # * Methods that read from the collection are overriden so that they check
+ # * Methods that read from the collection are overridden so that they check
# if the Chef::Node::Attribute has been modified since an instance of
# this class was generated. An error is raised if the object detects that
# 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,
- :write,
- :write!,
- :unlink,
- :unlink!,
- ]
-
- 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))
- 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
+ # 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
- def method_missing(symbol, *args)
- if symbol == :to_ary
- super
- elsif 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.,
@@ -219,26 +149,46 @@ 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
- h = Hash.new
+ def to_h
+ h = {}
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
+
+ # As Psych module, not respecting ImmutableMash object
+ # So first convert it to Hash/Array then parse it to `.to_yaml`
+ def to_yaml(*opts)
+ to_h.to_yaml(*opts)
+ end
+ # 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..e88e079895
--- /dev/null
+++ b/lib/chef/node/mixin/deep_merge_cache.rb
@@ -0,0 +1,61 @@
+#--
+# Copyright:: Copyright (c) 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.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..652061bc36
--- /dev/null
+++ b/lib/chef/node/mixin/immutablize_array.rb
@@ -0,0 +1,184 @@
+#--
+# Copyright:: Copyright (c) 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 = %i{
+ &
+ *
+ +
+ -
+ []
+ abbrev
+ all?
+ any?
+ assoc
+ at
+ bsearch
+ bsearch_index
+ chain
+ chunk
+ chunk_while
+ collect
+ collect_concat
+ combination
+ compact
+ count
+ cycle
+ deconstruct
+ detect
+ difference
+ dig
+ drop
+ drop_while
+ each
+ each_cons
+ each_entry
+ each_index
+ each_slice
+ each_with_index
+ each_with_object
+ empty?
+ entries
+ fetch
+ filter
+ filter_map
+ find
+ find_all
+ find_index
+ first
+ flat_map
+ flatten
+ grep
+ grep_v
+ group_by
+ include?
+ index
+ inject
+ intersection
+ 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
+ tally
+ to_a
+ to_ary
+ to_csv
+ to_h
+ to_plist
+ to_set
+ to_yaml
+ transpose
+ union
+ uniq
+ values_at
+ zip
+ |
+ }.freeze
+ # 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 = %i{
+ <<
+ []=
+ append
+ clear
+ collect!
+ compact!
+ concat
+ default=
+ default_proc=
+ delete
+ delete_at
+ delete_if
+ fill
+ filter!
+ flatten!
+ insert
+ keep_if
+ map!
+ merge!
+ pop
+ prepend
+ push
+ reject!
+ replace
+ reverse!
+ rotate!
+ select!
+ shift
+ shuffle!
+ slice!
+ sort!
+ sort_by!
+ uniq!
+ unshift
+ }.freeze
+
+ # 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..f6d885ca2b
--- /dev/null
+++ b/lib/chef/node/mixin/immutablize_hash.rb
@@ -0,0 +1,171 @@
+#--
+# Copyright:: Copyright (c) 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 = %i{
+ <
+ <=
+ >
+ >=
+ []
+ all?
+ any?
+ assoc
+ chain
+ chunk
+ chunk_while
+ collect
+ collect_concat
+ compact
+ compare_by_identity
+ compare_by_identity?
+ count
+ cycle
+ deconstruct_keys
+ 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
+ except
+ fetch
+ fetch_values
+ filter
+ filter_map
+ 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
+ tally
+ to_a
+ to_h
+ to_hash
+ to_plist
+ to_proc
+ to_set
+ to_xml_attributes
+ to_yaml
+ transform_keys
+ transform_values
+ uniq
+ value?
+ values
+ values_at
+ zip
+ }.freeze
+ DISALLOWED_MUTATOR_METHODS = %i{
+ []=
+ clear
+ collect!
+ compact!
+ default=
+ default_proc=
+ delete
+ delete_if
+ except!
+ filter!
+ keep_if
+ map!
+ merge!
+ rehash
+ reject!
+ replace
+ select!
+ shift
+ store
+ transform_keys!
+ transform_values!
+ unlink!
+ unlink
+ update
+ write!
+ write
+ }.freeze
+
+ # 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/mashy_array.rb b/lib/chef/node/mixin/mashy_array.rb
new file mode 100644
index 0000000000..55aedf4779
--- /dev/null
+++ b/lib/chef/node/mixin/mashy_array.rb
@@ -0,0 +1,68 @@
+#--
+# Copyright:: Copyright (c) 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
+ # missing methods for Arrays similar to Chef::Mash methods that call
+ # convert_value correctly.
+ module MashyArray
+ def <<(obj)
+ super(convert_value(obj))
+ end
+
+ def []=(*keys, value)
+ super(*keys, convert_value(value))
+ end
+
+ def push(*objs)
+ objs = objs.map { |obj| convert_value(obj) }
+ super(*objs)
+ end
+
+ def unshift(*objs)
+ objs = objs.map { |obj| convert_value(obj) }
+ super(*objs)
+ end
+
+ def insert(index, *objs)
+ objs = objs.map { |obj| convert_value(obj) }
+ super(index, *objs)
+ end
+
+ def collect!(&block)
+ super
+ map! { |x| convert_value(x) }
+ end
+
+ def map!(&block)
+ super
+ super { |x| convert_value(x) }
+ end
+
+ def fill(*args, &block)
+ super
+ map! { |x| convert_value(x) }
+ end
+
+ def replace(obj)
+ super(convert_value(obj))
+ 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..4d197e7cbd
--- /dev/null
+++ b/lib/chef/node/mixin/state_tracking.rb
@@ -0,0 +1,96 @@
+#--
+# Copyright:: Copyright (c) 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..0b85dbe9df 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,34 @@
# 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
+#
+
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
class Chef
class NodeMap
+ COLLISION_WARNING = <<~EOH.gsub(/\s+/, " ").strip
+ %{type_caps} %{key} built into %{client_name} is being overridden by the %{type} from a cookbook. Please upgrade your cookbook
+ or remove the cookbook from your run_list.
+ EOH
#
# Set a key/value pair on the map with a filter. The filter must be true
@@ -26,24 +52,42 @@ class Chef
# @param key [Object] Key to store
# @param value [Object] Value associated with the key
# @param filters [Hash] Node filter options to apply to key retrieval
+ # @param chef_version [String] version constraint to match against the running Chef::VERSION
#
# @yield [node] Arbitrary node filter as a block which takes a node argument
#
# @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, override: nil, chef_version: nil, target_mode: 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
+ new_matcher[:target_mode] = target_mode
+
+ if chef_version && Chef::VERSION !~ chef_version
+ return map
+ end
+
+ # Check if the key is already present and locked, unless the override is allowed.
+ # The checks to see if we should reject, in order:
+ # 1. Core override mode is not set.
+ # 2. The key exists.
+ # 3. At least one previous `provides` is now locked.
+ if map[key] && map[key].any? { |matcher| matcher[:locked] } && !map[key].any? { |matcher| matcher[:cookbook_override].is_a?(String) ? Chef::VERSION =~ matcher[:cookbook_override] : matcher[:cookbook_override] }
+ # If we ever use locked mode on things other than the resource and provider handler maps, this probably needs a tweak.
+ type_of_thing = if klass < Chef::Resource
+ "resource"
+ elsif klass < Chef::Provider
+ "provider"
+ else
+ klass.superclass.to_s
+ end
+ Chef::Log.warn( COLLISION_WARNING % { type: type_of_thing, key: key, type_caps: type_of_thing.capitalize, client_name: ChefUtils::Dist::Infra::PRODUCT } )
+ end
# The map is sorted in order of preference already; we just need to find
# our place in it (just before the first value with the same preference level).
@@ -51,7 +95,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)
@@ -68,14 +115,16 @@ class Chef
# @param node [Chef::Node] The Chef::Node object for the run, or `nil` to
# ignore all filters.
# @param key [Object] Key to look up
- # @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
+ def get(node, key)
+ return nil unless map.key?(key)
+
+ map[key].map do |matcher|
+ return matcher[:klass] if node_matches?(node, matcher)
+ end
+ nil
end
#
@@ -85,134 +134,194 @@ class Chef
# @param node [Chef::Node] The Chef::Node object for the run, or `nil` to
# ignore all filters.
# @param key [Object] Key to look up
- # @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)
+ def list(node, key)
+ return [] unless map.key?(key)
+
map[key].select do |matcher|
- node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
- end.map { |matcher| matcher[:value] }
- end
-
- # Seriously, don't use this, it's nearly certain to change on you
- # @return remaining
- # @api private
- def delete_canonical(key, value)
- remaining = map[key]
- if remaining
- remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:value]) == Array(value) }
- if remaining.empty?
- map.delete(key)
- remaining = nil
+ node_matches?(node, matcher)
+ 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
+
+ # Check if this map has been locked.
+ #
+ # @api internal
+ # @since 14.2
+ # @return [Boolean]
+ def locked?
+ if defined?(@locked)
+ @locked
+ else
+ false
+ end
+ end
+
+ # Set this map to locked mode. This is used to prevent future overwriting
+ # of existing names.
+ #
+ # @api internal
+ # @since 14.2
+ # @return [void]
+ def lock!
+ map.each do |key, matchers|
+ matchers.each do |matcher|
+ matcher[:locked] = true
end
end
- remaining
+ @locked = true
end
- protected
+ private
+
+ def platform_family_query_helper?(node, m)
+ method = "#{m}?".to_sym
+ ChefUtils::DSL::PlatformFamily.respond_to?(method) && ChefUtils::DSL::PlatformFamily.send(method, node)
+ end
#
# Succeeds if:
# - no negative matches (!value)
# - at least one positive match (value or :all), or no positive filters
#
- def matches_black_white_list?(node, filters, attribute)
+ def matches_block_allow_list?(node, filters, attribute)
# It's super common for the filter to be nil. Catch that so we don't
# spend any time here.
- return true if !filters[attribute]
+ return true unless filters[attribute]
+
filter_values = Array(filters[attribute])
value = node[attribute]
- # Split the blacklist and whitelist
- blacklist, whitelist = filter_values.partition { |v| v.is_a?(String) && v.start_with?("!") }
+ # Split the blocklist and allowlist
+ blocklist, allowlist = filter_values.partition { |v| v.is_a?(String) && v.start_with?("!") }
- # If any blacklist value matches, we don't match
- return false if blacklist.any? { |v| v[1..-1] == value }
+ if attribute == :platform_family
+ # If any blocklist value matches, we don't match
+ return false if blocklist.any? { |v| v[1..] == value || platform_family_query_helper?(node, v[1..]) }
- # If the whitelist is empty, or anything matches, we match.
- whitelist.empty? || whitelist.any? { |v| v == :all || v == value }
+ # If the allowlist is empty, or anything matches, we match.
+ allowlist.empty? || allowlist.any? { |v| v == :all || v == value || platform_family_query_helper?(node, v) }
+ else
+ # If any blocklist value matches, we don't match
+ return false if blocklist.any? { |v| v[1..] == value }
+
+ # If the allowlist is empty, or anything matches, we match.
+ allowlist.empty? || allowlist.any? { |v| v == :all || v == value }
+ end
end
def matches_version_list?(node, filters, attribute)
# It's super common for the filter to be nil. Catch that so we don't
# spend any time here.
- return true if !filters[attribute]
+ return true unless filters[attribute]
+
filter_values = Array(filters[attribute])
value = node[attribute]
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
+ # Succeeds if:
+ # - we are in target mode, and the target_mode filter is true
+ # - we are not in target mode
+ #
+ def matches_target_mode?(filters)
+ return true unless Chef::Config.target_mode?
+
+ !!filters[:target_mode]
+ end
+
def filters_match?(node, filters)
- matches_black_white_list?(node, filters, :os) &&
- matches_black_white_list?(node, filters, :platform_family) &&
- matches_black_white_list?(node, filters, :platform) &&
- matches_version_list?(node, filters, :platform_version)
+ matches_block_allow_list?(node, filters, :os) &&
+ matches_block_allow_list?(node, filters, :platform_family) &&
+ matches_block_allow_list?(node, filters, :platform) &&
+ matches_version_list?(node, filters, :platform_version) &&
+ matches_target_mode?(filters)
end
def block_matches?(node, block)
return true if block.nil?
+
block.call node
end
def node_matches?(node, matcher)
- return true if !node
- filters_match?(node, matcher[:filters]) && block_matches?(node, matcher[:block])
- end
+ return true unless node
- def canonical_matches?(canonical, matcher)
- return true if canonical.nil?
- !!canonical == !!matcher[:canonical]
+ filters_match?(node, matcher) && block_matches?(node, matcher[:block])
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
- # whitelists.
+ # Check for blocklists ('!windows'). Those always come *after* positive
+ # allowlists.
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..8b00b3c03c 100644
--- a/lib/chef/null_logger.rb
+++ b/lib/chef/null_logger.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,26 +27,21 @@ class Chef
# probably expected a real logger and not this "fake" one.
class NullLogger
- def fatal(message, &block)
- end
+ def fatal(message, &block); end
- def error(message, &block)
- end
+ def error(message, &block); end
- def warn(message, &block)
- end
+ def warn(message, &block); end
- def info(message, &block)
- end
+ def info(message, &block); end
- def debug(message, &block)
- end
+ def debug(message, &block); end
- def add(severity, message = nil, progname = nil)
- end
+ def trace(message, &block); end
- def <<(message)
- end
+ def add(severity, message = nil, progname = nil); end
+
+ def <<(message); end
def fatal?
false
@@ -68,5 +63,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..e2b7c49051 100644
--- a/lib/chef/org.rb
+++ b/lib/chef/org.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/json_compat"
-require "chef/mixin/params_validate"
-require "chef/server_api"
+require_relative "json_compat"
+require_relative "mixin/params_validate"
+require_relative "server_api"
class Chef
class Org
@@ -40,25 +40,25 @@ class Chef
def name(arg = nil)
set_or_return(:name, arg,
- :regex => /^[a-z0-9\-_]+$/)
+ regex: /^[a-z0-9\-_]+$/)
end
def full_name(arg = nil)
set_or_return(:full_name,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def private_key(arg = nil)
set_or_return(:private_key,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def guid(arg = nil)
set_or_return(:guid,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
- def to_hash
+ def to_h
result = {
"name" => @name,
"full_name" => @full_name,
@@ -68,20 +68,22 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
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_h.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_h.merge(new_org))
end
def destroy
@@ -89,22 +91,20 @@ class Chef
end
def save
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update
- else
- raise e
- end
+ create
+ rescue Net::HTTPClientException => e
+ if e.response.code == "409"
+ update
+ else
+ raise e
end
end
def associate_user(username)
- request_body = { :user => username }
+ request_body = { user: username }
response = chef_rest.post "organizations/#{@name}/association_requests", request_body
association_id = response["uri"].split("/").last
- chef_rest.put "users/#{username}/association_requests/#{association_id}", { :response => "accept" }
+ chef_rest.put "users/#{username}/association_requests/#{association_id}", { response: "accept" }
end
def dissociate_user(username)
@@ -124,11 +124,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.rb b/lib/chef/platform.rb
index 165715267b..db66da221d 100644
--- a/lib/chef/platform.rb
+++ b/lib/chef/platform.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
#
# Order of these headers is important: query helpers is needed by many things
-require "chef/platform/query_helpers"
-require "chef/platform/provider_mapping"
+require_relative "platform/query_helpers"
+require_relative "platform/provider_mapping"
class Chef
class Platform
diff --git a/lib/chef/platform/handler_map.rb b/lib/chef/platform/handler_map.rb
deleted file mode 100644
index da8f84237f..0000000000
--- a/lib/chef/platform/handler_map.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@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/node_map"
-
-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
- end
- end
- end
-end
diff --git a/lib/chef/platform/priority_map.rb b/lib/chef/platform/priority_map.rb
index c36d00ea5c..45fd1ccd99 100644
--- a/lib/chef/platform/priority_map.rb
+++ b/lib/chef/platform/priority_map.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/node_map"
+require_relative "../node_map"
class Chef
class Platform
class PriorityMap < Chef::NodeMap
- def priority(resource_name, priority_array, *filter)
- set_priority_array(resource_name.to_sym, priority_array, *filter)
+ def priority(resource_name, priority_array, **filter)
+ set_priority_array(resource_name.to_sym, priority_array, **filter)
end
# @api private
@@ -31,9 +31,9 @@ class Chef
end
# @api private
- def set_priority_array(key, priority_array, *filter, &block)
+ def set_priority_array(key, priority_array, **filter, &block)
priority_array = Array(priority_array)
- set(key, priority_array, *filter, &block)
+ set(key, priority_array, **filter, &block)
priority_array
end
end
diff --git a/lib/chef/platform/provider_handler_map.rb b/lib/chef/platform/provider_handler_map.rb
index 26acf150df..12881acddb 100644
--- a/lib/chef/platform/provider_handler_map.rb
+++ b/lib/chef/platform/provider_handler_map.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "singleton"
-require "chef/platform/handler_map"
+require "singleton" unless defined?(Singleton)
+require_relative "../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 bc565d92ef..a9fba1239b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,54 +16,16 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/exceptions"
-require "chef/mixin/params_validate"
-require "chef/version_constraint/platform"
-require "chef/provider"
+require_relative "../log"
+require_relative "../exceptions"
+require_relative "../mixin/params_validate"
+require_relative "../version_constraint/platform"
+require_relative "../provider"
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,132 +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 = if resource_type.class.name
- resource_type.class.name.split("::").last
- else
- convert_to_class_name(resource_type.resource_name.to_s)
- end
-
- 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/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb
index 0c8a728618..4507c77833 100644
--- a/lib/chef/platform/provider_priority_map.rb
+++ b/lib/chef/platform/provider_priority_map.rb
@@ -1,5 +1,5 @@
-require "singleton"
-require "chef/platform/priority_map"
+require "singleton" unless defined?(Singleton)
+require_relative "priority_map"
class Chef
class Platform
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index 7d522072a3..bd0703d72a 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,67 +16,31 @@
# limitations under the License.
#
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+
class Chef
class Platform
class << self
def windows?
- 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
+ ChefUtils.windows?
end
+ # @deprecated Windows Nano is not a thing anymore so this check shouldn't be used
def windows_nano_server?
- return false unless windows?
- require "win32/registry"
-
- # This method may be called before ohai runs (e.g., it may be used to
- # determine settings in config.rb). Chef::Win32::Registry.new uses
- # node attributes to verify the machine architecture which aren't
- # accessible before ohai runs.
- nano = nil
- key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"
- access = ::Win32::Registry::KEY_QUERY_VALUE | 0x0100 # nano is 64-bit only
- begin
- ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
- nano = reg["NanoServer"]
- end
- rescue ::Win32::Registry::Error
- # If accessing the registry key failed, then we're probably not on
- # nano. Fail through.
- end
- return nano == 1
+ false
end
+ # @deprecated we added this method due to Windows Server Nano, which is no longer a platform
def supports_msi?
return false unless windows?
- require "win32/registry"
-
- key = "System\\CurrentControlSet\\Services\\msiserver"
- access = ::Win32::Registry::KEY_QUERY_VALUE
- begin
- ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
- true
- end
- rescue ::Win32::Registry::Error
- false
- end
+ true
end
+ # @deprecated we don't support any release of Windows that isn't PS 3+
def supports_powershell_execution_bypass?(node)
- node[:languages] && node[:languages][:powershell] &&
- node[:languages][:powershell][:version].to_i >= 3
+ true
end
def supports_dsc?(node)
@@ -94,15 +58,16 @@ class Chef
end
def dsc_refresh_mode_disabled?(node)
- require "chef/util/powershell/cmdlet"
- cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
- metadata = cmdlet.run!.return_value
- metadata["RefreshMode"] == "Disabled"
+ require_relative "../powershell"
+ exec = Chef::PowerShell.new("Get-DscLocalConfigurationManager")
+ exec.error!
+ exec.result["RefreshMode"] == "Disabled"
end
def supported_powershell_version?(node, version_string)
return false unless node[:languages] && node[:languages][:powershell]
- require "rubygems"
+
+ require "rubygems" unless defined?(Gem)
Gem::Version.new(node[:languages][:powershell][:version]) >=
Gem::Version.new(version_string)
end
diff --git a/lib/chef/platform/rebooter.rb b/lib/chef/platform/rebooter.rb
index 74c8b2da1f..c3378e6313 100644
--- a/lib/chef/platform/rebooter.rb
+++ b/lib/chef/platform/rebooter.rb
@@ -16,10 +16,11 @@
# limitations under the License.
#
-require "chef/dsl/reboot_pending"
-require "chef/log"
-require "chef/platform"
-require "chef/application/exit_code"
+require_relative "../dsl/reboot_pending"
+require_relative "../log"
+require_relative "../platform"
+require_relative "../application/exit_code"
+require_relative "../mixin/shell_out"
class Chef
class Platform
@@ -33,12 +34,18 @@ class Chef
def reboot!(node)
reboot_info = node.run_context.reboot_info
- cmd = if Chef::Platform.windows?
+ cmd = case
+ when ChefUtils.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
msg = "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}"
@@ -49,8 +56,7 @@ class Chef
raise Chef::Exceptions::RebootFailed.new(e.message)
end
- raise Chef::Exceptions::Reboot.new(msg) if Chef::Application::ExitCode.enforce_rfc_062_exit_codes?
- Chef::Application::ExitCode.notify_reboot_exit_code_deprecation
+ 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..0d54e05069 100644
--- a/lib/chef/platform/resource_handler_map.rb
+++ b/lib/chef/platform/resource_handler_map.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "singleton"
-require "chef/platform/handler_map"
+require "singleton" unless defined?(Singleton)
+require_relative "../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/resource_priority_map.rb b/lib/chef/platform/resource_priority_map.rb
index 1871dcd5c6..8995a91f45 100644
--- a/lib/chef/platform/resource_priority_map.rb
+++ b/lib/chef/platform/resource_priority_map.rb
@@ -1,5 +1,5 @@
-require "singleton"
-require "chef/platform/priority_map"
+require "singleton" unless defined?(Singleton)
+require_relative "priority_map"
class Chef
class Platform
diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb
index 87b87d4c72..8f492260b0 100644
--- a/lib/chef/platform/service_helpers.rb
+++ b/lib/chef/platform/service_helpers.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,107 +16,42 @@
# limitations under the License.
#
-require "chef/chef_class"
+require_relative "../chef_class"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
+require_relative "../mixin/chef_utils_wiring" unless defined?(Chef::Mixin::ChefUtilsWiring)
class Chef
class Platform
- class ServiceHelpers
- class << self
- # This helper is mostly used to sort out the mess of different
- # linux mechanisms that can be used to start services. It does
- # not necessarily need to linux-specific, but currently all our
- # other service providers are narrowly platform-specific with no
- # alternatives.
- #
- # NOTE: if a system has (for example) chkconfig installed then we
- # should report that chkconfig is installed. The fact that a system
- # may also have systemd installed does not mean that we do not
- # report that systemd is also installed. This module is purely for
- # discovery of all the alternatives, handling the priority of the
- # different services is NOT a design concern of this module.
- #
- def service_resource_providers
- providers = []
+ module ServiceHelpers
+ include ChefUtils::DSL::Service
+ include Chef::Mixin::ChefUtilsWiring
- if ::File.exist?(Chef.path_to("/usr/sbin/update-rc.d"))
- providers << :debian
- end
+ def service_resource_providers
+ providers = []
- if ::File.exist?(Chef.path_to("/usr/sbin/invoke-rc.d"))
- providers << :invokercd
- end
+ providers << :debian if debianrcd?
+ providers << :invokercd if invokercd?
+ providers << :upstart if upstart?
+ providers << :insserv if insserv?
+ providers << :systemd if systemd?
+ providers << :redhat if redhatrcd?
- if ::File.exist?(Chef.path_to("/sbin/initctl"))
- providers << :upstart
- end
-
- if ::File.exist?(Chef.path_to("/sbin/insserv"))
- providers << :insserv
- end
-
- if systemd_is_init?
- providers << :systemd
- end
-
- if ::File.exist?(Chef.path_to("/sbin/chkconfig"))
- providers << :redhat
- end
-
- providers
- end
-
- def config_for_service(service_name)
- configs = []
-
- if ::File.exist?(Chef.path_to("/etc/init.d/#{service_name}"))
- configs << :initd
- end
-
- if ::File.exist?(Chef.path_to("/etc/init/#{service_name}.conf"))
- configs << :upstart
- end
-
- if ::File.exist?(Chef.path_to("/etc/xinetd.d/#{service_name}"))
- configs << :xinetd
- end
-
- if ::File.exist?(Chef.path_to("/etc/rc.d/#{service_name}"))
- configs << :etc_rcd
- end
-
- if ::File.exist?(Chef.path_to("/usr/local/etc/rc.d/#{service_name}"))
- configs << :usr_local_etc_rcd
- end
-
- if has_systemd_service_unit?(service_name) || has_systemd_unit?(service_name)
- configs << :systemd
- end
-
- configs
- end
-
- private
+ providers
+ end
- def systemd_is_init?
- ::File.exist?(Chef.path_to("/proc/1/comm")) &&
- ::File.open(Chef.path_to("/proc/1/comm")).gets.chomp == "systemd"
- end
+ def config_for_service(service_name)
+ configs = []
- def has_systemd_service_unit?(svc_name)
- %w{ /etc /usr/lib /lib /run }.any? do |load_path|
- ::File.exist?(
- Chef.path_to("#{load_path}/systemd/system/#{svc_name.gsub(/@.*$/, '@')}.service")
- )
- end
- end
+ configs << :initd if service_script_exist?(:initd, service_name)
+ configs << :upstart if service_script_exist?(:upstart, service_name)
+ configs << :xinetd if service_script_exist?(:xinetd, service_name)
+ configs << :systemd if service_script_exist?(:systemd, service_name)
+ configs << :etc_rcd if service_script_exist?(:etc_rcd, service_name)
- def has_systemd_unit?(svc_name)
- # TODO: stop supporting non-service units with service resource
- %w{ /etc /usr/lib /lib /run }.any? do |load_path|
- ::File.exist?(Chef.path_to("#{load_path}/systemd/system/#{svc_name}"))
- end
- end
+ configs
end
+
+ extend self
end
end
end
diff --git a/lib/chef/policy_builder.rb b/lib/chef/policy_builder.rb
index 56533e9a60..0440f6a8d9 100644
--- a/lib/chef/policy_builder.rb
+++ b/lib/chef/policy_builder.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/policy_builder/expand_node_object"
-require "chef/policy_builder/policyfile"
-require "chef/policy_builder/dynamic"
+require_relative "policy_builder/expand_node_object"
+require_relative "policy_builder/policyfile"
+require_relative "policy_builder/dynamic"
class Chef
diff --git a/lib/chef/policy_builder/dynamic.rb b/lib/chef/policy_builder/dynamic.rb
index 5b6bc40f9e..3d9d4c0b7d 100644
--- a/lib/chef/policy_builder/dynamic.rb
+++ b/lib/chef/policy_builder/dynamic.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,15 @@
# limitations under the License.
#
-require "forwardable"
+require "forwardable" unless defined?(Forwardable)
-require "chef/log"
-require "chef/run_context"
-require "chef/config"
-require "chef/node"
-require "chef/exceptions"
+require_relative "../log"
+require_relative "../run_context"
+require_relative "../config"
+require_relative "../node"
+require_relative "../exceptions"
+require_relative "expand_node_object"
+require_relative "policyfile"
class Chef
module PolicyBuilder
@@ -63,7 +65,7 @@ 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_legacy_mode]
@@ -74,6 +76,7 @@ class Chef
select_implementation(node)
implementation.finish_load_node(node)
node
+ events.node_load_success(node)
rescue Exception => e
events.node_load_failed(node_name, e, config)
raise
@@ -173,7 +176,7 @@ class Chef
end
def policyfile_set_in_config?
- config[:use_policyfile] || config[:policy_name] || config[:policy_group]
+ config[:policy_name] || config[:policy_group]
end
def policyfile_compat_mode_config?
diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index df6956cc77..965e4defe6 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,12 +19,12 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/server_api"
-require "chef/run_context"
-require "chef/config"
-require "chef/node"
-require "chef/chef_class"
+require_relative "../log"
+require_relative "../server_api"
+require_relative "../run_context"
+require_relative "../config"
+require_relative "../node"
+require_relative "../chef_class"
class Chef
module PolicyBuilder
@@ -68,66 +68,48 @@ class Chef
Chef.set_run_context(run_context)
end
- def setup_run_context(specific_recipes = nil)
- 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
- cookbook_collection = Chef::CookbookCollection.new(cl)
- cookbook_collection.validate!
- cookbook_collection.install_gems(events)
+ # This not only creates the run_context but this is where we kick off
+ # compiling the entire expanded run_list, loading all the libraries, resources,
+ # attribute files and recipes, and constructing the entire resource collection.
+ # (FIXME: break up creating the run_context and compiling the cookbooks)
+ #
+ def setup_run_context(specific_recipes = nil, run_context = nil)
+ run_context ||= Chef::RunContext.new
+ run_context.events = events
+ run_context.node = node
- run_context = Chef::RunContext.new(node, cookbook_collection, @events)
- else
- Chef::Cookbook::FileVendor.fetch_from_remote(api_service)
- cookbook_hash = sync_cookbooks
- cookbook_collection = Chef::CookbookCollection.new(cookbook_hash)
- cookbook_collection.validate!
- cookbook_collection.install_gems(events)
+ cookbook_collection =
+ 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
+ Chef::CookbookCollection.new(cl)
+ else
+ Chef::Cookbook::FileVendor.fetch_from_remote(api_service)
+ cookbook_hash = sync_cookbooks
+ Chef::CookbookCollection.new(cookbook_hash)
+ end
- run_context = Chef::RunContext.new(node, cookbook_collection, @events)
- end
+ cookbook_collection.validate!
+ cookbook_collection.install_gems(events)
+
+ run_context.cookbook_collection = cookbook_collection
- # TODO: this is really obviously not the place for this
- # FIXME: need same edits
+ # TODO: move this into the cookbook_compilation_start hook
setup_chef_class(run_context)
- # TODO: this is not the place for this. It should be in Runner or
- # CookbookCompiler or something.
+ events.cookbook_compilation_start(run_context)
+
run_context.load(@run_list_expansion)
if specific_recipes
specific_recipes.each do |recipe_file|
run_context.load_recipe_file(recipe_file)
end
end
- 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_legacy_mode]
- 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
+ events.cookbook_compilation_complete(run_context)
+
+ run_context
end
def finish_load_node(node)
@@ -157,7 +139,7 @@ class Chef
expand_run_list
Chef::Log.info("Run List is [#{node.run_list}]")
- Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]")
+ Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(", ")}]")
events.node_load_completed(node, @expanded_run_list_with_versions, Chef::Config)
events.run_list_expanded(@run_list_expansion)
@@ -197,12 +179,12 @@ 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)
cookbook_hash = api_service.post("environments/#{node.chef_environment}/cookbook_versions",
- { :run_list => @expanded_run_list_with_versions })
+ { run_list: @expanded_run_list_with_versions })
cookbook_hash = cookbook_hash.inject({}) do |memo, (key, value)|
memo[key] = Chef::CookbookVersion.from_hash(value)
@@ -232,7 +214,7 @@ class Chef
# override_runlist was provided. Chef::Client uses this to decide whether
# to do the final node save at the end of the run or not.
def temporary_policy?
- !node.override_runlist.empty?
+ node.override_runlist_set?
end
########################################
@@ -240,9 +222,9 @@ class Chef
########################################
def setup_run_list_override
- runlist_override_sanity_check!
- unless override_runlist.empty?
- node.override_runlist(*override_runlist)
+ unless override_runlist.nil?
+ runlist_override_sanity_check!
+ node.override_runlist = override_runlist
Chef::Log.warn "Run List override has been provided."
Chef::Log.warn "Original Run List: [#{node.primary_runlist}]"
Chef::Log.warn "Overridden Run List: [#{node.run_list}]"
@@ -253,7 +235,7 @@ class Chef
def runlist_override_sanity_check!
# Convert to array and remove whitespace
if override_runlist.is_a?(String)
- @override_runlist = override_runlist.split(",").map { |e| e.strip }
+ @override_runlist = override_runlist.split(",").map(&:strip)
end
@override_runlist = [override_runlist].flatten.compact
override_runlist.map! do |item|
@@ -266,7 +248,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 8d2437fef5..35282bf915 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,24 +19,19 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/run_context"
-require "chef/config"
-require "chef/node"
-require "chef/server_api"
+require_relative "../log"
+require_relative "../run_context"
+require_relative "../config"
+require_relative "../node"
+require_relative "../server_api"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
module PolicyBuilder
- # Policyfile is an experimental policy builder implementation that gets run
+ # Policyfile is a policy builder implementation that gets run
# list and cookbook version information from a single document.
#
- # == WARNING
- # This implementation is experimental. It may be changed in incompatible
- # ways in minor or even patch releases, or even abandoned altogether. If
- # using this with other tools, you may be forced to upgrade those tools in
- # lockstep with chef-client because of incompatible behavior changes.
- #
# == Unsupported Options:
# * override_runlist:: This could potentially be integrated into the
# policyfile, or replaced with a similar feature that has different
@@ -51,7 +46,34 @@ 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_h
+ # 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
+
+ alias_method :to_hash, :to_h
+
+ def to_json(*opts)
+ to_h.to_json(*opts)
+ end
+ end
attr_reader :events
attr_reader :node
@@ -69,7 +91,7 @@ class Chef
@node = nil
if Chef::Config[:solo_legacy_mode]
- raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead."
+ raise UnsupportedFeature, "Policyfile does not support chef-solo. Use #{ChefUtils::Dist::Infra::CLIENT} local mode instead."
end
if override_runlist
@@ -81,7 +103,7 @@ class Chef
end
if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty?
- raise UnsupportedFeature, "Policyfile does not work with Chef Environments."
+ raise UnsupportedFeature, "Policyfile does not work with an Environment configured."
end
end
@@ -134,9 +156,15 @@ class Chef
apply_policyfile_attributes
Chef::Log.info("Run List is [#{run_list}]")
- Chef::Log.info("Run List expands to [#{run_list_with_versions_for_display.join(', ')}]")
+ 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
@@ -148,19 +176,27 @@ class Chef
# run.
#
# @return [Chef::RunContext]
- def setup_run_context(specific_recipes = nil)
- Chef::Cookbook::FileVendor.fetch_from_remote(http_api)
+ def setup_run_context(specific_recipes = nil, run_context = nil)
+ run_context ||= Chef::RunContext.new
+ run_context.node = node
+ run_context.events = events
+
+ Chef::Cookbook::FileVendor.fetch_from_remote(api_service)
sync_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync)
cookbook_collection.validate!
cookbook_collection.install_gems(events)
- run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ run_context.cookbook_collection = cookbook_collection
setup_chef_class(run_context)
+ events.cookbook_compilation_start(run_context)
+
run_context.load(run_list_expansion_ish)
+ events.cookbook_compilation_complete(run_context)
+
setup_chef_class(run_context)
run_context
end
@@ -173,6 +209,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 +221,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 +273,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
@@ -258,7 +305,7 @@ class Chef
if named_run_list_requested?
named_run_list || raise(ConfigurationError,
"Policy '#{retrieved_policy_name}' revision '#{revision_id}' does not have named_run_list '#{named_run_list_name}'" +
- "(available named_run_lists: [#{available_named_run_lists.join(', ')}])")
+ "(available named_run_lists: [#{available_named_run_lists.join(", ")}])")
else
policy["run_list"]
end
@@ -266,8 +313,8 @@ class Chef
# @api private
def policy
- @policy ||= http_api.get(policyfile_location)
- rescue Net::HTTPServerException => e
+ @policy ||= api_service.get(policyfile_location)
+ rescue Net::HTTPClientException => e
raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}"
end
@@ -281,7 +328,7 @@ class Chef
end
end
- # Do some mimimal validation of the policyfile we fetched from the
+ # Do some minimal validation of the policyfile we fetched from the
# server. Compatibility mode relies on using data bags to store policy
# files; therefore no real validation will be performed server-side and
# we need to make additional checks to ensure the data will be formatted
@@ -294,7 +341,7 @@ class Chef
unless policy.key?("cookbook_locks")
errors << "Policyfile is missing cookbook_locks element"
end
- if run_list.kind_of?(Array)
+ if run_list.is_a?(Array)
run_list_errors = run_list.select do |maybe_recipe_spec|
validate_recipe_spec(maybe_recipe_spec)
end
@@ -363,6 +410,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
@@ -427,7 +475,7 @@ class Chef
end
# @api private
- # Fetches the CookbookVersion object for the given name and identifer
+ # Fetches the CookbookVersion object for the given name and identifier
# specified in the lock_data.
# TODO: This only implements Chef 11 compatibility mode, which means that
# cookbooks are fetched by the "dotted_decimal_identifier": a
@@ -451,8 +499,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 +544,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 +555,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..b49d3c58e4
--- /dev/null
+++ b/lib/chef/powershell.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright (c) 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 "ffi" unless defined?(FFI)
+require_relative "json_compat"
+
+class Chef
+ class PowerShell
+ extend FFI::Library
+
+ attr_reader :result
+ attr_reader :errors
+ attr_reader :verbose
+
+ # Run a command under PowerShell via FFI
+ # This implementation requires the managed dll and native wrapper to be in the library search
+ # path on Windows (i.e. c:\windows\system32 or in the same location as ruby.exe).
+ #
+ # Requires: .NET Framework 4.0 or higher on the target machine.
+ #
+ # @param script [String] script to run
+ # @return [Object] output
+ def initialize(script)
+ # This Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
+ # Every merge into that repo triggers a Habitat build and promotion. Running
+ # the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
+ # the built packages and copy the binaries to distro/ruby_bin_folder. Bundle install
+ # ensures that the correct architecture binaries are installed into the path.
+ @dll ||= "Chef.PowerShell.Wrapper.dll"
+ exec(script)
+ end
+
+ #
+ # Was there an error running the command
+ #
+ # @return [Boolean]
+ #
+ def error?
+ return true if errors.count > 0
+
+ false
+ end
+
+ class CommandFailed < RuntimeError; end
+
+ #
+ # @raise [Chef::PowerShell::CommandFailed] raise if the command failed
+ #
+ def error!
+ raise Chef::PowerShell::CommandFailed, "Unexpected exit in PowerShell command: #{@errors}" if error?
+ end
+
+ protected
+
+ def exec(script)
+ FFI.ffi_lib @dll
+ FFI.attach_function :execute_powershell, :ExecuteScript, [:string], :pointer
+ execution = FFI.execute_powershell(script).read_utf16string
+ hashed_outcome = Chef::JSONCompat.parse(execution)
+ @result = Chef::JSONCompat.parse(hashed_outcome["result"])
+ @errors = hashed_outcome["errors"]
+ @verbose = hashed_outcome["verbose"]
+ end
+ end
+end
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index a357ba9ee3..bbf06854d6 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -1,6 +1,7 @@
#
# Author:: John Keiser <jkeiser@chef.io>
-# Copyright:: Copyright 2015-2016, John Keiser.
+# Copyright:: Copyright 2015-2016, John Keiser
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +17,10 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/delayed_evaluator"
-require "chef/chef_class"
-require "chef/log"
+require_relative "exceptions"
+require_relative "delayed_evaluator"
+require_relative "chef_class"
+require_relative "log"
class Chef
#
@@ -51,6 +52,27 @@ class Chef
new(**options)
end
+ # This is to support #deprecated_property_alias, by emitting an alias and a
+ # deprecation 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 +82,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,12 +98,16 @@ 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
# properties) and cached. If not, the value will be frozen with Object#freeze
# to prevent users from modifying it in an instance.
+ # @option options [String] :default_description The description of the default value
+ # used in docs. Particularly useful when a default is computed or lazily eval'd.
+ # @option options [Boolean] :skip_docs This property should not be included in any
+ # documentation output
# @option options [Proc] :coerce A proc which will be called to
# transform the user input to canonical form. The value is passed in,
# and the transformed value returned as output. Lazy values will *not*
@@ -88,6 +116,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 }
@@ -96,27 +126,37 @@ class Chef
options[:instance_variable_name] = options[:instance_variable_name].to_sym if options[:instance_variable_name]
# Replace name_attribute with name_property
- if options.has_key?(:name_attribute)
+ if options.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}."
+ if options.key?(:name_property)
+ 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.key?(:default) && options.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
+
+ if options[:name_property]
+ options[:desired_state] = false unless options.key?(:desired_state)
+ 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 +193,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>`
@@ -160,7 +218,7 @@ class Chef
# @return [Symbol]
#
def instance_variable_name
- if options.has_key?(:instance_variable_name)
+ if options.key?(:instance_variable_name)
options[:instance_variable_name]
elsif name
:"@#{name}"
@@ -176,12 +234,31 @@ class Chef
# `nil`
#
def default
- return options[:default] if options.has_key?(:default)
+ return options[:default] if options.key?(:default)
return Chef::DelayedEvaluator.new { name } if name_property?
+
nil
end
#
+ # A description of the default value of this property.
+ #
+ # @return [String]
+ #
+ def default_description
+ options[:default_description]
+ end
+
+ #
+ # The equal_to field of this property.
+ #
+ # @return [Array, NilClass]
+ #
+ def equal_to
+ options[:equal_to]
+ end
+
+ #
# Whether this is part of the resource's natural identity or not.
#
# @return [Boolean]
@@ -198,7 +275,8 @@ class Chef
# @return [Boolean]
#
def desired_state?
- return true if !options.has_key?(:desired_state)
+ return true unless options.key?(:desired_state)
+
options[:desired_state]
end
@@ -217,7 +295,7 @@ class Chef
# @return [Boolean]
#
def has_default?
- options.has_key?(:default) || name_property?
+ options.key?(:default) || name_property?
end
#
@@ -225,8 +303,23 @@ class Chef
#
# @return [Boolean]
#
- def required?
- options[:required]
+ def required?(action = nil)
+ if !action.nil? && options[:required].is_a?(Array)
+ options[:required].include?(action)
+ else
+ !!options[:required]
+ end
+ end
+
+ #
+ # Whether this property should be skipped for documentation purposes.
+ #
+ # Defaults to false.
+ #
+ # @return [Boolean]
+ #
+ def skip_docs?
+ options.fetch(:skip_docs, false)
end
#
@@ -247,7 +340,7 @@ class Chef
#
def validation_options
@validation_options ||= options.reject do |k, v|
- [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable, :sensitive].include?(k)
+ %i{declared_in name instance_variable_name desired_state identity default name_property coerce required nillable sensitive description introduced deprecated default_description skip_docs}.include?(k)
end
end
@@ -272,33 +365,9 @@ class Chef
# `get`, the non-lazy, coerced, validated value will always be returned.
#
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
+ if NOT_PASSED == value # see https://github.com/chef/chef/pull/8781 before changing this
+ get(resource)
else
- # Anything else, such as myprop(value) is a set
set(resource, value)
end
end
@@ -327,38 +396,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)
@@ -367,7 +412,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
@@ -375,13 +420,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
#
@@ -400,7 +446,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.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
#
@@ -452,9 +508,9 @@ class Chef
# this property.
#
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?
+ if options.key?(:coerce)
+ # nil is never coerced
+ unless value.nil?
value = exec_in_resource(resource, options[:coerce], value)
end
end
@@ -468,15 +524,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
@@ -500,12 +556,12 @@ class Chef
# if you specify one of them in modified_options it overrides anything in
# the original options.
options = self.options
- if modified_options.has_key?(:name_property) ||
- modified_options.has_key?(:name_attribute) ||
- modified_options.has_key?(:default)
- options = options.reject { |k, v| k == :name_attribute || k == :name_property || k == :default }
+ if modified_options.key?(:name_property) ||
+ modified_options.key?(:name_attribute) ||
+ modified_options.key?(:default)
+ options = options.reject { |k, v| %i{name_attribute name_property default}.include?(k) }
end
- self.class.new(options.merge(modified_options))
+ self.class.new(**options.merge(modified_options))
end
#
@@ -516,30 +572,30 @@ class Chef
def emit_dsl
# We don't create the getter/setter if it's a custom property; we will
# be using the existing getter/setter to manipulate it instead.
- return if !instance_variable_name
+ return unless instance_variable_name
+
+ # Properties may override existing properties up the inheritance hierarchy, 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
#
@@ -588,8 +644,10 @@ 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.key?(:coerce) ||
+ (options.key?(:is) && Chef::Mixin::ParamsValidate.send(:_pv_is, { name => nil }, name, options[:is]))
+ rescue Chef::Exceptions::ValidationFailed, Chef::Exceptions::CannotValidateStaticallyError
+ false
end
# @api private
@@ -632,6 +690,25 @@ class Chef
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 properties, 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.key?(name)
+ end
+
def exec_in_resource(resource, proc, *args)
if resource
if proc.arity > args.size
@@ -646,51 +723,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 7cfddba0cb..81ed530fc7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,65 +17,123 @@
# limitations under the License.
#
-require "chef/mixin/from_file"
-require "chef/mixin/convert_to_class_name"
-require "chef/mixin/enforce_ownership_and_permissions"
-require "chef/mixin/why_run"
-require "chef/mixin/shell_out"
-require "chef/mixin/provides"
-require "chef/dsl/core"
-require "chef/platform/service_helpers"
-require "chef/node_map"
-require "forwardable"
+require_relative "mixin/from_file"
+require_relative "mixin/convert_to_class_name"
+require_relative "mixin/enforce_ownership_and_permissions"
+require_relative "mixin/why_run"
+require_relative "mixin/shell_out"
+require_relative "mixin/provides"
+require_relative "dsl/recipe"
+require_relative "platform/service_helpers"
+require_relative "node_map"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
- require "chef/mixin/why_run"
- require "chef/mixin/provides"
-
attr_accessor :new_resource
attr_accessor :current_resource
+ attr_accessor :after_resource
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
+ include Chef::DSL::Recipe
+ # the class only gets the Universal DSL (no resource_collection at class parsing time)
+ extend Chef::DSL::Universal
# supports the given resource and action (late binding)
def self.supports?(resource, action)
true
end
- #--
- # TODO: this should be a reader, and the action should be passed in the
+ # 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 `return` 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
+
+ # Use a partial code fragment. This can be used for code sharing between multiple resources.
+ #
+ # Do not wrap the code fragment in a class or module. It also does not support the use of super
+ # to subclass any methods defined in the fragment, the methods will just be overwritten.
+ #
+ # @param partial [String] the code fragment to eval against the class
+ #
+ def self.use(partial)
+ dirname = ::File.dirname(partial)
+ basename = ::File.basename(partial, ".rb")
+ basename = basename[1..] if basename.start_with?("_")
+ class_eval IO.read(::File.expand_path("#{dirname}/_#{basename}.rb", ::File.dirname(caller_locations.first.absolute_path)))
+ 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
# changing the arity would be a breaking change. Change this at the next
- # break, e.g., Chef 11.
+ # major release
attr_accessor :action
def initialize(new_resource, run_context)
@new_resource = new_resource
@action = action
@current_resource = nil
+ @after_resource = nil
@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)
end
+ # has why-run mode been enabled?
+ #
+ # @return [Boolean]
def whyrun_mode?
Chef::Config[:why_run]
end
+ # as of Chef 13 we enable why-run by default and require resources to override this to set false.
+ # We're keeping this method to prevent breaking the cookbook world for no real gain on our part.
+ #
+ # @return [Boolean]
def whyrun_supported?
- false
+ true
end
def node
@@ -91,21 +149,29 @@ class Chef
new_resource.cookbook_name
end
- def check_resource_semantics!
- end
+ # hook that subclasses can use to do lazy validation for where properties aren't flexible enough
+ def check_resource_semantics!; end
+ # a simple placeholder method that will be called / raise if a resource tries to
+ # use current_resource without defining a load_current_resource method.
def load_current_resource
raise Chef::Exceptions::Override, "You must override load_current_resource in #{self}"
end
- def define_resource_requirements
- end
+ def define_resource_requirements; end
- def cleanup_after_converge
+ def cleanup_after_converge; end
+
+ def load_after_resource
+ # This is a backwards compatible hack, custom resources properly wire up a new after_resource
+ # via load_current_value. It is acceptable for old style resources that cannot be easily made
+ # into custom resources to override this method and provide a proper after_resource.
+ @after_resource = @new_resource
end
+ # the :nothing action which is available on all resources by default
def action_nothing
- Chef::Log.debug("Doing nothing for #{@new_resource}")
+ logger.trace("Doing nothing for #{@new_resource}")
true
end
@@ -113,14 +179,20 @@ class Chef
run_context.events
end
+ def validate_required_properties!
+ # all we do is run through all the required properties for this action and vivify them
+ new_resource.class.properties.each { |name, property| property.required?(action) && property.get(new_resource) }
+ end
+
def run_action(action = nil)
@action = action unless action.nil?
- # TODO: it would be preferable to get the action to be executed in the
- # constructor...
-
+ # hook that subclasses can use to do lazy validation for where properties aren't flexible enough
check_resource_semantics!
+ # force the validation of required properties
+ validate_required_properties!
+
# user-defined LWRPs may include unsafe load_current_resource methods that cannot be run in whyrun mode
if whyrun_mode? && !whyrun_supported?
events.resource_current_state_load_bypassed(@new_resource, @action, @current_resource)
@@ -148,6 +220,9 @@ class Chef
set_updated_status
cleanup_after_converge
+
+ load_after_resource
+ events.resource_after_state_loaded(@new_resource, @action, @after_resource)
end
def process_resource_requirements
@@ -172,10 +247,42 @@ 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
+ @run_context.resource_collection.unified_mode = new_resource.class.unified_mode
+ runner = Chef::Runner.new(@run_context)
+ return_value = instance_eval(&block)
+ runner.converge
+ return_value
+ ensure
+ if run_context.resource_collection.any?(&:updated?)
+ new_resource.updated_by_last_action(true)
+ end
+ @run_context = old_run_context
+ end
+
#
# Handle patchy convergence safely.
#
@@ -194,30 +301,42 @@ class Chef
# @return [Boolean] whether the block was executed.
#
def converge_if_changed(*properties, &converge_block)
- if !converge_block
+ unless converge_block
raise ArgumentError, "converge_if_changed must be passed a block!"
end
- properties = new_resource.class.state_properties.map { |p| p.name } if properties.empty?
- properties = properties.map { |p| p.to_sym }
+ properties =
+ if properties.empty?
+ new_resource.class.state_properties
+ else
+ properties.map { |property| new_resource.class.properties[property] }
+ end
+
if current_resource
# Collect the list of modified properties
- specified_properties = properties.select { |property| new_resource.property_is_set?(property) }
+ specified_properties = properties.select { |property| property.is_set?(new_resource) || property.has_default? }
+ specified_properties = specified_properties.map(&:name).map(&:to_sym)
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
+ property_size = modified.map(&: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})"
@@ -229,15 +348,15 @@ class Chef
else
# The resource doesn't exist. Mark that we are *creating* this, and
# write down any properties we are setting.
- property_size = properties.map { |p| p.size }.max
+ property_size = properties.map(&:name).map(&:to_sym).map(&:size).max
created = properties.map do |property|
- default = " (default value)" unless new_resource.property_is_set?(property)
- properties_str = if sensitive
+ default = " (default value)" unless property.is_set?(new_resource)
+ properties_str = if new_resource.sensitive || property.sensitive?
"(suppressed sensitive property)"
else
- new_resource.send(property).inspect
+ new_resource.send(property.name.to_sym).inspect
end
- " set #{property.to_s.ljust(property_size)} to #{properties_str}#{default}"
+ " set #{property.name.to_sym.to_s.ljust(property_size)} to #{properties_str}#{default}"
end
converge_by([ "create #{new_resource.identity}" ] + created, &converge_block)
@@ -246,7 +365,7 @@ class Chef
end
def self.provides(short_name, opts = {}, &block)
- Chef.provider_handler_map.set(short_name, self, opts, &block)
+ Chef.provider_handler_map.set(short_name, self, **opts, &block)
end
def self.provides?(node, resource)
@@ -267,134 +386,41 @@ 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
+ # stores all actions that have been converged
+ #
+ # @return [ConvergeActions]
def converge_actions
@converge_actions ||= ConvergeActions.new(@new_resource, run_context, @action)
end
@@ -409,7 +435,7 @@ class Chef
# this block cannot interact with resources outside, e.g.,
# manipulating notifies.
- converge_by ("evaluate block and run any associated actions") do
+ converge_by("evaluate block and run any associated actions") do
saved_run_context = run_context
begin
@run_context = run_context.create_child
@@ -421,38 +447,10 @@ class Chef
end
end
- module DeprecatedLWRPClass
- def const_missing(class_name)
- if Chef::Provider.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.")
- Chef::Provider.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
- Chef::Provider.deprecated_constants[class_name.to_sym] = provider_class
- end
- end
-
- def deprecated_constants
- raise "Deprecated constants should be called only on Chef::Provider" unless self == Chef::Provider
- @deprecated_constants ||= {}
- end
- end
- extend DeprecatedLWRPClass
end
end
# Requiring things at the bottom breaks cycles
-require "chef/chef_class"
-require "chef/mixin/why_run"
-require "chef/resource_collection"
-require "chef/runner"
+require_relative "chef_class"
+require_relative "resource_collection"
+require_relative "runner"
diff --git a/lib/chef/provider/apt_repository.rb b/lib/chef/provider/apt_repository.rb
deleted file mode 100644
index 9e077c8cbb..0000000000
--- a/lib/chef/provider/apt_repository.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-#
-# 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 "chef/resource"
-require "chef/dsl/declare_resource"
-require "chef/mixin/shell_out"
-require "chef/mixin/which"
-require "chef/http/simple"
-require "chef/provider/noop"
-
-class Chef
- class Provider
- class AptRepository < Chef::Provider
- use_inline_resources
-
- include Chef::Mixin::ShellOut
- extend Chef::Mixin::Which
-
- provides :apt_repository do
- which("apt-get")
- end
-
- def whyrun_supported?
- true
- end
-
- 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
- end
- end
-
- declare_resource(:execute, "apt-cache gencaches") do
- ignore_failure true
- action :nothing
- end
-
- declare_resource(:apt_update, new_resource.name) do
- ignore_failure true
- action :nothing
- end
-
- components = if is_ppa_url?(new_resource.uri) && new_resource.components.empty?
- "main"
- else
- new_resource.components
- end
-
- repo = build_repo(
- new_resource.uri,
- new_resource.distribution,
- components,
- new_resource.trusted,
- new_resource.arch,
- new_resource.deb_src
- )
-
- declare_resource(:file, "/etc/apt/sources.list.d/#{new_resource.name}.list") do
- owner "root"
- group "root"
- mode "0644"
- content repo
- sensitive new_resource.sensitive
- action :create
- notifies :run, "execute[apt-cache gencaches]", :immediately
- notifies :update, "apt_update[#{new_resource.name}]", :immediately if new_resource.cache_rebuild
- end
- 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
- sensitive new_resource.sensitive
- action :delete
- notifies :update, "apt_update[#{new_resource.name}]", :immediately if new_resource.cache_rebuild
- end
-
- declare_resource(:apt_update, new_resource.name) do
- ignore_failure true
- action :nothing
- end
-
- end
- end
- end
-
- def is_key_id?(id)
- id = id[2..-1] if id.start_with?("0x")
- id =~ /^\h+$/ && [8, 16, 40].include?(id.length)
- end
-
- 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 ]+)/)
- z[1].split.join
- end
- end.compact
- end
-
- def key_is_valid?(cmd, key)
- valid = true
-
- so = shell_out(cmd)
- so.run_command
- so.stdout.split(/\n/).map do |t|
- if t =~ %r{^\/#{key}.*\[expired: .*\]$}
- Chef::Log.debug "Found expired key: #{t}"
- valid = false
- break
- end
- end
-
- Chef::Log.debug "key #{key} #{valid ? "is valid" : "is not valid"}"
- valid
- end
-
- def cookbook_name
- new_resource.cookbook || new_resource.cookbook_name
- end
-
- def has_cookbook_file?(fn)
- run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
- end
-
- def no_new_keys?(file)
- installed_keys = extract_fingerprints_from_cmd("apt-key finger")
- proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint #{file}")
- (installed_keys & proposed_keys).sort == proposed_keys.sort
- end
-
- def install_key_from_uri
- key_name = new_resource.key.split(%r{\/}).last
- 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
- mode "0644"
- sensitive new_resource.sensitive
- action :create
- 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
- notifies :run, "execute[apt-cache gencaches]", :immediately
- end
- end
-
- def install_key_from_keyserver(key = new_resource.key, keyserver = new_resource.keyserver)
- cmd = "apt-key adv --recv"
- cmd << " --keyserver-options http-proxy=#{new_resource.key_proxy}" if new_resource.key_proxy
- cmd << " --keyserver "
- cmd << if keyserver.start_with?("hkp://")
- keyserver
- else
- "hkp://#{keyserver}:80"
- end
-
- cmd << " #{key}"
-
- declare_resource(:execute, "install-key #{key}") do
- command cmd
- sensitive new_resource.sensitive
- not_if do
- present = extract_fingerprints_from_cmd("apt-key finger").any? do |fp|
- fp.end_with? key.upcase
- end
- present && key_is_valid?("apt-key list", 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)
- end
-
- 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('"')
- install_key_from_keyserver(key_id, "keyserver.ubuntu.com")
- rescue Net::HTTPServerException => e
- raise "Could not access Launchpad ppa API: #{e.message}"
- end
-
- def is_ppa_url?(url)
- url.start_with?("ppa:")
- end
-
- def make_ppa_url(ppa)
- return unless is_ppa_url?(ppa)
- owner, repo = ppa[4..-1].split("/")
- repo ||= "ppa"
-
- install_ppa_key(owner, repo)
- "http://ppa.launchpad.net/#{owner}/#{repo}/ubuntu"
- end
-
- def build_repo(uri, distribution, components, trusted, arch, add_src = false)
- uri = make_ppa_url(uri) if is_ppa_url?(uri)
-
- uri = '"' + uri + '"' unless uri.start_with?("'", '"')
- components = Array(components).join(" ")
- options = []
- options << "arch=#{arch}" if arch
- options << "trusted=yes" if trusted
- optstr = unless options.empty?
- "[" + options.join(" ") + "]"
- end
- info = [ optstr, uri, distribution, components ].compact.join(" ")
- repo = "deb #{info}\n"
- repo << "deb-src #{info}\n" if add_src
- repo
- end
- end
- end
-end
-
-Chef::Provider::Noop.provides :apt_repository
diff --git a/lib/chef/provider/apt_update.rb b/lib/chef/provider/apt_update.rb
deleted file mode 100644
index 0320e9a83f..0000000000
--- a/lib/chef/provider/apt_update.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-# 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 "chef/provider"
-require "chef/provider/noop"
-require "chef/mixin/which"
-
-class Chef
- class Provider
- class AptUpdate < Chef::Provider
- use_inline_resources
-
- extend Chef::Mixin::Which
-
- provides :apt_update do
- which("apt-get")
- end
-
- APT_CONF_DIR = "/etc/apt/apt.conf.d"
- STAMP_DIR = "/var/lib/apt/periodic"
-
- def whyrun_supported?
- true
- end
-
- def load_current_resource
- end
-
- action :periodic do
- if !apt_up_to_date?
- converge_by "update new lists of packages" do
- do_update
- end
- end
- end
-
- action :update do
- converge_by "force update new lists of packages" do
- do_update
- end
- end
-
- private
-
- # Determines whether we need to run `apt-get update`
- #
- # @return [Boolean]
- def apt_up_to_date?
- ::File.exist?("#{STAMP_DIR}/update-success-stamp") &&
- ::File.mtime("#{STAMP_DIR}/update-success-stamp") > Time.now - new_resource.frequency
- end
-
- def do_update
- [STAMP_DIR, APT_CONF_DIR].each do |d|
- declare_resource(:directory, d) do
- recursive true
- end
- 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\";};"
- action :create_if_missing
- end
-
- declare_resource(:execute, "apt-get -q update")
- end
-
- end
- end
-end
-
-Chef::Provider::Noop.provides :apt_update
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index 0d857aaa79..af52b0a36a 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,30 +16,23 @@
# limitations under the License.
#
-require "chef/provider/windows_script"
+require_relative "windows_script"
class Chef
class Provider
class Batch < Chef::Provider::WindowsScript
- provides :batch, os: "windows"
-
- def initialize(new_resource, run_context)
- super(new_resource, run_context, ".bat")
- end
+ provides :batch
def command
- basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
-
interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter)
- "\"#{interpreter_path}\" #{flags} \"#{script_file.path}\""
+ "\"#{interpreter_path}\" #{new_resource.flags} /c \"#{script_file_path}\""
end
- def flags
- @new_resource.flags.nil? ? "/c" : new_resource.flags + " /c"
+ def script_extension
+ ".bat"
end
-
end
end
end
diff --git a/lib/chef/provider/breakpoint.rb b/lib/chef/provider/breakpoint.rb
deleted file mode 100644
index a71c9e317d..0000000000
--- a/lib/chef/provider/breakpoint.rb
+++ /dev/null
@@ -1,38 +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.
-#
-
-class Chef
- class Provider
- class Breakpoint < Chef::Provider
-
- provides :breakpoint
-
- 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
-
- end
- end
-end
diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb
index 3ca0bbd47a..82d9eebf8f 100644
--- a/lib/chef/provider/cookbook_file.rb
+++ b/lib/chef/provider/cookbook_file.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,7 @@
# limitations under the License.
#
-require "chef/provider/file"
-require "chef/deprecation/provider/cookbook_file"
-require "chef/deprecation/warnings"
+require_relative "file"
class Chef
class Provider
@@ -26,25 +24,22 @@ 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..6cc2f33f34 100644
--- a/lib/chef/provider/cookbook_file/content.rb
+++ b/lib/chef/provider/cookbook_file/content.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/file_content_management/content_base"
-require "chef/file_content_management/tempfile"
+require_relative "../../file_content_management/content_base"
+require_relative "../../file_content_management/tempfile"
class Chef
class Provider
@@ -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 7baaeec0c5..7d37f34b1a 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -15,25 +15,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-require "chef/log"
-require "chef/mixin/command"
-require "chef/provider"
+require_relative "../log"
+require_relative "../provider"
class Chef
class Provider
class Cron < Chef::Provider
- include Chef::Mixin::Command
provides :cron, os: ["!aix", "!solaris2"]
- SPECIAL_TIME_VALUES = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly]
- CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :time, :command, :mailto, :path, :shell, :home, :environment]
- WEEKDAY_SYMBOLS = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday]
-
- CRON_PATTERN = /\A([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+|[a-zA-Z]{3})\s([-0-9*,\/]+|[a-zA-Z]{3})\s(.*)/
- SPECIAL_PATTERN = /\A(@(#{SPECIAL_TIME_VALUES.join('|')}))\s(.*)/
- ENV_PATTERN = /\A(\S+)=(\S*)/
+ SPECIAL_TIME_VALUES = %i{reboot yearly annually monthly weekly daily midnight hourly}.freeze
+ CRON_ATTRIBUTES = %i{minute hour day month weekday time command mailto path shell home environment}.freeze
+ CRON_PATTERN = %r{\A([-0-9*,/]+)\s([-0-9*,/]+)\s([-0-9*,/]+)\s([-0-9*,/]+|[a-zA-Z]{3})\s([-0-9*,/]+|[a-zA-Z]{3})\s(.*)}.freeze
+ SPECIAL_PATTERN = /\A(@(#{SPECIAL_TIME_VALUES.join('|')}))\s(.*)/.freeze
+ ENV_PATTERN = /\A(\S+)=(\S*)/.freeze
+ ENVIRONMENT_PROPERTIES = %w{MAILTO PATH SHELL HOME}.freeze
def initialize(new_resource, run_context)
super(new_resource, run_context)
@@ -42,21 +38,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 +57,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,48 +77,42 @@ 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
+ action :create do
+ 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
crontab << line unless cron_found
next
- when SPECIAL_PATTERN
- if cron_found
- cron_found = false
- crontab << newcron
- next
- end
- when CRON_PATTERN
+ when SPECIAL_PATTERN, CRON_PATTERN
if cron_found
cron_found = false
crontab << newcron
@@ -144,39 +130,34 @@ 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
+ action :delete do
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
next if cron_found
- when SPECIAL_PATTERN
- if cron_found
- cron_found = false
- next
- end
- when CRON_PATTERN
+ when SPECIAL_PATTERN, CRON_PATTERN
if cron_found
cron_found = false
next
@@ -187,10 +168,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
@@ -198,65 +179,101 @@ class Chef
private
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)
+ if ENVIRONMENT_PROPERTIES.include?(attr_name)
+ 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)
- end
- @new_resource.environment.each do |name, value|
- newcron << "#{name}=#{value}\n"
+ #
+ # @return [String] The string of Env Variables containing line breaks.
+ #
+ def env_var_str
+ str = []
+ %i{mailto path shell home}.each do |v|
+ str << "#{v.to_s.upcase}=\"#{new_resource.send(v)}\"" if new_resource.send(v)
end
- 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"
+ new_resource.environment.each do |name, value|
+ if ENVIRONMENT_PROPERTIES.include?(name)
+ unless new_resource.property_is_set?(name.downcase)
+ logger.warn("#{new_resource.name}: the environment property contains the '#{name}' variable, which should be set separately as a property.")
+ new_resource.send(name.downcase.to_sym, value.gsub(/^"|"$/, ""))
+ new_resource.environment.delete(name)
+ str << "#{name.to_s.upcase}=\"#{value}\""
+ else
+ raise Chef::Exceptions::Cron, "#{new_resource.name}: the '#{name}' property is set and environment property also contains the '#{name}' variable. Remove the variable from the environment property."
+ end
+ else
+ str << "#{name}=#{value}"
+ end
end
- newcron
+ str.join("\n")
end
- def weekday_in_crontab
- weekday_in_crontab = WEEKDAY_SYMBOLS.index(@new_resource.weekday)
- if weekday_in_crontab.nil?
- @new_resource.weekday
+ #
+ # @return [String] The Cron time string consisting five fields that Cron converts into a time interval.
+ #
+ def duration_str
+ if new_resource.time
+ "@#{new_resource.time}"
else
- weekday_in_crontab.to_s
+ "#{new_resource.minute} #{new_resource.hour} #{new_resource.day} #{new_resource.month} #{new_resource.weekday}"
end
end
+
+ #
+ # @return [String] The timeout command string formed as per time_out property.
+ #
+ def time_out_str
+ return "" if new_resource.time_out.empty?
+
+ str = " timeout"
+ str << " --preserve-status" if new_resource.time_out["preserve-status"].to_s.casecmp("true") == 0
+ str << " --foreground" if new_resource.time_out["foreground"].to_s.casecmp("true") == 0
+ str << " --kill-after #{new_resource.time_out["kill-after"]}" if new_resource.time_out["kill-after"]
+ str << " --signal #{new_resource.time_out["signal"]}" if new_resource.time_out["signal"]
+ str << " #{new_resource.time_out["duration"]};"
+ str
+ end
+
+ #
+ # @return [String] The command to be executed. The new line at the end has been added purposefully.
+ #
+ def cmd_str
+ " #{new_resource.command}\n"
+ end
+
+ # Concatenates various information and formulates a complete string that
+ # could be written in the crontab
+ #
+ # @return [String] A crontab string formed as per the user inputs.
+ #
+ def get_crontab_entry
+ # Initialize
+ newcron = []
+ newcron << "# Chef Name: #{new_resource.name}"
+ newcron << env_var_str unless env_var_str.empty?
+ newcron << duration_str + time_out_str + cmd_str
+
+ newcron.join("\n")
+ end
end
end
end
diff --git a/lib/chef/provider/cron/aix.rb b/lib/chef/provider/cron/aix.rb
index 015c1f2096..c1efafa024 100644
--- a/lib/chef/provider/cron/aix.rb
+++ b/lib/chef/provider/cron/aix.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/provider/cron/unix"
+require_relative "unix"
class Chef
class Provider
@@ -33,8 +33,11 @@ class Chef
raise Chef::Exceptions::Cron, "Aix cron entry does not support environment variables. Please set them in script and use script in cron."
end
- newcron = ""
- newcron << "# Chef Name: #{new_resource.name}\n"
+ if time_out_set?
+ raise Chef::Exceptions::Cron, "Aix cron entry does not support timeout."
+ end
+
+ newcron = "# Chef Name: #{new_resource.name}\n"
newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday}"
newcron << " #{@new_resource.command}\n"
@@ -44,6 +47,10 @@ class Chef
def env_vars_are_set?
@new_resource.environment.length > 0 || !@new_resource.mailto.nil? || !@new_resource.path.nil? || !@new_resource.shell.nil? || !@new_resource.home.nil?
end
+
+ def time_out_set?
+ !@new_resource.time_out.empty?
+ end
end
end
end
diff --git a/lib/chef/provider/cron/solaris.rb b/lib/chef/provider/cron/solaris.rb
index 58d6637674..02ad578b8a 100644
--- a/lib/chef/provider/cron/solaris.rb
+++ b/lib/chef/provider/cron/solaris.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/provider/cron/unix"
+require_relative "unix"
# Just to create an alias so 'Chef::Provider::Cron::Solaris' is exposed and accessible to existing consumers of class.
Chef::Provider::Cron::Solaris = Chef::Provider::Cron::Unix
diff --git a/lib/chef/provider/cron/unix.rb b/lib/chef/provider/cron/unix.rb
index 108e73c9d3..cb3a2f9a61 100644
--- a/lib/chef/provider/cron/unix.rb
+++ b/lib/chef/provider/cron/unix.rb
@@ -18,30 +18,29 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/provider"
-require "chef/provider/cron"
+require_relative "../../log"
+require_relative "../../provider"
+require_relative "../cron"
class Chef
class Provider
class Cron
class Unix < Chef::Provider::Cron
- include Chef::Mixin::ShellOut
-
provides :cron, os: "solaris2"
private
def read_crontab
- crontab = shell_out("/usr/bin/crontab -l", :user => @new_resource.user)
+ crontab = shell_out(%w{/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}"
end
return nil if status > 0
+
crontab.stdout.chomp << "\n"
end
@@ -53,7 +52,7 @@ class Chef
exit_status = 0
error_message = ""
begin
- crontab_write = shell_out("/usr/bin/crontab #{tempcron.path}", :user => @new_resource.user)
+ crontab_write = shell_out("/usr/bin/crontab", tempcron.path, user: @new_resource.user)
stderr = crontab_write.stderr
exit_status = crontab_write.status.exitstatus
# solaris9, 10 on some failures for example invalid 'mins' in crontab fails with exit code of zero :(
@@ -62,12 +61,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/deploy/timestamped.rb b/lib/chef/provider/deploy/timestamped.rb
deleted file mode 100644
index 5486b092d3..0000000000
--- a/lib/chef/provider/deploy/timestamped.rb
+++ /dev/null
@@ -1,34 +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.
-#
-
-class Chef
- class Provider
- class Deploy
- class Timestamped < Chef::Provider::Deploy
- provides :timestamped_deploy
- provides :deploy
-
- protected
-
- def release_slug
- Time.now.utc.strftime("%Y%m%d%H%M%S")
- end
- end
- end
- end
-end
diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb
index 619ab5d8b6..555340d91e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,12 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/log"
-require "chef/resource/directory"
-require "chef/provider"
-require "chef/provider/file"
-require "fileutils"
+require_relative "../config"
+require_relative "../log"
+require_relative "../resource/directory"
+require_relative "../provider"
+require_relative "file"
+require "fileutils" unless defined?(FileUtils)
class Chef
class Provider
@@ -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,51 +103,51 @@ 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
end
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)
+ action :create do
+ 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
+ action :delete do
+ 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)
- Chef::Log.info("#{@new_resource} deleted #{@new_resource.path} recursively")
+ # IO errors or permission errors that would prevent 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..a919d1deff 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 (c) 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.
@@ -15,25 +15,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/util/powershell/cmdlet"
-require "chef/util/dsc/local_configuration_manager"
-require "chef/mixin/powershell_type_coercions"
-require "chef/util/dsc/resource_store"
+require "timeout" unless defined?(Timeout)
+require_relative "../mixin/powershell_exec"
+require_relative "../util/dsc/local_configuration_manager"
+require_relative "../mixin/powershell_type_coercions"
+require_relative "../util/dsc/resource_store"
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
- def action_run
- if ! test_resource
+ action :run do
+ unless test_resource
converge_by(generate_description) do
result = set_resource
reboot_if_required
@@ -41,20 +43,15 @@ class Chef
end
end
- def load_current_resource
- end
-
- def whyrun_supported?
- true
- end
+ def load_current_resource; end
def define_resource_requirements
requirements.assert(:run) do |a|
a.assertion { supports_dsc_invoke_resource? }
- err = ["You must have Powershell version >= 5.0.10018.0 to use dsc_resource."]
+ err = ["You must have PowerShell version >= 5.0.10018.0 to use dsc_resource."]
a.failure_message Chef::Exceptions::ProviderNotFound,
err
- a.whyrun err + ["Assuming a previous resource installs Powershell 5.0.10018.0 or higher."]
+ a.whyrun err + ["Assuming a previous resource installs PowerShell 5.0.10018.0 or higher."]
a.block_action!
end
requirements.assert(:run) do |a|
@@ -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
@@ -123,71 +131,62 @@ class Chef
def test_resource
result = invoke_resource(:test)
add_dsc_verbose_log(result)
- return_dsc_resource_result(result, "InDesiredState")
+ result.result["InDesiredState"]
end
def set_resource
result = invoke_resource(:set)
add_dsc_verbose_log(result)
- create_reboot_resource if return_dsc_resource_result(result, "RebootRequired")
- result.return_value
+ create_reboot_resource if result.result["RebootRequired"]
+ result
end
def add_dsc_verbose_log(result)
# We really want this information from the verbose stream,
# however in some versions of WMF, Invoke-DscResource is not correctly
# writing to that stream and instead just dumping to stdout
- verbose_output = result.stream(:verbose)
- verbose_output = result.stdout if verbose_output.empty?
+ verbose_output = result.verbose.join("\n")
+ verbose_output = result.result if verbose_output.empty?
if @converge_description.nil? || @converge_description.empty?
@converge_description = verbose_output
else
- @converge_description << "\n"
+ @converge_description << "\n\n"
@converge_description << verbose_output
end
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"
- cmdlet = Chef::Util::Powershell::Cmdlet.new(
- node,
- "Invoke-DscResource #{switches}",
- output_format
- )
- cmdlet.run!({}, { :timeout => new_resource.timeout })
+ def module_info_object
+ @module_version.nil? ? module_name : "@{ModuleName='#{module_name}';ModuleVersion='#{@module_version}'}"
end
- def return_dsc_resource_result(result, property_name)
- if result.return_value.is_a?(Array)
- # WMF Feb 2015 Preview
- result.return_value[0][property_name]
- else
- # WMF April 2015 Preview
- result.return_value[property_name]
- end
+ def invoke_resource(method)
+ properties = translate_type(new_resource.properties)
+ switches = "-Method #{method} -Name #{new_resource.resource}"\
+ " -Property #{properties} -Module #{module_info_object} -Verbose"
+ Timeout.timeout(new_resource.timeout) {
+ powershell_exec!("Invoke-DscResource #{switches}")
+ }
end
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 66783ceb0f..d55f060f94 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 (c) 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.
@@ -16,35 +16,34 @@
# limitations under the License.
#
-require "chef/util/powershell/cmdlet"
-require "chef/util/dsc/configuration_generator"
-require "chef/util/dsc/local_configuration_manager"
-require "chef/util/path_helper"
+require_relative "../util/dsc/configuration_generator"
+require_relative "../util/dsc/local_configuration_manager"
+require_relative "../util/path_helper"
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 do |config_manager, document, shellout_flags|
- config_manager.set_configuration(document, shellout_flags)
+ set: Proc.new do |config_manager, document|
+ config_manager.set_configuration(document)
end,
- :test => Proc.new do |config_manager, document, shellout_flags|
- config_manager.test_configuration(document, shellout_flags)
+ test: Proc.new do |config_manager, document|
+ config_manager.test_configuration(document)
end }
end
- def action_run
- if ! @resource_converged
+ action :run do
+ unless @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,20 +57,16 @@ class Chef
end
end
- def whyrun_supported?
- true
- end
-
def define_resource_requirements
requirements.assert(:run) do |a|
err = [
"Could not find PowerShell DSC support on the system",
powershell_info_str,
- "Powershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.",
+ "PowerShell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.",
]
a.assertion { supports_dsc? }
a.failure_message Chef::Exceptions::ProviderNotFound, err.join(" ")
- a.whyrun err + ["Assuming a previous resource installs Powershell 4.0 or higher."]
+ a.whyrun err + ["Assuming a previous resource installs PowerShell 4.0 or higher."]
a.block_action!
end
end
@@ -89,20 +84,23 @@ class Chef
config_manager = Chef::Util::DSC::LocalConfigurationManager.new(@run_context.node, config_directory)
- shellout_flags = {
- :cwd => @dsc_resource.cwd,
- :environment => @dsc_resource.environment,
- :timeout => @dsc_resource.timeout,
- }
+ cwd = @dsc_resource.cwd || Dir.pwd
+ original_env = ENV.to_hash
begin
- configuration_document = generate_configuration_document(config_directory, configuration_flags)
- @operations[operation].call(config_manager, configuration_document, shellout_flags)
+ ENV.update(@dsc_resource.environment) if @dsc_resource.environment
+ Dir.chdir(cwd) do
+ Timeout.timeout(@dsc_resource.timeout) do
+ configuration_document = generate_configuration_document(config_directory, configuration_flags)
+ @operations[operation].call(config_manager, configuration_document)
+ end
+ end
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)
+ ENV.replace(original_env)
end
end
@@ -116,20 +114,14 @@ class Chef
end
def generate_configuration_document(config_directory, configuration_flags)
- shellout_flags = {
- :cwd => @dsc_resource.cwd,
- :environment => @dsc_resource.environment,
- :timeout => @dsc_resource.timeout,
- }
-
generator = Chef::Util::DSC::ConfigurationGenerator.new(@run_context.node, config_directory)
if @dsc_resource.command
- generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags, shellout_flags)
+ generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_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
- generator.configuration_document_from_script_code(@dsc_resource.code || "", configuration_flags, @dsc_resource.imports, shellout_flags)
+ 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)
end
end
@@ -165,7 +157,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"
@@ -175,9 +171,9 @@ class Chef
def powershell_info_str
if run_context && run_context.node[:languages] && run_context.node[:languages][:powershell]
- install_info = "Powershell #{run_context.node[:languages][:powershell][:version]} was found on the system."
+ install_info = "PowerShell #{run_context.node[:languages][:powershell][:version]} was found on the system."
else
- install_info = "Powershell was not found."
+ install_info = "PowerShell was not found."
end
end
end
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..20b8a40bf1 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,59 +16,53 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/provider"
-require "forwardable"
+require_relative "../log"
+require_relative "../provider"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
class Execute < Chef::Provider
extend Forwardable
- provides :execute
+ provides :execute, target_mode: true
- 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, :default_env, :timeout, :input
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 property, or specify a cwd property to the #{new_resource} resource"
end
end
- def timeout
- # original implementation did not specify a timeout, but ShellOut
- # *always* times out. So, set a very long default timeout
- new_resource.timeout || 3600
- end
-
- def action_run
+ action :run do
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!(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 +86,23 @@ 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[:input] = input if input
+ opts[:default_env] = default_env
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)
+ 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
@@ -117,9 +116,10 @@ class Chef
def sentinel_file
Pathname.new(Chef::Util::PathHelper.cleanpath(
- ( cwd && creates_relative? ) ? ::File.join(cwd, creates) : creates
+ ( 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..e2c07ad9f7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,22 +17,21 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/log"
-require "chef/resource/file"
-require "chef/provider"
-require "etc"
-require "fileutils"
-require "chef/scan_access_control"
-require "chef/mixin/checksum"
-require "chef/mixin/file_class"
-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"
+require_relative "../config"
+require_relative "../log"
+require_relative "../resource/file"
+require_relative "../provider"
+require "etc" unless defined?(Etc)
+require "fileutils" unless defined?(FileUtils)
+require_relative "../scan_access_control"
+require_relative "../mixin/checksum"
+require_relative "../mixin/file_class"
+require_relative "../mixin/enforce_ownership_and_permissions"
+require_relative "../util/backup"
+require_relative "../util/diff"
+require_relative "../util/selinux"
+require_relative "../file_content_management/deploy"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
# The Tao of File Providers:
# - the content provider must always return a tempfile that we can delete/mv
@@ -52,10 +51,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 +67,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? )
@@ -93,16 +84,16 @@ class Chef
end
# true if we are going to be creating a new file
- @needs_creating = !::File.exist?(new_resource.path) || needs_unlinking?
+ @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?
+ unless 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 +111,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
@@ -146,7 +137,7 @@ class Chef
end
end
- def action_create
+ action :create do
do_generate_content
do_validate_content
do_unlink
@@ -154,33 +145,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)
+ action :create_if_missing do
+ 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}")
+ action :delete do
+ 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 :touch do
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 +188,9 @@ 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 +220,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 (to #{file_class.realpath(new_resource.path)}). Managing the source file instead.")
+ logger.warn("Disable this warning by setting `manage_symlink_source true` on the resource")
+ logger.warn("In a future 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 +274,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 +304,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
@@ -350,20 +340,23 @@ class Chef
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}"
+ unless v.verify(tempfile.path)
+ backupfile = "#{Chef::Config[:file_cache_path]}/failed_validations/#{::File.basename(tempfile.path)}"
+ FileUtils.mkdir_p ::File.dirname(backupfile)
+ FileUtils.cp tempfile.path, backupfile
+ raise Chef::Exceptions::ValidationFailed.new "Proposed content for #{new_resource.path} failed verification #{new_resource.sensitive ? "[sensitive]" : "#{v}\n#{v.output}"}\nTemporary file moved to #{backupfile}"
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 +364,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
@@ -388,8 +381,8 @@ 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}")
+ deployment_strategy.deploy(tempfile.path, ::File.realpath(new_resource.path).force_encoding(Chef::Config[:ruby_encoding]))
+ 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)
@@ -401,21 +394,21 @@ class Chef
return if tempfile.nil?
# but a tempfile that has no path or doesn't exist should not happen
if tempfile.path.nil? || !::File.exists?(tempfile.path)
- raise "chef-client is confused, trying to deploy a file that has no path or does not exist..."
+ raise "#{ChefUtils::Dist::Infra::CLIENT} is confused, trying to deploy a file that has no path or does not exist..."
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 +430,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,28 +447,24 @@ 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?
+ if ChefUtils.windows?
# This is a work around for CHEF-3554.
# OC-6534: is tracking the real fix for this workaround.
# Add support for Windows equivalent, or implicit resource
# 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/file/content.rb b/lib/chef/provider/file/content.rb
index 1b60e10fea..30ca38d3b6 100644
--- a/lib/chef/provider/file/content.rb
+++ b/lib/chef/provider/file/content.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/file_content_management/content_base"
-require "chef/file_content_management/tempfile"
+require_relative "../../file_content_management/content_base"
+require_relative "../../file_content_management/tempfile"
class Chef
class Provider
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index d051bb1d92..c0f6f01c59 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/log"
-require "chef/provider"
-require "fileutils"
+require_relative "../exceptions"
+require_relative "../log"
+require_relative "../provider"
+require "fileutils" unless defined?(FileUtils)
class Chef
class Provider
@@ -28,78 +28,90 @@ class Chef
extend Forwardable
provides :git
- def_delegator :@new_resource, :destination, :cwd
+ GIT_VERSION_PATTERN = Regexp.compile('git version (\d+\.\d+.\d+)')
- def whyrun_supported?
- true
- end
+ 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
+ unless new_resource.user.nil?
+ requirements.assert(:all_actions) do |a|
+ a.assertion do
+
+ get_homedir(new_resource.user)
+ rescue ArgumentError
+ false
+
+ end
+ a.whyrun("User #{new_resource.user} does not exist, this run will fail unless it has been previously created. Assuming it would have been created.")
+ a.failure_message(Chef::Exceptions::User, "#{new_resource.user} required by resource #{new_resource.name} does not exist")
+ end
+ end
+
# Parent directory of the target must exist.
requirements.assert(:checkout, :sync) do |a|
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 #{cwd}, 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 =~ %r{^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}')."
+ "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}')."
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}'. " +
- "Verify your (case-sensitive) repository URL and revision.\n" +
- "`git ls-remote '#{@new_resource.repository}' '#{rev_search_pattern}'` output: #{@resolved_reference}"
+ "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}"
end
end
- def action_checkout
+ action :checkout do
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 #{cwd} 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 :export do
action_checkout
converge_by("complete the export by removing #{cwd}.git after checkout") do
FileUtils.rm_rf(::File.join(cwd, ".git"))
end
end
- def action_sync
+ action :sync do
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
@@ -107,8 +119,22 @@ class Chef
end
end
- def git_minor_version
- @git_minor_version ||= Gem::Version.new( git("--version").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?
@@ -120,7 +146,7 @@ class Chef
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 = git("rev-parse", "HEAD", cwd: cwd, returns: [0, 128]).stdout.strip
@@ -128,11 +154,16 @@ class Chef
sha_hash?(result) ? result : nil
end
+ def already_on_target_branch?
+ current_branch = git("rev-parse", "--abbrev-ref", "HEAD", cwd: cwd, returns: [0, 128]).stdout.strip
+ current_branch == (new_resource.checkout_branch || new_resource.revision)
+ 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
@@ -140,38 +171,52 @@ class Chef
end
def clone
- converge_by("clone from #{@new_resource.repository} into #{cwd}") do
- remote = @new_resource.remote
+ converge_by("clone from #{repo_url} 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_minor_version >= Gem::Version.new("1.7.10")
- clone_cmd << "\"#{@new_resource.repository}\""
+ 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}\""
- Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{cwd}"
+ logger.info "#{new_resource} cloning repo #{repo_url} 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 #{target_revision} branch #{new_resource.revision}") do
# checkout into a local branch rather than a detached HEAD
- git("branch", "-f", @new_resource.checkout_branch, sha_ref, cwd: cwd)
- git("checkout", @new_resource.checkout_branch, cwd: cwd)
- Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} onto: #{@new_resource.checkout_branch} reference: #{sha_ref}"
+ if new_resource.checkout_branch
+ # check out to a local branch
+ git("branch", "-f", new_resource.checkout_branch, target_revision, 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: #{target_revision}"
+ elsif sha_hash?(new_resource.revision) || !is_branch?
+ # detached head
+ git("checkout", target_revision, cwd: cwd)
+ logger.info "#{new_resource} checked out reference: #{target_revision}"
+ elsif already_on_target_branch?
+ # we are already on the proper branch
+ git("reset", "--hard", target_revision, cwd: cwd)
+ else
+ # need a branch with a tracking branch
+ git("branch", "-f", new_resource.revision, target_revision, cwd: cwd)
+ git("checkout", new_resource.revision, cwd: cwd)
+ git("branch", "-u", "#{new_resource.remote}/#{new_resource.revision}", cwd: cwd)
+ logger.info "#{new_resource} checked out branch: #{new_resource.revision} reference: #{target_revision}"
+ end
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"
+ 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)
- Chef::Log.info "#{@new_resource} enabling git submodules"
+ logger.info "#{new_resource} enabling git submodules"
# the --recursive flag means we require git 1.6.5+ now, see CHEF-1827
git("submodule", "update", "--init", "--recursive", cwd: cwd)
end
@@ -179,19 +224,31 @@ class Chef
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
- Chef::Log.debug "Fetching updates from #{new_resource.remote} and resetting to revision #{target_revision}"
- git("fetch", @new_resource.remote, cwd: cwd)
- git("fetch", @new_resource.remote, "--tags", cwd: cwd)
- git("reset", "--hard", target_revision, cwd: cwd)
+ 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)
+ if sha_hash?(new_resource.revision) || is_tag? || already_on_target_branch?
+ # detached head or if we are already on the proper branch
+ git("reset", "--hard", target_revision, cwd: cwd)
+ elsif new_resource.checkout_branch
+ # check out to a local branch
+ git("branch", "-f", new_resource.checkout_branch, target_revision, cwd: cwd)
+ git("checkout", new_resource.checkout_branch, cwd: cwd)
+ else
+ # need a branch with a tracking branch
+ git("branch", "-f", new_resource.revision, target_revision, cwd: cwd)
+ git("checkout", new_resource.revision, cwd: cwd)
+ git("branch", "-u", "#{new_resource.remote}/#{new_resource.revision}", cwd: cwd)
+ end
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}"
+ 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
@@ -202,7 +259,7 @@ 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)
- git("config", "--replace-all", "remote.#{remote_name}.url", remote_url, cwd: cwd)
+ git("config", "--replace-all", "remote.#{remote_name}.url", %{"#{remote_url}"}, cwd: cwd)
end
when 1
git("remote", "add", remote_name, remote_url, cwd: cwd)
@@ -219,23 +276,24 @@ 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
- else
- @target_revision = remote_resolve_reference
+ @target_revision ||=
+ begin
+ if sha_hash?(new_resource.revision)
+ @target_revision = new_resource.revision
+ else
+ @target_revision = remote_resolve_reference
+ end
end
- end
end
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,19 +308,28 @@ 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
def find_revision(refs, revision, suffix = "")
found = refs_search(refs, rev_match_pattern("refs/tags/", revision) + suffix)
- found = refs_search(refs, rev_match_pattern("refs/heads/", revision) + suffix) if found.empty?
- found = refs_search(refs, revision + suffix) if found.empty?
- found
+ if !found.empty?
+ @is_tag = true
+ found
+ else
+ found = refs_search(refs, rev_match_pattern("refs/heads/", revision) + suffix)
+ if !found.empty?
+ @is_branch = true
+ found
+ else
+ refs_search(refs, revision + suffix)
+ end
+ end
end
def rev_match_pattern(prefix, revision)
@@ -274,61 +341,85 @@ 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)
- git("ls-remote", "\"#{@new_resource.repository}\"", "\"#{rev_pattern}\"").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 is_branch?
+ !!@is_branch
+ end
+
+ def is_tag?
+ !!@is_tag
+ end
+
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"
- 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}'"
- end
+ env["HOME"] = get_homedir(new_resource.user)
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 git(*args, **run_opts)
git_command = ["git", args].compact.join(" ")
- Chef::Log.debug "running #{git_command}"
- shell_out!(git_command, run_options(run_opts))
+ logger.trace "running #{git_command}"
+ shell_out!(git_command, **run_options(run_opts))
end
def sha_hash?(string)
string =~ /^[0-9a-f]{40}$/
end
+ # Returns a message for sensitive repository URL if sensitive is true otherwise
+ # repository URL is returned
+ # @return [String]
+ def repo_url
+ if new_resource.sensitive
+ "**Suppressed Sensitive URL**"
+ else
+ new_resource.repository
+ end
+ end
+
+ # Returns the home directory of the user
+ # @param [String] user must be a string.
+ # @return [String] the home directory of the user.
+ #
+ def get_homedir(user)
+ require "etc" unless defined?(Etc)
+ case user
+ when Integer
+ Etc.getpwuid(user).dir
+ else
+ Etc.getpwnam(user.to_s).dir
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/group.rb b/lib/chef/provider/group.rb
index 8936bd2031..0cda3182ba 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,63 +16,55 @@
# limitations under the License.
#
-require "chef/provider"
-require "chef/mixin/shell_out"
-require "chef/mixin/command"
-require "etc"
+require_relative "../provider"
+require "etc" unless defined?(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(', ')}'")
+ a.failure_message(Chef::Exceptions::ConflictingMembersInGroup, "Attempting to both add and remove users from a group: '#{common_members.join(", ")}'")
# No why-run alternative
end
end
@@ -86,41 +78,48 @@ 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}"
+ unless group_gid_match?
+ @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
+ 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
+ unless members_to_be_removed.empty?
@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"
- end
+ elsif !group_members_match?
+ @change_desc << "replace group members with new list of members: #{new_resource.members.join(", ")}"
end
!@change_desc.empty?
end
+ def group_gid_match?
+ new_resource.gid.to_s == current_resource.gid.to_s
+ end
+
+ def group_members_match?
+ [new_resource.members].flatten.sort == [current_resource.members].flatten.sort
+ end
+
def has_current_group_member?(member)
- @current_resource.members.include?(member)
+ current_resource.members.include?(member)
end
def validate_member!(member)
@@ -129,47 +128,47 @@ class Chef
true
end
- def action_create
+ action :create do
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: #{change_desc.join(", ")}")
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
+ action :remove do
+ 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
+ action :manage do
+ return unless @group_exists && compare_group
+
+ converge_by(["manage group #{new_resource.group_name}"] + change_desc) do
+ manage_group
+ logger.info("#{new_resource} managed: #{change_desc.join(", ")}")
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
+ action :modify do
+ return unless compare_group
+
+ converge_by(["modify group #{new_resource.group_name}"] + change_desc) do
+ manage_group
+ logger.info("#{new_resource} modified: #{change_desc.join(", ")}")
end
end
diff --git a/lib/chef/provider/group/aix.rb b/lib/chef/provider/group/aix.rb
index 4a02d5ef8c..aa4d8ba4c4 100644
--- a/lib/chef/provider/group/aix.rb
+++ b/lib/chef/provider/group/aix.rb
@@ -16,8 +16,7 @@
# limitations under the License.
#
-require "chef/provider/group/groupadd"
-require "chef/mixin/shell_out"
+require_relative "groupadd"
class Chef
class Provider
@@ -33,48 +32,44 @@ class Chef
end
def create_group
- command = "mkgroup"
- command << set_options << " #{@new_resource.group_name}"
- run_command(:command => command)
+ shell_out!("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!("chgroup", options, new_resource.group_name)
end
modify_group_members
end
def remove_group
- run_command(:command => "rmgroup #{@new_resource.group_name}")
+ shell_out!("rmgroup", new_resource.group_name)
end
def add_member(member)
- shell_out!("chgrpmem -m + #{member} #{@new_resource.group_name}")
+ shell_out!("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!("chgrpmem", "-m", "=", members.join(","), new_resource.group_name)
end
def remove_member(member)
- shell_out!("chgrpmem -m - #{member} #{@new_resource.group_name}")
+ shell_out!("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..824ebe0477 100644
--- a/lib/chef/provider/group/dscl.rb
+++ b/lib/chef/provider/group/dscl.rb
@@ -1,6 +1,6 @@
#
# Author:: Dreamcat4 (<dreamcat4@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,31 +24,35 @@ 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(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)
result = dscl(*args)
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]
+ raise(Chef::Exceptions::Group, "dscl error: #{result.inspect}") if /No such key: /.match?(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,74 +61,80 @@ 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")
+ if groups_gids&.match?(Regexp.new("#{Regexp.escape(next_gid_guess.to_s)}\n"))
next_gid_guess += 1
else
gid = next_gid_guess
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 +142,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 +155,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..937a417dee 100644
--- a/lib/chef/provider/group/gpasswd.rb
+++ b/lib/chef/provider/group/gpasswd.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/provider/group/groupadd"
+require_relative "groupadd"
class Chef
class Provider
@@ -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!("gpasswd", "-M", "", new_resource.group_name)
else
- shell_out!("gpasswd -M \"\" #{@new_resource.group_name}")
+ shell_out!("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!("gpasswd", "-a", member, new_resource.group_name)
end
def remove_member(member)
- shell_out!("gpasswd -d #{member} #{@new_resource.group_name}")
+ shell_out!("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..63f1690f5e 100644
--- a/lib/chef/provider/group/groupadd.rb
+++ b/lib/chef/provider/group/groupadd.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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!("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!("groupmod", set_options)
modify_group_members
end
# Remove the group
def remove_group
- run_command(:command => "groupdel #{@new_resource.group_name}")
+ shell_out!("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,24 @@ 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..e4d69f34bf 100644
--- a/lib/chef/provider/group/groupmod.rb
+++ b/lib/chef/provider/group/groupmod.rb
@@ -1,6 +1,6 @@
#
# Author:: Dan Crosta (<dcrosta@late.am>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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!("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!("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!("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!("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!("group", "add", set_options(overwrite_gid: true))
- remove = "group del #{@new_resource.group_name}_bak"
- shell_out!(remove)
+ shell_out!("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..0639b8fdb5 100644
--- a/lib/chef/provider/group/pw.rb
+++ b/lib/chef/provider/group/pw.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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!(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!("pw", "groupmod", set_options)
else
member_options.each do |option|
- run_command(:command => command + option)
+ shell_out!("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!("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}) doesn't 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/solaris.rb b/lib/chef/provider/group/solaris.rb
new file mode 100644
index 0000000000..9a3e1e2b35
--- /dev/null
+++ b/lib/chef/provider/group/solaris.rb
@@ -0,0 +1,62 @@
+#
+# Author:: Joshua Justice (<jjustice6@bloomberg.net>)
+# Copyright:: Copyright (c) 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 "groupadd"
+
+class Chef
+ class Provider
+ class Group
+ class Solaris < Chef::Provider::Group::Groupadd
+
+ # this provides line is setup to only catch the solaris2 platform, but
+ # NOT other platforms in the Solaris platform_family. (See usermod provider.)
+ provides :group, platform: "solaris2"
+
+ def load_current_resource
+ super
+ end
+
+ def define_resource_requirements
+ super
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { ::File.exist?("/usr/sbin/usermod") && ::File.exist?("/usr/sbin/groupmod") }
+ a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/usermod or /usr/sbin/groupmod 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)
+ # Set the group to have exactly the list of members passed to it.
+ unless members.empty?
+ shell_out!("groupmod", "-U", members.join(","), new_resource.group_name)
+ end
+ end
+
+ def add_member(member)
+ shell_out!("usermod", "-G", "+#{new_resource.group_name}", member)
+ end
+
+ def remove_member(member)
+ shell_out!("usermod", "-G", "-#{new_resource.group_name}", member)
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/group/suse.rb b/lib/chef/provider/group/suse.rb
index a79038e25e..266e8e0fbc 100644
--- a/lib/chef/provider/group/suse.rb
+++ b/lib/chef/provider/group/suse.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/provider/group/groupadd"
+require_relative "groupadd"
+require "etc" unless defined?(Etc)
class Chef
class Provider
class Group
class Suse < Chef::Provider::Group::Groupadd
- provides :group, platform: "opensuse", platform_version: "< 12.3"
provides :group, platform: "suse", platform_version: "< 12.0"
def load_current_resource
@@ -32,30 +32,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
+
+ to_add(new_resource.members).all? { |member| Etc.getpwnam(member) }
+ rescue
+ false
+
+ 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!("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!("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 bef4b667a2..e00380659d 100644
--- a/lib/chef/provider/group/usermod.rb
+++ b/lib/chef/provider/group/usermod.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,14 @@
# limitations under the License.
#
-require "chef/provider/group/groupadd"
+require_relative "groupadd"
class Chef
class Provider
class Group
class Usermod < Chef::Provider::Group::Groupadd
- provides :group, os: %w{openbsd solaris2 hpux}
- provides :group, platform: "opensuse"
+ provides :group, os: %w{openbsd solaris2}
def load_current_resource
super
@@ -34,19 +33,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 +56,17 @@ 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.include?(: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!("usermod", append_flags, new_resource.group_name, member)
end
def remove_member(member)
@@ -77,12 +76,7 @@ class Chef
end
def append_flags
- case node[:platform]
- when "openbsd", "netbsd", "aix", "solaris2", "smartos", "omnios"
- "-G"
- when "solaris", "suse", "opensuse"
- "-a -G"
- end
+ "-G" if platform?("openbsd", "netbsd", "aix", "smartos", "omnios")
end
end
diff --git a/lib/chef/provider/group/windows.rb b/lib/chef/provider/group/windows.rb
index 5873e42a6b..dacfc348f7 100644
--- a/lib/chef/provider/group/windows.rb
+++ b/lib/chef/provider/group/windows.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/provider/user"
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/util/windows/net_group"
+require_relative "../user"
+if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+ require_relative "../../util/windows/net_group"
end
class Chef
@@ -30,26 +30,37 @@ 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 group_members_match?
+ sorted_members_sids = new_resource.members.map { |x| lookup_account_name(x) }.sort
+ sorted_current_sids = current_resource.members.sort
+ Chef::Log.debug("#{new_resource.name}: current_members: #{sorted_current_sids} vs new_members #{sorted_members_sids}")
+ sorted_members_sids == sorted_current_sids
+ end
+
+ def group_gid_match?
+ 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)
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 +69,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 +80,20 @@ 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)
+ elsif !group_members_match?
+ @net_group.local_set_members(new_resource.members)
end
+ @net_group.local_group_set_info(new_resource.comment) if new_resource.comment
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
@@ -89,7 +101,7 @@ class Chef
end
def locally_qualified_name(account_name)
- account_name.include?("\\") ? account_name : "#{ENV['COMPUTERNAME']}\\#{account_name}"
+ account_name.include?("\\") ? account_name : "#{ENV["COMPUTERNAME"]}\\#{account_name}"
end
def validate_member!(member)
@@ -97,12 +109,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..8e7a7f1fc9 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "tempfile"
-require "chef/http/simple"
+require "tempfile" unless defined?(Tempfile)
+require_relative "../http/simple"
class Chef
class Provider
@@ -27,90 +27,100 @@ 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
- def action_head
- message = check_message(@new_resource.message)
+ # Send a HEAD request to new_resource.url
+ action :head do
+ 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).to_s,
+ 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
- def action_get
- converge_by("#{@new_resource} GET to #{@new_resource.url}") do
+ # Send a GET request to new_resource.url
+ action :get 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).to_s,
+ 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
+ action :patch do
+ converge_by("#{new_resource} PATCH to #{new_resource.url}") do
+ message = check_message(new_resource.message)
+ body = @http.patch(
+ (new_resource.url).to_s,
+ 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
- def action_put
- converge_by("#{@new_resource} PUT to #{@new_resource.url}") do
- message = check_message(@new_resource.message)
+ # Send a PUT request to new_resource.url, with the message as the payload
+ action :put do
+ 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).to_s,
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
- def action_post
- converge_by("#{@new_resource} POST to #{@new_resource.url}") do
- message = check_message(@new_resource.message)
+ # Send a POST request to new_resource.url, with the message as the payload
+ action :post do
+ 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).to_s,
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
- def action_delete
- converge_by("#{@new_resource} DELETE to #{@new_resource.url}") do
+ # Send a DELETE request to new_resource.url
+ action :delete do
+ converge_by("#{new_resource} DELETE to #{new_resource.url}") do
body = @http.delete(
- "#{@new_resource.url}",
- @new_resource.headers
+ (new_resource.url).to_s,
+ 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
private
def check_message(message)
- if message.kind_of?(Proc)
+ if message.is_a?(Proc)
message.call
else
message
diff --git a/lib/chef/provider/ifconfig.rb b/lib/chef/provider/ifconfig.rb
index 4cfb257bb9..d08564e75d 100644
--- a/lib/chef/provider/ifconfig.rb
+++ b/lib/chef/provider/ifconfig.rb
@@ -16,80 +16,146 @@
# limitations under the License.
#
-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
+require_relative "../log"
+require_relative "../provider"
+require_relative "../resource/file"
+require_relative "../exceptions"
+autoload :ERB, "erb"
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
+ # @api private
+ # @return [String] the major.minor of the net-tools version as a string
+ attr_accessor :ifconfig_version
+
def initialize(new_resource, run_context)
super(new_resource, run_context)
@config_template = nil
@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.stdout.each_line do |line|
+ if /^net-tools (\d+\.\d+)/.match?(line)
+ @ifconfig_version = line.match(/^net-tools (\d+\.\d+)/)[1]
end
+ end
+ @net_tools_version.stderr.each_line do |line|
+ if /^net-tools (\d+\.\d+)/.match?(line)
+ @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_i < 2
+ # 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 /inet addr:/.match?(line)
+ @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? Regexp.last_match(1) : "nil") if /Bcast:/.match?(line)
+ @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? Regexp.last_match(1) : "nil") if /Mask:/.match?(line)
+ @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? Regexp.last_match(1) : "nil") if /MTU:/.match?(line)
+ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? Regexp.last_match(1) : "nil") if /Metric:/.match?(line)
+ end
+
+ next unless @interfaces.key?(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"])
+ @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_i >= 2
+ # 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
+ #
+ # Permalink for addr_regex : https://rubular.com/r/JrykUpfjRnYeQD
+ @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)[3] == ""
+ @int_name = line.match(addr_regex)[1]
+ @interfaces[@int_name] = {}
+ @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("mtu") && @interfaces[@int_name]["mtu"].nil?
+ else
+ @int_name = "#{line.match(addr_regex)[1]}:#{line.match(addr_regex)[3]}"
+ @interfaces[@int_name] = {}
+ @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("mtu") && @interfaces[@int_name]["mtu"].nil?
+ end
+ else
+ @interfaces[@int_name]["inet_addr"] = (line =~ /inet (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("inet") && @interfaces[@int_name]["inet_addr"].nil?
+ @interfaces[@int_name]["bcast"] = (line =~ /broadcast (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("broadcast") && @interfaces[@int_name]["bcast"].nil?
+ @interfaces[@int_name]["mask"] = (line =~ /netmask (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("netmask") && @interfaces[@int_name]["mask"].nil?
+ @interfaces[@int_name]["hwaddr"] = (line =~ /ether (\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("ether") && @interfaces[@int_name]["hwaddr"].nil?
+ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? Regexp.last_match(1) : "nil") if line.include?("Metric:") && @interfaces[@int_name]["metric"].nil?
+ 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
end
- @current_resource
+
+ current_resource
end
def define_resource_requirements
@@ -102,16 +168,14 @@ class Chef
end
end
- def action_add
+ action :add do
# 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!(command)
+ logger.info("#{new_resource} added")
end
end
end
@@ -119,56 +183,49 @@ class Chef
generate_config
end
- def action_enable
+ action :enable do
# 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!(command)
+ logger.info("#{new_resource} enabled")
end
end
- def action_delete
+ action :delete do
# 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!(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
- def action_disable
+ action :disable do
# 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!(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)
@@ -177,45 +234,47 @@ class Chef
def generate_config
return unless can_generate_config?
+
b = binding
- template = ::ERB.new(@config_template)
+ template = ::ERB.new(@config_template, nil, "-")
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..16e7ad0a8b 100644
--- a/lib/chef/provider/ifconfig/aix.rb
+++ b/lib/chef/provider/ifconfig/aix.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (kaustubh@clogeny.com)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,70 +16,66 @@
# limitations under the License.
#
-require "chef/provider/ifconfig"
+require_relative "../ifconfig"
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("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 property 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..9b359d7c54 100644
--- a/lib/chef/provider/ifconfig/debian.rb
+++ b/lib/chef/provider/ifconfig/debian.rb
@@ -16,42 +16,60 @@
# limitations under the License.
#
-require "chef/provider/ifconfig"
-require "chef/util/file_edit"
+require_relative "../ifconfig"
+require_relative "../../util/file_edit"
class Chef
class Provider
class Ifconfig
class Debian < Chef::Provider::Ifconfig
- provides :ifconfig, platform: %w{ubuntu}, platform_version: ">= 11.10"
- provides :ifconfig, platform: %w{debian}, platform_version: ">= 7.0"
+ provides :ifconfig, platform_family: %w{debian}
- 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
- when "dhcp" %>
-iface <%= @new_resource.device %> inet dhcp
-<% when "bootp" %>
-iface <%= @new_resource.device %> inet 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 %>
-<% end %>
-<% end %>
+<% if new_resource.device -%>
+<% if new_resource.onboot == "yes" -%>
+auto <%= new_resource.device %>
+<% end -%>
+<% case new_resource.bootproto
+ when "dhcp" -%>
+iface <%= new_resource.device %> <%= new_resource.family %> dhcp
+<% when "bootp" -%>
+iface <%= new_resource.device %> <%= new_resource.family %> bootp
+<% else -%>
+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 -%>
+ <% if new_resource.gateway -%>
+ gateway <%= new_resource.gateway %>
+ <% 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,19 +80,22 @@ iface <%= @new_resource.device %> inet static
protected
def enforce_interfaces_dot_d_sanity
- # 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)
- new_resource.updated_by_last_action(true) if dir.updated_by_last_action?
+ # 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 if it's missing
+ directory INTERFACES_DOT_D_DIR
+
# 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..a7f73c43dc 100644
--- a/lib/chef/provider/ifconfig/redhat.rb
+++ b/lib/chef/provider/ifconfig/redhat.rb
@@ -16,32 +16,74 @@
# limitations under the License.
#
-require "chef/provider/ifconfig"
+require_relative "../ifconfig"
class Chef
class Provider
class Ifconfig
class Redhat < Chef::Provider::Ifconfig
- provides :ifconfig, platform_family: %w{fedora rhel}
+ provides :ifconfig, platform_family: "fedora_derived"
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 -%>
+<% if new_resource.vlan -%>
+VLAN=<%= new_resource.vlan %>
+<% end -%>
+<% if new_resource.gateway -%>
+GATEWAY=<%= new_resource.gateway %>
+<% end -%>
+<% if new_resource.bridge -%>
+BRIDGE=<%= new_resource.bridge %>
+<% end -%>
}
- @config_path = "/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}"
+ @config_path = "/etc/sysconfig/network-scripts/ifcfg-#{new_resource.device}"
end
-
end
end
end
diff --git a/lib/chef/provider/launchd.rb b/lib/chef/provider/launchd.rb
index c58d4bfa34..b8ff9dfa4d 100644
--- a/lib/chef/provider/launchd.rb
+++ b/lib/chef/provider/launchd.rb
@@ -16,13 +16,12 @@
# limitations under the License.
#
-require "chef/provider"
-require "chef/resource/launchd"
-require "chef/resource/file"
-require "chef/resource/cookbook_file"
-require "chef/resource/macosx_service"
-require "plist"
-require "forwardable"
+require_relative "../provider"
+require_relative "../resource/file"
+require_relative "../resource/cookbook_file"
+require_relative "../resource/macosx_service"
+autoload :Plist, "plist"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
@@ -30,22 +29,10 @@ class Chef
extend Forwardable
provides :launchd, os: "darwin"
- def_delegators :@new_resource, *[
- :backup,
- :cookbook,
- :group,
- :label,
- :mode,
- :owner,
- :path,
- :source,
- :session_type,
- :type,
- ]
+ def_delegators :new_resource, :backup, :cookbook, :group, :label, :mode, :owner, :source, :session_type, :type
def load_current_resource
current_resource = Chef::Resource::Launchd.new(new_resource.name)
- @path = path ? path : gen_path_from_type
end
def gen_path_from_type
@@ -56,82 +43,86 @@ class Chef
types[type]
end
- def action_create
+ action :create do
manage_plist(:create)
end
- def action_create_if_missing
+ action :create_if_missing do
manage_plist(:create_if_missing)
end
- def action_delete
- # If you delete a service you want to make sure its not loaded or
- # the service will be in memory and you wont be able to stop it.
- if ::File.exists?(@path)
+ action :delete do
+ if ::File.exists?(path)
manage_service(:disable)
end
manage_plist(:delete)
end
- def action_enable
- if manage_plist(:create)
- manage_service(:restart)
- else
- manage_service(:enable)
+ action :enable do
+ manage_service(:nothing)
+ manage_plist(:create) do
+ notifies :restart, "macosx_service[#{label}]", :immediately
end
+ manage_service(:enable)
end
- def action_disable
+ action :disable do
+ return unless ::File.exist?(path)
+
manage_service(:disable)
end
- def manage_plist(action)
+ action :restart do
+ manage_service(:restart)
+ end
+
+ def manage_plist(action, &block)
if source
- res = cookbook_file_resource
+ cookbook_file path do
+ cookbook_name = new_resource.cookbook if new_resource.cookbook
+ copy_properties_from(new_resource, :backup, :group, :mode, :owner, :source)
+ action(action)
+ only_if { manage_agent?(action) }
+ instance_eval(&block) if block_given?
+ end
else
- res = file_resource
+ file path do
+ copy_properties_from(new_resource, :backup, :group, :mode, :owner)
+ content(file_content) if file_content?
+ action(action)
+ only_if { manage_agent?(action) }
+ instance_eval(&block) if block_given?
+ end
end
- res.run_action(action)
- new_resource.updated_by_last_action(true) if res.updated?
- res.updated
end
def manage_service(action)
- res = service_resource
- res.run_action(action)
- new_resource.updated_by_last_action(true) if res.updated?
- end
-
- def service_resource
- res = Chef::Resource::MacosxService.new(label, run_context)
- res.name(label) if label
- res.service_name(label) if label
- res.plist(@path) if @path
- res.session_type(session_type) if session_type
- res
- end
-
- def file_resource
- res = Chef::Resource::File.new(@path, run_context)
- res.name(@path) if @path
- res.backup(backup) if backup
- res.content(content) if content
- res.group(group) if group
- res.mode(mode) if mode
- res.owner(owner) if owner
- res
- end
-
- def cookbook_file_resource
- res = Chef::Resource::CookbookFile.new(@path, run_context)
- res.cookbook_name = cookbook if cookbook
- res.name(@path) if @path
- res.backup(backup) if backup
- res.group(group) if group
- res.mode(mode) if mode
- res.owner(owner) if owner
- res.source(source) if source
- res
+ plist_path = path
+ macosx_service label do
+ service_name(new_resource.label) if new_resource.label
+ plist(plist_path) if plist_path
+ copy_properties_from(new_resource, :session_type)
+ action(action)
+ only_if { manage_agent?(action) }
+ end
+ 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 = %i{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 define_resource_requirements
@@ -145,17 +136,18 @@ class Chef
end
end
- def content?
- !!content
+ def file_content?
+ !!file_content
end
- def content
- plist_hash = new_resource.hash || gen_hash
- Plist::Emit.dump(plist_hash) unless plist_hash.nil?
+ def file_content
+ plist_hash = new_resource.plist_hash || gen_hash
+ ::Plist::Emit.dump(plist_hash) unless plist_hash.nil?
end
def gen_hash
return nil unless new_resource.program || new_resource.program_arguments
+
{
"label" => "Label",
"program" => "Program",
@@ -168,10 +160,11 @@ class Chef
"environment_variables" => "EnvironmentVariables",
"exit_timeout" => "ExitTimeout",
"ld_group" => "GroupName",
- "hard_resource_limits" => "HardreSourceLimits",
+ "hard_resource_limits" => "HardResourceLimits",
"inetd_compatibility" => "inetdCompatibility",
"init_groups" => "InitGroups",
"keep_alive" => "KeepAlive",
+ "launch_events" => "LaunchEvents",
"launch_only_once" => "LaunchOnlyOnce",
"limit_load_from_hosts" => "LimitLoadFromHosts",
"limit_load_to_hosts" => "LimitLoadToHosts",
@@ -203,6 +196,11 @@ class Chef
memo[val] = new_resource.send(key) if new_resource.send(key)
end
end
+
+ # @api private
+ def path
+ @path ||= new_resource.path || gen_path_from_type
+ end
end
end
end
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index 16d30319b3..900d0516af 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/log"
-require "chef/mixin/file_class"
-require "chef/resource/link"
-require "chef/provider"
-require "chef/scan_access_control"
-require "chef/util/path_helper"
+require_relative "../config"
+require_relative "../log"
+require_relative "../mixin/file_class"
+require_relative "../resource/link"
+require_relative "../provider"
+require_relative "../scan_access_control"
+require_relative "../util/path_helper"
class Chef
class Provider
@@ -33,104 +33,97 @@ class Chef
include Chef::Mixin::EnforceOwnershipAndPermissions
include Chef::Mixin::FileClass
- def negative_complement(big)
- if big > 1073741823 # Fixnum max
- big -= (2**32) # diminished radix wrap to negative
- end
- big
- end
-
- 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
def canonicalize(path)
- Chef::Platform.windows? ? path.tr("/", '\\') : path
+ ChefUtils.windows? ? path.tr("/", '\\') : path
end
- def action_create
+ action :create do
# current_resource is the symlink that currently exists
# new_resource is the symlink we need to create
# 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 ChefUtils.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
@@ -139,17 +132,17 @@ class Chef
end
end
- def action_delete
- 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)
- Chef::Log.info("#{@new_resource} deleted")
+ action :delete do
+ if current_resource.to # Exists
+ if ChefUtils.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)
- Chef::Log.info("#{@new_resource} deleted")
+ 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
@@ -159,7 +152,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
deleted file mode 100644
index 567781cb41..0000000000
--- a/lib/chef/provider/log.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Author:: Cary Penniman (<cary@rightscale.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.
-#
-
-class Chef
-
- class Provider
-
- class Log
-
- # Chef log provider, allows logging to chef's logs from recipes
- 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
- def load_current_resource
- true
- end
-
- # Write the log to Chef's log
- #
- # === Return
- # true:: Always return true
- def action_write
- Chef::Log.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 cbf25f1e4f..a95927daf3 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,9 +18,9 @@
# limitations under the License.
#
-require "chef/provider"
-require "chef/dsl/recipe"
-require "chef/dsl/include_recipe"
+require_relative "../provider"
+require_relative "../dsl/recipe"
+require_relative "../dsl/include_recipe"
class Chef
class Provider
@@ -42,8 +42,7 @@ class Chef
# no-op `load_current_resource`. Allows simple LWRP providers to work
# without defining this method explicitly (silences
# Chef::Exceptions::Override exception)
- def load_current_resource
- end
+ def load_current_resource; end
# class methods
class <<self
@@ -52,7 +51,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
@@ -71,22 +70,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
deleted file mode 100644
index f8225ff63a..0000000000
--- a/lib/chef/provider/mdadm.rb
+++ /dev/null
@@ -1,93 +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/provider"
-
-class Chef
- class Provider
- class Mdadm < Chef::Provider
-
- 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}")
-
- device_not_found = 4
- mdadm = shell_out!("mdadm --detail --test #{@new_resource.raid_device}", :returns => [0, device_not_found])
- exists = (mdadm.status == 0)
- @current_resource.exists(exists)
- end
-
- def action_create
- 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}")
- shell_out!(command)
- Chef::Log.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})")
- end
- end
-
- def action_assemble
- 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}")
- shell_out!(command)
- Chef::Log.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})")
- end
- end
-
- def action_stop
- 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}")
- shell_out!(command)
- Chef::Log.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")
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/provider/mount.rb b/lib/chef/provider/mount.rb
index 9e9ee29bde..44fb94ca01 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,21 +17,15 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/mixin/shell_out"
-require "chef/provider"
+require_relative "../log"
+require_relative "../provider"
class Chef
class Provider
class Mount < Chef::Provider
- include Chef::Mixin::ShellOut
attr_accessor :unmount_retries
- def whyrun_supported?
- true
- end
-
def load_current_resource
true
end
@@ -41,73 +35,75 @@ class Chef
self.unmount_retries = 20
end
- def action_mount
+ action :mount do
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
- def action_umount
+ action :umount do
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
- def action_remount
+ action :remount do
if current_resource.mounted
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?
+ action :enable do
+ 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
- def action_disable
+ action :disable do
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 +118,17 @@ 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.
+ # We have to compare current resource device with device_fstab value
+ # because entry in /etc/fstab will be as per device_type.
+ # For Ex: 'LABEL=/tmp/ /mnt ext3 defaults 0 2', where 'device_type' is :label.
+ def device_unchanged?
+ @current_resource.device == device_fstab
+ 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
@@ -161,9 +168,24 @@ class Chef
if (tries -= 1) < 0
raise Chef::Exceptions::Mount, "Retries exceeded waiting for filesystem to unmount"
end
+
sleep 0.1
end
end
+
+ # Returns the new_resource device as per device_type
+ def device_fstab
+ # Removed "/" from the end of str, because it was causing idempotency issue.
+ device = @new_resource.device == "/" ? @new_resource.device : @new_resource.device.chomp("/")
+ case @new_resource.device_type
+ when :device
+ device
+ when :label
+ "LABEL=#{device}"
+ when :uuid
+ "UUID=#{device}"
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/mount/aix.rb b/lib/chef/provider/mount/aix.rb
index 12f0d67e6b..2cb29f5858 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +15,20 @@
# limitations under the License.
#
-require "chef/provider/mount"
+require_relative "../mount"
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|
+ 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}")
- when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/
+ logger.trace("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
+ when %r{^[/\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)
@@ -92,39 +121,40 @@ class Chef
def mount_fs
unless @current_resource.mounted
mountable?
- command = "mount -v #{@new_resource.fstype}"
+ command = [ "mount", "-v", @new_resource.fstype ]
- if !(@new_resource.options.nil? || @new_resource.options.empty?)
- command << " -o #{@new_resource.options.join(',')}"
+ unless @new_resource.options.nil? || @new_resource.options.empty?
+ command << "-o"
+ command << @new_resource.options.join(",")
end
command << case @new_resource.device_type
when :device
- " #{device_real}"
+ device_real
when :label
- " -L #{@new_resource.device}"
+ [ "-L", @new_resource.device ]
when :uuid
- " -U #{@new_resource.device}"
+ [ "-U", @new_resource.device ]
end
- command << " #{@new_resource.mount_point}"
+ 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
@@ -134,7 +164,7 @@ class Chef
disable_fs
end
::File.open("/etc/filesystems", "a") do |fstab|
- fstab.puts("#{@new_resource.mount_point}:")
+ fstab.puts("\n\n#{@new_resource.mount_point}:")
if network_device?
device_details = device_fstab.split(":")
fstab.puts("\tdev\t\t= #{device_details[1]}")
@@ -144,25 +174,36 @@ class Chef
end
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}")
+ fstab.puts "\toptions\t\t= #{@new_resource.options.join(",")}" unless @new_resource.options.nil? || @new_resource.options.empty?
+ 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
found_device = false
::File.open("/etc/filesystems", "r").each_line do |line|
case line
- when /^\/.+:\s*$/
- if line =~ /#{Regexp.escape(@new_resource.mount_point)}+:/
+ when %r{^/.+:\s*$}
+ if /#{Regexp.escape(@new_resource.mount_point)}+:/.match?(line)
found_device = true
else
found_device = false
end
end
- if !found_device
+ unless found_device
contents << line
end
end
@@ -170,7 +211,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/linux.rb b/lib/chef/provider/mount/linux.rb
new file mode 100644
index 0000000000..382e37d41a
--- /dev/null
+++ b/lib/chef/provider/mount/linux.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Antima Gupta (<agupta@chef.io>)
+# Copyright:: Copyright (c) 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 "../mount"
+
+class Chef
+ class Provider
+ class Mount
+ class Linux < Chef::Provider::Mount::Mount
+
+ provides :mount, os: "linux"
+
+ # Check to see if the volume is mounted.
+ # "findmnt" outputs the mount points with volume.
+ # Convert the mount_point of the resource to a real path in case it
+ # contains symlinks in its parents dirs.
+
+ def mounted?
+ mounted = false
+
+ real_mount_point = if ::File.exists? @new_resource.mount_point
+ ::File.realpath(@new_resource.mount_point)
+ else
+ @new_resource.mount_point
+ end
+
+ shell_out!("findmnt -rn").stdout.each_line do |line|
+ case line
+ # Permalink for device already mounted to mount point for : https://rubular.com/r/L0RNnD4gf2DJGl
+ when /\A#{Regexp.escape(real_mount_point)}\s+#{device_mount_regex}\s/
+ mounted = true
+ logger.trace("Special device #{device_logstring} mounted as #{real_mount_point}")
+ # Permalink for multiple devices mounted to the same mount point(i.e. '/proc') https://rubular.com/r/a356yzspU7N9TY
+ when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\s}
+ mounted = false
+ logger.trace("Special device #{$~[1]} mounted as #{real_mount_point}")
+ # Permalink for bind device mounted to an existing mount point: https://rubular.com/r/QAE0ilL3sm3Ldz
+ when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\[#{device_mount_regex}\]\s}
+ mounted = true
+ logger.trace("Bind device #{device_logstring} mounted as #{real_mount_point}")
+ # Permalink for network device mounted to an existing mount point: https://rubular.com/r/JRTXXGFdQtwCD6
+ when /\A#{Regexp.escape(real_mount_point)}\s+#{device_mount_regex}\[/
+ mounted = true
+ logger.trace("Network device #{device_logstring} mounted as #{real_mount_point}")
+ end
+ end
+ @current_resource.mounted(mounted)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb
index 07da6ac361..0bd81d5453 100644
--- a/lib/chef/provider/mount/mount.rb
+++ b/lib/chef/provider/mount/mount.rb
@@ -1,6 +1,6 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/provider/mount"
-require "chef/log"
+require_relative "../mount"
+require_relative "../../log"
class Chef
class Provider
@@ -47,27 +47,29 @@ 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?
# Check to see if there is a entry in /etc/fstab. Last entry for a volume wins.
enabled = false
+ unless ::File.exist?("/etc/fstab")
+ logger.debug "/etc/fstab not found, treating mount as not-enabled"
+ return
+ end
::File.foreach("/etc/fstab") do |line|
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")
- 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")
+ @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")
end
end
@current_resource.enabled(enabled)
@@ -89,10 +91,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}")
- when /^([\/\w])+\son\s#{Regexp.escape(real_mount_point)}\s+/
+ logger.trace("Special device #{device_logstring} mounted as #{real_mount_point}")
+ when %r{^([/\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)
@@ -101,132 +103,113 @@ class Chef
def mount_fs
unless @current_resource.mounted
mountable?
- command = "mount -t #{@new_resource.fstype}"
- command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty?
+ command = [ "mount", "-t", @new_resource.fstype ]
+ unless @new_resource.options.nil? || @new_resource.options.empty?
+ command << "-o"
+ command << @new_resource.options.join(",")
+ end
command << case @new_resource.device_type
when :device
- " #{device_real}"
+ device_real
when :label
- " -L #{@new_resource.device}"
+ [ "-L", @new_resource.device ]
when :uuid
- " -U #{@new_resource.device}"
+ [ "-U", @new_resource.device ]
end
- command << " #{@new_resource.mount_point}"
- shell_out!(command)
- Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
+ command << @new_resource.mount_point
+ shell_out!(*command)
+ 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}")
+ shell_out!("umount", @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)
+ 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
+ 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
if @current_resource.enabled
# The current options don't match what we have, so
- # disable, then enable.
- 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}")
+ # update the last matching entry with current option
+ # and order will remain the same.
+ edit_fstab
+ else
+ ::File.open("/etc/fstab", "a") do |fstab|
+ 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
end
def disable_fs
- if @current_resource.enabled
- contents = []
-
- found = false
- ::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")
- next
- else
- contents << line
- end
- end
-
- ::File.open("/etc/fstab", "w") do |fstab|
- contents.reverse_each { |line| fstab.puts line }
- end
- else
- Chef::Log.debug("#{@new_resource} is not enabled - nothing to do")
- end
+ edit_fstab(remove: true)
end
def network_device?
- @new_resource.device =~ /:/ || @new_resource.device =~ /\/\//
+ @new_resource.device.include?(":") || @new_resource.device.include?("//")
end
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
- def device_fstab
- case @new_resource.device_type
- when :device
- @new_resource.device
- when :label
- "LABEL=#{@new_resource.device}"
- when :uuid
- "UUID=#{@new_resource.device}"
- end
- end
-
def device_real
- if @real_device == nil
+ if @real_device.nil?
if @new_resource.device_type == :device
@real_device = @new_resource.device
else
@real_device = ""
- ret = shell_out("/sbin/findfs #{device_fstab}")
+ ret = shell_out("/sbin/findfs", device_fstab)
device_line = ret.stdout.lines.first # stdout.first consumes
@real_device = device_line.chomp unless device_line.nil?
end
end
- @real_device
+ # Removed "/" from the end of str, because it was causing idempotency issue.
+ @real_device == "/" ? @real_device : @real_device.chomp("/")
end
def device_logstring
case @new_resource.device_type
when :device
- "#{device_real}"
+ (device_real).to_s
when :label
"#{device_real} with label #{@new_resource.device}"
when :uuid
@@ -253,7 +236,7 @@ class Chef
if @new_resource.device_type == :device
device_mount_regex
else
- device_fstab
+ Regexp.union(device_fstab, device_mount_regex)
end
end
@@ -264,6 +247,35 @@ class Chef
@current_resource.pass == @new_resource.pass
end
+ # It will update or delete the entry from fstab.
+ def edit_fstab(remove: false)
+ if @current_resource.enabled
+ contents = []
+
+ found = false
+ ::File.readlines("/etc/fstab").reverse_each do |line|
+ if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s/
+ found = true
+ if remove
+ logger.trace("#{@new_resource} is removed from fstab")
+ else
+ contents << ("#{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 updated with new content in fstab")
+ end
+ next
+ else
+ contents << line
+ end
+ end
+
+ ::File.open("/etc/fstab", "w") do |fstab|
+ contents.reverse_each { |line| fstab.puts line }
+ end
+ else
+ logger.trace("#{@new_resource} is not enabled - nothing to do")
+ end
+ end
+
end
end
end
diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb
index a5a7a327cb..245e04f40e 100644
--- a/lib/chef/provider/mount/solaris.rb
+++ b/lib/chef/provider/mount/solaris.rb
@@ -1,8 +1,7 @@
-# Encoding: utf-8
# Author:: Hugo Fichter
# Author:: Lamont Granquist (<lamont@chef.io>)
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,16 +17,16 @@
# limitations under the License.
#
-require "chef/provider/mount"
-require "chef/log"
-require "forwardable"
+require_relative "../mount"
+require_relative "../../log"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
class Mount
# Mount Solaris File systems
class Solaris < Chef::Provider::Mount
- provides :mount, platform: %w{openindiana opensolaris nexentacore omnios solaris2 smartos}
+ provides :mount, platform_family: "solaris_based"
extend Forwardable
@@ -74,24 +73,27 @@ class Chef
end
def mount_fs
- actual_options = options || []
- actual_options.delete("noauto")
- command = "mount -F #{fstype}"
- command << " -o #{actual_options.join(',')}" unless actual_options.empty?
- command << " #{device} #{mount_point}"
+ actual_options = native_options(options)
+ actual_options.delete("-")
+ command = [ "mount", "-F", fstype ]
+ unless actual_options.empty?
+ command << "-o"
+ command << actual_options.join(",")
+ end
+ command << [ device, mount_point ]
shell_out!(command)
end
def umount_fs
- shell_out!("umount #{mount_point}")
+ shell_out!("umount", mount_point)
end
def remount_fs
# FIXME: Should remount always do the remount or only if the options change?
- actual_options = options || []
- actual_options.delete("noauto")
- mount_options = actual_options.empty? ? "" : ",#{actual_options.join(',')}"
- shell_out!("mount -o remount#{mount_options} #{mount_point}")
+ 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
def enable_fs
@@ -112,7 +114,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 +123,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 &&
@@ -150,13 +152,13 @@ class Chef
# /dev/dsk/c1t0d0s0 on / type ufs read/write/setuid/devices/intr/largefiles/logging/xattr/onerror=panic/dev=700040 on Tue May 1 11:33:55 2012
def mounted?
mounted = false
- shell_out!("mount -v").stdout.each_line do |line|
+ 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}")
+ when %r{^([/\w]+)\son\s#{Regexp.escape(mount_point)}\s+}
+ logger.trace("Special device #{Regexp.last_match[1]} is mounted as #{mount_point}")
mounted = false
end
end
@@ -168,7 +170,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]/
@@ -176,7 +179,7 @@ class Chef
# solaris /etc/vfstab format:
# device device mount FS fsck mount mount
# to mount to fsck point type pass at boot options
- when /^#{device_regex}\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
+ when %r{^#{device_regex}\s+[-/\w]+\s+#{Regexp.escape(mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)}
enabled = true
fstype = Regexp.last_match[1]
options = Regexp.last_match[4]
@@ -190,12 +193,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+/
+ when %r{^[-/\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 +223,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 +236,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 +250,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..81e9ad6b1d 100644
--- a/lib/chef/provider/mount/windows.rb
+++ b/lib/chef/provider/mount/windows.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/provider/mount"
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/util/windows/net_use"
- require "chef/util/windows/volume"
+require_relative "../mount"
+if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+ require_relative "../../util/windows/net_use"
+ require_relative "../../util/windows/volume"
end
class Chef
@@ -30,7 +30,7 @@ class Chef
provides :mount, os: "windows"
def is_volume(name)
- name =~ /^\\\\\?\\Volume\{[\w-]+\}\\$/ ? true : false
+ /^\\\\\?\\Volume\{[\w-]+\}\\$/.match?(name) ? true : false
end
def initialize(new_resource, run_context)
@@ -40,43 +40,43 @@ class Chef
def load_current_resource
if is_volume(@new_resource.device)
- @mount = Chef::Util::Windows::Volume.new(@new_resource.name)
- else #assume network drive
- @mount = Chef::Util::Windows::NetUse.new(@new_resource.name)
+ @mount = Chef::Util::Windows::Volume.new(@new_resource.mount_point)
+ else # assume network drive
+ @mount = Chef::Util::Windows::NetUse.new(@new_resource.mount_point)
end
@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
def mount_fs
unless @current_resource.mounted
- @mount.add(:remote => @new_resource.device,
- :username => @new_resource.username,
- :domainname => @new_resource.domain,
- :password => @new_resource.password)
- Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
+ @mount.add(remote: @new_resource.device,
+ username: @new_resource.username,
+ domainname: @new_resource.domain,
+ password: @new_resource.password)
+ 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..74e24f6a44 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) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,8 +26,8 @@ class Chef
end
def method_missing(method_sym, *arguments, &block)
- if method_sym.to_s =~ /^action_/
- Chef::Log.debug("NoOp-ing for #{method_sym}")
+ if /^action_/.match?(method_sym.to_s)
+ logger.trace("NoOp-ing for #{method_sym}")
else
super
end
diff --git a/lib/chef/provider/ohai.rb b/lib/chef/provider/ohai.rb
deleted file mode 100644
index 6b5a605ed5..0000000000
--- a/lib/chef/provider/ohai.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# Author:: Michael Leianrtas (<mleinartas@gmail.com>)
-# Copyright:: Copyright 2010-2016, Michael Leinartas
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "ohai"
-
-class Chef
- class Provider
- class Ohai < Chef::Provider
- provides :ohai
-
- def whyrun_supported?
- true
- end
-
- def load_current_resource
- true
- end
-
- def action_reload
- 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
- # 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
- node.automatic_attrs.merge! ohai.data
- Chef::Log.info("#{@new_resource} reloaded")
- end
- end
- end
- end
-end
diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb
deleted file mode 100644
index 69ecf2ddb9..0000000000
--- a/lib/chef/provider/osx_profile.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-#
-# Author:: Nate Walck (<nate.walck@gmail.com>)
-# Copyright:: Copyright 2015-2016, Facebook, 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/provider"
-require "chef/resource"
-require "chef/resource/file"
-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
-
- def load_current_resource
- @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_profile_hash = get_profile_hash(@new_resource.profile)
- @new_profile_hash["PayloadUUID"] =
- config_uuid(@new_profile_hash) if @new_profile_hash
-
- if @new_profile_hash
- @new_profile_identifier = @new_profile_hash["PayloadIdentifier"]
- else
- @new_profile_identifier = @new_resource.identifier ||
- @new_resource.profile_name
- end
-
- current_profile = nil
- if all_profiles && !all_profiles.empty?
- current_profile = all_profiles["_computerlevel"].find do |item|
- item["ProfileIdentifier"] == @new_profile_identifier
- end
- end
- @current_resource.profile(current_profile)
- end
-
- def define_resource_requirements
- requirements.assert(:remove) do |a|
- if @new_profile_identifier
- a.assertion do
- !@new_profile_identifier.nil? &&
- !@new_profile_identifier.end_with?(".mobileconfig") &&
- /^\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 do
- !new_profile_name.end_with?(".mobileconfig") &&
- /^\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 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 do
- @new_profile_hash.end_with?(".mobileconfig")
- end
- a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile"
- end
- end
- end
-
- def action_install
- unless profile_installed?
- converge_by("install profile #{@new_profile_identifier}") do
- profile_path = write_profile_to_disk
- install_profile(profile_path)
- get_installed_profiles(true)
- end
- end
- end
-
- def action_remove
- # Clean up profile after removing it
- if profile_installed?
- converge_by("remove profile #{@new_profile_identifier}") do
- remove_profile
- get_installed_profiles(true)
- end
- end
- end
-
- def load_profile_hash(new_profile)
- # file must exist in cookbook
- if new_profile.end_with?(".mobileconfig")
- unless cookbook_file_available?(new_profile)
- error_string = "#{self}: '#{new_profile}' not found in cookbook"
- raise Chef::Exceptions::FileNotFound, error_string
- end
- cookbook_profile = cache_cookbook_profile(new_profile)
- return read_plist(cookbook_profile)
- else
- return nil
- end
- end
-
- def cookbook_file_available?(cookbook_file)
- run_context.has_cookbook_file_in_cookbook?(
- @new_resource.cookbook_name, cookbook_file
- )
- end
-
- def get_cache_dir
- cache_dir = Chef::FileCache.create_cache_path(
- "profiles/#{@new_resource.cookbook_name}"
- )
- end
-
- def cache_cookbook_profile(cookbook_file)
- Chef::FileCache.create_cache_path(
- ::File.join(
- "profiles",
- @new_resource.cookbook_name,
- ::File.dirname(cookbook_file)
- )
- )
- remote_file = Chef::Resource::CookbookFile.new(
- ::File.join(
- get_cache_dir,
- "#{cookbook_file}.remote"
- ),
- run_context
- )
- remote_file.cookbook_name = @new_resource.cookbook_name
- remote_file.source(cookbook_file)
- remote_file.backup(false)
- remote_file.run_action(:create)
- remote_file.path
- end
-
- def get_profile_hash(new_profile)
- if new_profile.is_a?(Hash)
- return new_profile
- elsif new_profile.is_a?(String)
- return load_profile_hash(new_profile)
- end
- end
-
- def config_uuid(profile)
- # Make a UUID of the profile contents and return as string
- UUIDTools::UUID.sha1_create(
- UUIDTools::UUID_DNS_NAMESPACE,
- profile.to_s
- ).to_s
- end
-
- def write_profile_to_disk
- @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
- end
-
- def install_profile(profile_path)
- cmd = "profiles -I -F '#{profile_path}'"
- Chef::Log.debug("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}")
- shellout_results = shell_out(cmd)
- shellout_results.exitstatus
- end
-
- def get_installed_profiles(update = nil)
- if update
- node.run_state[:config_profiles] = query_installed_profiles
- else
- node.run_state[:config_profiles] ||= query_installed_profiles
- end
- end
-
- def query_installed_profiles
- # Dump all profile metadata to a tempfile
- tempfile = generate_tempfile
- write_installed_profiles(tempfile)
- installed_profiles = read_plist(tempfile)
- Chef::Log.debug("Saved profiles to run_state")
- # Clean up the temp file as we do not need it anymore
- ::File.unlink(tempfile)
- installed_profiles
- end
-
- def generate_tempfile
- tempfile = ::Dir::Tmpname.create("allprofiles.plist") {}
- end
-
- def write_installed_profiles(tempfile)
- cmd = "profiles -P -o '#{tempfile}'"
- shell_out!(cmd)
- end
-
- def read_plist(xml_file)
- Plist.parse_xml(xml_file)
- end
-
- def profile_installed?
- # Profile Identifier and UUID must match a currently installed profile
- if @current_resource.profile.nil? || @current_resource.profile.empty?
- false
- else
- if @new_resource.action.include?(:remove)
- true
- else
- @current_resource.profile["ProfileUUID"] ==
- @new_profile_hash["PayloadUUID"]
- end
- end
- end
-
- end
- end
-end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 3f641145e6..198286c75f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,53 @@
# limitations under the License.
#
-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_relative "../mixin/subclass_directive"
+require_relative "../log"
+require_relative "../file_cache"
+require_relative "../platform"
+require_relative "../decorator/lazy_array"
+require "shellwords" unless defined?(Shellwords)
class Chef
class Provider
class Package < Chef::Provider
- include Chef::Mixin::Command
- include Chef::Mixin::ShellOut
extend Chef::Mixin::SubclassDirective
- # subclasses declare this if they want all their arguments as arrays of packages and names
+ # subclasses declare this if they want all their arguments as arrays of packages and names.
+ # any new packages using this should also use allow_nils below.
+ #
subclass_directive :use_multipackage_api
- # subclasses declare this if they want sources (filenames) pulled from their package names
+
+ # subclasses declare this if they want sources (filenames) pulled from their package names.
+ # this is for package providers that take a path into the filesystem (rpm, dpkg).
+ #
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. all providers are encouraged to
+ # migrate to using this as it simplifies dealing with package aliases in subclasses.
+ #
+ subclass_directive :allow_nils
+
+ # subclasses that implement complex pattern matching using constraints, particularly the yum and
+ # dnf classes, should filter the installed version against the desired version constraint and
+ # return nil if it does not match. this means that 'nil' does not mean that no version of the
+ # package is installed, but that the installed version does not satisfy the desired constraints.
+ # (the package plus the constraints are not installed)
+ #
+ # [ this may arguably be useful for all package providers and it greatly simplifies the logic
+ # in the superclass that gets executed, so maybe this should always be used now? ]
+ #
+ # note that when using this feature that the current_resource.version must be loaded with the
+ # correct currently installed version, without doing the filtering -- for reporting and for
+ # correctly displaying version upgrades. that means there are 3 different arrays which must be
+ # loaded by the subclass: candidate_version, magic_version and current_resource.version.
+ #
+ # NOTE: magic_version is a terrible name, but I couldn't think of anything better, at least this
+ # way it stands out clearly.
+ #
+ subclass_directive :use_magic_version
+
#
# Hook that subclasses use to populate the candidate_version(s)
#
@@ -47,20 +74,19 @@ 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?
@@ -81,26 +107,19 @@ class Chef
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 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)
- end
- end
- end
+ prepare_for_installation
converge_by(install_description) do
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
@@ -108,6 +127,7 @@ class Chef
description = []
target_version_array.each_with_index do |target_version, i|
next if target_version.nil?
+
package_name = package_name_array[i]
description << "install version #{target_version} of package #{package_name}"
end
@@ -116,9 +136,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
@@ -127,7 +147,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
@@ -136,6 +156,7 @@ class Chef
description = []
target_version_array.each_with_index do |target_version, i|
next if target_version.nil?
+
package_name = package_name_array[i]
candidate_version = candidate_version_array[i]
current_version = current_version_array[i] || "uninstalled"
@@ -146,17 +167,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
@@ -181,43 +202,64 @@ 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
- Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
- return
- end
-
- unless @new_resource.response_file
- Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do")
- return
+ action :lock do
+ 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
+ logger.trace("#{new_resource} is already locked")
end
+ end
- 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|
- reconfig_package(name, version)
-
+ action :unlock do
+ 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
- Chef::Log.info("#{@new_resource} reconfigured")
end
else
- Chef::Log.debug("#{@new_resource} preseeding has not changed - nothing to do")
+ 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
+
+ # Subclasses will override this to a method and provide a preseed file path
+ def prepare_for_installation; end
+
# @todo use composition rather than inheritance
def multipackage_api_adapter(name, version)
@@ -248,13 +290,27 @@ class Chef
raise Chef::Exceptions::UnsupportedAction, "#{self} does not support pre-seeding package install/upgrade instructions"
end
- def reconfig_package(name, version)
+ def reconfig_package(name)
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 instead")
+ if options
+ " #{options.is_a?(Array) ? Shellwords.join(options) : options}"
+ else
+ ""
+ end
end
# Check the current_version against either the candidate_version or the new_version
@@ -264,21 +320,56 @@ class Chef
#
# This MUST have 'equality' semantics -- the exact thing matches the exact thing.
#
- # The current_version should probably be dropped out of the method signature, it should
- # always be the first argument.
- #
# 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
+ # 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.
#
- # `current_version_equals?(version)` would be a better name
+ # `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)
- return false unless current_version && target_version
- 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.
+ #
+ # (In other words, pull requests to introduce domain specific mangling of versions into this method
+ # will be closed -- that logic must go into the subclass -- we understand that this is far from perfect
+ # but it is a better default than outright buggy things like v1.to_f <=> v2.to_f)
+ #
+ def version_compare(v1, v2)
+ gem_v1 = Gem::Version.new(v1.gsub(/\A\s*(#{Gem::Version::VERSION_PATTERN}).*/, '\1'))
+ gem_v2 = Gem::Version.new(v2.gsub(/\A\s*(#{Gem::Version::VERSION_PATTERN}).*/, '\1'))
+
+ gem_v1 <=> gem_v2
end
# Check the current_version against the new_resource.version, possibly using fuzzy
@@ -286,54 +377,12 @@ class Chef
#
# Subclasses MAY override this to provide fuzzy matching on the resource ('>=' and '~>' stuff)
#
- # This should only ever be offered the same arguments (so they should most likely be
- # removed from the method signature).
+ # `version_satisfied_by?(version, constraint)` might be a better name to make this generic.
#
- # `new_version_satisfied?()` might be a better name
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}")
-
- if resource.updated_by_last_action?
- resource.path
- else
- false
- end
- end
-
- # @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}")
- # 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}")
-
- if template_available?(@new_resource.response_file)
- Chef::Log.debug("#{@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 = Chef::Resource::CookbookFile.new(cache_seed_to, run_context)
- else
- 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.backup(false)
- remote_file
- end
-
# helper method used by subclasses
#
def as_array(thing)
@@ -352,9 +401,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
@@ -369,8 +421,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
@@ -383,40 +438,51 @@ class Chef
@target_version_array ||=
begin
target_version_array = []
-
each_package do |package_name, new_version, current_version, candidate_version|
case action
when :upgrade
- if target_version_already_installed?(current_version, new_version)
- # this is an odd use case
- Chef::Log.debug("#{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 target_version_already_installed?(current_version, candidate_version)
- Chef::Log.debug("#{new_resource} #{package_name} #{candidate_version} is already installed")
+ if current_version.nil?
+ # with use_magic_version there may be a package installed, but it fails the user's
+ # requested new_resource.version constraints
+ logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
+ target_version_array.push(candidate_version)
+ elsif version_equals?(current_version, new_version)
+ # this is a short-circuit to avoid needing to (expensively) query the candidate_version which must come later
+ logger.trace("#{new_resource} #{package_name} #{new_version} is already installed")
target_version_array.push(nil)
elsif candidate_version.nil?
- Chef::Log.debug("#{new_resource} #{package_name} has no candidate_version to upgrade to")
+ logger.trace("#{new_resource} #{package_name} has no candidate_version to upgrade to")
+ 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 !allow_downgrade && version_compare(current_version, candidate_version) == 1
+ 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 new_version && !use_magic_version?
if version_requirement_satisfied?(current_version, new_version)
- Chef::Log.debug("#{new_resource} #{package_name} #{current_version} satisifies #{new_version} requirement")
+ logger.trace("#{new_resource} #{package_name} #{current_version} satisfies #{new_version} requirement")
+ target_version_array.push(nil)
+ elsif current_version && !allow_downgrade && version_compare(current_version, new_version) == 1
+ logger.warn("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{new_version}. Skipping...)")
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}")
+ # with use_magic_version there may be a package installed, but it fails the user's
+ # requested new_resource.version constraints
+ 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
@@ -472,8 +538,15 @@ class Chef
missing = []
each_package do |package_name, new_version, current_version, candidate_version|
next if new_version.nil? || current_version.nil?
- if !version_requirement_satisfied?(current_version, new_version) && candidate_version.nil?
- missing.push(package_name)
+
+ if use_magic_version?
+ if !magic_version && candidate_version.nil?
+ missing.push(package_name)
+ end
+ else
+ if !version_requirement_satisfied?(current_version, new_version) && candidate_version.nil?
+ missing.push(package_name)
+ end
end
end
missing
@@ -486,7 +559,7 @@ class Chef
def each_package
package_name_array.each_with_index do |package_name, i|
candidate_version = candidate_version_array[i]
- current_version = current_version_array[i]
+ current_version = use_magic_version? ? magic_version[i] : current_version_array[i]
new_version = new_version_array[i]
yield package_name, new_version, current_version, candidate_version
end
@@ -494,12 +567,12 @@ 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
@@ -511,12 +584,12 @@ class Chef
# @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
@@ -524,11 +597,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.
@@ -541,7 +617,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
@@ -550,43 +626,12 @@ class Chef
end
end
- # @todo: extract apt/dpkg specific preseeding to a helper class
- def template_available?(path)
- run_context.has_template_in_cookbook?(new_resource.cookbook_name, path)
- end
-
- # @todo: extract apt/dpkg specific preseeding to a helper class
- def cookbook_file_available?(path)
- run_context.has_cookbook_file_in_cookbook?(new_resource.cookbook_name, path)
- end
-
def allow_downgrade
- if @new_resource.respond_to?("allow_downgrade")
- @new_resource.allow_downgrade
- else
- false
- end
- end
-
- def shell_out_with_timeout(*command_args)
- shell_out(*add_timeout_option(command_args))
- end
-
- def shell_out_with_timeout!(*command_args)
- shell_out!(*add_timeout_option(command_args))
- end
-
- def add_timeout_option(command_args)
- 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)
- args << options
+ if new_resource.respond_to?("allow_downgrade")
+ new_resource.allow_downgrade
else
- args << { :timeout => new_resource.timeout ? new_resource.timeout : 900 }
+ true
end
- args
end
end
end
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
deleted file mode 100644
index 728f181055..0000000000
--- a/lib/chef/provider/package/aix.rb
+++ /dev/null
@@ -1,140 +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])
- when /^#{@new_resource.package_name}:/
- Chef::Log.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
- 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 8af089e14a..dbacaedb07 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,19 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/apt_package"
+require_relative "../package"
+require_relative "deb"
+require_relative "../../resource/apt_package"
class Chef
class Provider
class Package
class Apt < Chef::Provider::Package
+ include Chef::Provider::Package::Deb
use_multipackage_api
- provides :package, platform_family: "debian"
- provides :apt_package, os: "linux"
+ provides :package, platform_family: "debian", target_mode: true
+ provides :apt_package, target_mode: true
def initialize(new_resource, run_context)
super
@@ -44,7 +46,7 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { !new_resource.source }
- a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source attribute. Use dpkg provider instead")
+ a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source property. Use dpkg provider instead")
end
end
@@ -70,11 +72,28 @@ class Chef
@candidate_version ||= get_candidate_versions
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!("apt-mark", "showhold")
+ locked.stdout.each_line.map(&:strip)
+ end
+ end
+
def install_package(name, version)
package_name = name.zip(version).map do |n, v|
package_data[n][:virtual] ? n : "#{n}=#{v}"
end
- run_noninteractive("apt-get -q -y", default_release_options, new_resource.options, "install", package_name)
+ dgrade = "--allow-downgrades" if supports_allow_downgrade? && allow_downgrade
+ run_noninteractive("apt-get", "-q", "-y", dgrade, config_file_options, default_release_options, options, "install", package_name)
end
def upgrade_package(name, version)
@@ -85,38 +104,73 @@ class Chef
package_name = name.map do |n|
package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
end
- run_noninteractive("apt-get -q -y", new_resource.options, "remove", package_name)
+ run_noninteractive("apt-get", "-q", "-y", options, "remove", package_name)
end
def purge_package(name, version)
package_name = name.map do |n|
package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
end
- run_noninteractive("apt-get -q -y", new_resource.options, "purge", package_name)
+ 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)
+ def lock_package(name, version)
+ run_noninteractive("apt-mark", options, "hold", name)
end
- def reconfig_package(name, version)
- Chef::Log.info("#{new_resource} reconfiguring")
- run_noninteractive("dpkg-reconfigure", name)
+ def unlock_package(name, version)
+ run_noninteractive("apt-mark", options, "unhold", name)
end
private
- # 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(*args)
- shell_out_with_timeout!(a_to_s(*args), :env => { "DEBIAN_FRONTEND" => "noninteractive" })
+ # @return [String] version of apt-get which is installed
+ def apt_version
+ @apt_version ||= shell_out("apt-get --version").stdout.match(/^apt (\S+)/)[1]
+ end
+
+ # @return [Boolean] if apt-get supports --allow-downgrades
+ def supports_allow_downgrade?
+ return @supports_allow_downgrade unless @supports_allow_downgrade.nil?
+
+ @supports_allow_downgrade = ( version_compare(apt_version, "1.1.0") >= 0 )
+ end
+
+ # 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("dpkg", "--compare-versions", v1.to_s, "gt", v2.to_s).error?
+ 1
+ elsif !shell_out("dpkg", "--compare-versions", v1.to_s, "eq", v2.to_s).error?
+ 0
+ else
+ -1
+ 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
+ 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.include?("--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)
@@ -126,19 +180,20 @@ class Chef
case line
when /^\s{2}Installed: (.+)$/
current_version = ( $1 != "(none)" ) ? $1 : nil
- Chef::Log.debug("#{new_resource} installed version for #{pkg} is #{$1}")
+ logger.trace("#{new_resource} installed version for #{pkg} is #{$1}")
when /^\s{2}Candidate: (.+)$/
candidate_version = ( $1 != "(none)" ) ? $1 : nil
- Chef::Log.debug("#{new_resource} candidate version for #{pkg} is #{$1}")
+ 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
+ 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])
@@ -146,7 +201,8 @@ class Chef
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
- return set.to_a.first
+
+ set.to_a.first
end
def package_data_for(pkg)
@@ -161,15 +217,15 @@ class Chef
if newpkg
virtual = true
- Chef::Log.info("#{new_resource} is a virtual package, actually acting on package[#{newpkg}]")
+ logger.info("#{new_resource} is a virtual package, actually acting on package[#{newpkg}]")
current_version, candidate_version = resolve_package_versions(newpkg)
end
end
- return {
- current_version: current_version,
- candidate_version: candidate_version,
- virtual: virtual,
+ {
+ current_version: current_version,
+ candidate_version: candidate_version,
+ virtual: virtual,
}
end
diff --git a/lib/chef/provider/package/bff.rb b/lib/chef/provider/package/bff.rb
new file mode 100644
index 0000000000..430d3503b6
--- /dev/null
+++ b/lib/chef/provider/package/bff.rb
@@ -0,0 +1,143 @@
+#
+# Author:: Deepali Jagtap
+# Copyright:: Copyright (c) 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 "../package"
+require_relative "../../resource/package"
+require_relative "../../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.package_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.package_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("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("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("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!("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!("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!("installp", "-u", name)
+ logger.trace("#{new_resource} removed version #{new_resource.version}")
+ else
+ shell_out!("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..4aaa034fa4
--- /dev/null
+++ b/lib/chef/provider/package/cab.rb
@@ -0,0 +1,188 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "../package"
+require_relative "../../resource/cab_package"
+require_relative "../../mixin/shell_out"
+require_relative "../../mixin/uris"
+require_relative "../../mixin/checksum"
+require "cgi" unless defined?(CGI)
+
+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.path
+ end
+
+ def 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(::CGI.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)
+ with_os_architecture(nil) do
+ result = shell_out("dism.exe /Online /English #{command} /NoRestart", timeout: new_resource.timeout)
+ if result.exitstatus == -2146498530
+ raise Chef::Exceptions::Package, "The specified package is not applicable to this image." if result.stdout.include?("0x800f081e")
+
+ result.error!
+ end
+ result
+ 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["version"] == package["version"].chomp && 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 36bc170590..06db3c2979 100644
--- a/lib/chef/provider/package/chocolatey.rb
+++ b/lib/chef/provider/package/chocolatey.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,29 +15,28 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/chocolatey_package"
-require "chef/mixin/powershell_out"
+require_relative "../package"
+require_relative "../../resource/chocolatey_package"
+require_relative "../../win32/api/command_line_helper" if ChefUtils.windows?
class Chef
class Provider
class Package
class Chocolatey < Chef::Provider::Package
- include Chef::Mixin::PowershellOut
-
- provides :chocolatey_package, os: "windows"
+ include Chef::ReservedNames::Win32::API::CommandLineHelper if ChefUtils.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
-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
-variable is correctly set. You can verify this with the PowerShell command
-'#{PATHFINDING_POWERSHELL_COMMAND}'.
-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
+ variable is correctly set. You can verify this with the PowerShell command
+ '#{PATHFINDING_POWERSHELL_COMMAND}'.
+ EOS
# Responsible for building the current_resource.
#
@@ -54,7 +53,7 @@ EOS
# The check that Chocolatey is installed is in #choco_exe.
- # Chocolatey source attribute points to an alternate feed
+ # Chocolatey source property points to an alternate feed
# and not a package specific alternate source like other providers
# so we want to assert candidates exist for the alternate source
requirements.assert(:upgrade, :install) do |a|
@@ -80,17 +79,17 @@ EOS
name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }
name_nil_versions = name_versions_to_install.select { |n, v| v.nil? }
- name_has_versions = name_versions_to_install.reject { |n, v| v.nil? }
+ name_has_versions = name_versions_to_install.compact
# 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
unless name_nil_versions.empty?
cmd_names = name_nil_versions.keys
- choco_command("install -y", cmd_args, *cmd_names)
+ choco_command("install", "-y", cmd_args, *cmd_names)
end
end
@@ -102,17 +101,17 @@ EOS
name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }
name_nil_versions = name_versions_to_install.select { |n, v| v.nil? }
- name_has_versions = name_versions_to_install.reject { |n, v| v.nil? }
+ name_has_versions = name_versions_to_install.compact
# 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
unless name_nil_versions.empty?
cmd_names = name_nil_versions.keys
- choco_command("upgrade -y", cmd_args, *cmd_names)
+ choco_command("upgrade", "-y", cmd_args, *cmd_names)
end
end
@@ -121,27 +120,29 @@ EOS
# @param names [Array<String>] array of package names to install
# @param versions [Array<String>] array of versions to install
def remove_package(names, versions)
- 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
+ choco_command("uninstall", "-y", cmd_args(include_source: false), *names)
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
#
@@ -150,17 +151,18 @@ EOS
@choco_exe ||= begin
# if this check is in #define_resource_requirements, it won't get
# run before choco.exe gets called from #load_current_resource.
- exe_path = ::File.join(choco_install_path.to_s, "bin", "choco.exe")
+ exe_path = ::File.join(choco_install_path, "bin", "choco.exe")
raise Chef::Exceptions::MissingLibrary, CHOCO_MISSING_MSG unless ::File.exist?(exe_path)
+
exe_path
end
end
# lets us mock out an incorrect value for testing.
def choco_install_path
- @choco_install_path ||= powershell_out!(
- PATHFINDING_POWERSHELL_COMMAND
- ).stdout.chomp
+ result = powershell_exec!(PATHFINDING_POWERSHELL_COMMAND).result
+ result = "" if result.empty?
+ result
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!(choco_exe, *args, returns: new_resource.returns)
end
# Use the available_packages Hash helper to create an array suitable for
@@ -206,19 +208,9 @@ EOS
# @param include_source [Boolean] should the source parameter be added
# @return [String] options from new_resource or empty string
def cmd_args(include_source: true)
- cmd_args = [ new_resource.options ]
- cmd_args.push( "-source #{new_resource.source}" ) if new_resource.source && include_source
- args_to_string(*cmd_args)
- end
-
- # Helper to nicely convert variable string args into a single command line. It
- # will compact nulls or empty strings and join arguments with single spaces, without
- # introducing any double-spaces for missing args.
- #
- # @param args [String] variable number of string arguments
- # @return [String] nicely concatenated string or empty string
- def args_to_string(*args)
- args.reject { |i| i.nil? || i == "" }.join(" ")
+ cmd_args = new_resource.options.is_a?(String) ? command_line_to_argv_w_helper(new_resource.options) : Array(new_resource.options)
+ cmd_args += common_options(include_source: include_source)
+ cmd_args
end
# Available packages in chocolatey as a Hash of names mapped to versions
@@ -227,15 +219,24 @@ EOS
#
# @return [Hash] name-to-version mapping of available packages
def available_packages
- @available_packages ||=
- begin
- cmd = [ "list -r #{package_name_array.join ' '}" ]
- 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]
+ return @available_packages if @available_packages
+
+ @available_packages = {}
+ package_name_array.each do |pkg|
+ available_versions =
+ begin
+ cmd = [ "list", "-r", pkg ]
+ cmd += common_options
+ cmd.push( new_resource.list_options ) if new_resource.list_options
+
+ 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
@@ -243,11 +244,12 @@ 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 ||= 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)
+ # (names are downcased for case-insensitive matching)
#
# @param cmd [String] command to run
# @return [Hash] list output converted to ruby Hash
@@ -255,8 +257,9 @@ EOS
hash = {}
choco_command(*args).stdout.each_line do |line|
next if line.start_with?("Chocolatey v")
+
name, version = line.split("|")
- hash[name.downcase] = version.chomp
+ hash[name.downcase] = version&.chomp
end
hash
end
@@ -266,7 +269,15 @@ 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
+
+ def common_options(include_source: true)
+ args = []
+ args.push( [ "-source", new_resource.source ] ) if new_resource.source && include_source
+ args.push( [ "--user", new_resource.user ] ) if new_resource.user
+ args.push( [ "--password", new_resource.password ]) if new_resource.password
+ args
end
end
end
diff --git a/lib/chef/provider/package/deb.rb b/lib/chef/provider/package/deb.rb
new file mode 100644
index 0000000000..1b1b151c93
--- /dev/null
+++ b/lib/chef/provider/package/deb.rb
@@ -0,0 +1,131 @@
+#
+# Author:: Kapil Chouhan (<kapil.chouhan@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "../package"
+
+class Chef
+ class Provider
+ class Package
+ module Deb
+ def self.included(base)
+ base.class_eval do
+ use_multipackage_api
+
+ 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
+ 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
+ preseed_package(preseed_file)
+ multipackage_api_adapter(new_resource.package_name, current_resource.version) do |name, _version|
+ reconfig_package(name)
+ end
+ logger.info("#{new_resource} reconfigured")
+ end
+ else
+ logger.trace("#{new_resource} preseeding has not changed - nothing to do")
+ end
+ end
+
+ # This method is used for getting preseed file
+ # it will return preseed file path or false if response_file is present
+ def prepare_for_installation
+ if new_resource.response_file && 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)
+ end
+ end
+ end
+
+ def get_preseed_file(name, version)
+ resource = preseed_resource(name, version)
+ resource.run_action(:create)
+ logger.trace("#{new_resource} fetched preseed file to #{resource.path}")
+
+ if resource.updated_by_last_action?
+ resource.path
+ else
+ false
+ end
+ end
+
+ 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}")
+ # The full path where the preseed file will be stored
+ cache_seed_to = "#{file_cache_dir}/#{name}-#{version}.seed"
+
+ logger.trace("#{new_resource} fetching preseed file to #{cache_seed_to}")
+
+ 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)
+ 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}"
+ raise Chef::Exceptions::FileNotFound, message
+ end
+
+ remote_file.cookbook_name = new_resource.cookbook_name
+ remote_file.source(new_resource.response_file)
+ remote_file.backup(false)
+ remote_file
+ end
+
+ def preseed_package(preseed_file)
+ logger.info("#{new_resource} pre-seeding package installation instructions")
+ run_noninteractive("debconf-set-selections", preseed_file)
+ end
+
+ def reconfig_package(name)
+ logger.info("#{new_resource} reconfiguring")
+ run_noninteractive("dpkg-reconfigure", *name)
+ end
+
+ # Runs command via shell_out with magic environment to disable
+ # interactive prompts.
+ def run_noninteractive(*command)
+ shell_out!(*command, env: { "DEBIAN_FRONTEND" => "noninteractive" })
+ end
+
+ private
+
+ def template_available?(path)
+ run_context.has_template_in_cookbook?(new_resource.cookbook_name, path)
+ end
+
+ def cookbook_file_available?(path)
+ run_context.has_cookbook_file_in_cookbook?(new_resource.cookbook_name, path)
+ end
+ end
+ end
+ end
+ 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..5c74ad0414
--- /dev/null
+++ b/lib/chef/provider/package/dnf.rb
@@ -0,0 +1,279 @@
+#
+# Copyright:: Copyright (c) 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 "../package"
+require_relative "../../resource/dnf_package"
+require_relative "../../mixin/which"
+require_relative "../../mixin/shell_out"
+require_relative "../../mixin/get_source_from_package"
+require_relative "dnf/python_helper"
+require_relative "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
+ use_magic_version
+
+ # 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 load_after_resource
+ # force the installed version array to repopulate
+ @current_version = []
+ @after_resource = Chef::Resource::DnfPackage.new(new_resource.name)
+ after_resource.package_name(new_resource.package_name)
+ after_resource.version(get_current_versions)
+
+ after_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 magic_version
+ package_name_array.each_with_index.map do |pkg, i|
+ magical_version(i).version_with_arch
+ end
+ end
+
+ def get_current_versions
+ package_name_array.each_with_index.map do |pkg, i|
+ current_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| magical_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
+
+ # NB: the dnf_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 `dnf versionlock | grep '^pattern`` kind of approach
+ def lock_package(names, versions)
+ dnf("-d0", "-e0", "-y", options, "versionlock", "add", resolved_package_lock_names(names))
+ end
+
+ # NB: the dnf_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 `dnf versionlock | grep '^pattern`` kind of approach
+ def unlock_package(names, versions)
+ # dnf versionlock delete on rhel6 needs the glob nonsense in the following command
+ dnf("-d0", "-e0", "-y", options, "versionlock", "delete", resolved_package_lock_names(names).map { |n| "*:#{n}-*" })
+ end
+
+ private
+
+ # 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|
+ unless name.nil?
+ if magical_version(i).version.nil?
+ available_version(i).name
+ else
+ magical_version(i).name
+ end
+ end
+ end
+ end
+
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = dnf("versionlock", "list")
+ locked.stdout.each_line.map do |line|
+ line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip
+ end
+ end
+ end
+
+ def packages_all_locked?(names, versions)
+ resolved_package_lock_names(names).all? { |n| locked_packages.include? n }
+ end
+
+ def packages_all_unlocked?(names, versions)
+ !resolved_package_lock_names(names).any? { |n| locked_packages.include? n }
+ end
+
+ def version_gt?(v1, v2)
+ return false if v1.nil? || v2.nil?
+
+ python_helper.compare_versions(v1, v2) == 1
+ end
+
+ def version_equals?(v1, v2)
+ return false if v1.nil? || v2.nil?
+
+ python_helper.compare_versions(v1, v2) == 0
+ end
+
+ def version_compare(v1, v2)
+ python_helper.compare_versions(v1, v2)
+ end
+
+ def resolve_source_to_version_obj
+ shell_out!("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
+ end
+
+ # @return Array<Version>
+ def available_version(index)
+ @available_version ||= []
+
+ @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
+
+ @available_version[index]
+ end
+
+ # @return [Array<Version>]
+ def magical_version(index)
+ @magical_version ||= []
+ @magical_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], options: options)
+ else
+ python_helper.package_query(:whatinstalled, package_name_array[index], version: safe_version_array[index], arch: safe_arch_array[index], options: options)
+ end
+ @magical_version[index]
+ end
+
+ def current_version(index)
+ @current_version ||= []
+ @current_version[index] ||= if new_resource.source
+ python_helper.package_query(:whatinstalled, available_version(index).name, arch: safe_arch_array[index], options: options)
+ else
+ python_helper.package_query(:whatinstalled, package_name_array[index], arch: safe_arch_array[index], options: options)
+ end
+ @current_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!("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..1dc797a643
--- /dev/null
+++ b/lib/chef/provider/package/dnf/dnf_helper.py
@@ -0,0 +1,186 @@
+#!/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()
+ conf = base.conf
+ conf.read()
+ conf.installroot = '/'
+ conf.assumeyes = True
+ subst = conf.substitutions
+ subst.update_from_etc(conf.installroot)
+ try:
+ base.init_plugins()
+ base.pre_configure_plugins()
+ except AttributeError:
+ pass
+ base.read_all_repos()
+ repos = base.repos
+
+ if 'repos' in command:
+ for repo_pattern in command['repos']:
+ if 'enable' in repo_pattern:
+ for repo in repos.get_matching(repo_pattern['enable']):
+ repo.enable()
+ if 'disable' in repo_pattern:
+ for repo in repos.get_matching(repo_pattern['disable']):
+ repo.disable()
+
+ try:
+ base.configure_plugins()
+ except AttributeError:
+ pass
+ base.fill_sack(load_system_repo='auto')
+ 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 version_tuple(versionstr):
+ e = '0'
+ v = None
+ r = None
+ colon_index = versionstr.find(':')
+ if colon_index > 0:
+ e = str(versionstr[:colon_index])
+ dash_index = versionstr.find('-')
+ if dash_index > 0:
+ tmp = versionstr[colon_index + 1:dash_index]
+ if tmp != '':
+ v = tmp
+ arch_index = versionstr.find('.', dash_index)
+ if arch_index > 0:
+ r = versionstr[dash_index + 1:arch_index]
+ else:
+ r = versionstr[dash_index + 1:]
+ else:
+ tmp = versionstr[colon_index + 1:]
+ if tmp != '':
+ v = tmp
+ return (e, v, r)
+
+def versioncompare(versions):
+ sack = get_sack()
+ if (versions[0] is None) or (versions[1] is None):
+ outpipe.write('0\n')
+ outpipe.flush()
+ else:
+ evr_comparison = dnf.rpm.rpm.labelCompare(version_tuple(versions[0]), version_tuple(versions[1]))
+ outpipe.write('{}\n'.format(evr_comparison))
+ outpipe.flush()
+
+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:
+ # We assume that any glob is "*" so just omit the filter since the dnf libraries have no
+ # epoch__glob filter. That means "?" wildcards in epochs will fail. The workaround is to
+ # not use the version filter here but to put the version with all the globs in the package name.
+ if not dnf.util.is_glob_pattern(command['epoch']):
+ q = q.filterm(epoch=int(command['epoch']))
+ if 'version' in command:
+ if dnf.util.is_glob_pattern(command['version']):
+ q = q.filterm(version__glob=command['version'])
+ else:
+ q = q.filterm(version=command['version'])
+ if 'release' in command:
+ if dnf.util.is_glob_pattern(command['release']):
+ q = q.filterm(release__glob=command['release'])
+ else:
+ q = q.filterm(release=command['release'])
+
+ if 'arch' in command:
+ if dnf.util.is_glob_pattern(command['arch']):
+ q = q.filterm(arch__glob=command['arch'])
+ else:
+ q = q.filterm(arch=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:
+ outpipe.write('{} nil nil\n'.format(command['provides'].split().pop(0)))
+ outpipe.flush()
+ else:
+ # make sure we picked the package with the highest version
+ pkgs.sort
+ pkg = pkgs.pop()
+ outpipe.write('{} {}:{}-{} {}\n'.format(pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch))
+ outpipe.flush()
+
+# 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):
+ if base is not None:
+ base.close()
+ 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")
+
+try:
+ while 1:
+ # kill self if we get orphaned (tragic)
+ ppid = os.getppid()
+ if ppid == 1:
+ raise RuntimeError("orphaned")
+
+ setup_exit_handler()
+ line = inpipe.readline()
+
+ try:
+ command = json.loads(line)
+ except ValueError:
+ raise RuntimeError("bad json parse")
+
+ if command['action'] == "whatinstalled":
+ query(command)
+ elif command['action'] == "whatavailable":
+ query(command)
+ elif command['action'] == "versioncompare":
+ versioncompare(command['versions'])
+ else:
+ raise RuntimeError("bad command")
+finally:
+ if base is not None:
+ base.close()
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..1f6243b9b2
--- /dev/null
+++ b/lib/chef/provider/package/dnf/python_helper.rb
@@ -0,0 +1,220 @@
+#
+# Copyright:: Copyright (c) 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 "../../../mixin/which"
+require_relative "../../../mixin/shell_out"
+require_relative "version"
+require "singleton" unless defined?(Singleton)
+require "timeout" unless defined?(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 :inpipe
+ attr_accessor :outpipe
+ attr_accessor :wait_thr
+
+ DNF_HELPER = ::File.expand_path(::File.join(::File.dirname(__FILE__), "dnf_helper.py")).freeze
+
+ def dnf_command
+ # platform-python is used for system tools on RHEL 8 and is installed under /usr/libexec
+ @dnf_command ||= begin
+ cmd = which("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
+ shell_out("#{f} -c 'import dnf'").exitstatus == 0
+ end
+ raise Chef::Exceptions::Package, "cannot find dnf libraries, you may need to use yum_package" unless cmd
+
+ "#{cmd} #{DNF_HELPER}"
+ end
+ end
+
+ def start
+ ENV["PYTHONUNBUFFERED"] = "1"
+ @inpipe, inpipe_write = IO.pipe
+ outpipe_read, @outpipe = IO.pipe
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3("#{dnf_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 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
+
+ # @return Array<Version>
+ # NB: "options" here is the dnf_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)
+ # XXX: for now we restart before and after every query with an enablerepo/disablerepo to clean the helpers internal state
+ restart unless repo_opts.empty?
+ query_output = query(action, parameters)
+ version = parse_response(query_output.lines.last)
+ Chef::Log.trace "parsed #{version} from python helper"
+ 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 %i{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 "dnf-helper.py had stderr/stdout 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..d6b72a84bd
--- /dev/null
+++ b/lib/chef/provider/package/dnf/version.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright (c) 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}" unless version.nil?
+ end
+
+ def version_with_arch
+ "#{version}.#{arch}" unless version.nil?
+ end
+
+ def name_with_arch
+ "#{name}.#{arch}" unless name.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 e57f58e052..b2d1678caa 100644
--- a/lib/chef/provider/package/dpkg.rb
+++ b/lib/chef/provider/package/dpkg.rb
@@ -16,18 +16,20 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/package"
+require_relative "../package"
+require_relative "deb"
+require_relative "../../resource/package"
class Chef
class Provider
class Package
class Dpkg < Chef::Provider::Package
- DPKG_REMOVED = /^Status: deinstall ok config-files/
- DPKG_INSTALLED = /^Status: install ok installed/
- DPKG_VERSION = /^Version: (.+)$/
+ include Chef::Provider::Package::Deb
+ DPKG_REMOVED = /^Status: deinstall ok config-files/.freeze
+ DPKG_INSTALLED = /^Status: install ok installed/.freeze
+ DPKG_VERSION = /^Version: (.+)$/.freeze
- provides :dpkg_package, os: "linux"
+ provides :dpkg_package
use_multipackage_api
use_package_name_for_source
@@ -73,43 +75,48 @@ 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)
install_package(name, version)
end
- def preseed_package(preseed_file)
- Chef::Log.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")
- 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("dpkg", "--compare-versions", v1.to_s, "gt", v2.to_s).error?
+ 1
+ elsif !shell_out("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!("dpkg", "-s", package_name, returns: [0, 1])
package_installed = false
status.stdout.each_line do |line|
case line
@@ -120,12 +127,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)
@@ -134,12 +141,6 @@ class Chef
end
end
- # 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" })
- end
-
# Returns true if all sources exist. Returns false if any do not, or if no
# sources were specified.
#
@@ -148,7 +149,7 @@ class Chef
resolved_source_array.all? { |s| s && ::File.exist?(s) }
end
- # Helper to return all the nanes of the missing sources for error messages.
+ # Helper to return all the names of the missing sources for error messages.
#
# @return [Array<String>] Array of missing sources
def missing_sources
@@ -163,10 +164,7 @@ class Chef
#
# @return [Hash] Mapping of package names to sources
def name_sources
- @name_sources =
- begin
- Hash[*package_name_array.zip(resolved_source_array).flatten]
- end
+ @name_sources ||= Hash[*package_name_array.zip(resolved_source_array).flatten]
end
# Helper to construct Hash of names-to-package-information.
@@ -176,8 +174,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!("dpkg-deb", "-W", src)
status.stdout
end
Hash[*package_name_array.zip(pkginfos).flatten]
@@ -185,17 +183,11 @@ class Chef
end
def name_candidate_version
- @name_candidate_version ||=
- begin
- Hash[name_pkginfo.map { |k, v| [k, v ? v.split("\t")[1].strip : nil] }]
- end
+ @name_candidate_version ||= name_pkginfo.transform_values { |v| v ? v.split("\t")[1]&.strip : nil }
end
def name_package_name
- @name_package_name ||=
- begin
- Hash[name_pkginfo.map { |k, v| [k, v ? v.split("\t")[0] : nil] }]
- end
+ @name_package_name ||= name_pkginfo.transform_values { |v| v ? v.split("\t")[0] : nil }
end
# Return candidate version array from pkg-deb -W against the source file(s).
@@ -217,7 +209,7 @@ class Chef
#
# @return [Boolean] true if we're doing :install or :upgrade
def installing?
- [:install, :upgrade].include?(action)
+ %i{install upgrade}.include?(action)
end
end
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..46a01e754c 100644
--- a/lib/chef/provider/package/freebsd/base.rb
+++ b/lib/chef/provider/package/freebsd/base.rb
@@ -19,9 +19,9 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package"
-require "chef/mixin/get_source_from_package"
+require_relative "../../../resource/package"
+require_relative "../../package"
+require_relative "../../../mixin/get_source_from_package"
class Chef
class Provider
@@ -37,28 +37,31 @@ class Chef
case port
# When the package name starts with a '/' treat it as the full path to the ports directory.
- when /^\//
+ when %r{^/}
port
# Otherwise if the package name contains a '/' not at the start (like 'www/wordpress') treat
# as a relative path from /usr/ports.
- when /\//
+ when %r{/}
"/usr/ports/#{port}"
# 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!("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
+
path
end
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 } : {}
+ options[:env] = nil
+ options[:returns] = [0, 1]
+ make_v = shell_out!("make", "-V", variable, **options)
+ make_v.exitstatus == 0 ? make_v.stdout.strip.split($OUTPUT_RECORD_SEPARATOR).first : nil
end
end
@@ -67,19 +70,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
deleted file mode 100644
index 78d9449454..0000000000
--- a/lib/chef/provider/package/freebsd/pkg.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# Authors:: Bryan McLellan (btm@loftninjas.org)
-# Matthew Landauer (matthew@openaustralia.org)
-# Richard Manyanza (liseki@nyikacraftsmen.com)
-# Copyright:: Copyright 2009-2016, Bryan McLellan, Matthew Landauer
-# Copyright:: Copyright 2014-2016, Richard Manyanza
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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/freebsd/base"
-require "chef/util/path_helper"
-
-class Chef
- class Provider
- class Package
- module Freebsd
- class Pkg < Base
- include PortsHelper
-
- def install_package(name, version)
- 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
- else
- shell_out_with_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}")
-
- 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}")
-
- else
- shell_out_with_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
- end
-
- # The name of the package (without the version number) as understood by pkg_add and pkg_info.
- def package_name
- if supports_ports?
- if makefile_variable_value("PKGNAME", port_path) =~ /^(.+)-[^-]+$/
- $1
- else
- raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
- end
- else
- @new_resource.package_name
- end
- end
-
- def latest_link_name
- makefile_variable_value("LATEST_LINK", port_path)
- end
-
- def current_installed_version
- pkg_info = shell_out_with_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
- when /^http/, /^ftp/
- repo_candidate_version
- when /^\//
- file_candidate_version
- else
- ports_candidate_version
- end
- end
-
- def file_candidate_version_path
- Dir[Chef::Util::PathHelper.escape_glob_dir("#{@new_resource.source}/#{@current_resource.package_name}") + "*"][-1].to_s
- end
-
- def file_candidate_version
- file_candidate_version_path.split(/-/).last.split(/.tbz/).first
- end
-
- def repo_candidate_version
- "0.0.0"
- end
-
- def ports_candidate_version
- makefile_variable_value("PORTVERSION", port_path)
- end
-
- def port_path
- port_dir @new_resource.package_name
- end
-
- end
- end
- end
- end
-end
diff --git a/lib/chef/provider/package/freebsd/pkgng.rb b/lib/chef/provider/package/freebsd/pkgng.rb
index 8a805140ce..077464b5c2 100644
--- a/lib/chef/provider/package/freebsd/pkgng.rb
+++ b/lib/chef/provider/package/freebsd/pkgng.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require "chef/provider/package/freebsd/base"
+require_relative "base"
class Chef
class Provider
@@ -25,46 +25,46 @@ class Chef
class Pkgng < Base
def install_package(name, version)
- 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}")
-
+ unless current_resource.version
+ case new_resource.source
+ when %r{^(http|ftp|/)}
+ shell_out!("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!("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!("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, 1])
+ # pkgng up to version 1.15.99.7 returns 70 for pkg not found,
+ # later versions return 1
+ pkg_info = shell_out!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 1, 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!("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..fabc736800 100644
--- a/lib/chef/provider/package/freebsd/port.rb
+++ b/lib/chef/provider/package/freebsd/port.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require "chef/provider/package/freebsd/base"
+require_relative "base"
class Chef
class Provider
@@ -26,20 +26,16 @@ 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!("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!("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])
- else
- shell_out_with_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 = shell_out!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 70])
+ pkg_info.stdout[/^#{Regexp.escape(new_resource.package_name)}-(.+)/, 1]
end
def candidate_version
@@ -51,7 +47,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..2b60c0a1ec 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -2,8 +2,7 @@
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Graeme Mathieson (<mathie@woss.name>)
#
-# Copyright 2011-2016, Chef Software Inc.
-# Copyright 2014-2016, Chef Software, Inc <legal@chef.io>
+# Copyright:: Copyright (c) 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.
@@ -18,62 +17,67 @@
# limitations under the License.
#
-require "etc"
-require "chef/mixin/homebrew_user"
+require "etc" unless defined?(Etc)
+require_relative "../../mixin/homebrew_user"
class Chef
class Provider
class Package
class Homebrew < Chef::Provider::Package
+ allow_nils
+ use_multipackage_api
- provides :package, os: "darwin", override: true
+ provides :package, os: "darwin"
provides :homebrew_package
include Chef::Mixin::HomebrewUser
def load_current_resource
- self.current_resource = Chef::Resource::Package.new(new_resource.name)
+ @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
-
- @candidate_version = candidate_version
-
- Chef::Log.debug("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
+ current_resource.version(get_current_versions)
+ logger.trace("#{new_resource} current package version(s): #{current_resource.version}") if current_resource.version
current_resource
end
- def install_package(name, version)
- unless current_resource.version == version
- brew("install", new_resource.options, name)
+ def candidate_version
+ package_name_array.map do |package_name|
+ available_version(package_name)
end
end
- def upgrade_package(name, version)
- current_version = current_resource.version
-
- if current_version.nil? || current_version.empty?
- install_package(name, version)
- elsif current_version != version
- brew("upgrade", new_resource.options, name)
+ def get_current_versions
+ package_name_array.map do |package_name|
+ installed_version(package_name)
end
end
- def remove_package(name, version)
- if current_resource.version
- brew("uninstall", new_resource.options, name)
- end
+ def install_package(names, versions)
+ brew_cmd_output("install", options, names.compact)
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)
+ # upgrades are a bit harder in homebrew than other package formats. If you try to
+ # brew upgrade a package that isn't installed it will fail so if a user specifies
+ # the action of upgrade we need to figure out which packages need to be installed
+ # and which packages can be upgrades. We do this by checking if brew_info has an entry
+ # via the installed_version helper.
+ def upgrade_package(names, versions)
+ # @todo when we no longer support Ruby 2.6 this can be simplified to be a .filter_map
+ upgrade_pkgs = names.select { |x| x if installed_version(x) }.compact
+ install_pkgs = names.select { |x| x unless installed_version(x) }.compact
+
+ brew_cmd_output("upgrade", options, upgrade_pkgs) unless upgrade_pkgs.empty?
+ brew_cmd_output("install", options, install_pkgs) unless install_pkgs.empty?
+ end
+
+ def remove_package(names, versions)
+ brew_cmd_output("uninstall", options, names.compact)
end
- def brew(*args)
- get_response_from_command("brew #{args.join(' ')}")
+ # Homebrew doesn't really have a notion of purging, do a "force remove"
+ def purge_package(names, versions)
+ brew_cmd_output("uninstall", "--force", options, names.compact)
end
# We implement a querying method that returns the JSON-as-Hash
@@ -83,9 +87,50 @@ class Chef
# information, but that is not any more robust than using the
# command-line interface that returns the same thing.
#
- # https://github.com/Homebrew/homebrew/wiki/Querying-Brew
+ # https://docs.brew.sh/Querying-Brew
+ #
+ # @returns [Hash] a hash of package information where the key is the package name
def brew_info
- @brew_info ||= Chef::JSONCompat.from_json(brew("info", "--json=v1", new_resource.package_name)).first
+ @brew_info ||= begin
+ command_array = ["info", "--json=v1"].concat package_name_array
+ # convert the array of hashes into a hash where the key is the package name
+
+ cmd_output = brew_cmd_output(command_array, allow_failure: true)
+
+ if cmd_output.empty?
+ # we had some kind of failure so we need to iterate through each package to find them
+ package_name_array.each_with_object({}) do |package_name, hsh|
+ cmd_output = brew_cmd_output("info", "--json=v1", package_name, allow_failure: true)
+ if cmd_output.empty?
+ hsh[package_name] = {}
+ else
+ json = Chef::JSONCompat.from_json(cmd_output).first
+ hsh[json["name"]] = json
+ end
+ end
+ else
+ Hash[Chef::JSONCompat.from_json(cmd_output).collect { |pkg| [pkg["name"], pkg] }]
+ end
+ end
+ end
+
+ #
+ # Return the package information given a package name or package alias
+ #
+ # @param [String] name_or_alias The name of the package or its alias
+ #
+ # @return [Hash] Package information
+ #
+ def package_info(package_name)
+ # return the package hash if it's in the brew info hash
+ return brew_info[package_name] if brew_info[package_name]
+
+ # check each item in the hash to see if we were passed an alias
+ brew_info.each_value do |p|
+ return p if p["full_name"] == package_name || p["aliases"].include?(package_name)
+ end
+
+ {}
end
# Some packages (formula) are "keg only" and aren't linked,
@@ -94,15 +139,20 @@ class Chef
# "current" (as in latest). Otherwise, we will use the version
# that brew thinks is linked as the current version.
#
- def current_installed_version
- if brew_info["keg_only"]
- if brew_info["installed"].empty?
+ # @param [String] package name
+ #
+ # @returns [String] package version
+ def installed_version(i)
+ p_data = package_info(i)
+
+ if p_data["keg_only"]
+ if p_data["installed"].empty?
nil
else
- brew_info["installed"].last["version"]
+ p_data["installed"].last["version"]
end
else
- brew_info["linked_keg"]
+ p_data["linked_keg"]
end
end
@@ -115,19 +165,33 @@ class Chef
# forward project.
#
# https://github.com/Homebrew/homebrew/wiki/Acceptable-Formulae#stable-versions
- def candidate_version
- brew_info["versions"]["stable"]
- end
+ #
+ # @param [String] package name
+ #
+ # @returns [String] package version
+ def available_version(i)
+ p_data = package_info(i)
- private
+ # nothing is available
+ return nil if p_data.empty?
- def get_response_from_command(command)
+ p_data["versions"]["stable"]
+ end
+
+ def brew_cmd_output(*command, **options)
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 'brew #{command.join(" ")}' as user '#{homebrew_user.name}'"
+
+ # allow the calling method to decide if the cmd should raise or not
+ # brew_info uses this when querying out available package info since a bad
+ # package name will raise and we want to surface a nil available package so that
+ # the package provider can magically handle that
+ shell_out_cmd = options[:allow_failure] ? :shell_out : :shell_out!
+
# 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 = send(shell_out_cmd, "brew", *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..ee997d147f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,17 +18,16 @@
#
require "open3"
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
+require_relative "../package"
+require_relative "../../resource/package"
class Chef
class Provider
class Package
class Ips < Chef::Provider::Package
- provides :package, platform: %w{openindiana opensolaris omnios solaris2}
- provides :ips_package, os: "solaris2"
+ provides :package, platform: %w{openindiana omnios 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("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!("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.package_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!(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!( "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..a653dbd418 100644
--- a/lib/chef/provider/package/macports.rb
+++ b/lib/chef/provider/package/macports.rb
@@ -2,30 +2,28 @@ class Chef
class Provider
class Package
class Macports < Chef::Provider::Package
-
- provides :package, os: "darwin"
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.package_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 +35,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 +44,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!(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!(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!(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!( "port", options, "upgrade", name, "@#{version}" )
end
end
@@ -84,15 +82,16 @@ class Chef
def get_response_from_command(command)
output = nil
- status = shell_out_with_timeout(command)
+ status = shell_out(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
end
diff --git a/lib/chef/provider/package/msu.rb b/lib/chef/provider/package/msu.rb
new file mode 100644
index 0000000000..4208260cbe
--- /dev/null
+++ b/lib/chef/provider/package/msu.rb
@@ -0,0 +1,166 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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_relative "../package"
+require_relative "../../resource/msu_package"
+require_relative "../../mixin/shell_out"
+require_relative "cab"
+require_relative "../../util/path_helper"
+require_relative "../../mixin/uris"
+require_relative "../../mixin/checksum"
+require "cgi" unless defined?(CGI)
+
+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(::CGI.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
+ timeout new_resource.timeout
+ 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
+ timeout new_resource.timeout
+ action :remove
+ end
+ end
+ end
+
+ def extract_msu_contents(msu_file, destination)
+ with_os_architecture(nil) do
+ shell_out!("#{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 8043c01693..6e32a424e2 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -20,10 +20,10 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package"
-require "chef/mixin/get_source_from_package"
-require "chef/exceptions"
+require_relative "../../resource/package"
+require_relative "../package"
+require_relative "../../mixin/get_source_from_package"
+require_relative "../../exceptions"
class Chef
class Provider
@@ -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
@@ -53,11 +53,11 @@ class Chef
# Below are incomplete/missing features for this package provider
requirements.assert(:all_actions) do |a|
a.assertion { !new_resource.source }
- a.failure_message(Chef::Exceptions::Package, "The openbsd package provider does not support the source attribute")
+ a.failure_message(Chef::Exceptions::Package, "The openbsd package provider does not support the source property")
end
requirements.assert(:all_actions) do |a|
a.assertion do
- if new_resource.package_name =~ /^(.+?)--(.+)/
+ if /^(.+?)--(.+)/.match?(new_resource.package_name)
!new_resource.version
else
true
@@ -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!("pkg_add", "-r", package_string(name, version), env: { "PKG_PATH" => pkg_path }).status
+ logger.trace("#{new_resource.package_name} installed")
end
end
@@ -81,49 +81,52 @@ 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!("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!("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!("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
[]
when 1
results[0]
else
- raise Chef::Exceptions::Package, "#{new_resource.name} has multiple matching candidates. Please use a more specific name" if results.length > 1
+ raise Chef::Exceptions::Package, "#{new_resource.package_name} has multiple matching candidates. Please use a more specific name" if results.length > 1
end
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
diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb
index bd8028d881..2de2d12579 100644
--- a/lib/chef/provider/package/pacman.rb
+++ b/lib/chef/provider/package/pacman.rb
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
+require_relative "../package"
+require_relative "../../resource/package"
class Chef
class Provider
@@ -26,64 +25,55 @@ 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)
-
- Chef::Log.debug("#{@new_resource} checking pacman for #{@new_resource.package_name}")
- status = shell_out_with_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)
- end
- end
-
- unless status.exitstatus == 0 || status.exitstatus == 1
- raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!"
- end
+ use_multipackage_api
+ allow_nils
- @current_resource
- end
-
- def candidate_version
- return @candidate_version if @candidate_version
+ def load_current_resource
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version = []
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.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
- end
- end
+ repos = Regexp.union(repos)
+ status = shell_out("pacman", "-Sl")
unless status.exitstatus == 0 || status.exitstatus == 1
raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!"
end
- unless @candidate_version
- raise Chef::Exceptions::Package, "pacman does not have a version of package #{@new_resource.package_name}"
+ pkg_db_data = status.stdout
+ @candidate_version = []
+ package_name_array.each do |pkg|
+ pkg_data = pkg_db_data.match(/(#{repos}) #{pkg} (?<candidate>.*?-[0-9]+)(?<installed> \[.*?( (?<current>.*?-[0-9]+))?\])?\n/m)
+ unless pkg_data
+ raise Chef::Exceptions::Package, "pacman does not have a version of package #{pkg}"
+ end
+
+ @candidate_version << pkg_data[:candidate]
+ if pkg_data[:installed]
+ current_resource.version << (pkg_data[:current] || pkg_data[:candidate])
+ else
+ current_resource.version << nil
+ end
end
+ current_resource
+ end
+
+ def candidate_version
@candidate_version
end
def install_package(name, version)
- shell_out_with_timeout!( "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
+ shell_out!("pacman", "--sync", "--noconfirm", "--noprogressbar", options, *name)
end
def upgrade_package(name, version)
@@ -91,7 +81,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!("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..853d0b8cfc 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -1,6 +1,6 @@
#
# Author:: Vasiliy Tolstov (<v.tolstov@selfip.ru>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/package"
+require_relative "../package"
+require_relative "../../resource/package"
class Chef
class Provider
@@ -25,41 +25,41 @@ 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!("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!("cave", "-L", "warning", "resolve", "-x", options, pkg)
end
def upgrade_package(name, version)
@@ -67,13 +67,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!("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 52b46b04b4..9975010e5b 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -1,6 +1,6 @@
#
# Author:: Ezra Zygmuntowicz (<ezra@engineyard.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,9 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
-require "chef/util/path_helper"
+require_relative "../package"
+require_relative "../../resource/portage_package"
+require_relative "../../util/path_helper"
class Chef
class Provider
@@ -29,13 +28,13 @@ class Chef
provides :package, platform: "gentoo"
provides :portage_package
- PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}
+ PACKAGE_NAME_PATTERN = %r{^(?:([^/]+)/)?([^/]+)$}.freeze
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::PortagePackage.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)
@@ -47,59 +46,54 @@ class Chef
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("portageq", "best_visible", "/", new_resource.package_name)
+
+ if pkginfo.exitstatus != 0
+ pkginfo.stderr.each_line do |line|
+ # cspell:disable-next-line
+ if /[Uu]nqualified atom .*match.* multiple/.match?(line)
+ 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 /-r\d+$/.match?(pkginfo.stdout)
+ # 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 +107,7 @@ class Chef
pkg = "~#{name}-#{$1}"
end
- shell_out!( "emerge -g --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}" )
+ shell_out!( "emerge", "-g", "--color", "n", "--nospinner", "--quiet", options, pkg )
end
def upgrade_package(name, version)
@@ -121,13 +115,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!( "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..1f59360c61
--- /dev/null
+++ b/lib/chef/provider/package/powershell.rb
@@ -0,0 +1,137 @@
+# Author:: Dheeraj Dubey(dheeraj.dubey@msystechnologies.com)
+# Copyright:: Copyright (c) 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 "../package"
+require_relative "../../resource/powershell_package"
+require_relative "../../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_version < 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|
+ cmd = powershell_out(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
+ next if cmd.nil?
+ raise Chef::Exceptions::PowershellCmdletException, "Failed to install package due to catalog signing error, use skip_publisher_check to force install" if /SkipPublisherCheck/.match?(cmd.stderr)
+ 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)
+ cmdlet_name = command.first
+ command.unshift("(")
+ # PowerShell Gallery requires tls 1.2
+ command.unshift("if ([Net.ServicePointManager]::SecurityProtocol -lt [Net.SecurityProtocolType]::Tls12) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 };")
+ # -WarningAction SilentlyContinue is used to suppress the warnings from stdout
+ %w{-Force -ForceBootstrap -WarningAction SilentlyContinue}.each do |arg|
+ command.push(arg)
+ end
+ command.push("-RequiredVersion #{version}") if version
+ command.push("-Source #{new_resource.source}") if new_resource.source && cmdlet_name =~ Regexp.union(/Install-Package/, /Find-Package/)
+ command.push("-SkipPublisherCheck") if new_resource.skip_publisher_check && cmdlet_name !~ /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..dafb0b9949 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -1,6 +1,6 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,17 +15,16 @@
# 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"
+require_relative "../package"
+require_relative "../../resource/package"
+require_relative "../../mixin/get_source_from_package"
+require_relative "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.package_name} not found: #{new_resource.source}"
+ a.whyrun "Assuming package #{new_resource.package_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.package_name}."
end
end
@@ -48,74 +47,79 @@ 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!("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("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!("rpm", options, "-U", "--oldpackage", new_resource.source)
else
- shell_out_with_timeout!( "rpm #{@new_resource.options} -U #{@new_resource.source}" )
+ shell_out!("rpm", options, "-U", new_resource.source)
end
+ else
+ shell_out!("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!("rpm", options, "-e", "#{name}-#{version}")
else
- shell_out_with_timeout!( "rpm #{@new_resource.options} -e #{name}" )
+ shell_out!("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 187197d143..a0b569b8e3 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,79 +17,97 @@
# limitations under the License.
#
-require "uri"
-require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
-require "chef/mixin/get_source_from_package"
+autoload :URI, "uri"
+require_relative "../package"
+require_relative "../../resource/package"
+require_relative "../../mixin/get_source_from_package"
+require_relative "../../mixin/which"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
# Class methods on Gem are defined in rubygems
-require "rubygems"
+autoload :Gem, "rubygems"
# Ruby 1.9's gem_prelude can interact poorly with loading the full rubygems
# explicitly like this. Make sure rubygems/specification is always last in this
# list
-require "rubygems/version"
-require "rubygems/dependency"
-require "rubygems/spec_fetcher"
-require "rubygems/platform"
-require "rubygems/package"
-require "rubygems/dependency_installer"
-require "rubygems/uninstaller"
-require "rubygems/specification"
+Gem.autoload :Version, "rubygems/version"
+Gem.autoload :Dependency, "rubygems/dependency"
+Gem.autoload :SpecFetcher, "rubygems/spec_fetcher"
+Gem.autoload :Platform, "rubygems/platform"
+Gem.autoload :Package, "rubygems/package"
+Gem.autoload :DependencyInstaller, "rubygems/dependency_installer"
+Gem.autoload :Uninstaller, "rubygems/uninstaller"
+Gem.autoload :Specification, "rubygems/specification"
class Chef
class Provider
class Package
class Rubygems < Chef::Provider::Package
class GemEnvironment
- # HACK: trigger gem config load early. Otherwise it can get lazy
- # loaded during operations where we've set Gem.sources to an
- # alternate value and overwrite it with the defaults.
- Gem.configuration
+ DEFAULT_UNINSTALLER_OPTS = { ignore: true, executables: true }.freeze
- DEFAULT_UNINSTALLER_OPTS = { :ignore => true, :executables => true }
+ def initialize(*args)
+ super
+ # HACK: trigger gem config load early. Otherwise it can get lazy
+ # loaded during operations where we've set Gem.sources to an
+ # alternate value and overwrite it with the defaults.
+ Gem.configuration
+ end
- ##
# The paths where rubygems should search for installed gems.
# Implemented by subclasses.
def gem_paths
raise NotImplementedError
end
- ##
# A rubygems source index containing the list of gemspecs for all
# available gems in the gem installation.
# Implemented by subclasses
- # === Returns
- # Gem::SourceIndex
+ #
+ # @return [Gem::SourceIndex]
+ #
def gem_source_index
raise NotImplementedError
end
- ##
# A rubygems specification object containing the list of gemspecs for all
# available gems in the gem installation.
# Implemented by subclasses
- # For rubygems >= 1.8.0
- # === Returns
- # Gem::Specification
+ #
+ # @return [Gem::Specification]
+ #
def gem_specification
raise NotImplementedError
end
- ##
+ def rubygems_version
+ raise NotImplementedError
+ end
+
# Lists the installed versions of +gem_name+, constrained by the
# version spec in +gem_dep+
- # === Arguments
- # Gem::Dependency +gem_dep+ is a Gem::Dependency object, its version
- # specification constrains which gems are returned.
- # === Returns
- # [Gem::Specification] an array of Gem::Specification objects
+ #
+ # @param gem_dep [Gem::Dependency] the version specification that constrains
+ # which gems are used.
+ # @return [Array<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 because the only code that
+ # uses this method calls `max_by` so it doesn't need to be sorted.
+ stubs
+ else # >= rubygems 1.8 behavior
gem_specification.find_all_by_name(gem_dep.name, gem_dep.requirement)
- else
- gem_source_index.search(gem_dep)
end
end
@@ -133,11 +151,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
@@ -170,7 +188,7 @@ class Chef
# 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
+ rs.specs.find { |s| s.name == gem_dependency.name }
rescue Gem::UnsatisfiableDependencyError
nil
end
@@ -178,11 +196,11 @@ class Chef
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}" }
+ source_list = sources.compact.empty? ? "[#{Gem.sources.to_a.join(", ")}]" : "[#{sources.join(", ")}]"
+ logger.warn { "failed to find gem #{gem_dependency} from #{source_list}" }
nil
end
end
@@ -217,7 +235,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
@@ -232,7 +250,7 @@ class Chef
private
def logger
- Chef::Log.logger
+ Chef::Log.with_child({ subsystem: "gem_installer_environment" })
end
end
@@ -251,6 +269,10 @@ class Chef
Gem::Specification
end
+ def rubygems_version
+ Gem::VERSION
+ end
+
def candidate_version_from_remote(gem_dependency, *sources)
with_gem_sources(*sources) do
find_newest_remote_version(gem_dependency, *sources)
@@ -260,7 +282,7 @@ class Chef
end
class AlternateGemEnvironment < GemEnvironment
- JRUBY_PLATFORM = /(:?universal|x86_64|x86)\-java\-[0-9\.]+/
+ JRUBY_PLATFORM = /(:?universal|x86_64|x86)\-java\-[0-9\.]+/.freeze
def self.gempath_cache
@gempath_cache ||= {}
@@ -278,6 +300,10 @@ class Chef
@gem_binary_location = gem_binary_location
end
+ def rubygems_version
+ @rubygems_version ||= shell_out!("#{@gem_binary_location} --version").stdout.chomp
+ end
+
def gem_paths
if self.class.gempath_cache.key?(@gem_binary_location)
self.class.gempath_cache[@gem_binary_location]
@@ -285,7 +311,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
@@ -322,11 +348,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
@@ -352,57 +378,54 @@ 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",
- "may only be used when installing a gem to the same Ruby installation that Chef is",
- "running under. See https://docs.chef.io/resource_gem_package.html for more information.",
+ "using this installation of #{ChefUtils::Dist::Infra::PRODUCT} because it runs with its own packaged Ruby. A hash",
+ "may only be used when installing a gem to the same Ruby installation that #{ChefUtils::Dist::Infra::PRODUCT} is",
+ "running under. See https://docs.chef.io/resources/gem_package/ for more information.",
"Error raised at #{new_resource} from #{new_resource.source_line}",
].join("\n")
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']}")
+ if %r{/(opscode|chef|chefdk)/embedded/bin}.match?(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']}")
+ elsif RbConfig::CONFIG["bindir"].sub(/^\w:/, "") == "/opscode/chef/embedded/bin"
+ logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG["bindir"]}")
# windows, with the drive letter removed
true
else
@@ -411,50 +434,41 @@ 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]$/
+ scheme = nil if /^[a-z]$/.match?(scheme)
%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
- # rubygems 2.6.3 ensures that gem lists are sorted newest first
- pos = if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.6.3")
- :first
- else
- :last
- end
-
# 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.send(pos)
- 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.send(pos)
- 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
@@ -469,22 +483,38 @@ class Chef
end
end
+ ##
+ # If `include_default_source` is nil, return true if the global
+ # `rubygems_url` was set or if `clear_sources` and `source` on the
+ # resource are not set.
+ # If `include_default_source` is not nil, it has been set explicitly on
+ # the resource and that value should be used.
+ def include_default_source?
+ if new_resource.include_default_source.nil?
+ !!Chef::Config[:rubygems_url] || !(new_resource.source || new_resource.clear_sources)
+ else
+ new_resource.include_default_source
+ end
+ end
+
def gem_sources
- @new_resource.source ? Array(@new_resource.source) : nil
+ srcs = [ new_resource.source ]
+ srcs << (Chef::Config[:rubygems_url] || "https://rubygems.org") if 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
@@ -494,13 +524,14 @@ class Chef
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
+ @gem_env.candidate_version_from_file(gem_dependency, new_resource.source).to_s
end
end
end
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
@@ -511,18 +542,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?
- @gem_env.install(@new_resource.source)
+ elsif new_resource.gem_binary.nil?
+ @gem_env.install(new_resource.source)
else
install_via_gem_command(name, version)
end
@@ -530,23 +561,35 @@ class Chef
end
def gem_binary_path
- @new_resource.gem_binary || "gem"
+ new_resource.gem_binary || "gem"
+ end
+
+ ##
+ # If `clear_sources` is nil, clearing sources is implied if a `source`
+ # was added or if the global rubygems URL is set. If `clear_sources`
+ # is not nil, it has been set explicitly on the resource and its value
+ # should be used.
+ def clear_sources?
+ if new_resource.clear_sources.nil?
+ !!(new_resource.source || Chef::Config[:rubygems_url])
+ else
+ new_resource.clear_sources
+ end
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 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!("#{gem_binary_path} install #{name} -q #{rdoc_string} -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!("#{gem_binary_path} install \"#{name}\" -q #{rdoc_string} #{src_str}#{opts}", env: nil)
end
end
@@ -555,11 +598,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
@@ -570,9 +613,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!("#{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!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", env: nil)
end
end
@@ -582,8 +625,20 @@ class Chef
private
+ def rdoc_string
+ if needs_nodocument?
+ "--no-document"
+ else
+ "--no-rdoc --no-ri"
+ end
+ end
+
+ def needs_nodocument?
+ Gem::Requirement.new(">= 3.0.0.beta1").satisfied_by?(Gem::Version.new(gem_env.rubygems_version))
+ end
+
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..a44280ec75 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");
@@ -19,9 +19,9 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/package"
-require "chef/mixin/get_source_from_package"
+require_relative "../package"
+require_relative "../../resource/package"
+require_relative "../../mixin/get_source_from_package"
class Chef
class Provider
@@ -30,35 +30,36 @@ 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!("/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
def candidate_version
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!("/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 +71,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!("/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!("/opt/local/bin/pkgin", "-y", "remove", package, env: nil)
end
end
diff --git a/lib/chef/provider/package/snap.rb b/lib/chef/provider/package/snap.rb
new file mode 100644
index 0000000000..81af09e04d
--- /dev/null
+++ b/lib/chef/provider/package/snap.rb
@@ -0,0 +1,427 @@
+#
+# Author:: S.Cavallo (<smcavallo@hotmail.com>)
+# Copyright:: Copyright (c) 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 "../package"
+require_relative "../../resource/snap_package"
+require_relative "../../mixin/shell_out"
+require "socket" unless defined?(Socket)
+require "json" unless defined?(JSON)
+
+class Chef
+ class Provider
+ class Package
+ class Snap < Chef::Provider::Package
+ allow_nils
+ use_multipackage_api
+ use_package_name_for_source
+
+ provides :snap_package
+
+ def load_current_resource
+ @current_resource = Chef::Resource::SnapPackage.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)
+ end
+ end
+
+ def get_current_versions
+ package_name_array.each_with_index.map do |pkg, i|
+ installed_version(i)
+ end.compact
+ end
+
+ def install_package(names, versions)
+ if new_resource.source
+ install_snap_from_source(names, new_resource.source)
+ else
+ install_snaps(names)
+ end
+ end
+
+ def upgrade_package(names, versions)
+ if new_resource.source
+ install_snap_from_source(names, new_resource.source)
+ else
+ if get_current_versions.empty?
+ install_snaps(names, versions)
+ else
+ update_snaps(names)
+ end
+ end
+ end
+
+ def remove_package(names, versions)
+ uninstall_snaps(names)
+ end
+
+ alias purge_package remove_package
+
+ private
+
+ # @return Array<Version>
+ def available_version(index)
+ @available_version ||= []
+
+ @available_version[index] ||= if new_resource.source
+ get_snap_version_from_source(new_resource.source)
+ else
+ get_latest_package_version(package_name_array[index], new_resource.channel)
+ end
+
+ @available_version[index]
+ end
+
+ # @return [Array<Version>]
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= get_installed_package_version_by_name(package_name_array[index])
+ @installed_version[index]
+ 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
+
+ # ToDo: Support authentication
+ # ToDo: Support private snap repos
+ # https://github.com/snapcore/snapd/wiki/REST-API
+
+ # ToDo: Would prefer to use net/http over socket
+ def call_snap_api(method, uri, post_data = nil?)
+ request = "#{method} #{uri} HTTP/1.0\r\n" +
+ "Accept: application/json\r\n" +
+ "Content-Type: application/json\r\n"
+ if method == "POST"
+ pdata = post_data.to_json.to_s
+ request.concat("Content-Length: #{pdata.bytesize}\r\n\r\n#{pdata}")
+ end
+ request.concat("\r\n")
+
+ # while it is expected to allow clients to connect using https over
+ # a tcp socket, at this point only a unix socket is supported. the
+ # socket is /run/snapd.socket note - UNIXSocket is not defined on
+ # windows systems
+ if defined?(::UNIXSocket)
+ UNIXSocket.open("/run/snapd.socket") do |socket|
+ # send request, read the response, split the response and parse
+ # the body
+ socket.write(request)
+
+ # WARNING!!! HERE BE DRAGONs
+ #
+ # So snapd doesn't return an EOF at the end of its body, so
+ # doing a normal read will just hang forever.
+ #
+ # Well, sort of. if, after it writes everything, you then send
+ # yet-another newline, it'll then send its EOF and promptly
+ # disconnect closing the pipe and preventing reading. so, you
+ # have to read first, and therein lies the EOF problem.
+ #
+ # So you can do non-blocking reads with selects, but it
+ # makes every read take about 5 seconds. If, instead, we
+ # read the last line char-by-char, it's about half a second.
+ #
+ # Reading a character at a time isn't efficient, and since we
+ # know that http headers always have a blank line after them,
+ # we can read lines until we find a blank line and *then* read
+ # a character at a time. snap returns all the json on a single
+ # line, so once you pass headers you must read a character a
+ # time.
+ #
+ # - jaymzh
+
+ Chef::Log.trace(
+ "snap_package[#{new_resource.package_name}]: reading headers"
+ )
+ loop do
+ response = socket.readline
+ break if response.strip.empty? # finished headers
+ end
+ Chef::Log.trace(
+ "snap_package[#{new_resource.package_name}]: past headers, " +
+ "onto the body..."
+ )
+ result = nil
+ body = ""
+ socket.each_char do |c|
+ body << c
+ # we know we're not done if we don't have a char that
+ # can end JSON
+ next unless ["}", "]"].include?(c)
+
+ begin
+ result = JSON.parse(body)
+ # if we get here, we were able to parse the json so we
+ # are done reading
+ break
+ rescue JSON::ParserError
+ next
+ end
+ end
+ result
+ end
+ end
+ end
+
+ def get_change_id(id)
+ call_snap_api("GET", "/v2/changes/#{id}")
+ end
+
+ def get_id_from_async_response(response)
+ if response["type"] == "error"
+ raise "status: #{response["status"]}, kind: #{response["result"]["kind"]}, message: #{response["result"]["message"]}"
+ end
+
+ response["change"]
+ end
+
+ def wait_for_completion(id)
+ n = 0
+ waiting = true
+ while waiting
+ result = get_change_id(id)
+ case result["result"]["status"]
+ when "Do", "Doing", "Undoing", "Undo"
+ # Continue
+ when "Abort", "Hold", "Error"
+ raise result
+ when "Done"
+ waiting = false
+ else
+ # How to handle unknown status
+ end
+ n += 1
+ raise "Snap operating timed out after #{n} seconds." if n == 300
+
+ sleep(1)
+ end
+ end
+
+ def snapctl(*args)
+ shell_out!("snap", *args)
+ end
+
+ def get_snap_version_from_source(path)
+ body = {
+ "context-id" => "get_snap_version_from_source_#{path}",
+ "args" => ["info", path],
+ }.to_json
+
+ # json = call_snap_api('POST', '/v2/snapctl', body)
+ response = snapctl(["info", path])
+ Chef::Log.trace(response)
+ response.error!
+ get_version_from_stdout(response.stdout)
+ end
+
+ def get_version_from_stdout(stdout)
+ stdout.match(/version: (\S+)/)[1]
+ end
+
+ def install_snap_from_source(name, path)
+ # json = call_snap_api('POST', '/v2/snapctl', body)
+ response = snapctl(["install", path])
+ Chef::Log.trace(response)
+ response.error!
+ end
+
+ def install_snaps(snap_names, versions)
+ snap_names.each do |snap|
+ response = post_snap(snap, "install", new_resource.channel, new_resource.options)
+ id = get_id_from_async_response(response)
+ wait_for_completion(id)
+ end
+ end
+
+ def update_snaps(snap_names)
+ response = post_snaps(snap_names, "refresh", nil, new_resource.options)
+ id = get_id_from_async_response(response)
+ wait_for_completion(id)
+ end
+
+ def uninstall_snaps(snap_names)
+ response = post_snaps(snap_names, "remove", nil, new_resource.options)
+ id = get_id_from_async_response(response)
+ wait_for_completion(id)
+ end
+
+ # Constructs the multipart/form-data required to sideload packages
+ # https://github.com/snapcore/snapd/wiki/REST-API#sideload-request
+ #
+ # @param snap_name [String] An array of snap package names to install
+ # @param action [String] The action. Valid: install or try
+ # @param options [Hash] Misc configuration Options
+ # @param path [String] Path to the package on disk
+ # @param content_length [Integer] byte size of the snap file
+ def generate_multipart_form_data(snap_name, action, options, path, content_length)
+ snap_options = options.map do |k, v|
+ <<~SNAP_OPTION
+ Content-Disposition: form-data; name="#{k}"
+
+ #{v}
+ --#{snap_name}
+ SNAP_OPTION
+ end
+
+ <<~SNAP_S
+ Host:
+ Content-Type: multipart/form-data; boundary=#{snap_name}
+ Content-Length: #{content_length}
+
+ --#{snap_name}
+ Content-Disposition: form-data; name="action"
+
+ #{action}
+ --#{snap_name}
+ #{snap_options.join("\n").chomp}
+ Content-Disposition: form-data; name="snap"; filename="#{path}"
+
+ <#{content_length} bytes of snap file data>
+ --#{snap_name}
+ SNAP_S
+ end
+
+ # Constructs json to post for snap changes
+ #
+ # @param snap_names [Array] An array of snap package names to install
+ # @param action [String] The action. install, refresh, remove, revert, enable, disable or switch
+ # @param channel [String] The release channel. Ex. stable
+ # @param options [Hash] Misc configuration Options
+ # @param revision [String] A revision/version
+ def generate_snap_json(snap_names, action, channel, options, revision = nil)
+ request = {
+ "action" => action,
+ "snaps" => snap_names,
+ }
+ if %w{install refresh switch}.include?(action) && channel
+ request["channel"] = channel
+ end
+
+ # No defensive handling of params
+ # Snap will throw the proper exception if called improperly
+ # And we can provide that exception to the end user
+ if options
+ request["classic"] = true if options.include?("classic")
+ request["devmode"] = true if options.include?("devmode")
+ request["jailmode"] = true if options.include?("jailmode")
+ request["ignore_validation"] = true if options.include?("ignore-validation")
+ end
+ request["revision"] = revision unless revision.nil?
+ request
+ end
+
+ # Post to the snap api to update snaps
+ #
+ # @param snap_names [Array] An array of snap package names to install
+ # @param action [String] The action. install, refresh, remove, revert, enable, disable or switch
+ # @param channel [String] The release channel. Ex. stable
+ # @param options [Hash] Misc configuration Options
+ # @param revision [String] A revision/version
+ def post_snaps(snap_names, action, channel, options, revision = nil)
+ json = generate_snap_json(snap_names, action, channel, options, revision = nil)
+ call_snap_api("POST", "/v2/snaps", json)
+ end
+
+ def post_snap(snap_name, action, channel, options, revision = nil)
+ json = generate_snap_json(snap_name, action, channel, options, revision = nil)
+ json.delete("snaps")
+ call_snap_api("POST", "/v2/snaps/#{snap_name}", json)
+ end
+
+ def get_latest_package_version(name, channel)
+ json = call_snap_api("GET", "/v2/find?name=#{name}")
+ if json["status-code"] != 200
+ raise Chef::Exceptions::Package, json["result"], caller
+ end
+
+ unless json["result"][0]["channels"]["latest/#{channel}"]
+ raise Chef::Exceptions::Package, "No version of #{name} in channel #{channel}", caller
+ end
+
+ # Return the version matching the channel
+ json["result"][0]["channels"]["latest/#{channel}"]["version"]
+ end
+
+ def get_installed_packages
+ json = call_snap_api("GET", "/v2/snaps")
+ # We only allow 200 or 404s
+ unless [200, 404].include? json["status-code"]
+ raise Chef::Exceptions::Package, json["result"], caller
+ end
+
+ json["result"]
+ end
+
+ def get_installed_package_version_by_name(name)
+ result = get_installed_package_by_name(name)
+ # Return nil if not installed
+ if result["status-code"] == 404
+ nil
+ else
+ result["version"]
+ end
+ end
+
+ def get_installed_package_by_name(name)
+ json = call_snap_api("GET", "/v2/snaps/#{name}")
+ # We only allow 200 or 404s
+ unless [200, 404].include? json["status-code"]
+ raise Chef::Exceptions::Package, json["result"], caller
+ end
+
+ json["result"]
+ end
+
+ def get_installed_package_conf(name)
+ json = call_snap_api("GET", "/v2/snaps/#{name}/conf")
+ json["result"]
+ end
+
+ def set_installed_package_conf(name, value)
+ response = call_snap_api("PUT", "/v2/snaps/#{name}/conf", value)
+ id = get_id_from_async_response(response)
+ wait_for_completion(id)
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb
index 1c393e6a20..7094428236 100644
--- a/lib/chef/provider/package/solaris.rb
+++ b/lib/chef/provider/package/solaris.rb
@@ -1,6 +1,6 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,10 +15,9 @@
# 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"
+require_relative "../package"
+require_relative "../../resource/package"
+require_relative "../../mixin/get_source_from_package"
class Chef
class Provider
@@ -27,51 +26,49 @@ class Chef
include Chef::Mixin::GetSourceFromPackage
- 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.package_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.package_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("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("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 +76,58 @@ 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("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!(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!(*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!( "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!( "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 753d3c279e..c722d8222c 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,12 @@
# limitations under the License.
#
-require "chef/mixin/uris"
-require "chef/resource/windows_package"
-require "chef/provider/package"
-require "chef/util/path_helper"
-require "chef/mixin/checksum"
+require_relative "../../mixin/uris"
+require_relative "../../resource/windows_package"
+require_relative "../package"
+require_relative "../../util/path_helper"
+require_relative "../../mixin/checksum"
+autoload :CGI, "cgi"
class Chef
class Provider
@@ -30,28 +31,41 @@ 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"
+ autoload :RegistryUninstallEntry, ::File.expand_path("windows/registry_uninstall_entry.rb", __dir__)
def define_resource_requirements
+ if new_resource.checksum
+ requirements.assert(:install) do |a|
+ a.assertion { new_resource.checksum == checksum(source_location) }
+ a.failure_message Chef::Exceptions::Package, "Checksum on resource (#{short_cksum(new_resource.checksum)}) does not match checksum on content (#{short_cksum(source_location)})"
+ end
+ end
+
requirements.assert(:install) do |a|
a.assertion { new_resource.source || msi? }
- a.failure_message Chef::Exceptions::NoWindowsPackageSource, "Source for package #{new_resource.name} must be specified in the resource's source property for package to be installed because the package_name property is used to test for the package installation state for this package type."
+ a.failure_message Chef::Exceptions::NoWindowsPackageSource, "Source for package #{new_resource.package_name} must be specified in the resource's source property for package to be installed because the package_name property is used to test for the package installation state for this package type."
+ end
+
+ unless uri_scheme?(new_resource.source)
+ requirements.assert(:install) do |a|
+ a.assertion { ::File.exist?(new_resource.source) }
+ a.failure_message Chef::Exceptions::Package, "Source for package #{new_resource.package_name} does not exist"
+ a.whyrun "Assuming source file #{new_resource.source} would have been created."
+ end
end
end
- # load_current_resource is run in Chef::Provider#run_action when not in whyrun_mode?
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")
- current_resource.version(:unknown.to_s)
- else
- current_resource.version(package_provider.installed_version)
- new_resource.version(package_provider.package_version) if package_provider.package_version
+ if uri_scheme?(new_resource.source) && action == :install
+ download_source_file
end
+ @current_resource = Chef::Resource::WindowsPackage.new(new_resource.name)
+ current_resource.version(package_provider.installed_version)
+ new_resource.version(package_provider.package_version) if package_provider.package_version
+
current_resource
end
@@ -59,12 +73,12 @@ class Chef
@package_provider ||= begin
case installer_type
when :msi
- Chef::Log.debug("#{new_resource} is MSI")
- require "chef/provider/package/windows/msi"
+ logger.trace("#{new_resource} is MSI")
+ require_relative "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}'")
- require "chef/provider/package/windows/exe"
+ logger.trace("#{new_resource} is EXE with type '#{installer_type}'")
+ require_relative "windows/exe"
Chef::Provider::Package::Windows::Exe.new(resource_for_provider, installer_type, uninstall_registry_entries)
end
end
@@ -104,8 +118,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
@@ -113,24 +127,13 @@ class Chef
if basename == "setup.exe"
:installshield
else
- raise Chef::Exceptions::CannotDetermineWindowsInstallerType, "Installer type for Windows Package '#{new_resource.name}' not specified and cannot be determined from file extension '#{file_extension}'"
+ raise Chef::Exceptions::CannotDetermineWindowsInstallerType, "Installer type for Windows Package '#{new_resource.package_name}' not specified and cannot be determined from file extension '#{file_extension}'"
end
end
end
end
end
- def action_install
- if uri_scheme?(new_resource.source)
- download_source_file
- load_current_resource
- else
- validate_content!
- end
-
- super
- end
-
# Chef::Provider::Package action_install + action_remove call install_package + remove_package
# Pass those calls to the correct sub-provider
def install_package(name, version)
@@ -156,6 +159,18 @@ class Chef
# this package provider does not support package arrays
# However, There may be multiple versions for a single
# package so the first element may be a nested array
+ #
+ # FIXME: this breaks the semantics of the superclass and needs to get unwound. Since these package
+ # providers don't support multipackage they can't put multiple versions into this array. The windows
+ # package managers need this in order to uninstall multiple installed version, and they should track
+ # that in something like an `uninstall_version_array` of their own. The superclass does not implement
+ # this kind of feature. Doing this here breaks LSP and will create bugs since the superclass will not
+ # expect it at all. The `current_resource.version` also MUST NOT be an array if the package provider
+ # is not multipackage. The existing implementation of package_provider.installed_version should probably
+ # be what `uninstall_version_array` is, and then that list should be sorted and last/first'd into the
+ # current_resource.version. The current_version_array method was not intended to be overwritten by
+ # subclasses (but ruby provides no feature to block doing so -- it is already marked as private).
+ #
def current_version_array
[ current_resource.version ]
end
@@ -165,7 +180,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 +198,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,10 +225,11 @@ 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
+ # XXX: this is crazy
@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
@@ -206,24 +237,29 @@ class Chef
r.timeout(new_resource.timeout)
r.returns(new_resource.returns)
r.options(new_resource.options)
+ r.sensitive(new_resource.sensitive)
end
end
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 #{default_download_cache_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)
-
+ # It seems correct that this is a build_resource rather than declare_resource/DSL use since updating should not trigger a notification
+ # unless the downloaded file is actually installed. The case where the remote_file downloads the package but the package is already
+ # installed on the target should not trigger a notification since the running state did not change.
+ @source_resource ||= build_resource(:remote_file, default_download_cache_path) do
+ source(new_resource.source)
+ cookbook_name = new_resource.cookbook_name
+ checksum(new_resource.checksum)
+ backup(false)
+
+ # since the source_resource can mutate here, we save off the @source_resource so we can inspect the actual state later
if new_resource.remote_file_attributes
new_resource.remote_file_attributes.each do |(k, v)|
- r.send(k.to_sym, v)
+ send(k.to_sym, v)
end
end
end
@@ -231,7 +267,7 @@ class Chef
def default_download_cache_path
uri = ::URI.parse(new_resource.source)
- filename = ::File.basename(::URI.unescape(uri.path))
+ filename = ::File.basename(::CGI.unescape(uri.path))
file_cache_dir = Chef::FileCache.create_cache_path("package/")
Chef::Util::PathHelper.cleanpath("#{file_cache_dir}/#{filename}")
end
@@ -247,22 +283,13 @@ class Chef
end
end
- def validate_content!
- if new_resource.checksum
- source_checksum = checksum(source_location)
- if new_resource.checksum != source_checksum
- raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(source_checksum))
- end
- end
- end
-
def msi?
return true if new_resource.installer_type == :msi
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 44a2f19d1e..191e8fb4ae 100644
--- a/lib/chef/provider/package/windows/exe.rb
+++ b/lib/chef/provider/package/windows/exe.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Matt Wrock <matt@mattwrock.com>
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require "chef/mixin/shell_out"
+require_relative "../../../mixin/shell_out"
class Chef
class Provider
@@ -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",
@@ -62,16 +64,16 @@ class Chef
unattended_flags,
expand_options(new_resource.options),
"& exit %%%%ERRORLEVEL%%%%",
- ].join(" "), timeout: new_resource.timeout, returns: new_resource.returns
+ ].join(" "), default_env: false, timeout: new_resource.timeout, returns: new_resource.returns, sensitive: new_resource.sensitive
)
end
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), { :timeout => new_resource.timeout, :returns => new_resource.returns })
+ .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), default_env: false, timeout: new_resource.timeout, returns: new_resource.returns)
end
end
@@ -85,13 +87,14 @@ class Chef
" ",
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..1ce8add80d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,25 +16,27 @@
# 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"
+require_relative "../../../win32/api/installer" if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+require_relative "../../../mixin/shell_out"
class Chef
class Provider
class Package
class Windows
class MSI
- include Chef::ReservedNames::Win32::API::Installer if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
+ include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
include Chef::Mixin::ShellOut
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,45 +47,47 @@ 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
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)}", default_env: false, 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)}", default_env: false, 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}'")
- 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 })
- end
+ .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 %r{ /q}.match?(uninstall_string.downcase)
+ logger.trace("#{new_resource} removing MSI package version using '#{uninstall_string}'")
+ shell_out!(uninstall_string, default_env: false, timeout: new_resource.timeout, returns: new_resource.returns)
+ end
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 a693558883..6e7b825256 100644
--- a/lib/chef/provider/package/windows/registry_uninstall_entry.rb
+++ b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Matt Wrock <matt@mattwrock.com>
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,9 @@
# limitations under the License.
#
-require "win32/registry" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+module Win32
+ autoload :Registry, File.expand_path("../../../monkey_patches/win32/registry", __dir__) if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+end
class Chef
class Provider
@@ -26,7 +28,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)],
@@ -37,39 +39,51 @@ class Chef
begin
::Win32::Registry.open(hkey[0], UNINSTALL_SUBKEY, desired) do |reg|
reg.each_key do |key, _wtime|
- 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))
- end
- rescue ::Win32::Registry::Error => ex
- Chef::Log.debug("Registry error opening key '#{key}' on node #{desired}: #{ex}")
+
+ entry = reg.open(key, desired)
+ display_name = read_registry_property(entry, "DisplayName")
+ 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
+ logger.trace("Registry error opening key '#{key}' on node #{desired}: #{ex}")
+
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,6 +92,7 @@ class Chef
attr_reader :display_version
attr_reader :uninstall_string
attr_reader :data
+ attr_reader :logger
UNINSTALL_SUBKEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'.freeze
end
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 74d52946f7..76b9b15172 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,447 +15,264 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/provider/package"
-require "chef/resource/yum_package"
-require "chef/mixin/get_source_from_package"
-require "chef/provider/package/yum/rpm_utils"
-require "chef/provider/package/yum/yum_cache"
+require_relative "../package"
+require_relative "../../resource/yum_package"
+require_relative "../../mixin/which"
+require_relative "../../mixin/shell_out"
+require_relative "../../mixin/get_source_from_package"
+require_relative "yum/python_helper"
+require_relative "yum/version"
+# the stubs in the YumCache class are still an external API
+require_relative "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"
+ allow_nils
+ use_multipackage_api
+ use_package_name_for_source
- include Chef::Mixin::GetSourceFromPackage
+ provides :package, platform_family: "fedora_derived"
- def initialize(new_resource, run_context)
- super
+ provides :yum_package
- @yum = YumCache.instance
- @yum.yum_binary = yum_binary
+ #
+ # 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 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 load_current_resource
+ flushcache if new_resource.flush_cache[:before]
- # Extra attributes
- #
+ @current_resource = Chef::Resource::YumPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
- 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
+ current_resource
end
- def arch
- if @new_resource.respond_to?("arch")
- @new_resource.arch
- else
- nil
+ 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 set_arch(arch)
- if @new_resource.respond_to?("arch")
- @new_resource.arch(arch)
+ def candidate_version
+ package_name_array.each_with_index.map do |pkg, i|
+ available_version(i).version_with_arch
end
end
- def flush_cache
- if @new_resource.respond_to?("flush_cache")
- @new_resource.flush_cache
- else
- { :before => false, :after => false }
+ def get_current_versions
+ package_name_array.each_with_index.map do |pkg, i|
+ installed_version(i).version_with_arch
end
end
- # Helpers
- #
+ def install_package(names, versions)
+ method = nil
+ methods = []
+ names.each_with_index do |n, i|
+ next if n.nil?
- def yum_arch(arch)
- arch ? ".#{arch}" : nil
- end
+ av = available_version(i)
+ name = av.name # resolve the name via the available/candidate version
+ iv = python_helper.package_query(:whatinstalled, av.name_with_arch, options: options)
- 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
+ method = "install"
+
+ # 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 event of single-package
+ # rules where the user explicitly allowed it
+ method = "downgrade"
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}"
+ methods << method
end
- end
-
- # Standard Provider methods for Parent
- #
- def load_current_resource
- if flush_cache[:before]
- @yum.reload
+ # 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
- 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
+ if new_resource.source
+ yum(options, "-y", method, new_resource.source)
else
- @yum.disable_extra_repo_control
+ 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
- # 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
+ # yum upgrade does not work on uninstalled packaged, while install will upgrade
+ alias upgrade_package install_package
- @current_resource = Chef::Resource::YumPackage.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ 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
- 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
+ alias purge_package remove_package
- 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
+ action :flush_cache do
+ flushcache
+ end
- 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
+ # 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
+
+ # 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
- if @new_resource.version
- new_resource =
- "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch(parch)}"
+ private
+
+ # 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|
+ unless name.nil?
+ if installed_version(i).version.nil?
+ available_version(i).name
else
- new_resource = "#{@new_resource.package_name}#{yum_arch(parch)}"
+ installed_version(i).name
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
-
- 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
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
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = yum("versionlock", "list")
+ locked.stdout.each_line.map do |line|
+ line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip
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
+ 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
+ def packages_all_locked?(names, versions)
+ resolved_package_lock_names(names).all? { |n| locked_packages.include? n }
+ 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
+ def packages_all_unlocked?(names, versions)
+ !resolved_package_lock_names(names).any? { |n| locked_packages.include? n }
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
+ def version_gt?(v1, v2)
+ return false if v1.nil? || v2.nil?
- if flush_cache[:after]
- @yum.reload
- else
- @yum.reload_installed
- end
+ python_helper.compare_versions(v1, v2) == 1
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 version_equals?(v1, v2)
+ return false if v1.nil? || v2.nil?
+
+ python_helper.compare_versions(v1, v2) == 0
end
- def upgrade_package(name, version)
- install_package(name, version)
+ def version_compare(v1, v2)
+ return false if v1.nil? || v2.nil?
+
+ python_helper.compare_versions(v1, v2)
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(" ")
- else
- remove_str = as_array(name).map do |n|
- a = arch_for_name(n)
- "#{n}#{yum_arch(a)}"
- end.join(" ")
+ def resolve_source_to_version_obj
+ shell_out!("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
- yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}")
+ end
- if flush_cache[:after]
- @yum.reload
- else
- @yum.reload_installed
- end
+ # @return Array<Version>
+ def available_version(index)
+ @available_version ||= []
+
+ @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
+
+ @available_version[index]
end
- def purge_package(name, version)
- remove_package(name, version)
+ # @return Array<Version>
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= if new_resource.source
+ python_helper.package_query(:whatinstalled, available_version(index).name, arch: safe_arch_array[index], options: options)
+ else
+ python_helper.package_query(:whatinstalled, package_name_array[index], arch: safe_arch_array[index], options: options)
+ end
+ @installed_version[index]
end
- private
+ # 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 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
+ 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
- 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 yum(*args)
+ shell_out!(yum_binary, *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
- # Transform the package_name into a requirement, might contain a version, could just be
- # a match for virtual provides
- require_string = name
+ [ new_resource.version ]
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
+ end
- [new_package_name, new_package_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
+ [ 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..7758383b95
--- /dev/null
+++ b/lib/chef/provider/package/yum/python_helper.rb
@@ -0,0 +1,228 @@
+#
+# Copyright:: Copyright (c) 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 "../../../mixin/which"
+require_relative "../../../mixin/shell_out"
+require_relative "version"
+require "singleton" unless defined?(Singleton)
+require "timeout" unless defined?(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 ||= begin
+ cmd = which("platform-python", "python", "python2", "python2.7", extra_path: "/usr/libexec") do |f|
+ shell_out("#{f} -c 'import yum'").exitstatus == 0
+ end
+ raise Chef::Exceptions::Package, "cannot find yum libraries, you may need to use dnf_package" unless cmd
+
+ "#{cmd} #{YUM_HELPER}"
+ end
+ 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"
+ false
+ elsif query_output == "True"
+ 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
+
+ # @return 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)
+ # XXX: for now we restart before and after every query with an enablerepo/disablerepo to clean the helpers internal state
+ restart unless repo_opts.empty?
+ query_output = query(action, parameters)
+ version = parse_response(query_output.lines.last)
+ Chef::Log.trace "parsed #{version} from python helper"
+ 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 %i{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
index 032597d047..7514d57bce 100644
--- a/lib/chef/provider/package/yum/rpm_utils.rb
+++ b/lib/chef/provider/package/yum/rpm_utils.rb
@@ -1,6 +1,6 @@
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,16 @@
# limitations under the License.
#
-require "chef/provider/package"
+require_relative "../../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 abandoned 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
@@ -37,7 +46,7 @@ class Chef
lead = 0
tail = evr.size
- if %r{^([\d]+):}.match(evr) # rubocop:disable Performance/RedundantMatch
+ if /^(\d+):/.match(evr) # rubocop:disable Performance/RedundantMatch
epoch = $1.to_i
lead = $1.length + 1
elsif evr[0].ord == ":".ord
@@ -45,7 +54,7 @@ class Chef
lead = 1
end
- if %r{:?.*-(.*)$}.match(evr) # rubocop:disable Performance/RedundantMatch
+ if /:?.*-(.*)$/.match(evr) # rubocop:disable Performance/RedundantMatch
release = $1
tail = evr.length - release.length - lead - 1
@@ -124,9 +133,7 @@ class Chef
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
+ y_pos += 1 while (y_pos <= y_pos_max) && (isalnum(y[y_pos]) == false)
# 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)
@@ -145,29 +152,21 @@ class Chef
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
+ x_seg_pos += 1 while (x_seg_pos <= x_pos_max) && isdigit(x[x_seg_pos])
# 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_seg_pos += 1 while (y_seg_pos <= y_pos_max) && isdigit(y[y_seg_pos])
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_seg_pos += 1 while (x_seg_pos <= x_pos_max) && isalpha(x[x_seg_pos])
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_seg_pos += 1 while (y_seg_pos <= y_pos_max) && isalpha(y[y_seg_pos])
y_comp = y[y_pos, y_seg_pos - y_pos]
end
@@ -210,9 +209,9 @@ class Chef
# the most unprocessed characters left wins
if (x_pos_max - x_pos) > (y_pos_max - y_pos)
- return 1
+ 1
else
- return -1
+ -1
end
end
@@ -230,17 +229,17 @@ class Chef
@v = args[1]
@r = args[2]
else
- raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " +
+ 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
+ alias epoch e
+ alias version v
+ alias release r
def self.parse(*args)
- self.new(*args)
+ new(*args)
end
def <=>(other)
@@ -317,7 +316,7 @@ class Chef
return cmp
end
- return 0
+ 0
end
end
@@ -339,7 +338,7 @@ class Chef
@a = args[4]
@provides = args[5]
else
- raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " +
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " \
"or 'name, epoch, version, release, arch, provides'"
end
@@ -349,8 +348,8 @@ class Chef
end
end
attr_reader :n, :a, :version, :provides
- alias :name :n
- alias :arch :a
+ alias name n
+ alias arch a
def <=>(other)
compare(other)
@@ -395,7 +394,7 @@ class Chef
end
end
- return 0
+ 0
end
def to_s
@@ -423,7 +422,7 @@ class Chef
@version = RPMVersion.new(e, v, r)
@flag = args[4] || :==
else
- raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " +
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " \
"'name, epoch, version, release, flag'"
end
end
@@ -434,25 +433,25 @@ class Chef
# "mtr >= 2:0.71-3.0"
# "mta"
def self.parse(string)
- if %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}.match(string) # rubocop:disable Performance/RedundantMatch
+ if /^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$/.match(string) # rubocop:disable Performance/RedundantMatch
name = $1
- if $2 == "="
- flag = :==
- else
- flag = :"#{$2}"
- end
+ flag = if $2 == "="
+ :==
+ else
+ :"#{$2}"
+ end
version = $3
- return self.new(name, version, flag)
+ new(name, version, flag)
else
name = string
- return self.new(name, nil, nil)
+ new(name, nil, nil)
end
end
# Test if another RPMDependency satisfies our requirements
def satisfy?(y)
- unless y.kind_of?(RPMDependency)
+ unless y.is_a?(RPMDependency)
raise ArgumentError, "Expecting an RPMDependency object"
end
@@ -481,7 +480,7 @@ class Chef
return true
end
- return false
+ false
end
end
@@ -504,11 +503,11 @@ class Chef
class RPMDb
def initialize
# package name => [ RPMPackage, RPMPackage ] of different versions
- @rpms = Hash.new
+ @rpms = {}
# package nevra => RPMPackage for lookups
- @index = Hash.new
+ @index = {}
# provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
- @provides = Hash.new
+ @provides = {}
# RPMPackages listed as available
@available = Set.new
# RPMPackages listed as installed
@@ -516,16 +515,16 @@ class Chef
end
def [](package_name)
- self.lookup(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
+ pkgs.sort.reverse
else
- return nil
+ nil
end
end
@@ -537,11 +536,11 @@ class Chef
# 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)
+ unless new_rpm.is_a?(RPMDbPackage)
raise ArgumentError, "Expecting an RPMDbPackage object"
end
- @rpms[new_rpm.n] ||= Array.new
+ @rpms[new_rpm.n] ||= []
# we may already have this one, like when the installed list is refreshed
idx = @index[new_rpm.nevra]
@@ -552,7 +551,7 @@ class Chef
@rpms[new_rpm.n] << new_rpm
new_rpm.provides.each do |provide|
- @provides[provide.name] ||= Array.new
+ @provides[provide.name] ||= []
@provides[provide.name] << new_rpm
end
@@ -574,7 +573,7 @@ class Chef
end
def <<(*args)
- self.push(args)
+ push(args)
end
def clear
@@ -596,7 +595,7 @@ class Chef
def size
@rpms.size
end
- alias :length :size
+ alias length size
def available_size
@available.size
@@ -615,7 +614,7 @@ class Chef
end
def whatprovides(rpmdep)
- unless rpmdep.kind_of?(RPMDependency)
+ unless rpmdep.is_a?(RPMDependency)
raise ArgumentError, "Expecting an RPMDependency object"
end
@@ -632,7 +631,7 @@ class Chef
end
end
- return what
+ what
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..6107a54532
--- /dev/null
+++ b/lib/chef/provider/package/yum/version.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright (c) 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 name_with_arch
+ "#{name}.#{arch}" unless name.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-dump.py b/lib/chef/provider/package/yum/yum-dump.py
deleted file mode 100644
index 6183460195..0000000000
--- a/lib/chef/provider/package/yum/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/yum_cache.rb b/lib/chef/provider/package/yum/yum_cache.rb
index 7462529113..b65e1e560f 100644
--- a/lib/chef/provider/package/yum/yum_cache.rb
+++ b/lib/chef/provider/package/yum/yum_cache.rb
@@ -1,6 +1,6 @@
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,357 +16,74 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/provider/package"
-require "chef/mixin/which"
-require "chef/mixin/shell_out"
-require "singleton"
-require "chef/provider/package/yum/rpm_utils"
+require_relative "python_helper"
+require_relative "../../package"
+require "singleton" unless defined?(Singleton)
+
+#
+# These are largely historical APIs, the YumCache object no longer exists and this is a
+# facade 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
- # 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 yum_dump_path
- ::File.join(::File.dirname(__FILE__), "yum-dump.py")
- end
-
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
-
- status = nil
-
- begin
- status = shell_out!("#{python_bin} #{yum_dump_path}#{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("#{yum_dump_path} 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
+ python_helper.restart
end
def reload
- @next_refresh = :all
+ python_helper.restart
end
def reload_installed
- @next_refresh = :installed
+ python_helper.restart
end
def reload_provides
- @next_refresh = :provides
+ python_helper.restart
end
def reset
- @rpmdb.clear
+ python_helper.restart
end
def reset_installed
- @rpmdb.clear_installed
+ python_helper.restart
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
- end
- end
-
- return false
+ def available_version(name, arch = nil)
+ p = python_helper.package_query(:whatavailable, name, arch: arch)
+ "#{p.version}.#{p.arch}" unless p.version.nil?
end
- # Returns a array of packages satisfying an RPMDependency
- def packages_from_require(rpmdep)
- refresh
- @rpmdb.whatprovides(rpmdep)
+ def installed_version(name, arch = nil)
+ p = python_helper.package_query(:whatinstalled, name, arch: arch)
+ "#{p.version}.#{p.arch}" unless p.version.nil?
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
+ def package_available?(name, arch = nil)
+ p = python_helper.package_query(:whatavailable, name, arch: arch)
+ !p.version.nil?
end
- # Return the latest available version for a package.arch
- def available_version(package_name, arch = nil)
- version(package_name, arch, true, false)
+ # 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
- 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
+ # @api private
+ def python_helper
+ @python_helper ||= PythonHelper.instance
end
end # YumCache
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..47cbe2efe6
--- /dev/null
+++ b/lib/chef/provider/package/yum/yum_helper.py
@@ -0,0 +1,216 @@
+#!/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 bool(re.search('\\s+', command['provides'])):
+ # handles flags (<, >, =, etc) and versions, but no wildcareds
+ # raises error for any invalid input like: 'FOO BAR BAZ'
+ 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):
+ if base is not None:
+ 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")
+
+try:
+ while 1:
+ # kill self if we get orphaned (tragic)
+ ppid = os.getppid()
+ if ppid == 1:
+ raise RuntimeError("orphaned")
+
+ setup_exit_handler()
+ line = inpipe.readline()
+
+ try:
+ command = json.loads(line)
+ except ValueError, e:
+ raise RuntimeError("bad json parse")
+
+ 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")
+finally:
+ if base is not None:
+ base.closeRpmDB()
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index e20a7332f7..a1a433cbdf 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
#
# Authors:: Adam Jacob (<adam@chef.io>)
# Ionuț Arțăriși (<iartarisi@suse.cz>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
-# Copyright 2013-2016, SUSE Linux GmbH
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2013-2016, SUSE Linux GmbH
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,70 +18,119 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/zypper_package"
+require_relative "../package"
+require_relative "../../resource/zypper_package"
class Chef
class Provider
class Package
class Zypper < Chef::Provider::Package
use_multipackage_api
+ allow_nils
provides :package, platform_family: "suse"
- provides :zypper_package, os: "linux"
+ provides :zypper_package
- def get_versions(package_name)
- candidate_version = current_version = nil
+ def load_current_resource
+ @current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
+ current_resource
+ end
+
+ def install_package(name, version)
+ zypper_package("install", global_options, *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
+ end
+
+ def upgrade_package(name, version)
+ # `zypper install` upgrades packages, we rely on the idempotency checks to get action :install behavior
+ install_package(name, version)
+ end
+
+ def remove_package(name, version)
+ zypper_package("remove", global_options, *options, name, version)
+ end
+
+ def purge_package(name, version)
+ zypper_package("remove", global_options, *options, "--clean-deps", name, version)
+ end
+
+ def lock_package(name, version)
+ zypper_package("addlock", global_options, *options, name, version)
+ end
+
+ def unlock_package(name, version)
+ zypper_package("removelock", global_options, *options, name, version)
+ end
+
+ private
+
+ def get_current_versions
+ package_name_array.each_with_index.map { |pkg, i| installed_version(i) }
+ end
+
+ def candidate_version
+ @candidate_version ||= package_name_array.each_with_index.map { |pkg, i| available_version(i) }
+ end
+
+ def resolve_current_version(package_name)
+ latest_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!("zypper", "--non-interactive", "info", package_name)
status.stdout.each_line do |line|
case line
when /^Version *: (.+) *$/
- candidate_version = $1.strip
- Chef::Log.debug("#{new_resource} version #{candidate_version}")
- when /^Installed *: Yes *$/
+ latest_version = $1.strip
+ logger.trace("#{new_resource} version #{latest_version}")
+ when /^Installed *: Yes.*$/ # http://rubular.com/r/9StcAMjOn6
is_installed = true
- Chef::Log.debug("#{new_resource} is installed")
+ logger.trace("#{new_resource} is installed")
when /^Status *: out-of-date \(version (.+) installed\) *$/
current_version = $1.strip
- Chef::Log.debug("#{new_resource} out of date version #{current_version}")
+ logger.trace("#{new_resource} out of date version #{current_version}")
end
end
- current_version = candidate_version if is_installed
- { current_version: current_version, candidate_version: candidate_version }
+ current_version ||= latest_version if is_installed
+ current_version
end
- def versions
- @versions ||=
- begin
- raw_versions = package_name_array.map do |package_name|
- get_versions(package_name)
+ def resolve_available_version(package_name, new_version)
+ search_string = new_version.nil? ? package_name : "#{package_name}=#{new_version}"
+ so = shell_out!("zypper", "--non-interactive", "search", "-s", "--provides", "--match-exact", "--type=package", search_string)
+ so.stdout.each_line do |line|
+ if md = line.match(/^(\S*)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(.*)$/)
+ (status, name, type, version, arch, repo) = [ md[1], md[2], md[3], md[4], md[5], md[6] ]
+ next if version == "Version" # header
+
+ # sometimes even though we request a specific version in the search string above and have match exact, we wind up
+ # with other versions in the output, particularly getting the installed version when downgrading.
+ if new_version
+ next unless version == new_version || version.start_with?("#{new_version}-")
end
- Hash[*package_name_array.zip(raw_versions).flatten]
- end
- end
- def get_candidate_versions
- package_name_array.map do |package_name|
- versions[package_name][:candidate_version]
+ return version
+ end
end
+ nil
end
- def get_current_versions
- package_name_array.map do |package_name|
- versions[package_name][:current_version]
- end
+ def available_version(index)
+ @available_version ||= []
+ @available_version[index] ||= resolve_available_version(package_name_array[index], safe_version_array[index])
+ @available_version[index]
end
- def load_current_resource
- @current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
- current_resource.package_name(new_resource.package_name)
-
- @candidate_version = get_candidate_versions
- current_resource.version(get_current_versions)
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= resolve_current_version(package_name_array[index])
+ @installed_version[index]
+ end
- current_resource
+ def zip(names, versions)
+ names.zip(versions).map do |n, v|
+ (v.nil? || v.empty?) ? n : "#{n}=#{v}"
+ end.compact
end
def zypper_version
@@ -90,53 +138,55 @@ class Chef
`zypper -V 2>&1`.scan(/\d+/).join(".").to_f
end
- def install_package(name, version)
- zypper_package("install --auto-agree-with-licenses", name, version)
+ def zypper_package(command, global_options, *options, names, versions)
+ zipped_names = zip(names, versions)
+ if zypper_version < 1.0
+ shell_out!("zypper", global_options, gpg_checks, command, *options, "-y", names)
+ else
+ shell_out!("zypper", global_options, "--non-interactive", gpg_checks, command, *options, zipped_names)
+ end
end
- def upgrade_package(name, version)
- # `zypper install` upgrades packages, we rely on the idempotency checks to get action :install behavior
- install_package(name, version)
+ def gpg_checks
+ "--no-gpg-checks" unless new_resource.gpg_check
end
- def remove_package(name, version)
- zypper_package("remove", name, version)
+ def allow_downgrade
+ "--oldpackage" if new_resource.allow_downgrade
end
- def purge_package(name, version)
- zypper_package("remove --clean-deps", name, version)
+ def global_options
+ new_resource.global_options
end
- private
+ def packages_all_locked?(names, versions)
+ names.all? { |n| locked_packages.include? n }
+ end
- def zip(names, versions)
- names.zip(versions).map do |n, v|
- (v.nil? || v.empty?) ? n : "#{n}=#{v}"
- end
+ def packages_all_unlocked?(names, versions)
+ names.all? { |n| !locked_packages.include? n }
end
- def zypper_package(command, 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))
- else
- shell_out_with_timeout!(a_to_s("zypper --non-interactive", gpg_checks, command, zipped_names))
- end
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = shell_out!("zypper", "locks")
+ locked.stdout.each_line.map do |line|
+ line.split("|").shift(2).last.strip
+ end
+ 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"
+ 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
+
end
end
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index ab85ec35ac..8360dd873b 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,87 +16,89 @@
# limitations under the License.
#
-require "chef/platform/query_helpers"
-require "chef/provider/windows_script"
+require_relative "../platform/query_helpers"
+require_relative "windows_script"
class Chef
class Provider
class PowershellScript < Chef::Provider::WindowsScript
+ # FIXME: use composition not inheritance
- provides :powershell_script, os: "windows"
+ provides :powershell_script
- def initialize(new_resource, run_context)
- super(new_resource, run_context, ".ps1")
- add_exit_status_wrapper
- end
-
- def action_run
+ action :run do
validate_script_syntax!
- super
+ super()
end
- def command
- 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)
+ # Set InputFormat to None as PowerShell will hang if STDIN is redirected
+ # http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
+ DEFAULT_FLAGS = "-NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass -InputFormat None".freeze
+ def command
# Must use -File rather than -Command to launch the script
# file created by the base class that contains the script
# code -- otherwise, powershell.exe does not propagate the
# error status of a failed Windows process that ran at the
# end of the script, it gets changed to '1'.
#
- # Nano only supports -Command
- cmd = "\"#{interpreter_path}\" #{flags}"
- if Chef::Platform.windows_nano_server?
- cmd << " -Command \". '#{script_file.path}'\""
- else
- cmd << " -File \"#{script_file.path}\""
- end
- cmd
+ [
+ %Q{"#{interpreter_path}"},
+ DEFAULT_FLAGS,
+ new_resource.flags,
+ %Q{-File "#{script_file_path}"},
+ ].join(" ")
end
- def flags
- interpreter_flags = [*default_interpreter_flags].join(" ")
+ protected
- if ! (@new_resource.flags.nil?)
- interpreter_flags = [@new_resource.flags, interpreter_flags].join(" ")
+ def interpreter_path
+ # Powershell.exe is always in "v1.0" folder (for backwards compatibility)
+ # pwsh is the other interpreter and we will assume that it is on the path.
+ # It will exist in different folders depending on the installed version.
+ # There can also be multiple versions installed. Depending on how it was installed,
+ # there might be a registry entry pointing to the installation path. The key will
+ # differ depending on version and architecture. It seems best to let the PATH
+ # determine the file path to use since that will provide the same pwsh.exe one
+ # would invoke from any shell.
+ if interpreter == "powershell"
+ Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", "#{interpreter}.exe")
+ else
+ interpreter
end
-
- interpreter_flags
end
- protected
-
- # Process exit codes are strange with PowerShell and require
- # 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")
+ def code
+ code = wrapper_script
+ 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")
+ code
end
def validate_script_syntax!
- interpreter_arguments = default_interpreter_flags.join(" ")
Tempfile.open(["chef_powershell_script-user-code", ".ps1"]) do |user_script_file|
# Wrap the user's code in a PowerShell script block so that
# it isn't executed. However, syntactically invalid script
# in that block will still trigger a syntax error which is
# exactly what we want here -- verify the syntax without
# actually running the script.
- user_code_wrapped_in_powershell_script_block = <<-EOH
-{
- #{@new_resource.code}
-}
-EOH
+ user_code_wrapped_in_powershell_script_block = <<~EOH
+ {
+ #{new_resource.code}
+ }
+ EOH
user_script_file.puts user_code_wrapped_in_powershell_script_block
# A .close or explicit .flush required to ensure the file is
# written to the file system at this point, which is required since
# the intent is to execute the code just written to it.
user_script_file.close
- validation_command = "\"#{interpreter}\" #{interpreter_arguments} -Command \". '#{user_script_file.path}'\""
+ validation_command = [
+ %Q{"#{interpreter_path}"},
+ DEFAULT_FLAGS,
+ new_resource.flags,
+ %Q{-Command ". '#{user_script_file.path}'"},
+ ].join(" ")
# Note that other script providers like bash allow syntax errors
# to be suppressed by setting 'returns' to a value that the
@@ -110,106 +112,99 @@ EOH
# means a non-zero return and thus a syntactically invalid script.
with_os_architecture(node, architecture: new_resource.architecture) do
- shell_out!(validation_command, { returns: [0] })
+ shell_out!(validation_command, returns: [0])
end
end
end
- def default_interpreter_flags
- return [] if Chef::Platform.windows_nano_server?
-
- # Execution policy 'Bypass' is preferable since it doesn't require
- # user input confirmation for files such as PowerShell modules
- # downloaded from the Internet. However, 'Bypass' is not supported
- # prior to PowerShell 3.0, so the fallback is 'Unrestricted'
- execution_policy = Chef::Platform.supports_powershell_execution_bypass?(run_context.node) ? "Bypass" : "Unrestricted"
-
- [
- "-NoLogo",
- "-NonInteractive",
- "-NoProfile",
- "-ExecutionPolicy #{execution_policy}",
- # Powershell will hang if STDIN is redirected
- # http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
- "-InputFormat None",
- ]
- end
-
+ # Process exit codes are strange with PowerShell and require
+ # special handling to cover common use cases.
# A wrapper script is used to launch user-supplied script while
# still obtaining useful process exit codes. Unless you
- # explicitly call exit in Powershell, the powershell.exe
+ # explicitly call exit in PowerShell, the powershell.exe
# interpreter returns only 0 for success or 1 for failure. Since
# we'd like to get specific exit codes from executable tools run
- # with Powershell, we do some work using the automatic variables
+ # with PowerShell, we do some work using the automatic variables
# $? and $LASTEXITCODE to return the process exit code of the
# last process run in the script if it is the last command
# executed, otherwise 0 or 1 based on whether $? is set to true
# (success, where we return 0) or false (where we return 1).
def wrapper_script
- <<-EOH
-# Chef Client wrapper for powershell_script resources
-
-# LASTEXITCODE can be uninitialized -- make it explictly 0
-# to avoid incorrect detection of failure (non-zero) codes
-$global:LASTEXITCODE = 0
-
-# Catch any exceptions -- without this, exceptions will result
-# In a zero return code instead of the desired non-zero code
-# that indicates a failure
-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 chefscriptresult -visibility private
-
-# Initialize a variable we use to capture $? inside a block
-$global:lastcmdlet = $null
-
-# Execute the user's code in a script block --
-$chefscriptresult =
-{
- #{@new_resource.code}
-
- # This assignment doesn't affect the block's return value
- $global:lastcmdlet = $?
-}.invokereturnasis()
-
-# Assume failure status of 1 -- success cases
-# will have to override this
-$exitstatus = 1
-
-# If convert_boolean_return is enabled, the block's return value
-# gets precedence in determining our exit status
-if ($interpolatedexitcode -and $chefscriptresult -ne $null -and $chefscriptresult.gettype().name -eq 'boolean')
-{
- $exitstatus = [int32](!$chefscriptresult)
-}
-elseif ($lastcmdlet)
-{
- # Otherwise, a successful cmdlet execution defines the status
- $exitstatus = 0
-}
-elseif ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 )
-{
- # If the cmdlet status is failed, allow the Win32 status
- # in $LASTEXITCODE to define exit status. This handles the case
- # where no cmdlets, only Win32 processes have run since $?
- # will be set to $false whenever a Win32 process returns a non-zero
- # status.
- $exitstatus = $LASTEXITCODE
-}
-
-# Print STDOUT for the script execution
-Write-Output $chefscriptresult
-
-# If this script is launched with -File, the process exit
-# status of PowerShell.exe will be $exitstatus. If it was
-# launched with -Command, it will be 0 if $exitstatus was 0,
-# 1 (i.e. failed) otherwise.
-exit $exitstatus
-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 explicitly 0
+ # to avoid incorrect detection of failure (non-zero) codes
+ $global:LASTEXITCODE = 0
+
+ # Catch any exceptions -- without this, exceptions will result
+ # In a zero return code instead of the desired non-zero code
+ # that indicates a failure
+ 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 chefscriptresult -visibility private
+
+ # Initialize a variable we use to capture $? inside a block
+ $global:lastcmdlet = $null
+
+ # Execute the user's code in a script block --
+ $chefscriptresult =
+ {
+ #{new_resource.code}
+
+ # This assignment doesn't affect the block's return value
+ $global:lastcmdlet = $?
+ }.invokereturnasis()
+
+ # Assume failure status of 1 -- success cases
+ # will have to override this
+ $exitstatus = 1
+
+ # If convert_boolean_return is enabled, the block's return value
+ # gets precedence in determining our exit status
+ if ($interpolatedexitcode -and $chefscriptresult -ne $null -and $chefscriptresult.gettype().name -eq 'boolean')
+ {
+ $exitstatus = [int32](!$chefscriptresult)
+ }
+ elseif ($lastcmdlet)
+ {
+ # Otherwise, a successful cmdlet execution defines the status
+ $exitstatus = 0
+ }
+ elseif ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 )
+ {
+ # If the cmdlet status is failed, allow the Win32 status
+ # in $LASTEXITCODE to define exit status. This handles the case
+ # where no cmdlets, only Win32 processes have run since $?
+ # will be set to $false whenever a Win32 process returns a non-zero
+ # status.
+ $exitstatus = $LASTEXITCODE
+ }
+
+ # Print STDOUT for the script execution
+ Write-Output $chefscriptresult
+
+ # If this script is launched with -File, the process exit
+ # status of PowerShell.exe will be $exitstatus. If it was
+ # launched with -Command, it will be 0 if $exitstatus was 0,
+ # 1 (i.e. failed) otherwise.
+ exit $exitstatus
+ EOH
end
+ def script_extension
+ ".ps1"
+ end
end
end
end
diff --git a/lib/chef/provider/reboot.rb b/lib/chef/provider/reboot.rb
deleted file mode 100644
index 34eee9236d..0000000000
--- a/lib/chef/provider/reboot.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# Author:: Chris Doherty <cdoherty@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef, 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/provider"
-
-class Chef
- class Provider
- class Reboot < Chef::Provider
- provides :reboot
-
- def whyrun_supported?
- true
- end
-
- 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
- end
-
- def request_reboot
- node.run_context.request_reboot(
- :delay_mins => @new_resource.delay_mins,
- :reason => @new_resource.reason,
- :timestamp => Time.now,
- :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}'"
- 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}'"
- request_reboot
- throw :end_client_run_early
- end
- end
-
- def action_cancel
- converge_by("cancel any existing end-of-run reboot request") do
- Chef::Log.warn "Reboot canceled: '#{@new_resource.name}'"
- node.run_context.cancel_reboot
- end
- end
- end
- end
-end
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index 5e8dbe9bd8..316a2a1081 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 (c) 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.
@@ -17,15 +17,15 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/log"
-require "chef/resource/file"
-require "chef/mixin/checksum"
-require "chef/provider"
-require "etc"
-require "fileutils"
-require "chef/scan_access_control"
-require "chef/win32/registry"
+require_relative "../config"
+require_relative "../log"
+require_relative "../resource/file"
+require_relative "../mixin/checksum"
+require_relative "../provider"
+require "etc" unless defined?(Etc)
+require "fileutils" unless defined?(FileUtils)
+require_relative "../scan_access_control"
+require_relative "../win32/registry"
class Chef
@@ -35,31 +35,29 @@ class Chef
include Chef::Mixin::Checksum
- def whyrun_supported?
- true
- end
+ WORD_TYPES = %i{dword dword_big_endian qword}.freeze
def running_on_windows!
- unless Chef::Platform.windows?
+ unless ChefUtils.windows?
raise Chef::Exceptions::Win32NotWindows, "Attempt to manipulate the windows registry on a non-windows node"
end
end
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,85 +68,129 @@ class Chef
end
end
+ def key_missing?(values, name)
+ values.each do |v|
+ return true unless v.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 recursive == 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)
+ action :create do
+ 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|
- if @name_hash.has_key?(value[:name].downcase)
+ new_resource.unscrubbed_values.each do |value|
+ if @name_hash.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
+ value[:data] = value[:data].to_i if WORD_TYPES.include?(value[:type])
+
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 = if new_resource.sensitive
+ value.merge(data: "*sensitive value suppressed*")
+ else
+ value
+ end
+
+ 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 = if new_resource.sensitive
+ value.merge(data: "*sensitive value suppressed*")
+ else
+ value
+ end
+
+ 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)
+ action :create_if_missing do
+ 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|
- unless @name_hash.has_key?(value[:name].downcase)
- converge_by("create value #{value}") do
- registry.set_value(@new_resource.key, value)
+ new_resource.unscrubbed_values.each do |value|
+ unless @name_hash.key?(value[:name].downcase)
+ converge_by_value = if new_resource.sensitive
+ value.merge(data: "*sensitive value suppressed*")
+ else
+ value
+ end
+
+ 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 @name_hash.has_key?(value[:name].downcase)
- converge_by("delete value #{value}") do
- registry.delete_value(@new_resource.key, value)
+ action :delete do
+ if registry.key_exists?(new_resource.key)
+ new_resource.unscrubbed_values.each do |value|
+ if @name_hash.key?(value[:name].downcase)
+ 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
end
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)
+ action :delete_key do
+ 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 15b71c43bd..0c2cce4026 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,15 @@
# limitations under the License.
#
-require "chef/provider/directory"
-require "chef/resource/file"
-require "chef/resource/directory"
-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_relative "directory"
+require_relative "../resource/file"
+require_relative "../resource/directory"
+require_relative "../resource/cookbook_file"
+require_relative "../mixin/file_class"
+require_relative "../platform/query_helpers"
+require_relative "../util/path_helper"
-require "forwardable"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
@@ -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.
#
@@ -62,8 +58,8 @@ class Chef
# Handle action :create.
#
- def action_create
- super
+ action :create do
+ super()
# Transfer files
files_to_transfer.each do |cookbook_file_relative_path|
@@ -77,7 +73,7 @@ class Chef
# Handle action :create_if_missing.
#
- def action_create_if_missing
+ action :create_if_missing do
# if this action is called, ignore the existing overwrite flag
@overwrite = false
action_create
@@ -106,7 +102,7 @@ class Chef
if purge
Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob_dir(path), "**", "*"), ::File::FNM_DOTMATCH).sort!.reverse!.each do |file|
# skip '.' and '..'
- next if [".", ".."].include?(Pathname.new(file).basename().to_s)
+ next if [".", ".."].include?(Pathname.new(file).basename.to_s)
# Clean the path. This is required because of the ::File.join
file = Chef::Util::PathHelper.cleanpath(file)
@@ -115,7 +111,7 @@ class Chef
next if managed_files.include?(file)
if ::File.directory?(file)
- if !Chef::Platform.windows? && file_class.symlink?(file.dup)
+ if !ChefUtils.windows? && file_class.symlink?(file.dup)
# Unix treats dir symlinks as files
purge_file(file)
else
@@ -151,11 +147,11 @@ class Chef
new_resource.updated_by_last_action(true) if res.updated?
end
- # Get the files to tranfer. This returns files in lexicographical sort order.
+ # Get the files to transfer. This returns files in lexicographical sort order.
#
# 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
@@ -212,7 +208,7 @@ class Chef
# Set the sensitivity level
res.sensitive(new_resource.sensitive)
res.source(::File.join(source, relative_source_path))
- if Chef::Platform.windows? && files_rights
+ if ChefUtils.windows? && files_rights
files_rights.each_pair do |permission, *args|
res.rights(permission, *args)
end
@@ -248,8 +244,8 @@ class Chef
def directory_resource(dir)
res = Chef::Resource::Directory.new(dir, run_context)
res.cookbook_name = resource_cookbook
- if Chef::Platform.windows? && rights
- # rights are only meant to be applied to the toppest-level directory;
+ if ChefUtils.windows? && rights
+ # rights are only meant to be applied to the most top-level directory;
# Windows will handle inheritance.
if dir == path
rights.each do |r|
@@ -268,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..8c366adc10 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -1,7 +1,7 @@
#
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,34 +17,48 @@
# limitations under the License.
#
-require "chef/provider/file"
-require "chef/deprecation/provider/remote_file"
-require "chef/deprecation/warnings"
+require_relative "file"
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
+ 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..b8c7483f21 100644
--- a/lib/chef/provider/remote_file/cache_control_data.rb
+++ b/lib/chef/provider/remote_file/cache_control_data.rb
@@ -3,7 +3,7 @@
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Lamont Granquist (<lamont@chef.io>)
# Copyright:: Copyright 2013-2016, Jesse Campbell
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,11 +19,11 @@
# limitations under the License.
#
-require "stringio"
-require "chef/file_cache"
-require "chef/json_compat"
-require "chef/digester"
-require "chef/exceptions"
+require "stringio" unless defined?(StringIO)
+require_relative "../../file_cache"
+require_relative "../../json_compat"
+require_relative "../../digester"
+require_relative "../../exceptions"
class Chef
class Provider
@@ -146,14 +146,14 @@ class Chef
def load_json_data
path = sanitized_cache_file_path(sanitized_cache_file_basename)
- if Chef::FileCache.has_key?(path)
+ if Chef::FileCache.key?(path)
Chef::FileCache.load(path)
else
old_path = sanitized_cache_file_path(sanitized_cache_file_basename_md5)
- if Chef::FileCache.has_key?(old_path)
+ if Chef::FileCache.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..665da47e8d 100644
--- a/lib/chef/provider/remote_file/content.rb
+++ b/lib/chef/provider/remote_file/content.rb
@@ -1,7 +1,7 @@
#
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,13 @@
# limitations under the License.
#
-require "uri"
-require "tempfile"
-require "chef/file_content_management/content_base"
-require "chef/mixin/uris"
+require "uri" unless defined?(URI)
+require "tempfile" unless defined?(Tempfile)
+require_relative "../../file_content_management/content_base"
+require_relative "../../mixin/uris"
+module Net
+ autoload :FTPError, "net/ftp"
+end
class Chef
class Provider
@@ -32,10 +35,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 +57,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::HTTPClientException, 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..dc6de6d223 100644
--- a/lib/chef/provider/remote_file/fetcher.rb
+++ b/lib/chef/provider/remote_file/fetcher.rb
@@ -1,7 +1,7 @@
#
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,10 @@ class Chef
def self.for_resource(uri, new_resource, current_resource)
if network_share?(uri)
+ unless ChefUtils.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
@@ -45,7 +49,7 @@ class Chef
def self.network_share?(source)
case source
when String
- !!(%r{\A\\\\[A-Za-z0-9+\-\.]+} =~ source)
+ !!(/\A\\\\[A-Za-z0-9+\-\.]+/ =~ source)
else
false
end
diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb
index b382c20c31..44a6d1c6e8 100644
--- a/lib/chef/provider/remote_file/ftp.rb
+++ b/lib/chef/provider/remote_file/ftp.rb
@@ -16,11 +16,14 @@
# limitations under the License.
#
-require "uri"
-require "tempfile"
-require "net/ftp"
-require "chef/provider/remote_file"
-require "chef/file_content_management/tempfile"
+autoload :URI, "uri"
+autoload :CGI, "cgi"
+autoload :Tempfile, "tempfile"
+module Net
+ autoload :FTP, "net/ftp"
+end
+require_relative "../remote_file"
+require_relative "../../file_content_management/tempfile"
class Chef
class Provider
@@ -57,7 +60,7 @@ class Chef
def user
if uri.userinfo
- URI.unescape(uri.user)
+ CGI.unescape(uri.user)
else
"anonymous"
end
@@ -65,7 +68,7 @@ class Chef
def pass
if uri.userinfo
- URI.unescape(uri.password)
+ CGI.unescape(uri.password)
else
nil
end
diff --git a/lib/chef/provider/remote_file/http.rb b/lib/chef/provider/remote_file/http.rb
index ad044f9e3c..ef2461848d 100644
--- a/lib/chef/provider/remote_file/http.rb
+++ b/lib/chef/provider/remote_file/http.rb
@@ -17,10 +17,10 @@
# limitations under the License.
#
-require "chef/http/simple"
-require "chef/digester"
-require "chef/provider/remote_file"
-require "chef/provider/remote_file/cache_control_data"
+require_relative "../../http/simple"
+require_relative "../../digester"
+require_relative "../remote_file"
+require_relative "cache_control_data"
class Chef
class Provider
@@ -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
@@ -122,10 +130,13 @@ class Chef
# 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 uri.to_s =~ /gz$/
- Chef::Log.debug("Turning gzip compression off due to filename ending in gz")
+ if /gz$/.match?(uri.to_s)
+ logger.trace("Turning gzip compression off due to filename ending in gz")
opts[:disable_gzip] = true
end
+ if new_resource.ssl_verify_mode
+ opts[:ssl_verify_mode] = new_resource.ssl_verify_mode
+ end
opts
end
diff --git a/lib/chef/provider/remote_file/local_file.rb b/lib/chef/provider/remote_file/local_file.rb
index 613db02337..c68c4eecd5 100644
--- a/lib/chef/provider/remote_file/local_file.rb
+++ b/lib/chef/provider/remote_file/local_file.rb
@@ -16,9 +16,10 @@
# limitations under the License.
#
-require "uri"
-require "tempfile"
-require "chef/provider/remote_file"
+require "uri" unless defined?(URI)
+require "cgi" unless defined?(CGI)
+require "tempfile" unless defined?(Tempfile)
+require_relative "../remote_file"
class Chef
class Provider
@@ -35,20 +36,20 @@ class Chef
# CHEF-4472: Remove the leading slash from windows paths that we receive from a file:// URI
def fix_windows_path(path)
- path.gsub(/^\/([a-zA-Z]:)/, '\1')
+ path.gsub(%r{^/([a-zA-Z]:)}, '\1')
end
def source_path
@source_path ||= begin
- path = URI.unescape(uri.path)
- Chef::Platform.windows? ? fix_windows_path(path) : path
+ path = CGI.unescape(uri.path)
+ ChefUtils.windows? ? fix_windows_path(path) : path
end
end
# 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..b08aeb55cc 100644
--- a/lib/chef/provider/remote_file/network_file.rb
+++ b/lib/chef/provider/remote_file/network_file.rb
@@ -16,17 +16,21 @@
# limitations under the License.
#
-require "uri"
-require "tempfile"
-require "chef/provider/remote_file"
+require "uri" unless defined?(URI)
+require "tempfile" unless defined?(Tempfile)
+require_relative "../remote_file"
+require_relative "../../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 21c5c4ca04..be2a34fc54 100644
--- a/lib/chef/provider/remote_file/sftp.rb
+++ b/lib/chef/provider/remote_file/sftp.rb
@@ -16,11 +16,14 @@
# limitations under the License.
#
-require "uri"
-require "tempfile"
-require "net/sftp"
-require "chef/provider/remote_file"
-require "chef/file_content_management/tempfile"
+autoload :URI, "uri"
+autoload :CGI, "cgi"
+autoload :Tempfile, "tempfile"
+module Net
+ autoload :SFTP, "net/sftp"
+end
+require_relative "../remote_file"
+require_relative "../../file_content_management/tempfile"
class Chef
class Provider
@@ -47,7 +50,7 @@ class Chef
end
def user
- URI.unescape(uri.user)
+ CGI.unescape(uri.user)
end
def fetch
@@ -58,11 +61,11 @@ class Chef
def sftp
host = port ? "#{hostname}:#{port}" : hostname
- @sftp ||= Net::SFTP.start(host, user, :password => pass)
+ @sftp ||= Net::SFTP.start(host, user, password: pass)
end
def pass
- URI.unescape(uri.password)
+ CGI.unescape(uri.password)
end
def validate_path!
diff --git a/lib/chef/provider/resource_update.rb b/lib/chef/provider/resource_update.rb
index e069a8201c..d3c674dd22 100644
--- a/lib/chef/provider/resource_update.rb
+++ b/lib/chef/provider/resource_update.rb
@@ -31,7 +31,7 @@ class Chef
attr_accessor :type
attr_accessor :name
- attr_accessor :duration #ms
+ attr_accessor :duration # ms
attr_accessor :status
attr_accessor :initial_state
attr_accessor :final_state
diff --git a/lib/chef/provider/route.rb b/lib/chef/provider/route.rb
index 64c89aac6d..614d56aa22 100644
--- a/lib/chef/provider/route.rb
+++ b/lib/chef/provider/route.rb
@@ -1,6 +1,7 @@
#
# Author:: Bryan McLellan (btm@loftninjas.org), Jesse Nelson (spheromak@gmail.com)
# Copyright:: Copyright 2009-2016, Bryan McLellan
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,214 +17,232 @@
# limitations under the License.
#
-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
+require_relative "../log"
+require_relative "../provider"
+autoload :IPAddr, "ipaddr"
+
+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}")
+ 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 unless 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
+ action :add do
+ # 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!(*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
+ action :delete do
+ if is_running
+ command = generate_command(:delete)
+ converge_by("run #{command.join(" ")} to delete route ") do
+ shell_out!(*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
+ if platform_family?("rhel", "amazon", "fedora")
+ conf = {}
+ # FIXME FIXME FIXME FIXME: whatever this walking-the-run-context API is, it needs to be removed.
+ # walk the collection
+ rc = run_context.parent_run_context || run_context
+ rc.resource_collection.each do |resource|
+ next unless resource.is_a? Chef::Resource::Route
+
+ # default to eth0
+ dev = resource.device || "eth0"
+
+ 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..80f96205dc 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,20 +20,16 @@
class Chef
class Provider
class RubyBlock < Chef::Provider
- provides :ruby_block
-
- def whyrun_supported?
- true
- end
+ provides :ruby_block, target_mode: true
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")
+ action :run do
+ 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..8e4951bbcb 100644
--- a/lib/chef/provider/script.rb
+++ b/lib/chef/provider/script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "tempfile"
-require "chef/provider/execute"
-require "forwardable"
+require_relative "execute"
+require "forwardable" unless defined?(Forwardable)
class Chef
class Provider
@@ -33,53 +32,15 @@ class Chef
provides :ruby
provides :script
- def_delegators :@new_resource, :interpreter, :flags
-
- attr_accessor :code
-
- def initialize(new_resource, run_context)
- super
- self.code = new_resource.code
- end
+ def_delegators :new_resource, :interpreter, :flags, :code
def command
- "\"#{interpreter}\" #{flags} \"#{script_file.path}\""
- end
-
- def load_current_resource
- 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"
- end
+ "\"#{interpreter}\" #{flags}"
end
- def action_run
- script_file.puts(code)
- script_file.close
-
- set_owner_and_group
-
- super
-
- unlink_script_file
+ def input
+ code
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)
- end
-
- def script_file
- @script_file ||= Tempfile.open("chef-script")
- end
-
- def unlink_script_file
- script_file && script_file.close!
- end
-
end
end
end
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index e693bd2eed..7a2d2fc86c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,14 +17,14 @@
# limitations under the License.
#
-require "chef/mixin/command"
-require "chef/provider"
+require_relative "../provider"
+require "chef-utils" unless defined?(ChefUtils::CANARY)
class Chef
class Provider
class Service < Chef::Provider
-
- include Chef::Mixin::Command
+ include Chef::Platform::ServiceHelpers
+ extend Chef::Platform::ServiceHelpers
def supports
@supports ||= new_resource.supports.dup
@@ -35,10 +35,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 +47,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 +72,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
@@ -84,98 +80,98 @@ class Chef
end
end
- def action_enable
- if @current_resource.enabled
- Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
+ action :enable 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
+ action :disable 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")
+ action :mask 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
+ action :unmask 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
+ action :start 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
+ action :stop 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
+ action :restart 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
+ action :reload 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 +212,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
@@ -239,20 +235,20 @@ class Chef
# Linux
#
- require "chef/chef_class"
- require "chef/provider/service/systemd"
- require "chef/provider/service/insserv"
- require "chef/provider/service/redhat"
- require "chef/provider/service/arch"
- require "chef/provider/service/gentoo"
- require "chef/provider/service/upstart"
- require "chef/provider/service/debian"
- require "chef/provider/service/invokercd"
+ require_relative "../chef_class"
+ require_relative "service/systemd"
+ require_relative "service/insserv"
+ require_relative "service/redhat"
+ require_relative "service/arch"
+ require_relative "service/gentoo"
+ require_relative "service/upstart"
+ require_relative "service/debian"
+ require_relative "service/invokercd"
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: "rpm_based"
end
end
end
diff --git a/lib/chef/provider/service/aix.rb b/lib/chef/provider/service/aix.rb
index 201f9ff5f9..54355a7bc1 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 (c) 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.
#
-require "chef/provider/service"
+require_relative "../service"
class Chef
class Provider
@@ -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..e845629fe7 100644
--- a/lib/chef/provider/service/aixinit.rb
+++ b/lib/chef/provider/service/aixinit.rb
@@ -1,6 +1,6 @@
#
# Author:: kaustubh (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/provider/service/init"
+require_relative "init"
class Chef
class Provider
class Service
class AixInit < Chef::Provider::Service::Init
- RC_D_SCRIPT_NAME = /\/etc\/rc.d\/rc2.d\/([SK])(\d\d|)/i
+ RC_D_SCRIPT_NAME = %r{/etc/rc.d/rc2.d/([SK])(\d\d|)}i.freeze
def initialize(new_resource, run_context)
super
@@ -38,18 +38,18 @@ class Chef
@current_resource
end
- def action_enable
+ action :enable do
if @new_resource.priority.nil?
priority_ok = true
else
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..400da506b1 100644
--- a/lib/chef/provider/service/arch.rb
+++ b/lib/chef/provider/service/arch.rb
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "chef/provider/service/init"
+require_relative "init"
class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
provides :service, platform_family: "arch"
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:etc_rcd)
+ service_script_exist?(:etc_rcd, resource.service_name)
end
def initialize(new_resource, run_context)
@@ -32,8 +32,9 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
end
def load_current_resource
- raise Chef::Exceptions::Service, "Could not find /etc/rc.conf" unless ::File.exists?("/etc/rc.conf")
- raise Chef::Exceptions::Service, "No DAEMONS found in /etc/rc.conf" unless ::File.read("/etc/rc.conf") =~ /DAEMONS=\((.*)\)/m
+ raise Chef::Exceptions::Service, "Could not find /etc/rc.conf" unless ::File.exist?("/etc/rc.conf")
+ raise Chef::Exceptions::Service, "No DAEMONS found in /etc/rc.conf" unless /DAEMONS=\((.*)\)/m.match?(::File.read("/etc/rc.conf"))
+
super
@current_resource.enabled(daemons.include?(@current_resource.service_name))
@@ -41,7 +42,7 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
end
# Get list of all daemons from the file '/etc/rc.conf'.
- # Mutiple lines and background form are supported. Example:
+ # Multiple lines and background form are supported. Example:
# DAEMONS=(\
# foobar \
# @example \
@@ -60,13 +61,13 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
# FIXME: Multiple entries of DAEMONS will cause very bad results :)
def update_daemons(entries)
- content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(' ')})")
+ content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(" ")})")
::File.open("/etc/rc.conf", "w") do |f|
f.write(content)
end
end
- def enable_service()
+ def enable_service
new_daemons = []
entries = daemons
@@ -92,7 +93,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 9d11032055..17e0b19b9c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,27 +16,26 @@
# limitations under the License.
#
-require "chef/provider/service/init"
+require_relative "init"
class Chef
class Provider
class Service
class Debian < Chef::Provider::Service::Init
- provides :service, platform_family: "debian" do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian)
+ provides :service, platform_family: "debian" do
+ debianrcd?
end
- UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
- UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
+ UPDATE_RC_D_ENABLED_MATCHES = %r{/rc[\dS].d/S|not installed}i.freeze
+ UPDATE_RC_D_PRIORITIES = %r{/rc([\dS]).d/([SK])(\d\d)}i.freeze
+ RUNLEVELS = %w{ 1 2 3 4 5 S }.freeze
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
+ service_script_exist?(:initd, resource.service_name)
end
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
@@ -47,48 +46,64 @@ class Chef
shared_resource_requirements
requirements.assert(:all_actions) do |a|
update_rcd = "/usr/sbin/update-rc.d"
- a.assertion { ::File.exists? update_rcd }
+ a.assertion { ::File.exist? update_rcd }
a.failure_message Chef::Exceptions::Service, "#{update_rcd} does not exist!"
# no whyrun recovery - this is a base system component of debian
# distros and must be present
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.",
"If this service is not properly installed prior to this point, this will fail."] do
- temp_priorities = { "6" => [:stop, "20"],
- "0" => [:stop, "20"],
- "1" => [:stop, "20"],
- "2" => [:start, "20"],
- "3" => [:start, "20"],
- "4" => [:start, "20"],
- "5" => [:start, "20"] }
- current_resource.priority(temp_priorities)
- end
+ temp_priorities = { "6" => [:stop, "20"],
+ "0" => [:stop, "20"],
+ "1" => [:stop, "20"],
+ "2" => [:start, "20"],
+ "3" => [:start, "20"],
+ "4" => [:start, "20"],
+ "5" => [:start, "20"] }
+ current_resource.priority(temp_priorities)
+ end
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 /^### BEGIN INIT INFO/.match?(line)
+ in_info = true
+ elsif /^### END INIT INFO/.match?(line)
+ break acc
+ 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,18 +113,16 @@ 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 do |runlevel, arguments|
- Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
+ 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
+ if RUNLEVELS.include?(runlevel) && arguments[0] == :start
enabled = true
end
end
@@ -118,18 +131,18 @@ class Chef
end
# Override method from parent to ensure priority is up-to-date
- def action_enable
+ action :enable do
if new_resource.priority.nil?
priority_ok = true
else
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
@@ -137,44 +150,46 @@ class Chef
end
def enable_service
- if new_resource.priority.is_a? Integer
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults #{new_resource.priority} #{100 - new_resource.priority}")
- elsif new_resource.priority.is_a? Hash
- # we call the same command regardless of we're enabling or disabling
- # users passing a Hash are responsible for setting their own start priorities
+ # We call the same command regardless if we're enabling or disabling
+ # Users passing a Hash are responsible for setting their own stop priorities
+ if new_resource.priority.is_a? Hash
set_priority
- else # No priority, go with update-rc.d defaults
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
+ return
end
+
+ start_priority = new_resource.priority.is_a?(Integer) ? new_resource.priority : 20
+ # Stop processes in reverse order of start using '100 - start_priority'.
+ stop_priority = 100 - start_priority
+
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults #{start_priority} #{stop_priority}")
end
def disable_service
- if new_resource.priority.is_a? Integer
- # Stop processes in reverse order of start using '100 - start_priority'
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop #{100 - new_resource.priority} 2 3 4 5 .")
- elsif new_resource.priority.is_a? Hash
- # we call the same command regardless of we're enabling or disabling
- # users passing a Hash are responsible for setting their own stop priorities
+ if new_resource.priority.is_a? Hash
+ # We call the same command regardless if we're enabling or disabling
+ # Users passing a Hash are responsible for setting their own stop priorities
set_priority
- else
- # no priority, using '100 - 20 (update-rc.d default)' to stop in reverse order of start
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop 80 2 3 4 5 .")
+ return
end
+
+ shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} disable")
end
def set_priority
- args = ""
- new_resource.priority.each do |level, o|
- action = o[0]
- priority = o[1]
- args += "#{action} #{priority} #{level} . "
- end
shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove")
- shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{args}")
+
+ # Reset priorities to default values before applying customizations. This way
+ # the final state will always be consistent, regardless if all runlevels were
+ # provided.
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults")
+ new_resource.priority.each do |level, (action, _priority)|
+ disable_or_enable = (action == :start ? "enable" : "disable")
+
+ shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{disable_or_enable} #{level}")
+ end
end
end
end
diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb
index 76d8c1d17b..5f6ee0ff74 100644
--- a/lib/chef/provider/service/freebsd.rb
+++ b/lib/chef/provider/service/freebsd.rb
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "chef/resource/service"
-require "chef/provider/service/init"
-require "chef/mixin/command"
+require_relative "../../resource/service"
+require_relative "init"
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
@@ -68,13 +67,13 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { enabled_state_found }
- # for consistentcy with original behavior, this will not fail in non-whyrun mode;
+ # for consistency with original behavior, this will not fail in non-whyrun mode;
# rather it will silently set enabled state=>false
a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled."
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
@@ -84,7 +83,7 @@ class Chef
if new_resource.start_command
super
else
- shell_out_with_systems_locale!("#{init_command} faststart")
+ shell_out!("#{init_command} faststart", default_env: false)
end
end
@@ -92,7 +91,7 @@ class Chef
if new_resource.stop_command
super
else
- shell_out_with_systems_locale!("#{init_command} faststop")
+ shell_out!("#{init_command} faststop", default_env: false)
end
end
@@ -100,7 +99,7 @@ class Chef
if new_resource.restart_command
super
elsif supports[:restart]
- shell_out_with_systems_locale!("#{init_command} fastrestart")
+ shell_out!("#{init_command} fastrestart", default_env: false)
else
stop_service
sleep 1
@@ -119,7 +118,7 @@ class Chef
private
def read_rc_conf
- ::File.open("/etc/rc.conf", "r") { |file| file.readlines }
+ ::File.open("/etc/rc.conf", "r", &:readlines)
end
def write_rc_conf(lines)
@@ -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
@@ -162,9 +161,9 @@ class Chef
case line
when /^#{Regexp.escape(var_name)}="(\w+)"/
enabled_state_found!
- if $1 =~ /^yes$/i
+ if $1.casecmp?("yes")
current_resource.enabled true
- elsif $1 =~ /^(no|none)$/i
+ elsif $1.casecmp?("no") || $1.casecmp?("none")
current_resource.enabled false
end
end
@@ -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..4678435129 100644
--- a/lib/chef/provider/service/gentoo.rb
+++ b/lib/chef/provider/service/gentoo.rb
@@ -1,7 +1,7 @@
#
# Author:: Lee Jensen (<ljensen@engineyard.com>)
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,8 @@
# limitations under the License.
#
-require "chef/provider/service/init"
-require "chef/mixin/command"
-require "chef/util/path_helper"
+require_relative "init"
+require_relative "../../util/path_helper"
class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
@@ -35,20 +34,20 @@ class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
@current_resource.enabled(
Dir.glob("/etc/runlevels/**/#{Chef::Util::PathHelper.escape_glob_dir(@current_resource.service_name)}").any? do |file|
@found_script = true
- exists = ::File.exists? file
+ exists = ::File.exist? 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
def define_resource_requirements
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?("/sbin/rc-update") }
+ a.assertion { ::File.exist?("/sbin/rc-update") }
a.failure_message Chef::Exceptions::Service, "/sbin/rc-update does not exist"
# no whyrun recovery -t his is a core component whose presence is
# unlikely to be affected by what we do in the course of a chef run
@@ -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..a4225fa89d 100644
--- a/lib/chef/provider/service/init.rb
+++ b/lib/chef/provider/service/init.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "chef/provider/service/simple"
-require "chef/mixin/command"
-require "chef/platform/service_helpers"
+require_relative "simple"
+require_relative "../../platform/service_helpers"
class Chef
class Provider
@@ -30,7 +29,7 @@ class Chef
provides :service, os: "!windows"
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
+ service_script_exist?(:initd, resource.service_name)
end
def initialize(new_resource, run_context)
@@ -57,7 +56,7 @@ class Chef
if @new_resource.start_command
super
else
- shell_out_with_systems_locale!("#{default_init_command} start")
+ shell_out!("#{default_init_command} start", default_env: false)
end
end
@@ -65,7 +64,7 @@ class Chef
if @new_resource.stop_command
super
else
- shell_out_with_systems_locale!("#{default_init_command} stop")
+ shell_out!("#{default_init_command} stop", default_env: false)
end
end
@@ -73,7 +72,7 @@ class Chef
if @new_resource.restart_command
super
elsif supports[:restart]
- shell_out_with_systems_locale!("#{default_init_command} restart")
+ shell_out!("#{default_init_command} restart", default_env: false)
else
stop_service
sleep 1
@@ -85,7 +84,7 @@ class Chef
if @new_resource.reload_command
super
elsif supports[:reload]
- shell_out_with_systems_locale!("#{default_init_command} reload")
+ shell_out!("#{default_init_command} reload", default_env: false)
end
end
end
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
index 76b2ee7477..212ee8827b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,27 +16,29 @@
# limitations under the License.
#
-require "chef/provider/service/init"
-require "chef/util/path_helper"
+require_relative "init"
+require_relative "../../util/path_helper"
class Chef
class Provider
class Service
class Insserv < Chef::Provider::Service::Init
- provides :service, platform_family: %w{debian rhel fedora suse} do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv)
+ provides :service, platform_family: %w{debian rhel fedora suse amazon} do
+ insserv?
end
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
+ service_script_exist?(:initd, resource.service_name)
end
def load_current_resource
super
# Look for a /etc/rc.*/SnnSERVICE link to signify that the service would be started in a runlevel
- if Dir.glob("/etc/rc**/S*#{Chef::Util::PathHelper.escape_glob_dir(current_resource.service_name)}").empty?
+ service_name = Chef::Util::PathHelper.escape_glob_dir(current_resource.service_name)
+
+ if Dir.glob("/etc/rc*/**/S*#{service_name}").empty?
current_resource.enabled false
else
current_resource.enabled true
@@ -45,12 +47,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/invokercd.rb b/lib/chef/provider/service/invokercd.rb
index 9477afec48..17bedd0634 100644
--- a/lib/chef/provider/service/invokercd.rb
+++ b/lib/chef/provider/service/invokercd.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,19 @@
# limitations under the License.
#
-require "chef/provider/service/init"
+require_relative "init"
class Chef
class Provider
class Service
class Invokercd < Chef::Provider::Service::Init
- provides :service, platform_family: "debian", override: true do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd)
+ provides :service, platform_family: "debian", override: true do
+ invokercd?
end
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
+ service_script_exist?(:initd, resource.service_name)
end
def initialize(new_resource, run_context)
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
index 648cd9748b..2152789a6e 100644
--- a/lib/chef/provider/service/macosx.rb
+++ b/lib/chef/provider/service/macosx.rb
@@ -1,6 +1,7 @@
#
# Author:: Igor Afonov <afonov@gmail.com>
# Copyright:: Copyright 2011-2016, Igor Afonov
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +17,19 @@
# limitations under the License.
#
-require "etc"
-require "rexml/document"
-require "chef/resource/service"
-require "chef/resource/macosx_service"
-require "chef/provider/service/simple"
-require "chef/util/path_helper"
+require "etc" unless defined?(Etc)
+autoload :REXML, "rexml/document"
+require_relative "../../resource/service"
+require_relative "../../resource/macosx_service"
+require_relative "simple"
+require_relative "../../util/path_helper"
class Chef
class Provider
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
@@ -42,31 +43,28 @@ class Chef
PLIST_DIRS = gather_plist_dirs
- def this_version_or_newer?(this_version)
- Gem::Version.new(node["platform_version"]) >= Gem::Version.new(this_version)
- end
-
def load_current_resource
@current_resource = Chef::Resource::MacosxService.new(@new_resource.name)
@current_resource.service_name(@new_resource.service_name)
@plist_size = 0
- @plist = @new_resource.plist ? @new_resource.plist : find_service_plist
+ @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}'")
- cmd = "su "
- param = this_version_or_newer?("10.10") ? "" : "-l "
- @base_user_cmd = cmd + param + "#{@console_user} -c"
- # Default LauchAgent session should be Aqua
+ @console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
+ logger.trace("#{new_resource} console_user: '#{@console_user}'")
+
+ @base_user_cmd = "su -l #{@console_user} -c"
+ logger.trace("#{new_resource} base_user_cmd: '#{@base_user_cmd}'")
+
+ # 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
@@ -83,7 +81,7 @@ class Chef
end
requirements.assert(:all_actions) do |a|
- a.assertion { ::File.exists?(@plist.to_s) }
+ a.assertion { ::File.exist?(@plist.to_s) }
a.failure_message Chef::Exceptions::Service,
"Could not find plist for #{@new_resource}"
end
@@ -107,7 +105,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 +117,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
@@ -139,14 +137,23 @@ class Chef
end
end
- # On OS/X, enabling a service has the side-effect of starting it,
+ # On macOS, enabling a service has the side-effect of starting it,
# and disabling a service has the side-effect of stopping it.
#
- # This makes some sense on OS/X since launchctl is an "init"-style
+ # This makes some sense on macOS since launchctl is an "init"-style
# supervisor that will restart daemons that are crashing, etc.
+ #
+ # FIXME: Does this make any sense at all? The difference between enabled and
+ # running as state would seem to only be useful for completely broken
+ # services (enabled, not restarting, but not running => totally broken?).
+ #
+ # It seems like otherwise :enable is equivalent to :start, and :disable is
+ # equivalent to :stop? But just with strangely different behavior in the
+ # face of a broken service?
+ #
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 +161,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
@@ -173,15 +180,15 @@ class Chef
def shell_out_as_user(cmd)
if @console_user
- shell_out_with_systems_locale("#{@base_user_cmd} '#{cmd}'")
+ shell_out("#{@base_user_cmd} '#{cmd}'", default_env: false)
else
- shell_out_with_systems_locale(cmd)
+ shell_out(cmd, default_env: false)
end
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 +204,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
@@ -214,7 +221,7 @@ class Chef
return nil if @plist.nil?
# Plist must exist by this point
- raise Chef::Exceptions::FileNotFound, "Cannot find #{@plist}!" unless ::File.exists?(@plist)
+ raise Chef::Exceptions::FileNotFound, "Cannot find #{@plist}!" unless ::File.exist?(@plist)
# Most services have the same internal label as the name of the
# plist file. However, there is no rule saying that *has* to be
@@ -223,8 +230,9 @@ class Chef
# plist files can come in XML or Binary formats. this command
# will make sure we get XML every time.
- plist_xml = shell_out_with_systems_locale!(
- "plutil -convert xml1 -o - #{@plist}"
+ plist_xml = shell_out!(
+ "plutil -convert xml1 -o - #{@plist}",
+ default_env: false
).stdout
plist_doc = REXML::Document.new(plist_xml)
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
index c60bbf170c..2b484f9fc8 100644
--- a/lib/chef/provider/service/openbsd.rb
+++ b/lib/chef/provider/service/openbsd.rb
@@ -16,10 +16,8 @@
# limitations under the License.
#
-require "chef/mixin/command"
-require "chef/mixin/shell_out"
-require "chef/provider/service/init"
-require "chef/resource/service"
+require_relative "init"
+require_relative "../../resource/service"
class Chef
class Provider
@@ -28,12 +26,10 @@ class Chef
provides :service, os: "openbsd"
- include Chef::Mixin::ShellOut
-
attr_reader :init_command, :rc_conf, :rc_conf_local, :enabled_state_found
- RC_CONF_PATH = "/etc/rc.conf"
- RC_CONF_LOCAL_PATH = "/etc/rc.conf.local"
+ RC_CONF_PATH = "/etc/rc.conf".freeze
+ RC_CONF_LOCAL_PATH = "/etc/rc.conf.local".freeze
def initialize(new_resource, run_context)
super
@@ -49,7 +45,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,14 +68,14 @@ 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
end
def enable_service
- if !is_enabled?
+ unless is_enabled?
if is_builtin?
if is_enabled_by_default?
update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, "")
@@ -92,10 +88,10 @@ class Chef
old_services_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
old_services_list = old_services_list ? old_services_list[1].split(" ") : []
new_services_list = old_services_list + [new_resource.service_name]
- if rc_conf_local =~ /^pkg_scripts="(.*)"/
- new_rcl = rc_conf_local.sub(/^pkg_scripts="(.*)"/, "pkg_scripts=\"#{new_services_list.join(' ')}\"")
+ if /^pkg_scripts="(.*)"/.match?(rc_conf_local)
+ new_rcl = rc_conf_local.sub(/^pkg_scripts="(.*)"/, "pkg_scripts=\"#{new_services_list.join(" ")}\"")
else
- new_rcl = rc_conf_local + "\n" + "pkg_scripts=\"#{new_services_list.join(' ')}\"\n"
+ new_rcl = rc_conf_local + "\n" + "pkg_scripts=\"#{new_services_list.join(" ")}\"\n"
end
update_rcl new_rcl
end
@@ -117,7 +113,7 @@ class Chef
old_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
old_list = old_list ? old_list[1].split(" ") : []
new_list = old_list - [new_resource.service_name]
- update_rcl rc_conf_local.sub(/^pkg_scripts="(.*)"/, pkg_scripts = "#{new_list.join(' ')}")
+ update_rcl rc_conf_local.sub(/^pkg_scripts="(.*)"/, pkg_scripts = (new_list.join(" ")).to_s)
end
end
end
@@ -133,7 +129,7 @@ class Chef
end
def update_rcl(value)
- FileUtils.touch RC_CONF_LOCAL_PATH if !::File.exists? RC_CONF_LOCAL_PATH
+ FileUtils.touch RC_CONF_LOCAL_PATH unless ::File.exist? RC_CONF_LOCAL_PATH
::File.write(RC_CONF_LOCAL_PATH, value)
@rc_conf_local = value
end
@@ -159,7 +155,7 @@ class Chef
result = false
var_name = builtin_service_enable_variable_name
if var_name
- if rc_conf =~ /^#{Regexp.escape(var_name)}=(.*)/
+ if /^#{Regexp.escape(var_name)}=(.*)/.match?(rc_conf)
result = true
end
end
@@ -171,7 +167,7 @@ class Chef
var_name = builtin_service_enable_variable_name
if var_name
if m = rc_conf.match(/^#{Regexp.escape(var_name)}=(.*)/)
- if !(m[1] =~ /"?[Nn][Oo]"?/)
+ unless /"?[Nn][Oo]"?/.match?(m[1])
result = true
end
end
@@ -187,12 +183,12 @@ class Chef
if var_name
if m = rc_conf_local.match(/^#{Regexp.escape(var_name)}=(.*)/)
@enabled_state_found = true
- if !(m[1] =~ /"?[Nn][Oo]"?/) # e.g. looking for httpd_flags=NO
+ unless /"?[Nn][Oo]"?/.match?(m[1]) # e.g. looking for httpd_flags=NO
result = true
end
end
end
- if !@enabled_state_found
+ unless @enabled_state_found
result = is_enabled_by_default?
end
else
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index 200a2d3400..14b55bef85 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 (c) 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.
#
-require "chef/provider/service/init"
+require_relative "init"
class Chef
class Provider
@@ -28,15 +28,15 @@ class Chef
# @api private
attr_accessor :current_run_levels
- provides :service, platform_family: %w{rhel fedora suse} do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat)
+ provides :service, platform_family: "rpm_based" do
+ redhatrcd?
end
- CHKCONFIG_ON = /\d:on/
- CHKCONFIG_MISSING = /No such/
+ CHKCONFIG_ON = /\d:on/.freeze
+ CHKCONFIG_MISSING = /No such/.freeze
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
+ service_script_exist?(:initd, resource.service_name)
end
def initialize(new_resource, run_context)
@@ -56,7 +56,7 @@ class Chef
requirements.assert(:all_actions) do |a|
chkconfig_file = "/sbin/chkconfig"
- a.assertion { ::File.exists? chkconfig_file }
+ a.assertion { ::File.exist? chkconfig_file }
a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} does not exist!"
end
@@ -80,14 +80,14 @@ class Chef
super
- if ::File.exists?("/sbin/chkconfig")
- chkconfig = shell_out!("/sbin/chkconfig --list #{current_resource.service_name}", :returns => [0, 1])
+ if ::File.exist?("/sbin/chkconfig")
+ chkconfig = shell_out!("/sbin/chkconfig --list #{current_resource.service_name}", returns: [0, 1])
unless run_levels.nil? || run_levels.empty?
all_levels_match = true
- chkconfig.stdout.split(/\s+/)[1..-1].each do |level|
+ chkconfig.stdout.split(/\s+/)[1..].each do |level|
index = level.split(":").first
status = level.split(":").last
- if level =~ CHKCONFIG_ON
+ if CHKCONFIG_ON.match?(level)
@current_run_levels << index.to_i
all_levels_match = false unless run_levels.include?(index.to_i)
else
@@ -106,18 +106,18 @@ class Chef
# @api private
def levels
- (run_levels.nil? || run_levels.empty?) ? "" : "--level #{run_levels.join('')} "
+ (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?
+ shell_out! "/sbin/chkconfig --level #{disable_levels.join("")} #{new_resource.service_name} off" unless disable_levels.empty?
end
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 d75e85989f..0d1a9ae786 100644
--- a/lib/chef/provider/service/simple.rb
+++ b/lib/chef/provider/service/simple.rb
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "chef/provider/service"
-require "chef/resource/service"
-require "chef/mixin/command"
+require_relative "../service"
+require_relative "../../resource/service"
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|
@@ -78,7 +73,8 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion do
@new_resource.status_command || supports[:status] ||
- (!ps_cmd.nil? && !ps_cmd.empty?) end
+ (!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|
@@ -88,16 +84,16 @@ class Chef
end
def start_service
- shell_out_with_systems_locale!(@new_resource.start_command)
+ shell_out!(@new_resource.start_command, default_env: false)
end
def stop_service
- shell_out_with_systems_locale!(@new_resource.stop_command)
+ shell_out!(@new_resource.stop_command, default_env: false)
end
def restart_service
if @new_resource.restart_command
- shell_out_with_systems_locale!(@new_resource.restart_command)
+ shell_out!(@new_resource.restart_command, default_env: false)
else
stop_service
sleep 1
@@ -106,35 +102,35 @@ class Chef
end
def reload_service
- shell_out_with_systems_locale!(@new_resource.reload_command)
+ shell_out!(@new_resource.reload_command, default_env: false)
end
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.
- # Temporarily catching different types of exceptions here until we get Shellout fixed.
- # TODO: Remove the line before one we get the ShellOut fix.
+ # 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.
@status_load_success = false
@current_resource.running false
nil
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.
@@ -145,9 +141,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)
@@ -157,7 +153,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.
@@ -168,6 +164,7 @@ class Chef
end
def ps_cmd
+ # XXX: magic attributes are a shitty api, need something better here and deprecate this attribute
@run_context.node[:command] && @run_context.node[:command][:ps]
end
end
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index 868b3e1ac1..fa4a17bd0c 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -1,6 +1,6 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,8 @@
# limitations under the License.
#
-require "chef/provider/service"
-require "chef/resource/service"
-require "chef/mixin/command"
+require_relative "../service"
+require_relative "../../resource/service"
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
@@ -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,11 +76,11 @@ class Chef
def restart_service
## svcadm restart doesn't supports sync(-s) option
disable_service
- return enable_service
+ enable_service
end
def service_status
- cmd = shell_out!(@status_command, "-l", @current_resource.service_name, :returns => [0, 1])
+ cmd = shell_out!(@status_command, "-l", @current_resource.service_name, returns: [0, 1])
# Example output
# $ svcs -l rsyslog
# fmri svc:/application/rsyslog:default
@@ -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 712f0f60c3..9b0cf3abd8 100644
--- a/lib/chef/provider/service/systemd.rb
+++ b/lib/chef/provider/service/systemd.rb
@@ -1,7 +1,7 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
# Author:: Davide Cavalca (<dcavalca@fb.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,22 +17,23 @@
# limitations under the License.
#
-require "chef/resource/service"
-require "chef/provider/service/simple"
-require "chef/mixin/which"
+require_relative "../../resource/service"
+require_relative "simple"
+require_relative "../../mixin/which"
+require "shellwords" unless defined?(Shellwords)
class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
include Chef::Mixin::Which
- provides :service, os: "linux" do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd)
+ provides :service, os: "linux", target_mode: true do |node|
+ systemd?
end
attr_accessor :status_check_success
def self.supports?(resource, action)
- Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:systemd)
+ service_script_exist?(:systemd, resource.service_name)
end
def load_current_resource
@@ -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)
@@ -50,6 +51,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
current_resource.running(false)
current_resource.enabled(false)
current_resource.masked(false)
+ current_resource.indirect(false)
end
else
current_resource.running(is_active?)
@@ -57,12 +59,12 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
current_resource.enabled(is_enabled?)
current_resource.masked(is_masked?)
+ current_resource.indirect(is_indirect?)
current_resource
end
# systemd supports user services just fine
- def user_services_requirements
- end
+ def user_services_requirements; end
def define_resource_requirements
shared_resource_requirements
@@ -74,14 +76,40 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
end
end
+ def systemd_service_status
+ @systemd_service_status ||= begin
+ # Collect all the status information for a service and returns it at once
+ options, args = get_systemctl_options_args
+ s = shell_out!(systemctl_path, args, "show", "-p", "UnitFileState", "-p", "ActiveState", new_resource.service_name, options)
+ # e.g. /bin/systemctl --system show -p UnitFileState -p ActiveState sshd.service
+ # Returns something like:
+ # ActiveState=active
+ # UnitFileState=enabled
+ status = {}
+ s.stdout.each_line do |line|
+ k, v = line.strip.split("=")
+ status[k] = v
+ end
+
+ # Assert requisite keys exist
+ unless status.key?("UnitFileState") && status.key?("ActiveState")
+ raise Chef::Exceptions::Service, "'#{systemctl_path} show' not reporting status for #{new_resource.service_name}!"
+ end
+
+ status
+ end
+ end
+
def get_systemctl_options_args
if new_resource.user
- uid = node["etc"]["passwd"][new_resource.user]["uid"]
+ raise NotImplementedError, "#{new_resource} does not support the user property on a target_mode host (yet)" if Chef::Config.target_mode?
+
+ uid = Etc.getpwnam(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 +117,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!(systemctl_path, args, "start", new_resource.service_name, default_env: false, **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!(systemctl_path, args, "stop", new_resource.service_name, default_env: false, **options)
end
end
end
@@ -123,7 +151,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!(systemctl_path, args, "restart", new_resource.service_name, default_env: false, **options)
end
end
@@ -133,7 +161,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!(systemctl_path, args, "reload", new_resource.service_name, default_env: false, **options)
else
start_service
end
@@ -141,39 +169,53 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
end
def enable_service
+ if current_resource.masked || current_resource.indirect
+ logger.trace("#{new_resource} cannot be enabled: it is masked or indirect")
+ return
+ end
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} enable #{new_resource.service_name}", options)
+ shell_out!(systemctl_path, args, "enable", new_resource.service_name, **options)
end
def disable_service
+ if current_resource.masked || current_resource.indirect
+ logger.trace("#{new_resource} cannot be disabled: it is masked or indirect")
+ return
+ end
options, args = get_systemctl_options_args
- shell_out!("#{systemctl_path} #{args} disable #{new_resource.service_name}", options)
+ shell_out!(systemctl_path, args, "disable", 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", 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", 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
+ # Note: "activating" is not active (as with type=notify or a oneshot)
+ systemd_service_status["ActiveState"] == "active"
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
+ # See https://github.com/systemd/systemd/blob/master/src/systemctl/systemctl-is-enabled.c
+ # Note: enabled-runtime is excluded because this is volatile, and the state of enabled-runtime
+ # specifically means that the service is not enabled
+ %w{enabled static generated alias indirect}.include?(systemd_service_status["UnitFileState"])
+ end
+
+ def is_indirect?
+ systemd_service_status["UnitFileState"] == "indirect"
end
def is_masked?
- options, args = get_systemctl_options_args
- s = shell_out("#{systemctl_path} #{args} is-enabled #{new_resource.service_name}", options)
- s.exitstatus != 0 && s.stdout.include?("masked")
+ # Note: masked-runtime is excluded, because runtime is volatile, and
+ # because masked-runtime is not masked.
+ systemd_service_status["UnitFileState"] == "masked"
end
private
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index 6e2a3b6473..2b9e304160 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -16,29 +16,32 @@
# limitations under the License.
#
-require "chef/resource/service"
-require "chef/provider/service/simple"
-require "chef/mixin/command"
-require "chef/util/file_edit"
+require_relative "../../resource/service"
+require_relative "simple"
+require_relative "../../util/file_edit"
class Chef
class Provider
class Service
class Upstart < Chef::Provider::Service::Simple
- provides :service, platform_family: "debian", override: true do |node|
- Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart)
+ # 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
+ upstart?
end
- UPSTART_STATE_FORMAT = /\S+ \(?(start|stop)?\)? ?[\/ ](\w+)/
+ UPSTART_STATE_FORMAT = %r{\S+ \(?(start|stop)?\)? ?[/ ](\w+)}.freeze
+ # 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)
+ service_script_exist?(:upstart, resource.service_name)
end
# Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in.
# In chef, when we ask a service to start, we expect it to have started before performing the next step
- # since we have top down dependencies. Which is to say we may follow witha resource next that requires
+ # since we have top down dependencies. Which is to say we may follow with a resource next that requires
# that service to be running. According to [2] we can trust that sending a 'goal' such as start will not
# return until that 'goal' is reached, or some error has occurred.
#
@@ -48,6 +51,7 @@ class Chef
def initialize(new_resource, run_context)
# TODO: re-evaluate if this is needed after integrating cookbook fix
raise ArgumentError, "run_context cannot be nil" unless run_context
+
super
run_context.node
@@ -79,7 +83,7 @@ class Chef
# Do not call super, only call shared requirements
shared_resource_requirements
requirements.assert(:all_actions) do |a|
- if !@command_success
+ unless @command_success
whyrun_msg = if @new_resource.status_command
"Provided status command #{@new_resource.status_command} failed."
else
@@ -106,42 +110,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_goal_state == "start"
- @current_resource.running true
+ @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}")
+ if ::File.exist?("#{@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
@@ -149,39 +153,44 @@ 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
else
- shell_out_with_systems_locale!("/sbin/start #{@job}")
+ shell_out!("/sbin/start #{@job}", default_env: false)
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
else
- shell_out_with_systems_locale!("/sbin/stop #{@job}")
+ shell_out!("/sbin/stop #{@job}", default_env: false)
end
end
+
+ @upstart_service_running = false
end
def restart_service
@@ -189,13 +198,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
@@ -203,21 +218,23 @@ class Chef
super
else
# upstart >= 0.6.3-4 supports reload (HUP)
- shell_out_with_systems_locale!("/sbin/reload #{@job}")
+ shell_out!("/sbin/reload #{@job}", default_env: false)
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
@@ -225,17 +242,16 @@ class Chef
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[1]
- 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..98aad4fe29 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,97 +18,94 @@
# limitations under the License.
#
-require "chef/provider/service/simple"
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/win32/error"
+require_relative "simple"
+require_relative "../../win32_service_constants"
+if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+ require_relative "../../win32/error"
require "win32/service"
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"
- MANUAL = "demand start"
- DISABLED = "disabled"
+ # Win32::Service.get_start_type
+ AUTO_START = "auto start".freeze
+ MANUAL = "demand start".freeze
+ DISABLED = "disabled".freeze
- #Win32::Service.get_current_state
- RUNNING = "running"
- STOPPED = "stopped"
- CONTINUE_PENDING = "continue pending"
- PAUSE_PENDING = "pause pending"
- PAUSED = "paused"
- START_PENDING = "start pending"
- STOP_PENDING = "stop pending"
+ # Win32::Service.get_current_state
+ RUNNING = "running".freeze
+ STOPPED = "stopped".freeze
+ CONTINUE_PENDING = "continue pending".freeze
+ PAUSE_PENDING = "pause pending".freeze
+ PAUSED = "paused".freeze
+ START_PENDING = "start pending".freeze
+ STOP_PENDING = "stop pending".freeze
- TIMEOUT = 60
-
- SERVICE_RIGHT = "SeServiceLogonRight"
-
- def whyrun_supported?
- false
- end
+ SERVICE_RIGHT = "SeServiceLogonRight".freeze
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
if Win32::Service.exists?(@new_resource.service_name)
- # reconfiguration is idempotent, so just do it.
- new_config = {
- service_name: @new_resource.service_name,
- service_start_name: @new_resource.run_as_user,
- password: @new_resource.run_as_password,
- }.reject { |k, v| v.nil? || v.length == 0 }
-
- Win32::Service.configure(new_config)
- Chef::Log.info "#{@new_resource} configured with #{new_config.inspect}"
-
- if new_config.has_key?(:service_start_name)
- 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
- end
+ configure_service_run_as_properties
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
- begin
- Win32::Service.start(@new_resource.service_name)
- rescue SystemCallError => ex
- if ex.errno == ERROR_SERVICE_LOGON_FAILED
- Chef::Log.error ex.message
- raise Chef::Exceptions::Service,
+
+ Win32::Service.start(@new_resource.service_name)
+ rescue SystemCallError => ex
+ if ex.errno == ERROR_SERVICE_LOGON_FAILED
+ 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
- raise ex
- end
+ else
+ raise ex
end
+
end
wait_for_state(RUNNING)
end
@@ -117,7 +114,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 +123,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 +133,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 +156,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 +164,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,85 +172,140 @@ 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
- def action_enable
- if current_start_type != AUTO_START
+ 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
+
+ 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
+
+ action :enable do
+ 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
+ action :disable do
+ 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")
+ action :configure_startup 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
+ converge_delayed_start
+
# Avoid changing enabled from true/false for now
@new_resource.enabled(nil)
end
private
+ def configure_service_run_as_properties
+ return unless new_resource.property_is_set?(:run_as_user)
+
+ new_config = {
+ service_name: new_resource.service_name,
+ service_start_name: new_resource.run_as_user,
+ password: new_resource.run_as_password,
+ }.reject { |k, v| v.nil? || v.length == 0 }
+
+ Win32::Service.configure(new_config)
+ logger.info "#{new_resource} configured."
+
+ grant_service_logon(new_resource.run_as_user) if new_resource.run_as_user != "localsystem"
+ end
+
+ #
+ # Queries the delayed auto-start setting of the auto-start service. If
+ # the service is not auto-start, this will return nil.
+ #
+ # @return [Boolean, nil]
+ #
+ def current_delayed_start
+ case Win32::Service.delayed_start(new_resource.service_name)
+ when 0
+ false
+ when 1
+ true
+ end
+ end
+
def grant_service_logon(username)
+ return if Chef::ReservedNames::Win32::Security.get_account_right(canonicalize_username(username)).include?(SERVICE_RIGHT)
+
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
# remove characters that make for broken or wonky filenames.
def clean_username_for_path(username)
- username.gsub(/[\/\\. ]+/, "_")
+ username.gsub(%r{[/\\. ]+}, "_")
end
def canonicalize_username(username)
@@ -264,8 +316,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
@@ -274,40 +327,154 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
retries = 0
loop do
break if current_state == desired_state
- raise Timeout::Error if ( retries += 1 ) > resource_timeout
+ raise Timeout::Error if ( retries += 1 ) > @new_resource.timeout
+
sleep 1
end
end
- def resource_timeout
- @resource_timeout ||= @new_resource.timeout || TIMEOUT
- end
-
def spawn_command_thread
worker = Thread.new do
yield
end
- Timeout.timeout(resource_timeout) do
+ Timeout.timeout(@new_resource.timeout) do
worker.join
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]
+ service_name: @new_resource.service_name,
+ 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
+ converge_if_changed :delayed_start do
+ config = {}
+ config[:service_name] = new_resource.service_name
+ config[:delayed_start] = new_resource.delayed_start ? 1 : 0
+
+ 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..18fc9d3a3c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,12 @@
# limitations under the License.
#
-#TODO subversion and git should both extend from a base SCM provider.
+# TODO subversion and git should both extend from a base SCM provider.
-require "chef/log"
-require "chef/provider"
-require "chef/mixin/command"
+require_relative "../log"
+require_relative "../provider"
require "chef-config/mixin/fuzzy_hostname_matcher"
-require "fileutils"
+require "fileutils" unless defined?(FileUtils)
class Chef
class Provider
@@ -30,21 +29,16 @@ class Chef
provides :subversion
- SVN_INFO_PATTERN = /^([\w\s]+): (.+)$/
+ SVN_INFO_PATTERN = /^([\w\s]+): (.+)$/.freeze
- 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 %i{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,47 +47,47 @@ 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
+ action :checkout do
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
- def action_export
+ action :export do
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
+ action :force_export do
+ converge_by("export #{new_resource.repository} into #{new_resource.destination}") do
shell_out!(export_command, run_options)
end
end
- def action_sync
+ action :sync do
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,11 +122,11 @@ 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 /^\d+$/.match?(new_resource.revision)
+ new_resource.revision
else
- 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
+ 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)
end
@@ -142,28 +136,35 @@ 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
+ svn_info = shell_out!(command, run_options(cwd: cwd, returns: [0, 1])).stdout
extract_revision_info(svn_info)
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
+ env = {}
+ if new_resource.user
+ run_opts[:user] = new_resource.user
+ env["HOME"] = get_homedir(new_resource.user)
+ end
+ run_opts[:group] = new_resource.group if new_resource.group
+ 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
private
def cwd
- @new_resource.destination
+ new_resource.destination
end
def verbose
@@ -181,7 +182,8 @@ 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 +192,15 @@ 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?
@@ -208,26 +211,40 @@ class Chef
def scm(*args)
binary = svn_binary
- binary = "\"#{binary}\"" if binary =~ /\s/
+ binary = "\"#{binary}\"" if /\s/.match?(binary)
[binary, *args].compact.join(" ")
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 ||
- (Chef::Platform.windows? ? "svn.exe" : "svn")
+ new_resource.svn_binary ||
+ (ChefUtils.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
+
+ # Returns the home directory of the user
+ # @param [String] user must be a string.
+ # @return [String] the home directory of the user.
+ #
+ def get_homedir(user)
+ require "etc" unless defined?(Etc)
+ case user
+ when Integer
+ Etc.getpwuid(user).dir
+ else
+ Etc.getpwnam(user.to_s).dir
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/support/yum_repo.erb b/lib/chef/provider/support/yum_repo.erb
index 7d9a2d09e2..7a55c1b7d2 100644
--- a/lib/chef/provider/support/yum_repo.erb
+++ b/lib/chef/provider/support/yum_repo.erb
@@ -4,8 +4,13 @@
[<%= @config.repositoryid %>]
name=<%= @config.description %>
<% if @config.baseurl %>
-baseurl=<%= @config.baseurl %>
-<% end %>
+baseurl=<%= case @config.baseurl
+ when Array
+ @config.baseurl.join("\n ")
+ else
+ @config.baseurl
+ end %>
+<% end -%>
<% if @config.cost %>
cost=<%= @config.cost %>
<% end %>
@@ -24,7 +29,9 @@ exclude=<%= @config.exclude %>
failovermethod=<%= @config.failovermethod %>
<% end %>
<% if @config.fastestmirror_enabled %>
-fastestmirror_enabled=<%= @config.fastestmirror_enabled %>
+fastestmirror_enabled=1
+<% else %>
+fastestmirror_enabled=0
<% end %>
<% if @config.gpgcheck %>
gpgcheck=1
@@ -54,6 +61,9 @@ keepalive=1
<% if @config.metadata_expire %>
metadata_expire=<%= @config.metadata_expire %>
<% end %>
+<% if @config.metalink %>
+metalink=<%= @config.metalink %>
+<% end %>
<% if @config.mirrorlist %>
mirrorlist=<%= @config.mirrorlist %>
<% end %>
@@ -105,6 +115,9 @@ sslclientkey=<%= @config.sslclientkey %>
<% unless @config.sslverify.nil? %>
sslverify=<%= ( @config.sslverify ) ? 'true' : 'false' %>
<% end %>
+<% if @config.throttle %>
+throttle=<%= @config.throttle %>
+<% end %>
<% if @config.timeout %>
timeout=<%= @config.timeout %>
<% 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 4aa4cd0aa2..97043e48c7 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");
@@ -16,29 +16,31 @@
# limitations under the License.
#
-require "chef/provider"
-require "chef/mixin/which"
-require "chef/mixin/shell_out"
-require "chef/resource/file"
+require_relative "../provider"
+require_relative "../mixin/which"
+require_relative "../resource/file"
+require_relative "../resource/file/verification/systemd_unit"
require "iniparse"
+require "shellwords" unless defined?(Shellwords)
class Chef
class Provider
class SystemdUnit < Chef::Provider
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?)
current_resource.active(active?)
current_resource.masked(masked?)
current_resource.static(static?)
+ current_resource.indirect(indirect?)
current_resource.triggers_reload(new_resource.triggers_reload)
current_resource
@@ -53,164 +55,207 @@ class Chef
end
end
- def action_create
+ action :create do
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
end
end
- def action_delete
+ action :delete do
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_enable
+ action :preset do
+ converge_by("restoring enable/disable preset configuration for unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:preset, new_resource.unit_name)
+ end
+ end
+
+ action :revert do
+ converge_by("reverting to vendor version of unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:revert, new_resource.unit_name)
+ end
+ end
+
+ action :enable do
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
+ if current_resource.indirect
+ logger.trace("#{new_resource.unit_name} is an indirect 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)
+ unless current_resource.enabled || current_resource.static || current_resource.indirect
+ converge_by("enabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:enable, new_resource.unit_name)
+ logger.info("#{new_resource} enabled")
end
end
end
- def action_disable
+ action :disable do
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)
+ if current_resource.indirect
+ logger.trace("#{new_resource.unit_name} is an indirect unit, enabling is a NOP.")
+ end
+
+ if current_resource.enabled && !current_resource.static && !current_resource.indirect
+ converge_by("disabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:disable, new_resource.unit_name)
+ logger.info("#{new_resource} disabled")
end
end
end
- def action_mask
+ action :reenable do
+ converge_by("reenabling unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:reenable, new_resource.unit_name)
+ logger.info("#{new_resource} reenabled")
+ end
+ end
+
+ action :mask do
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)
+ logger.info("#{new_resource} masked")
end
end
end
- def action_unmask
+ action :unmask do
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)
+ logger.info("#{new_resource} unmasked")
end
end
end
- def action_start
+ action :start do
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, default_env: false)
+ logger.info("#{new_resource} started")
end
end
end
- def action_stop
+ action :stop do
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, default_env: false)
+ logger.info("#{new_resource} stopped")
end
end
end
- def action_restart
- converge_by("restarting unit: #{new_resource.name}") do
- systemctl_execute!(:restart, new_resource.name)
+ action :restart do
+ converge_by("restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!(:restart, new_resource.unit_name, default_env: false)
+ logger.info("#{new_resource} restarted")
end
end
- def action_reload
+ action :reload do
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, default_env: false)
+ logger.info("#{new_resource} reloaded")
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.name}") do
- systemctl_execute!("try-restart", new_resource.name)
+ action :try_restart do
+ converge_by("try-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("try-restart", new_resource.unit_name, default_env: false)
+ logger.info("#{new_resource} try-restarted")
end
end
- def action_reload_or_restart
- converge_by("reload-or-restarting unit: #{new_resource.name}") do
- systemctl_execute!("reload-or-restart", new_resource.name)
+ action :reload_or_restart do
+ converge_by("reload-or-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("reload-or-restart", new_resource.unit_name, default_env: false)
+ logger.info("#{new_resource} reload-or-restarted")
end
end
- def action_reload_or_try_restart
- converge_by("reload-or-try-restarting unit: #{new_resource.name}") do
- systemctl_execute!("reload-or-try-restart", new_resource.name)
+ action :reload_or_try_restart do
+ converge_by("reload-or-try-restarting unit: #{new_resource.unit_name}") do
+ systemctl_execute!("reload-or-try-restart", new_resource.unit_name, default_env: false)
+ logger.info("#{new_resource} reload-or-try-restarted")
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
+
+ def indirect?
+ systemctl_execute("is-enabled", new_resource.unit_name).stdout.include?("indirect")
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
- def manage_unit_file(action = :nothing)
- Chef::Resource::File.new(unit_path, run_context).tap do |f|
- f.owner "root"
- f.group "root"
- f.mode "0644"
- f.content new_resource.to_ini
- f.verify systemd_analyze_cmd if systemd_analyze_path
- end.run_action(action)
+ def manage_unit_file(the_action = :nothing)
+ file unit_path do
+ owner "root"
+ group "root"
+ mode "0644"
+ sensitive new_resource.sensitive
+ content new_resource.to_ini
+ verify :systemd_unit if new_resource.verify
+ action the_action
+ end
end
def daemon_reload
- shell_out_with_systems_locale!("#{systemctl_path} daemon-reload")
+ shell_out!(systemctl_cmd, "daemon-reload", **systemctl_opts, default_env: false)
end
- def systemctl_execute!(action, unit)
- shell_out_with_systems_locale!("#{systemctl_cmd} #{action} #{unit}", systemctl_opts)
+ def systemctl_execute!(action, unit, **options)
+ shell_out!(systemctl_cmd, action, unit, **systemctl_opts.merge(options))
end
- def systemctl_execute(action, unit)
- shell_out("#{systemctl_cmd} #{action} #{unit}", systemctl_opts)
+ def systemctl_execute(action, unit, **options)
+ shell_out(systemctl_cmd, action, unit, **systemctl_opts.merge(options))
end
def systemctl_cmd
- @systemctl_cmd ||= "#{systemctl_path} #{systemctl_args}"
+ @systemctl_cmd ||= [ systemctl_path, systemctl_args ]
end
def systemctl_path
@@ -224,24 +269,17 @@ class Chef
def systemctl_opts
@systemctl_opts ||=
if new_resource.user
+ uid = Etc.getpwnam(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
{}
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
diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb
index 3c46a6eb7d..6662821aae 100644
--- a/lib/chef/provider/template.rb
+++ b/lib/chef/provider/template.rb
@@ -1,7 +1,7 @@
#--
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,27 +17,21 @@
# limitations under the License.
#
-require "chef/provider/template_finder"
-require "chef/provider/file"
-require "chef/deprecation/provider/template"
-require "chef/deprecation/warnings"
+require_relative "template_finder"
+require_relative "file"
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,9 @@ 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..a0977b5523 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -1,6 +1,7 @@
+# rubocop: disable Performance/InefficientHashSearch
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +17,8 @@
# limitations under the License.
#
-require "chef/mixin/template"
-require "chef/file_content_management/content_base"
+require_relative "../../mixin/template"
+require_relative "../../file_content_management/content_base"
class Chef
class Provider
@@ -29,14 +30,37 @@ class Chef
def template_location
@template_file_cache_location ||= begin
- template_finder.find(new_resource.source, :local => new_resource.local, :cookbook => new_resource.cookbook)
+ template_finder.find(new_resource.source, local: new_resource.local, cookbook: new_resource.cookbook)
end
end
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 1e8b925071..fa120a1624 100644
--- a/lib/chef/provider/template_finder.rb
+++ b/lib/chef/provider/template_finder.rb
@@ -1,6 +1,6 @@
#--
# Author:: Andrea Campi (<andrea.campi@zephirworks.com>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,19 +43,11 @@ class Chef
protected
def template_source_name(name, options)
- if options[:source]
- options[:source]
- else
- name
- end
+ options[:source] || name
end
def find_cookbook_name(options)
- if options[:cookbook]
- options[:cookbook]
- else
- @cookbook_name
- end
+ options[:cookbook] || @cookbook_name
end
end
end
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 2bc4cc10bc..803d87d820 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,15 @@
# limitations under the License.
#
-require "chef/provider"
-require "chef/mixin/command"
-require "etc"
+require_relative "../provider"
+require "etc" unless defined?(Etc)
class Chef
class Provider
class User < Chef::Provider
- include Chef::Mixin::Command
attr_accessor :user_exists, :locked
+ attr_accessor :change_desc
def initialize(new_resource, run_context)
super
@@ -36,74 +35,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.to_i == 0
+ 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,77 +108,82 @@ 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)
+ @change_desc = []
+ if !new_resource.home.nil? && Pathname.new(new_resource.home).cleanpath != Pathname.new(current_resource.home).cleanpath
+ @change_desc << "change homedir from #{current_resource.home} to #{new_resource.home}"
end
- 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
+ %i{comment shell password uid gid}.each do |user_attrib|
+ new_val = new_resource.send(user_attrib)
+ cur_val = current_resource.send(user_attrib)
+ if !new_val.nil? && new_val.to_s != cur_val.to_s
+ @change_desc << "change #{user_attrib} from #{cur_val} to #{new_val}"
+ end
end
- changed.any?
+ !@change_desc.empty?
end
- def action_create
+ action :create do
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}"] + change_desc) do
manage_user
- Chef::Log.info("#{@new_resource} altered")
+ logger.info("#{new_resource} altered, #{change_desc.join(", ")}")
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
+ action :remove do
+ return unless @user_exists
+
+ converge_by("remove user #{new_resource.username}") do
+ remove_user
+ logger.info("#{new_resource} removed")
end
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
+ action :manage do
+ return unless @user_exists && compare_user
+
+ converge_by(["manage user #{new_resource.username}"] + change_desc) do
+ manage_user
+ logger.info("#{new_resource} managed: #{change_desc.join(", ")}")
end
end
- def action_modify
- if compare_user
- converge_by("modify user #{@new_resource.username}") do
- manage_user
- Chef::Log.info("#{@new_resource} modified")
- end
+ action :modify do
+ return unless compare_user
+
+ converge_by(["modify user #{new_resource.username}"] + change_desc) do
+ manage_user
+ logger.info("#{new_resource} modified: #{change_desc.join(", ")}")
end
end
- def action_lock
- if check_lock() == false
- converge_by("lock the user #{@new_resource.username}") do
+ action :lock 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 action_unlock
- if check_lock() == true
- converge_by("unlock user #{@new_resource.username}") do
+ action :unlock do
+ if check_lock == true
+ converge_by("unlock user #{new_resource.username}") do
unlock_user
- Chef::Log.info("#{@new_resource} unlocked")
+ logger.info("#{new_resource} unlocked")
end
else
- Chef::Log.debug("#{@new_resource} already unlocked - nothing to do")
+ logger.trace("#{new_resource} already unlocked - nothing to do")
end
end
@@ -210,6 +210,24 @@ class Chef
def check_lock
raise NotImplementedError
end
+
+ private
+
+ #
+ # helpers for subclasses
+ #
+
+ 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 if new_resource.home.nil?
+ return true if current_resource.home.nil?
+
+ # Pathname#cleanpath matches more edge conditions than File.expand_path()
+ new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath
+ end
end
end
end
diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb
index 8ac229ae4d..740f9943d3 100644
--- a/lib/chef/provider/user/aix.rb
+++ b/lib/chef/provider/user/aix.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,83 +14,117 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/provider/user/useradd"
+require_relative "../user"
class Chef
class Provider
class User
- class Aix < Chef::Provider::User::Useradd
+ class Aix < Chef::Provider::User
provides :user, os: "aix"
provides :aix_user
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
-
def create_user
- super
+ shell_out!("useradd", universal_options, useradd_options, new_resource.username)
add_password
end
def manage_user
add_password
manage_home
- super
+ return if universal_options.empty? && usermod_options.empty?
+
+ shell_out!("usermod", universal_options, usermod_options, new_resource.username)
+ end
+
+ def remove_user
+ shell_out!("userdel", userdel_options, new_resource.username)
end
- # Aix does not support -r like other unix, sytem account is created by adding to 'system' group
+ # Aix does not support -r like other unix, system account is created by adding to 'system' group
def useradd_options
opts = []
opts << "-g" << "system" if new_resource.system
+ if updating_home?
+ if new_resource.manage_home
+ logger.trace("#{new_resource} managing the users home directory")
+ opts << "-m"
+ else
+ logger.trace("#{new_resource} setting home to #{new_resource.home}")
+ end
+ end
opts
end
+ def userdel_options
+ opts = []
+ opts << "-r" if new_resource.manage_home
+ opts << "-f" if new_resource.force
+ opts
+ end
+
+ def usermod_options
+ []
+ end
+
def check_lock
- lock_info = shell_out!("lsuser -a account_locked #{new_resource.username}")
- if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/)
+ lock_info = shell_out!("lsuser", "-a", "account_locked", new_resource.username)
+ if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.include?("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!("chuser", "account_locked=true", new_resource.username)
end
def unlock_user
- shell_out!("chuser account_locked=false #{new_resource.username}")
+ shell_out!("chuser", "account_locked=false", new_resource.username)
+ end
+
+ def universal_options
+ opts = []
+ opts << "-c" << new_resource.comment if should_set?(:comment)
+ opts << "-g" << new_resource.gid if should_set?(:gid)
+ 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
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 -c -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
+ 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 821fa8e8a7..cd10403e4a 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,19 @@
# limitations under the License.
#
-require "mixlib/shellout"
-require "chef/provider/user"
-require "openssl"
-require "plist"
-require "chef/util/path_helper"
+require_relative "../../mixin/shell_out"
+require_relative "../user"
+require_relative "../../resource/user/dscl_user"
+autoload :OpenSSL, "openssl"
+autoload :Plist, "plist"
+require_relative "../../util/path_helper"
class Chef
class Provider
class User
#
# The most tricky bit of this provider is the way it deals with user passwords.
- # Mac OS X has different password shadow calculations based on the version.
+ # macOS has different password shadow calculations based on the version.
# < 10.7 => password shadow calculation format SALTED-SHA1
# => stored in: /var/db/shadow/hash/#{guid}
# => shadow binary length 68 bytes
@@ -41,7 +42,7 @@ class Chef
# => shadow binary length 128 bytes
# => Salt / Iterations are stored separately in the same file
#
- # This provider only supports Mac OSX versions 10.7 and above
+ # This provider only supports macOS versions 10.7 to 10.13
class Dscl < Chef::Provider::User
attr_accessor :user_info
@@ -49,29 +50,29 @@ class Chef
attr_accessor :password_shadow_conversion_algorithm
provides :dscl_user
- provides :user, os: "darwin"
+ provides :user, os: "darwin", platform_version: "<= 10.13"
+
+ # 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
requirements.assert(:all_actions) do |a|
- a.assertion { mac_osx_version_less_than_10_7? == false }
- a.failure_message(Chef::Exceptions::User, "Chef::Provider::User::Dscl only supports Mac OS X versions 10.7 and above.")
- 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
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if new_resource.password && mac_osx_version_greater_than_10_7?
+ if new_resource.password
# SALTED-SHA512 password shadow hashes are not supported on 10.8 and above.
!salted_sha512?(new_resource.password)
else
@@ -85,7 +86,7 @@ in 'password', with the associated 'salt' and 'iterations'.")
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(new_resource.password)
+ if new_resource.password && salted_sha512_pbkdf2?(new_resource.password)
# salt and iterations should be specified when
# SALTED-SHA512-PBKDF2 password shadow hash is given
!new_resource.salt.nil? && !new_resource.iterations.nil?
@@ -96,24 +97,10 @@ in 'password', with the associated 'salt' and 'iterations'.")
a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hash is given without associated \
'salt' and 'iterations'. Please specify 'salt' and 'iterations' in order to set the user password using shadow hash.")
end
-
- requirements.assert(:create, :modify, :manage) do |a|
- a.assertion do
- if new_resource.password && !mac_osx_version_greater_than_10_7?
- # On 10.7 SALTED-SHA512-PBKDF2 is not supported
- !salted_sha512_pbkdf2?(new_resource.password)
- else
- true
- end
- end
- a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hashes are not supported on \
-Mac OS X version 10.7. Please specify a SALTED-SHA512 shadow hash in 'password' attribute to set the \
-user password using shadow hash.")
- end
end
def load_current_resource
- @current_resource = Chef::Resource::User.new(new_resource.username)
+ @current_resource = Chef::Resource::User::DsclUser.new(new_resource.username)
current_resource.username(new_resource.username)
@user_info = read_user_info
@@ -131,13 +118,9 @@ user password using shadow hash.")
# Calling shell_out directly since we want to give an input stream
shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string)
- shadow_hash = Plist.parse_xml(shadow_hash_xml)
+ shadow_hash = ::Plist.parse_xml(shadow_hash_xml)
- if shadow_hash["SALTED-SHA512"]
- # Convert the shadow value from Base64 encoding to hex before consuming them
- @password_shadow_conversion_algorithm = "SALTED-SHA512"
- current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack("H*").first)
- elsif shadow_hash["SALTED-SHA512-PBKDF2"]
+ if shadow_hash["SALTED-SHA512-PBKDF2"] # 10.7+ contains this, but we retain the check in case it goes away in the future
@password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2"
# Convert the entropy from Base64 encoding to hex before consuming them
current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first)
@@ -145,14 +128,14 @@ user password using shadow hash.")
# Convert the salt from Base64 encoding to hex before consuming them
current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack("H*").first)
else
- raise(Chef::Exceptions::User, "Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}")
+ raise(Chef::Exceptions::User, "Unknown shadow_hash format: #{shadow_hash.keys.join(" ")}")
end
end
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
@@ -194,7 +177,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
#
@@ -203,13 +186,13 @@ 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
#
# Sets the user id for the user using dscl.
# If a `uid` is not specified, it finds the next available one starting
- # from 200 if `system` is set, 500 otherwise.
+ # from 200 if `system` is set, 501 otherwise.
#
def dscl_set_uid
# XXX: mutates the new resource
@@ -219,27 +202,27 @@ 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
#
# Find the next available uid on the system. starting with 200 if `system` is set,
- # 500 otherwise.
+ # 501 otherwise.
#
def get_free_uid(search_limit = 1000)
uid = nil
- base_uid = new_resource.system ? 200 : 500
+ 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")
+ if users_uids&.match?(Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n"))
next_uid_guess += 1
else
uid = next_uid_guess
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
#
@@ -247,39 +230,40 @@ 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
#
@@ -288,11 +272,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
- 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?
@@ -303,37 +287,34 @@ user password using shadow hash.")
move_home
end
end
- run_dscl("create /Users/#{new_resource.username} NFSHomeDirectory '#{new_resource.home}'")
+ run_dscl("create", "/Users/#{new_resource.username}", "NFSHomeDirectory", new_resource.home)
end
def validate_home_dir_specification!
- unless new_resource.home =~ /^\//
+ unless %r{^/}.match?(new_resource.home)
raise(Chef::Exceptions::InvalidHomeDirectory, "invalid path spec for User: '#{new_resource.username}', home directory: '#{new_resource.home}'")
end
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
- skel = "/System/Library/User Template/English.lproj"
- raise(Chef::Exceptions::User, "can't find skel at: #{skel}") unless ::File.exists?(skel)
- shell_out! "ditto '#{skel}' '#{new_resource.home}'"
- ::FileUtils.chown_R(new_resource.username, new_resource.gid.to_s, new_resource.home)
+ shell_out!("/usr/sbin/createhomedir", "-c", "-u", (new_resource.username).to_s)
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
@@ -342,10 +323,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
@@ -362,9 +343,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("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
@@ -389,47 +369,31 @@ user password using shadow hash.")
salt = nil
iterations = nil
- if mac_osx_version_10_7?
- hash_value = if salted_sha512?(new_resource.password)
- new_resource.password
- else
- # 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
- end
-
- shadow_info["SALTED-SHA512"] = StringIO.new
- shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value)
- shadow_info
+ if salted_sha512_pbkdf2?(new_resource.password)
+ entropy = convert_to_binary(new_resource.password)
+ salt = convert_to_binary(new_resource.salt)
+ iterations = new_resource.iterations
else
- if salted_sha512_pbkdf2?(new_resource.password)
- entropy = convert_to_binary(new_resource.password)
- salt = convert_to_binary(new_resource.salt)
- iterations = new_resource.iterations
- else
- salt = OpenSSL::Random.random_bytes(32)
- iterations = new_resource.iterations # Use the default if not specified by the user
-
- entropy = OpenSSL::PKCS5.pbkdf2_hmac(
- new_resource.password,
- salt,
- iterations,
- 128,
- OpenSSL::Digest::SHA512.new
- )
- end
-
- pbkdf_info = {}
- pbkdf_info["entropy"] = StringIO.new
- pbkdf_info["entropy"].string = entropy
- pbkdf_info["salt"] = StringIO.new
- pbkdf_info["salt"].string = salt
- pbkdf_info["iterations"] = iterations
-
- shadow_info["SALTED-SHA512-PBKDF2"] = pbkdf_info
+ salt = OpenSSL::Random.random_bytes(32)
+ iterations = new_resource.iterations # Use the default if not specified by the user
+
+ entropy = OpenSSL::PKCS5.pbkdf2_hmac(
+ new_resource.password,
+ salt,
+ iterations,
+ 128,
+ OpenSSL::Digest.new("SHA512")
+ )
end
+ pbkdf_info = {}
+ pbkdf_info["entropy"] = StringIO.new
+ pbkdf_info["entropy"].string = entropy
+ pbkdf_info["salt"] = StringIO.new
+ pbkdf_info["salt"].string = salt
+ pbkdf_info["iterations"] = iterations
+
+ shadow_info["SALTED-SHA512-PBKDF2"] = pbkdf_info
shadow_info
end
@@ -438,27 +402,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
#
@@ -466,7 +430,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
#
@@ -474,7 +438,7 @@ user password using shadow hash.")
#
def locked?
if authentication_authority
- !!(authentication_authority =~ /DisabledUser/ )
+ !!(authentication_authority.include?("DisabledUser"))
else
false
end
@@ -484,7 +448,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
#
@@ -496,11 +460,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
#
@@ -514,29 +478,16 @@ user password using shadow hash.")
return false if new_resource.password.nil?
# Dscl provider supports both plain text passwords and shadow hashes.
- if mac_osx_version_10_7?
- if salted_sha512?(new_resource.password)
- diverged?(:password)
- else
- !salted_sha512_password_match?
- end
+ #
+ # Some system users don't have salts; this can happen if the system is
+ # upgraded and the user hasn't logged in yet. In this case, we will force
+ # the password to be updated.
+ return true if current_resource.salt.nil?
+
+ if salted_sha512_pbkdf2?(new_resource.password)
+ diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
else
- # When a system is upgraded to a version 10.7+ shadow hashes of the users
- # will be updated when the user logs in. So it's possible that we will have
- # SALTED-SHA512 password in the current_resource. In that case we will force
- # password to be updated.
- return true if salted_sha512?(current_resource.password)
-
- # Some system users don't have salts; this can happen if the system is
- # upgraded and the user hasn't logged in yet. In this case, we will force
- # the password to be updated.
- return true if current_resource.salt.nil?
-
- if salted_sha512_pbkdf2?(new_resource.password)
- diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
- else
- !salted_sha512_pbkdf2_password_match?
- end
+ !salted_sha512_pbkdf2_password_match?
end
end
@@ -546,7 +497,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
@@ -563,14 +514,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
@@ -585,12 +536,12 @@ 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("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_info = Plist.parse_xml(user_plist_info)
+ user_plist_info = run_plutil("convert", "xml1", "-o", "-", user_plist_file)
+ user_info = ::Plist.parse_xml(user_plist_info)
rescue Chef::Exceptions::PlistUtilCommandFailed
end
@@ -603,15 +554,16 @@ 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}")
+ ::Plist::Emit.save_plist(user_info, user_plist_file)
+ run_plutil("convert", "binary1", user_plist_file)
end
#
# Sets a value in user information hash using Chef attributes as keys.
#
def dscl_set(user_hash, key, value)
- raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
+ raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.key?(key)
+
user_hash[DSCL_PROPERTY_MAP[key]] = [ value ]
user_hash
end
@@ -620,58 +572,39 @@ user password using shadow hash.")
# Gets a value from user information hash using Chef attributes as keys.
#
def dscl_get(user_hash, key)
- raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
+ raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.key?(key)
+
# DSCL values are set as arrays
value = user_hash[DSCL_PROPERTY_MAP[key]]
value.nil? ? value : value.first
end
#
- # System Helpets
+ # System Helpers
#
- def mac_osx_version
- # This provider will only be invoked on node[:platform] == "mac_os_x"
- # We do not check or assert that here.
- node[:platform_version]
- end
-
- def mac_osx_version_10_7?
- mac_osx_version.start_with?("10.7.")
- end
-
- def mac_osx_version_less_than_10_7?
- versions = mac_osx_version.split(".")
- # Make integer comparison in order not to report 10.10 less than 10.7
- (versions[0].to_i <= 10 && versions[1].to_i < 7)
- end
-
- def mac_osx_version_greater_than_10_7?
- versions = mac_osx_version.split(".")
- # Make integer comparison in order not to report 10.10 less than 10.7
- (versions[0].to_i >= 10 && versions[1].to_i > 7)
- end
-
def run_dscl(*args)
- result = shell_out("dscl . -#{args.join(' ')}")
+ result = shell_out("dscl", ".", "-#{args[0]}", args[1..])
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: /
+ raise(Chef::Exceptions::DsclCommandFailed, "dscl error: #{result.inspect}") if result.stdout.include?("No such key: ")
+
result.stdout
end
def run_plutil(*args)
- result = shell_out("plutil -#{args.join(' ')}")
+ result = shell_out("plutil", "-#{args[0]}", args[1..])
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("plutil", "-convert", "xml1", "-o", "-", "-", input: binary_plist_string).stdout
end
def convert_to_binary(string)
@@ -682,13 +615,6 @@ user password using shadow hash.")
!!(string =~ /^[[:xdigit:]]{136}$/)
end
- def salted_sha512_password_match?
- # Salt is included in the first 4 bytes of shadow data
- salt = current_resource.password.slice(0, 8)
- shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + new_resource.password)
- current_resource.password == salt + shadow
- end
-
def salted_sha512_pbkdf2?(string)
!!(string =~ /^[[:xdigit:]]{256}$/)
end
@@ -701,7 +627,7 @@ user password using shadow hash.")
salt,
current_resource.iterations,
128,
- OpenSSL::Digest::SHA512.new
+ OpenSSL::Digest.new("SHA512")
).unpack("H*").first == current_resource.password
end
diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb
index 4a2491d806..40b5985cb1 100644
--- a/lib/chef/provider/user/linux.rb
+++ b/lib/chef/provider/user/linux.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/provider/user"
+require_relative "../user"
class Chef
class Provider
@@ -24,23 +24,28 @@ class Chef
provides :user, os: "linux"
def create_user
- shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username))
+ shell_out!("useradd", universal_options, useradd_options, new_resource.username)
end
def manage_user
- shell_out!(*clean_array("usermod", universal_options, usermod_options, new_resource.username))
+ manage_u = shell_out("usermod", universal_options, usermod_options, new_resource.username, returns: [0, 12])
+ if manage_u.exitstatus == 12 && manage_u.stderr !~ /exists/
+ raise Chef::Exceptions::User, "Unable to modify home directory for #{new_resource.username}"
+ end
+
+ manage_u.error!
end
def remove_user
- shell_out!(*clean_array("userdel", userdel_options, new_resource.username))
+ shell_out!("userdel", userdel_options, new_resource.username)
end
def lock_user
- shell_out!(*clean_array("usermod", "-L", new_resource.username))
+ shell_out!("usermod", "-L", new_resource.username)
end
def unlock_user
- shell_out!(*clean_array("usermod", "-U", new_resource.username))
+ shell_out!("usermod", "-U", new_resource.username)
end
# common to usermod and useradd
@@ -52,14 +57,15 @@ class Chef
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 non_unique
+ 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 manage_home
+ if new_resource.manage_home
opts << "-m"
end
end
@@ -69,43 +75,31 @@ class Chef
def useradd_options
opts = []
opts << "-r" if new_resource.system
- if manage_home
- opts << "-m"
- else
- opts << "-M"
- end
+ opts << if new_resource.manage_home
+ "-m"
+ else
+ "-M"
+ end
opts
end
def userdel_options
opts = []
- opts << "-r" if manage_home
+ 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("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/
- if whyrun_mode?
- return false
- else
- raise Chef::Exceptions::User, "User #{new_resource.username} does not exist when checking lock status for #{new_resource}"
- end
+ if /does not exist/.match?(passwd_s.stderr)
+ 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)
@@ -114,24 +108,14 @@ class Chef
# 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/
+ @locked = false if /^[PN]/.match?(status_line[1])
+ @locked = true if /^L/.match?(status_line[1])
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
-
- def non_unique
- # XXX: THIS GOES AWAY IN CHEF-13 AND BECOMES JUST new_resource.non_unique
- new_resource.non_unique || new_resource.supports[:non_unique]
- end
-
- def manage_home
- # XXX: THIS GOES AWAY IN CHEF-13 AND BECOMES JUST new_resource.manage_home
- new_resource.manage_home || new_resource.supports[:manage_home]
- end
end
end
end
diff --git a/lib/chef/provider/user/mac.rb b/lib/chef/provider/user/mac.rb
new file mode 100644
index 0000000000..5604244f7f
--- /dev/null
+++ b/lib/chef/provider/user/mac.rb
@@ -0,0 +1,680 @@
+#
+# Author:: Ryan Cragun (<ryan@chef.io>)
+# Copyright:: Copyright (c) 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 "../../resource"
+require_relative "../../dsl/declare_resource"
+require_relative "../../mixin/shell_out"
+require_relative "../../mixin/which"
+require_relative "../user"
+require_relative "../../resource/user/mac_user"
+autoload :Plist, "plist"
+
+class Chef
+ class Provider
+ class User
+ # A macOS user provider that is compatible with default TCC restrictions
+ # in macOS 10.14. See resource/user/mac_user.rb for complete description
+ # of the mac_user resource and how it differs from the dscl resource used
+ # on previous platforms.
+ class MacUser < Chef::Provider::User
+ include Chef::Mixin::Which
+
+ provides :mac_user
+ provides :user, os: "darwin", platform_version: ">= 10.14"
+
+ attr_reader :user_plist, :admin_group_plist
+
+ def load_current_resource
+ @current_resource = Chef::Resource::User::MacUser.new(new_resource.username)
+ current_resource.username(new_resource.username)
+
+ reload_admin_group_plist
+ reload_user_plist
+
+ if user_plist
+ current_resource.uid(user_plist[:uid][0])
+ current_resource.gid(user_plist[:gid][0])
+ current_resource.home(user_plist[:home][0])
+ current_resource.shell(user_plist[:shell][0])
+ current_resource.comment(user_plist[:comment][0])
+
+ if user_plist[:is_hidden]
+ current_resource.hidden(user_plist[:is_hidden][0] == "1" ? true : false)
+ end
+
+ shadow_hash = user_plist[:shadow_hash]
+ if shadow_hash
+ current_resource.password(shadow_hash[0]["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*")[0])
+ current_resource.salt(shadow_hash[0]["SALTED-SHA512-PBKDF2"]["salt"].string.unpack("H*")[0])
+ current_resource.iterations(shadow_hash[0]["SALTED-SHA512-PBKDF2"]["iterations"].to_i)
+ end
+
+ current_resource.secure_token(secure_token_enabled?)
+ current_resource.admin(admin_user?)
+ else
+ @user_exists = false
+ logger.trace("#{new_resource} user does not exist")
+ end
+
+ current_resource
+ end
+
+ def reload_admin_group_plist
+ @admin_group_plist = nil
+
+ admin_group_xml = run_dscl("read", "/Groups/admin")
+ return nil unless admin_group_xml && admin_group_xml != ""
+
+ @admin_group_plist = Plist.new(::Plist.parse_xml(admin_group_xml))
+ end
+
+ def reload_user_plist
+ @user_plist = nil
+
+ # Load the user information.
+ begin
+ user_xml = run_dscl("read", "/Users/#{new_resource.username}")
+ rescue Chef::Exceptions::DsclCommandFailed
+ return nil
+ end
+
+ return nil if user_xml.nil? || user_xml == ""
+
+ @user_plist = Plist.new(::Plist.parse_xml(user_xml))
+
+ return unless user_plist[:shadow_hash]
+
+ shadow_hash_hex = user_plist[:shadow_hash][0]
+ return unless shadow_hash_hex && shadow_hash_hex != ""
+
+ # The password information is stored in the ShadowHashData key in the
+ # plist. However, parsing it is a bit tricky as the value is itself
+ # another encoded binary plist. We have to extract the encoded plist,
+ # decode it from hex to a binary plist and then convert the binary
+ # into XML plist. From there we can extract the hash data.
+ #
+ # NOTE: `dscl -read` and `plutil -convert` return different values for
+ # ShadowHashData.
+ #
+ # `dscl` returns the value encoded as a hex string and stored as a <string>
+ # `plutil` returns the value encoded as a base64 string stored as <data>
+ #
+ # eg:
+ #
+ # spellchecker: disable
+ #
+ # <array>
+ # <string>77687920 63616e27 74206170 706c6520 6275696c 6420636f 6e736973 74656e74 20746f6f 6c696e67</string>
+ # </array>
+ #
+ # vs
+ #
+ # <array>
+ # <data>AADKAAAKAA4LAA0MAAAAAAAAAAA=</data>
+ # </array>
+ #
+ # spellchecker: disable
+ #
+ begin
+ shadow_binary_plist = [shadow_hash_hex.delete(" ")].pack("H*")
+ shadow_xml_plist = shell_out("plutil", "-convert", "xml1", "-o", "-", "-", input: shadow_binary_plist).stdout
+ user_plist[:shadow_hash] = ::Plist.parse_xml(shadow_xml_plist)
+ rescue Chef::Exceptions::PlistUtilCommandFailed, Chef::Exceptions::DsclCommandFailed
+ nil
+ end
+ end
+
+ #
+ # User Provider Callbacks
+ #
+
+ def create_user
+ cmd = [-"-addUser", new_resource.username]
+ cmd += ["-fullName", new_resource.comment] if prop_is_set?(:comment)
+ cmd += ["-UID", prop_is_set?(:uid) ? new_resource.uid : get_free_uid]
+ cmd += ["-shell", new_resource.shell]
+ cmd += ["-home", new_resource.home]
+ cmd += ["-admin"] if new_resource.admin
+
+ # We can technically create a new user without the admin credentials
+ # but without them the user cannot enable SecureToken, thus they cannot
+ # create other secure users or enable FileVault full disk encryption.
+ if prop_is_set?(:admin_username) && prop_is_set?(:admin_password)
+ cmd += ["-adminUser", new_resource.admin_username]
+ cmd += ["-adminPassword", new_resource.admin_password]
+ end
+
+ # sysadminctl doesn't exit with a non-zero exit code if it encounters
+ # a problem. We'll check stderr and make sure we see that it finished
+ # correctly.
+ res = run_sysadminctl(cmd)
+ unless /creating user/.match?(res.downcase)
+ raise Chef::Exceptions::User, "error when creating user: #{res}"
+ end
+
+ # Wait for the user to show up in the ds cache
+ wait_for_user
+
+ # Reload with up-to-date user information
+ reload_user_plist
+ reload_admin_group_plist
+
+ if prop_is_set?(:hidden)
+ set_hidden
+ end
+
+ if prop_is_set?(:password)
+ converge_by("set password") { set_password }
+ end
+
+ if new_resource.manage_home
+ # "sysadminctl -addUser" will create the home directory if it's
+ # the default /Users/<username>, otherwise it sets it in plist
+ # but does not create it. Here we'll ensure that it gets created
+ # if we've been given a directory that is not the default.
+ unless ::File.directory?(new_resource.home) && ::File.exist?(new_resource.home)
+ converge_by("create home directory") do
+ shell_out!("createhomedir -c -u #{new_resource.username}")
+ end
+ end
+ end
+
+ if prop_is_set?(:gid)
+ # NOTE: Here we're managing the primary group of the user which is
+ # a departure from previous behavior. We could just set the
+ # PrimaryGroupID for the user and move on if we decide that actual
+ # group management should be done outside of the core resource.
+ group_name, group_id, group_action = user_group_info
+
+ group group_name do
+ members new_resource.username
+ gid group_id if group_id
+ action group_action
+ append true
+ end
+
+ converge_by("create primary group ID") do
+ run_dscl("create", "/Users/#{new_resource.username}", "PrimaryGroupID", group_id)
+ end
+ end
+
+ if diverged?(:secure_token)
+ converge_by("alter SecureToken") { toggle_secure_token }
+ end
+
+ reload_user_plist
+ end
+
+ def compare_user
+ @change_desc = []
+ %i{comment shell uid gid salt password admin secure_token hidden}.each do |attr|
+ if diverged?(attr)
+ desc = "Update #{attr}"
+ unless %i{password gid secure_token hidden}.include?(attr)
+ desc << " from #{current_resource.send(attr)} to #{new_resource.send(attr)}"
+ end
+ @change_desc << desc
+ end
+ end
+ !@change_desc.empty?
+ end
+
+ def manage_user
+ %i{uid home}.each do |prop|
+ raise Chef::Exceptions::User, "cannot modify #{prop} on macOS >= 10.14" if diverged?(prop)
+ end
+
+ if diverged?(:password)
+ converge_by("alter password") { set_password }
+ end
+
+ if diverged?(:comment)
+ converge_by("alter comment") do
+ run_dscl("create", "/Users/#{new_resource.username}", "RealName", new_resource.comment)
+ end
+ end
+
+ if diverged?(:shell)
+ converge_by("alter shell") do
+ run_dscl("create", "/Users/#{new_resource.username}", "UserShell", new_resource.shell)
+ end
+ end
+
+ if diverged?(:secure_token)
+ converge_by("alter SecureToken") { toggle_secure_token }
+ end
+
+ if diverged?(:admin)
+ converge_by("alter admin group membership") do
+ group "admin" do
+ if new_resource.admin
+ members new_resource.username
+ else
+ excluded_members new_resource.username
+ end
+
+ action :create
+ append true
+ end
+
+ admins = admin_group_plist[:group_members]
+ if new_resource.admin
+ admins << user_plist[:guid][0]
+ else
+ admins.reject! { |m| m == user_plist[:guid][0] }
+ end
+
+ run_dscl("create", "/Groups/admin", "GroupMembers", admins)
+ end
+
+ reload_admin_group_plist
+ end
+
+ group_name, group_id, group_action = user_group_info
+ group group_name do
+ gid group_id if group_id
+ members new_resource.username
+ action group_action
+ append true
+ end
+
+ if diverged?(:gid)
+ converge_by("alter group membership") do
+ run_dscl("create", "/Users/#{new_resource.username}", "PrimaryGroupID", group_id)
+ end
+ end
+
+ if diverged?(:hidden)
+ converge_by("alter hidden") { set_hidden }
+ end
+
+ reload_user_plist
+ end
+
+ def remove_user
+ cmd = ["-deleteUser", new_resource.username]
+ cmd << new_resource.manage_home ? "-secure" : "-keepHome"
+ if %i{admin_username admin_password}.all? { |p| prop_is_set?(p) }
+ cmd += ["-adminUser", new_resource.admin_username]
+ cmd += ["-adminPassword", new_resource.admin_password]
+ end
+
+ # sysadminctl doesn't exit with a non-zero exit code if it encounters
+ # a problem. We'll check stderr and make sure we see that it finished
+ res = run_sysadminctl(cmd)
+ unless /deleting record|not found/.match?(res.downcase)
+ raise Chef::Exceptions::User, "error deleting user: #{res}"
+ end
+
+ reload_user_plist
+ @user_exists = false
+ end
+
+ def lock_user
+ run_dscl("append", "/Users/#{new_resource.username}", "AuthenticationAuthority", ";DisabledUser;")
+
+ reload_user_plist
+ end
+
+ def unlock_user
+ auth_string = user_plist[:auth_authority].reject! { |tag| tag == ";DisabledUser;" }.join.strip
+
+ run_dscl("create", "/Users/#{new_resource.username}", "AuthenticationAuthority", auth_string)
+
+ reload_user_plist
+ end
+
+ def locked?
+ user_plist[:auth_authority].any? { |tag| tag == ";DisabledUser;" }
+ rescue
+ false
+ end
+
+ def check_lock
+ @locked = locked?
+ end
+
+ #
+ # Methods
+ #
+
+ def diverged?(prop)
+ prop = prop.to_sym
+
+ case prop
+ when :password
+ password_diverged?
+ when :gid
+ user_group_diverged?
+ when :secure_token
+ secure_token_diverged?
+ when :hidden
+ hidden_diverged?
+ else
+ # Other fields are have been set on current resource so just compare
+ # them.
+ !new_resource.send(prop).nil? && (new_resource.send(prop) != current_resource.send(prop))
+ end
+ end
+
+ # Find the next available uid on the system.
+ # Starting with 200 if `system` is set, 501 otherwise.
+ def get_free_uid(search_limit = 1000)
+ uid = nil
+ base_uid = new_resource.system ? 200 : 501
+ next_uid_guess = base_uid
+ users_uids = run_dscl("list", "/Users", "uid")
+ while next_uid_guess < search_limit + base_uid
+ if users_uids&.match?(Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n"))
+ next_uid_guess += 1
+ else
+ uid = next_uid_guess
+ break
+ end
+ end
+ uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
+ end
+
+ # Attempt to resolve the group name, gid, and the action required for
+ # associated group resource. If a group exists we'll modify it, otherwise
+ # create it.
+ def user_group_info
+ @user_group_info ||= begin
+ if new_resource.gid.is_a?(String)
+ begin
+ g = Etc.getgrnam(new_resource.gid)
+ [g.name, g.gid.to_s, :modify]
+ rescue
+ [new_resource.gid, nil, :create]
+ end
+ else
+ begin
+ g = Etc.getgrgid(new_resource.gid)
+ [g.name, g.gid.to_s, :modify]
+ rescue
+ [g.username, nil, :create]
+ end
+ end
+ end
+ end
+
+ def secure_token_enabled?
+ user_plist[:auth_authority].any? { |tag| tag == ";SecureToken;" }
+ rescue
+ false
+ end
+
+ def secure_token_diverged?
+ new_resource.secure_token ? !secure_token_enabled? : secure_token_enabled?
+ end
+
+ def toggle_secure_token
+ # Check for this lazily as we only need to validate for these credentials
+ # if we're toggling secure token.
+ unless %i{admin_username admin_password secure_token_password}.all? { |p| prop_is_set?(p) }
+ raise Chef::Exceptions::User, "secure_token_password, admin_username and admin_password properties are required to modify SecureToken"
+ end
+
+ cmd = (new_resource.secure_token ? %w{-secureTokenOn} : %w{-secureTokenOff})
+ cmd += [new_resource.username, "-password", new_resource.secure_token_password]
+ cmd += ["-adminUser", new_resource.admin_username]
+ cmd += ["-adminPassword", new_resource.admin_password]
+
+ # sysadminctl doesn't exit with a non-zero exit code if it encounters
+ # a problem. We'll check stderr and make sure we see that it finished
+ res = run_sysadminctl(cmd)
+ unless /done/.match?(res.downcase)
+ raise Chef::Exceptions::User, "error when modifying SecureToken: #{res}"
+ end
+
+ # HACK: When SecureToken is enabled or disabled it requires the user
+ # password in plaintext, which it verifies and uses as a key. It also
+ # takes the liberty of _rehashing_ the password with a random salt and
+ # iterations count and saves it back into the user ShadowHashData.
+ #
+ # Therefore, if we're configuring a user based upon existing shadow
+ # hash data we'll have to set the password again so that future runs
+ # of the client don't show password drift.
+ set_password if prop_is_set?(:salt)
+ end
+
+ def user_group_diverged?
+ return false unless prop_is_set?(:gid)
+
+ group_name, group_id = user_group_info
+ current_resource.gid != group_id.to_i
+ end
+
+ def hidden_diverged?
+ return false unless prop_is_set?(:hidden)
+
+ (current_resource.hidden ? 1 : 0) != hidden_value.to_i
+ end
+
+ def set_hidden
+ run_dscl("create", "/Users/#{new_resource.username}", "IsHidden", hidden_value.to_i)
+ end
+
+ def hidden_value
+ new_resource.hidden ? 1 : 0
+ end
+
+ def password_diverged?
+ # There are three options for configuring the password:
+ # * ShadowHashData which includes the hash data as:
+ # * hashed entropy as the "password"
+ # * salt
+ # * iterations
+ # * Plaintext password
+ # * Not configuring it
+
+ # Check for no desired password configuration
+ return false unless prop_is_set?(:password)
+
+ # Check for ShadowHashData divergence by comparing the entropy,
+ # salt, and iterations.
+ if prop_is_set?(:salt)
+ return true if %i{salt iterations}.any? { |prop| diverged?(prop) }
+
+ return new_resource.password != current_resource.password
+ end
+
+ # Check for plaintext password divergence. We don't actually know
+ # what the stored password is but we can hash the given password with
+ # stored salt and iterations, and compare the resulting entropy with
+ # the saved entropy.
+ OpenSSL::PKCS5.pbkdf2_hmac(
+ new_resource.password,
+ convert_to_binary(current_resource.salt),
+ current_resource.iterations.to_i,
+ 128,
+ OpenSSL::Digest.new("SHA512")
+ ).unpack("H*")[0] != current_resource.password
+ end
+
+ def admin_user?
+ admin_group_plist[:group_members].any? { |mem| mem == user_plist[:guid][0] }
+ rescue
+ false
+ end
+
+ def convert_to_binary(string)
+ string.unpack("a2" * (string.size / 2)).collect { |i| i.hex.chr }.join
+ end
+
+ def set_password
+ if prop_is_set?(:salt)
+ entropy = StringIO.new(convert_to_binary(new_resource.password))
+ salt = StringIO.new(convert_to_binary(new_resource.salt))
+ else
+ salt = StringIO.new(OpenSSL::Random.random_bytes(32))
+ entropy = StringIO.new(
+ OpenSSL::PKCS5.pbkdf2_hmac(
+ new_resource.password,
+ salt.string,
+ new_resource.iterations,
+ 128,
+ OpenSSL::Digest.new("SHA512")
+ )
+ )
+ end
+
+ shadow_hash = user_plist[:shadow_hash] ? user_plist[:shadow_hash][0] : {}
+ shadow_hash["SALTED-SHA512-PBKDF2"] = {
+ "entropy" => entropy,
+ "salt" => salt,
+ "iterations" => new_resource.iterations,
+ }
+
+ shadow_hash_binary = StringIO.new
+ shell_out("plutil", "-convert", "binary1", "-o", "-", "-",
+ input: shadow_hash.to_plist,
+ live_stream: shadow_hash_binary)
+
+ # Apple seem to have killed their dsimport documentation about the
+ # dsimport record format. Perhaps that means our days of being able to
+ # use dsimport without an admin password or perhaps at all could be
+ # numbered. Here is the record format for posterity:
+ #
+ # End of record character
+ # Escape character
+ # Field separator
+ # Value separator
+ # Record type (Users, Groups, Computers, ComputerGroups, ComputerLists)
+ # Number of properties
+ # Property 1
+ # ...
+ # Property N
+ #
+ # The user password shadow data format breaks down as:
+ #
+ # 0x0A End of record denoted by \n
+ # 0x5C Escaping is denoted by \
+ # 0x3A Fields are separated by :
+ # 0x2C Values are separated by ,
+ # dsRecTypeStandard:Users The record type we're configuring
+ # 2 How many properties we're going to set
+ # dsAttrTypeStandard:RecordName Property 1: our users record name
+ # base64:dsAttrTypeNative:ShadowHashData Property 2: our shadow hash data
+
+ import_file = ::File.join(Chef::Config["file_cache_path"], "#{new_resource.username}_password_dsimport")
+ ::File.open(import_file, "w+", 0600) do |f|
+ f.write <<~DSIMPORT
+ 0x0A 0x5C 0x3A 0x2C dsRecTypeStandard:Users 2 dsAttrTypeStandard:RecordName base64:dsAttrTypeNative:ShadowHashData
+ #{new_resource.username}:#{::Base64.strict_encode64(shadow_hash_binary.string)}
+ DSIMPORT
+ end
+
+ run_dscl("delete", "/Users/#{new_resource.username}", "ShadowHashData")
+ run_dsimport(import_file, "/Local/Default", "M")
+ run_dscl("create", "/Users/#{new_resource.username}", "Password", "********")
+ ensure
+ ::File.delete(import_file) if import_file && ::File.exist?(import_file)
+ end
+
+ def wait_for_user
+ timeout = Time.now + 5
+
+ loop do
+
+ run_dscl("read", "/Users/#{new_resource.username}", "ShadowHashData")
+ break
+ rescue Chef::Exceptions::DsclCommandFailed => e
+ if Time.now < timeout
+ sleep 0.1
+ else
+ raise Chef::Exceptions::User, e.message
+ end
+
+ end
+ end
+
+ def run_dsimport(*args)
+ shell_out!("dsimport", args)
+ end
+
+ def run_sysadminctl(args)
+ # sysadminctl doesn't exit with a non-zero code when errors are encountered
+ # and outputs everything to STDERR instead of STDOUT and STDERR. Therefore we'll
+ # return the STDERR and let the caller handle it.
+ shell_out!("sysadminctl", args).stderr
+ end
+
+ def run_dscl(*args)
+ result = shell_out("dscl", "-plist", ".", "-#{args[0]}", args[1..])
+ 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 /No such key: /.match?(result.stdout)
+
+ result.stdout
+ end
+
+ def run_plutil(*args)
+ result = shell_out("plutil", "-#{args[0]}", args[1..])
+ raise(Chef::Exceptions::PlistUtilCommandFailed, "plutil error: #{result.inspect}") unless result.exitstatus == 0
+
+ result.stdout
+ end
+
+ def prop_is_set?(prop)
+ v = new_resource.send(prop.to_sym)
+
+ !v.nil? && v != ""
+ end
+
+ class Plist
+ DSCL_PROPERTY_MAP = {
+ uid: "dsAttrTypeStandard:UniqueID",
+ guid: "dsAttrTypeStandard:GeneratedUID",
+ gid: "dsAttrTypeStandard:PrimaryGroupID",
+ home: "dsAttrTypeStandard:NFSHomeDirectory",
+ shell: "dsAttrTypeStandard:UserShell",
+ comment: "dsAttrTypeStandard:RealName",
+ password: "dsAttrTypeStandard:Password",
+ auth_authority: "dsAttrTypeStandard:AuthenticationAuthority",
+ shadow_hash: "dsAttrTypeNative:ShadowHashData",
+ group_members: "dsAttrTypeStandard:GroupMembers",
+ is_hidden: "dsAttrTypeNative:IsHidden",
+ }.freeze
+
+ attr_accessor :plist_hash, :property_map
+
+ def initialize(plist_hash = {}, property_map = DSCL_PROPERTY_MAP)
+ @plist_hash = plist_hash
+ @property_map = property_map
+ end
+
+ def get(key)
+ return nil unless property_map.key?(key)
+
+ plist_hash[property_map[key]]
+ end
+ alias_method :[], :get
+
+ def set(key, value)
+ return nil unless property_map.key?(key)
+
+ plist_hash[property_map[key]] = [ value ]
+ end
+ alias_method :[]=, :set
+
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb
index a1d7671c28..9e21e5a816 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 (c) 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.
#
-require "chef/provider/user"
+require_relative "../user"
class Chef
class Provider
@@ -27,49 +27,45 @@ class Chef
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!("pw", "useradd", set_options)
modify_password
end
def manage_user
- command = "pw usermod"
- command << set_options
- run_command(:command => command)
+ shell_out!("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!(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!("pw", "lock", new_resource.username)
end
def unlock_user
- run_command(:command => "pw unlock #{@new_resource.username}")
+ shell_out!("pw", "unlock", new_resource.username)
end
def set_options
- opts = " #{@new_resource.username}"
+ opts = [ new_resource.username ]
field_list = {
"comment" => "-c",
@@ -78,35 +74,30 @@ 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 8d3df9e68b..abf6cd94c8 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 (c) Chef Software Inc.
# Copyright:: Copyright 2015-2016, Dave Eddy
# License:: Apache License, Version 2.0
#
@@ -18,49 +18,42 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/provider/user/useradd"
+require_relative "../user"
class Chef
class Provider
class User
- class Solaris < Chef::Provider::User::Useradd
+ class Solaris < Chef::Provider::User
provides :solaris_user
- provides :user, os: %w{omnios solaris2}
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
+ provides :user, os: %w{openindiana illumos omnios solaris2 smartos}
- attr_writer :password_file
-
- def initialize(new_resource, run_context)
- @password_file = "/etc/shadow"
- super
- end
+ PASSWORD_FILE = "/etc/shadow".freeze
def create_user
- super
+ shell_out!("useradd", universal_options, useradd_options, new_resource.username)
manage_password
end
def manage_user
manage_password
- super
+ return if universal_options.empty? && usermod_options.empty?
+
+ shell_out!("usermod", universal_options, usermod_options, new_resource.username)
end
- def check_lock
- shadow_line = shell_out!("getent", "shadow", new_resource.username).stdout.strip rescue nil
+ def remove_user
+ shell_out!("userdel", userdel_options, new_resource.username)
+ end
- # if the command fails we return nil, this can happen if the user
- # in question doesn't exist
- return nil if shadow_line.nil?
+ def check_lock
+ user = IO.read(PASSWORD_FILE).match(/^#{Regexp.escape(new_resource.username)}:([^:]*):/)
- # convert "dave:NP:16507::::::\n" to "NP"
- fields = shadow_line.split(":")
+ # If we're in whyrun mode, and the user is not created, we assume it will be
+ return false if whyrun_mode? && 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\*?/)
+ raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if user.nil?
- @locked
+ @locked = user[1].start_with?("*LK*")
end
def lock_user
@@ -73,19 +66,66 @@ class Chef
private
- 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
+ def universal_options
+ opts = []
+ opts << "-c" << new_resource.comment if should_set?(:comment)
+ opts << "-g" << new_resource.gid if should_set?(:gid)
+ 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
+ if updating_home?
+ if new_resource.manage_home
+ logger.trace("#{new_resource} managing the users home directory")
+ opts << "-m"
+ else
+ logger.trace("#{new_resource} setting home to #{new_resource.home}")
+ end
+ end
+ 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 userdel_options
+ opts = []
+ opts << "-r" if new_resource.manage_home
+ opts << "-f" if new_resource.force
+ opts
+ end
+
+ # Solaris does not support system users and has no '-r' option, solaris also
+ # lacks '-M' and defaults to no-manage-home.
+ def useradd_options
+ opts = []
+ opts << "-m" if new_resource.manage_home
+ opts
+ end
+
+ def manage_password
+ 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
+ # XXX: this was straight copypasta'd back in 2013 and I don't think we've ever evaluated using
+ # a pipe to passwd(1) or evaluating modern ruby-shadow. See https://github.com/chef/chef/pull/721
def write_shadow_file
buffer = Tempfile.new("shadow", "/etc")
- ::File.open(@password_file) do |shadow_file|
+ ::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,20 +135,20 @@ class Chef
buffer.close
# FIXME: mostly duplicates code with file provider deploying a file
- s = ::File.stat(@password_file)
- mode = s.mode & 07777
+ s = ::File.stat(PASSWORD_FILE)
+ mode = s.mode & 0o7777
uid = s.uid
gid = s.gid
FileUtils.chown uid, gid, buffer.path
FileUtils.chmod mode, buffer.path
- FileUtils.mv buffer.path, @password_file
+ FileUtils.mv buffer.path, PASSWORD_FILE
end
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
deleted file mode 100644
index 68b62812a7..0000000000
--- a/lib/chef/provider/user/useradd.rb
+++ /dev/null
@@ -1,164 +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 "pathname"
-require "chef/provider/user"
-
-class Chef
- class Provider
- class User
- class Useradd < Chef::Provider::User
- # MAJOR XXX: this should become the base class of all Useradd providers instead of the linux implementation
-
- UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
-
- def create_user
- command = compile_command("useradd") do |useradd|
- useradd.concat(universal_options)
- useradd.concat(useradd_options)
- end
- shell_out!(*command)
- end
-
- def manage_user
- unless universal_options.empty?
- command = compile_command("usermod") do |u|
- u.concat(universal_options)
- end
- shell_out!(*command)
- end
- end
-
- def remove_user
- command = [ "userdel" ]
- command << "-r" if managing_home_dir?
- command << "-f" if new_resource.force
- command << new_resource.username
- shell_out!(*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])
- 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?
-
- status_line = passwd_s.stdout.split(" ")
- case status_line[1]
- when /^P/
- @locked = false
- when /^N/
- @locked = false
- when /^L/
- @locked = true
- end
-
- 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 = passwd_version_check.stdout.chomp
-
- unless passwd_version == "passwd-0.73-1"
- raise_lock_error = true
- end
- else
- raise_lock_error = true
- end
-
- raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if raise_lock_error
- end
-
- @locked
- end
-
- def lock_user
- shell_out!("usermod", "-L", new_resource.username)
- end
-
- def unlock_user
- shell_out!("usermod", "-U", new_resource.username)
- end
-
- def compile_command(base_command)
- base_command = Array(base_command)
- yield base_command
- base_command << new_resource.username
- base_command
- end
-
- def universal_options
- @universal_options ||=
- begin
- opts = []
- # magic allows UNIVERSAL_OPTIONS to be overridden in a subclass
- self.class::UNIVERSAL_OPTIONS.each do |field, option|
- update_options(field, option, opts)
- end
- if updating_home?
- opts << "-d" << new_resource.home
- if managing_home_dir?
- Chef::Log.debug("#{new_resource} managing the users home directory")
- opts << "-m"
- else
- Chef::Log.debug("#{new_resource} setting home to #{new_resource.home}")
- end
- end
- 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
- end
-
- def useradd_options
- opts = []
- opts << "-r" if new_resource.system
- opts << "-M" unless managing_home_dir?
- opts
- end
-
- def updating_home?
- # will return false if paths are equivalent
- # 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]
- end
-
- end
- end
- end
-end
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index b086a1e32b..32b2c35264 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -1,6 +1,6 @@
#
# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Copyright:: Copyright 2010-2016, VMware, Inc.
+# Copyright:: Copyright 2010-2019, VMware, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,9 @@
# limitations under the License.
#
-require "chef/provider/user"
-require "chef/exceptions"
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/util/windows/net_user"
-end
+require_relative "../user"
+require_relative "../../exceptions"
+require_relative "../../util/windows/net_user" if Chef::Platform.windows?
class Chef
class Provider
@@ -31,31 +29,30 @@ class Chef
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::WindowsUser.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.full_name(user_info[:full_name])
+ current_resource.comment(user_info[:comment])
+ 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,13 +61,20 @@ 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")
- return true
+ @change_desc = []
+ unless @net_user.validate_credentials(new_resource.password)
+ @change_desc << "update password"
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)
+
+ %i{uid comment home shell full_name}.any? do |user_attrib|
+ new_val = new_resource.send(user_attrib)
+ cur_val = current_resource.send(user_attrib)
+ if !new_val.nil? && new_val != cur_val
+ @change_desc << "change #{user_attrib} from #{cur_val} to #{new_val}"
+ end
end
+
+ !@change_desc.empty?
end
def create_user
@@ -98,26 +102,26 @@ class Chef
end
def set_options
- opts = { :name => @new_resource.username }
+ opts = { name: new_resource.username }
field_list = {
- "comment" => "full_name",
+ "full_name" => "full_name",
+ "comment" => "comment",
"home" => "home_dir",
"uid" => "user_id",
"shell" => "script_path",
"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..e261cb4386 100644
--- a/lib/chef/provider/whyrun_safe_ruby_block.rb
+++ b/lib/chef/provider/whyrun_safe_ruby_block.rb
@@ -21,11 +21,11 @@ class Chef
class WhyrunSafeRubyBlock < Chef::Provider::RubyBlock
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")
+ action :run do
+ 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_script.rb b/lib/chef/provider/windows_script.rb
index 3b0202790c..a93319a35a 100644
--- a/lib/chef/provider/windows_script.rb
+++ b/lib/chef/provider/windows_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,59 +16,121 @@
# limitations under the License.
#
-require "chef/provider/script"
-require "chef/mixin/windows_architecture_helper"
+require_relative "script"
+require_relative "../mixin/windows_architecture_helper"
+require_relative "../win32/security" if ChefUtils.windows?
+require "tempfile" unless defined?(Tempfile)
class Chef
class Provider
class WindowsScript < Chef::Provider::Script
- attr_reader :is_forced_32bit
-
protected
- include Chef::Mixin::WindowsArchitectureHelper
-
- def initialize( new_resource, run_context, script_extension = "")
- super( new_resource, run_context )
- @script_extension = script_extension
-
- target_architecture = if new_resource.architecture.nil?
- node_windows_architecture(run_context.node)
- else
- new_resource.architecture
- end
+ attr_accessor :script_file_path
- @is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture)
+ include Chef::Mixin::WindowsArchitectureHelper
- @is_forced_32bit = forced_32bit_override_required?(run_context.node, target_architecture)
+ def target_architecture
+ @target_architecture ||= if new_resource.architecture.nil?
+ node_windows_architecture(run_context.node)
+ else
+ new_resource.architecture
+ end
end
- public
+ def basepath
+ if forced_32bit_override_required?(run_context.node, target_architecture)
+ wow64_directory
+ else
+ run_context.node["kernel"]["os_info"]["system_directory"]
+ end
+ end
- def action_run
+ def with_wow64_redirection_disabled
wow64_redirection_state = nil
- if @is_wow64
- wow64_redirection_state = disable_wow64_file_redirection(@run_context.node)
+ if wow64_architecture_override_required?(run_context.node, target_architecture)
+ wow64_redirection_state = disable_wow64_file_redirection(run_context.node)
end
begin
- super
+ yield
rescue
raise
ensure
- if ! wow64_redirection_state.nil?
- restore_wow64_file_redirection(@run_context.node, wow64_redirection_state)
+ unless wow64_redirection_state.nil?
+ restore_wow64_file_redirection(run_context.node, wow64_redirection_state)
end
end
end
- def script_file
- base_script_name = "chef-script"
- temp_file_arguments = [ base_script_name, @script_extension ]
+ def command
+ "\"#{interpreter}\" #{flags} \"#{script_file_path}\""
+ end
+
+ def grant_alternate_user_read_access(file_path)
+ # 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(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 with_temp_script_file
+ Tempfile.open(["chef-script", script_extension]) do |script_file|
+ script_file.puts(code)
+ script_file.close
+
+ grant_alternate_user_read_access(script_file.path)
+
+ # This needs to be set here so that the call to #command in Execute works.
+ self.script_file_path = script_file.path
+
+ yield
+
+ self.script_file_path = nil
+ end
+ end
+
+ def input
+ nil
+ end
+
+ public
+
+ action :run do
+ with_wow64_redirection_disabled do
+ with_temp_script_file do
+ super()
+ end
+ end
+ end
- @script_file ||= Tempfile.open(temp_file_arguments)
+ def script_extension
+ raise Chef::Exceptions::Override, "You must override #{__method__} in #{self}"
end
end
end
diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb
index 09ff2c5512..b0dfe20f1b 100644
--- a/lib/chef/provider/yum_repository.rb
+++ b/lib/chef/provider/yum_repository.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,34 +16,28 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/dsl/declare_resource"
-require "chef/mixin/shell_out"
-require "chef/mixin/which"
-require "chef/http/simple"
-require "chef/provider/noop"
+require_relative "../resource"
+require_relative "../dsl/declare_resource"
+require_relative "../mixin/which"
+require_relative "noop"
class Chef
class Provider
class YumRepository < Chef::Provider
- use_inline_resources
-
extend Chef::Mixin::Which
provides :yum_repository do
which "yum"
end
- def whyrun_supported?; true; end
-
def load_current_resource; end
action :create do
- declare_resource(:template, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+ declare_resource(:template, ::File.join(new_resource.reposdir, "#{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__)
+ source ::File.expand_path("support/yum_repo.erb", __dir__)
local true
end
sensitive new_resource.sensitive
@@ -52,7 +46,7 @@ class Chef
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[yum-cache-reload-#{new_resource.repositoryid}]", :immediately
+ notifies :create, "ruby_block[package-cache-reload-#{new_resource.repositoryid}]", :immediately
end
end
@@ -68,28 +62,37 @@ class Chef
only_if { new_resource.enabled }
end
- # reload internal Chef yum cache
- declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
- block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ # 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
- declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
- action :delete
- notifies :run, "execute[yum clean all #{new_resource.repositoryid}]", :immediately
- notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately
- end
-
+ # 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 | grep -P '^#{new_resource.repositoryid}([ \t]|$)'"
- action :nothing
+ only_if "yum repolist all | grep -P '^#{new_resource.repositoryid}([ \t]|$)'"
end
- declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
- block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ declare_resource(:file, ::File.join(new_resource.reposdir, "#{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
@@ -101,8 +104,13 @@ class Chef
only_if { new_resource.enabled }
end
- declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
- block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+ 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
diff --git a/lib/chef/provider/zypper_repository.rb b/lib/chef/provider/zypper_repository.rb
new file mode 100644
index 0000000000..53dae74948
--- /dev/null
+++ b/lib/chef/provider/zypper_repository.rb
@@ -0,0 +1,188 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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 "../resource"
+require_relative "../dsl/declare_resource"
+require_relative "noop"
+require "shellwords" unless defined?(Shellwords)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+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", __dir__)
+ 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 #{ChefUtils::Dist::Infra::PRODUCT}."
+ end
+ end
+
+ # the version of gpg installed on the system
+ #
+ # @return [Gem::Version] the version of GPG
+ def gpg_version
+ so = shell_out!("gpg --version")
+ # matches 2.0 and 2.2 versions from SLES 12 and 15: https://rubular.com/r/e6D0WfGK6SXvUp
+ version = /gpg \(GnuPG\)\s*(.*)/.match(so.stdout)[1]
+ logger.trace("GPG package version is #{version}")
+ Gem::Version.new(version)
+ 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("/bin/rpm -qa gpg-pubkey*")
+ # expected output & match: http://rubular.com/r/RdF7EcXEtb
+ status = /gpg-pubkey-#{short_key_id(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's short key id from a local file. Learning moment: This 8 hex value ID
+ # is sometimes incorrectly called the fingerprint. The fingerprint is the full length value
+ # and googling for that will just result in sad times.
+ #
+ # @param [String] key_path the path to the key on the local filesystem
+ #
+ # @return [String] the short key id of the key
+ def short_key_id(key_path)
+ if gpg_version >= Gem::Version.new("2.2") # SLES 15+
+ so = shell_out!("gpg --import-options import-show --dry-run --import --with-colons #{key_path}")
+ # expected output and match: https://rubular.com/r/uXWJo3yfkli1qA
+ short_key_id = /fpr:*\h*(\h{8}):/.match(so.stdout)[1].downcase
+ else # SLES 12 and earlier
+ so = shell_out!("gpg --with-fingerprint #{key_path}")
+ # expected output and match: http://rubular.com/r/BpfMjxySQM
+ short_key_id = %r{pub\s*\S*/(\S*)}.match(so.stdout)[1].downcase
+ end
+ logger.trace("GPG short key ID of key at #{key_path} is #{short_key_id}")
+ short_key_id
+ 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..94727a1043 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/platform/priority_map"
+require_relative "exceptions"
+require_relative "platform/priority_map"
class Chef
#
@@ -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
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 affa5ca2c1..7652d60896 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,133 +16,123 @@
# limitations under the License.
#
-require "chef/provider/apt_update"
-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"
-require "chef/provider/group"
-require "chef/provider/http_request"
-require "chef/provider/ifconfig"
-require "chef/provider/launchd"
-require "chef/provider/link"
-require "chef/provider/log"
-require "chef/provider/ohai"
-require "chef/provider/mdadm"
-require "chef/provider/mount"
-require "chef/provider/noop"
-require "chef/provider/package"
-require "chef/provider/powershell_script"
-require "chef/provider/osx_profile"
-require "chef/provider/reboot"
-require "chef/provider/remote_directory"
-require "chef/provider/remote_file"
-require "chef/provider/route"
-require "chef/provider/ruby_block"
-require "chef/provider/script"
-require "chef/provider/service"
-require "chef/provider/subversion"
-require "chef/provider/systemd_unit"
-require "chef/provider/template"
-require "chef/provider/user"
-require "chef/provider/whyrun_safe_ruby_block"
-require "chef/provider/yum_repository"
+require_relative "provider/batch"
+require_relative "provider/cookbook_file"
+require_relative "provider/cron"
+require_relative "provider/cron/solaris"
+require_relative "provider/cron/aix"
+require_relative "provider/directory"
+require_relative "provider/dsc_script"
+require_relative "provider/dsc_resource"
+require_relative "provider/execute"
+require_relative "provider/file"
+require_relative "provider/git"
+require_relative "provider/group"
+require_relative "provider/http_request"
+require_relative "provider/ifconfig"
+require_relative "provider/launchd"
+require_relative "provider/link"
+require_relative "provider/mount"
+require_relative "provider/noop"
+require_relative "provider/package"
+require_relative "provider/powershell_script"
+require_relative "provider/remote_directory"
+require_relative "provider/remote_file"
+require_relative "provider/route"
+require_relative "provider/ruby_block"
+require_relative "provider/script"
+require_relative "provider/service"
+require_relative "provider/subversion"
+require_relative "provider/systemd_unit"
+require_relative "provider/template"
+require_relative "provider/user"
+require_relative "provider/whyrun_safe_ruby_block"
+require_relative "provider/yum_repository"
+require_relative "provider/zypper_repository"
-require "chef/provider/env/windows"
+require_relative "provider/package/apt"
+require_relative "provider/package/chocolatey"
+require_relative "provider/package/dpkg"
+require_relative "provider/package/dnf"
+require_relative "provider/package/freebsd/port"
+require_relative "provider/package/freebsd/pkgng"
+require_relative "provider/package/homebrew"
+require_relative "provider/package/ips"
+require_relative "provider/package/macports"
+require_relative "provider/package/openbsd"
+require_relative "provider/package/pacman"
+require_relative "provider/package/portage"
+require_relative "provider/package/paludis"
+require_relative "provider/package/rpm"
+require_relative "provider/package/rubygems"
+require_relative "provider/package/yum"
+require_relative "provider/package/zypper"
+require_relative "provider/package/solaris"
+require_relative "provider/package/smartos"
+require_relative "provider/package/bff"
+require_relative "provider/package/cab"
+require_relative "provider/package/powershell"
+require_relative "provider/package/msu"
+require_relative "provider/package/snap"
-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/freebsd/port"
-require "chef/provider/package/freebsd/pkg"
-require "chef/provider/package/freebsd/pkgng"
-require "chef/provider/package/homebrew"
-require "chef/provider/package/ips"
-require "chef/provider/package/macports"
-require "chef/provider/package/openbsd"
-require "chef/provider/package/pacman"
-require "chef/provider/package/portage"
-require "chef/provider/package/paludis"
-require "chef/provider/package/rpm"
-require "chef/provider/package/rubygems"
-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_relative "provider/service/arch"
+require_relative "provider/service/freebsd"
+require_relative "provider/service/gentoo"
+require_relative "provider/service/init"
+require_relative "provider/service/invokercd"
+require_relative "provider/service/debian"
+require_relative "provider/service/openbsd"
+require_relative "provider/service/redhat"
+require_relative "provider/service/insserv"
+require_relative "provider/service/simple"
+require_relative "provider/service/systemd"
+require_relative "provider/service/upstart"
+require_relative "provider/service/windows"
+require_relative "provider/service/solaris"
+require_relative "provider/service/macosx"
+require_relative "provider/service/aixinit"
+require_relative "provider/service/aix"
-require "chef/provider/service/arch"
-require "chef/provider/service/freebsd"
-require "chef/provider/service/gentoo"
-require "chef/provider/service/init"
-require "chef/provider/service/invokercd"
-require "chef/provider/service/debian"
-require "chef/provider/service/openbsd"
-require "chef/provider/service/redhat"
-require "chef/provider/service/insserv"
-require "chef/provider/service/simple"
-require "chef/provider/service/systemd"
-require "chef/provider/service/upstart"
-require "chef/provider/service/windows"
-require "chef/provider/service/solaris"
-require "chef/provider/service/macosx"
-require "chef/provider/service/aixinit"
-require "chef/provider/service/aix"
+require_relative "provider/user/aix"
+require_relative "provider/user/dscl"
+require_relative "provider/user/linux"
+require_relative "provider/user/mac"
+require_relative "provider/user/pw"
+require_relative "provider/user/solaris"
+require_relative "provider/user/windows"
-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_relative "provider/group/aix"
+require_relative "provider/group/dscl"
+require_relative "provider/group/gpasswd"
+require_relative "provider/group/groupadd"
+require_relative "provider/group/groupmod"
+require_relative "provider/group/pw"
+require_relative "provider/group/solaris"
+require_relative "provider/group/suse"
+require_relative "provider/group/usermod"
+require_relative "provider/group/windows"
-require "chef/provider/group/aix"
-require "chef/provider/group/dscl"
-require "chef/provider/group/gpasswd"
-require "chef/provider/group/groupadd"
-require "chef/provider/group/groupmod"
-require "chef/provider/group/pw"
-require "chef/provider/group/suse"
-require "chef/provider/group/usermod"
-require "chef/provider/group/windows"
+require_relative "provider/mount/mount"
+require_relative "provider/mount/aix"
+require_relative "provider/mount/solaris"
+require_relative "provider/mount/windows"
+require_relative "provider/mount/linux"
-require "chef/provider/mount/mount"
-require "chef/provider/mount/aix"
-require "chef/provider/mount/solaris"
-require "chef/provider/mount/windows"
+require_relative "provider/remote_file/ftp"
+require_relative "provider/remote_file/sftp"
+require_relative "provider/remote_file/http"
+require_relative "provider/remote_file/local_file"
+require_relative "provider/remote_file/network_file"
+require_relative "provider/remote_file/fetcher"
-require "chef/provider/deploy/revision"
-require "chef/provider/deploy/timestamped"
+require_relative "provider/lwrp_base"
+require_relative "provider/registry_key"
-require "chef/provider/remote_file/ftp"
-require "chef/provider/remote_file/sftp"
-require "chef/provider/remote_file/http"
-require "chef/provider/remote_file/local_file"
-require "chef/provider/remote_file/network_file"
-require "chef/provider/remote_file/fetcher"
+require_relative "provider/file/content"
+require_relative "provider/remote_file/content"
+require_relative "provider/cookbook_file/content"
+require_relative "provider/template/content"
-require "chef/provider/lwrp_base"
-require "chef/provider/registry_key"
-
-require "chef/provider/file/content"
-require "chef/provider/remote_file/content"
-require "chef/provider/cookbook_file/content"
-require "chef/provider/template/content"
-
-require "chef/provider/ifconfig/redhat"
-require "chef/provider/ifconfig/debian"
-require "chef/provider/ifconfig/aix"
+require_relative "provider/ifconfig/redhat"
+require_relative "provider/ifconfig/debian"
+require_relative "provider/ifconfig/aix"
diff --git a/lib/chef/pwsh.rb b/lib/chef/pwsh.rb
new file mode 100644
index 0000000000..3d067eb0d6
--- /dev/null
+++ b/lib/chef/pwsh.rb
@@ -0,0 +1,71 @@
+#
+# Author:: Matt Wrock (<mwrock@chef.io>)
+# Copyright:: Copyright (c) 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 Pwsh < Chef::PowerShell
+
+ # Run a command under pwsh (powershell core) via FFI
+ # This implementation requires the managed dll, native wrapper and a
+ # published, self contained dotnet core directory tree to exist in the
+ # bindir directory.
+ #
+ # @param script [String] script to run
+ # @return [Object] output
+ def initialize(script)
+ @dll = Pwsh.dll
+ super
+ end
+
+ protected
+
+ def exec(script)
+ # Note that we need to override the location of the shared dotnet core library
+ # location. With most .net core applications, you can simply publish them as a
+ # "self-contained" application allowing consumers of the application to run them
+ # and use its own stand alone version of the .net core runtime. However because
+ # this is simply a dll and not an exe, it will look for the runtime in the shared
+ # .net core installation folder. By setting DOTNET_MULTILEVEL_LOOKUP to 0 we can
+ # override that folder's location with DOTNET_ROOT. To avoid the possibility of
+ # interfering with other .net core processes that might rely on the common shared
+ # location, we revert these variables after the script completes.
+ original_dml = ENV["DOTNET_MULTILEVEL_LOOKUP"]
+ original_dotnet_root = ENV["DOTNET_ROOT"]
+ original_dotnet_root_x86 = ENV["DOTNET_ROOT(x86)"]
+
+ ENV["DOTNET_MULTILEVEL_LOOKUP"] = "0"
+ ENV["DOTNET_ROOT"] = RbConfig::CONFIG["bindir"]
+ ENV["DOTNET_ROOT(x86)"] = RbConfig::CONFIG["bindir"]
+
+ super
+ ensure
+ ENV["DOTNET_MULTILEVEL_LOOKUP"] = original_dml
+ ENV["DOTNET_ROOT"] = original_dotnet_root
+ ENV["DOTNET_ROOT(x86)"] = original_dotnet_root_x86
+ end
+
+ def self.dll
+ # This Powershell DLL source lives here: https://github.com/chef/chef-powershell-shim
+ # Every merge into that repo triggers a Habitat build and promotion. Running
+ # the rake :update_chef_exec_dll task in this (chef/chef) repo will pull down
+ # the built packages and copy the binaries to distro/ruby_bin_folder. Bundle install
+ # ensures that the correct architecture binaries are installed into the path.
+ # Also note that the version of pwsh is determined by which assemblies the dll was
+ # built with. To update powershell, those dependencies must be bumped.
+ @dll ||= Dir.glob("#{RbConfig::CONFIG["bindir"]}/**/Chef.PowerShell.Wrapper.Core.dll").last
+ end
+ end
+end
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index 77d82f83ab..972edf9649 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,10 @@
# limitations under the License.
#
-require "chef/dsl/recipe"
-require "chef/mixin/from_file"
-require "chef/mixin/deprecation"
+autoload :YAML, "yaml"
+require_relative "dsl/recipe"
+require_relative "mixin/from_file"
+require_relative "mixin/deprecation"
class Chef
# == Chef::Recipe
@@ -47,6 +48,7 @@ class Chef
[ $1.to_sym, $2 ]
when /^::(.+)/
raise "current_cookbook is nil, cannot resolve #{recipe_name}" if current_cookbook.nil?
+
[ current_cookbook.to_sym, $1 ]
else
[ recipe_name.to_sym, "default" ]
@@ -58,7 +60,7 @@ class Chef
@recipe_name = recipe_name
@run_context = run_context
# TODO: 5/19/2010 cw/tim: determine whether this can be removed
- @params = Hash.new
+ @params = {}
end
# Used in DSL mixins
@@ -66,32 +68,11 @@ 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)
end
- # Returns true if the node is tagged with *all* of the supplied +tags+.
- #
- # === Parameters
- # tags<Array>:: A list of tags
- #
- # === Returns
- # true<TrueClass>:: If all the parameters are present
- # false<FalseClass>:: If any of the parameters are missing
- def tagged?(*tags)
- tags.each do |tag|
- return false unless run_context.node.tags.include?(tag)
- end
- true
- end
-
# Removes the list of tags from the node.
#
# === Parameters
@@ -104,5 +85,48 @@ class Chef
run_context.node.tags.delete(tag)
end
end
+
+ def from_yaml_file(filename)
+ self.source_file = filename
+ if File.file?(filename) && File.readable?(filename)
+ yaml_contents = IO.read(filename)
+ if ::YAML.load_stream(yaml_contents).length > 1
+ raise ArgumentError, "YAML recipe '#{filename}' contains multiple documents, only one is supported"
+ end
+
+ from_yaml(yaml_contents)
+ else
+ raise IOError, "Cannot open or read file '#{filename}'!"
+ end
+ end
+
+ def from_yaml(string)
+ res = ::YAML.safe_load(string)
+ unless res.is_a?(Hash) && res.key?("resources")
+ raise ArgumentError, "YAML recipe '#{source_file}' must contain a top-level 'resources' hash (YAML sequence), i.e. 'resources:'"
+ end
+
+ from_hash(res)
+ end
+
+ def from_hash(hash)
+ hash["resources"].each do |rhash|
+ type = rhash.delete("type").to_sym
+ name = rhash.delete("name")
+ res = declare_resource(type, name)
+ rhash.each do |key, value|
+ # FIXME?: we probably need a way to instance_exec a string that contains block code against the property?
+ res.send(key, value)
+ end
+ end
+ end
+
+ def to_s
+ "cookbook: #{cookbook_name || "(none)"}, recipe: #{recipe_name || "(none)"} "
+ end
+
+ def inspect
+ to_s
+ end
end
end
diff --git a/lib/chef/request_id.rb b/lib/chef/request_id.rb
index 33e54d9edc..9aad3532a5 100644
--- a/lib/chef/request_id.rb
+++ b/lib/chef/request_id.rb
@@ -1,5 +1,5 @@
# Author:: Prajakta Purohit (<prajakta@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +15,8 @@
# limitations under the License.
#
-require "securerandom"
-require "singleton"
+require "securerandom" unless defined?(SecureRandom)
+require "singleton" unless defined?(Singleton)
class Chef
class RequestID
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index d11fa1c80c..e572f0667d 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -18,30 +18,30 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/dsl/data_query"
-require "chef/dsl/registry_helper"
-require "chef/dsl/reboot_pending"
-require "chef/dsl/resources"
-require "chef/mixin/convert_to_class_name"
-require "chef/guard_interpreter/resource_guard_interpreter"
-require "chef/resource/conditional"
-require "chef/resource/conditional_action_not_nothing"
-require "chef/resource/action_class"
-require "chef/resource_collection"
-require "chef/node_map"
-require "chef/node"
-require "chef/platform"
-require "chef/resource/resource_notification"
-require "chef/provider_resolver"
-require "chef/resource_resolver"
-require "chef/provider"
-require "set"
-
-require "chef/mixin/deprecation"
-require "chef/mixin/properties"
-require "chef/mixin/provides"
-require "chef/dsl/universal"
+require_relative "exceptions"
+require_relative "dsl/reboot_pending"
+require_relative "dsl/resources"
+require_relative "dsl/declare_resource"
+require_relative "json_compat"
+require_relative "mixin/convert_to_class_name"
+require_relative "guard_interpreter/resource_guard_interpreter"
+require_relative "resource/conditional"
+require_relative "resource/conditional_action_not_nothing"
+require_relative "resource/action_class"
+require_relative "resource_collection"
+require_relative "node_map"
+require_relative "node"
+require_relative "platform"
+require_relative "resource/resource_notification"
+require_relative "provider_resolver"
+require_relative "resource_resolver"
+require_relative "provider"
+autoload :Set, "set"
+
+require_relative "mixin/deprecation"
+require_relative "mixin/properties"
+require_relative "mixin/provides"
+require_relative "dsl/universal"
class Chef
class Resource
@@ -50,12 +50,12 @@ class Chef
# Generic User DSL (not resource-specific)
#
- include Chef::DSL::DataQuery
- include Chef::DSL::RegistryHelper
+ include Chef::DSL::DeclareResource
include Chef::DSL::RebootPending
extend Chef::Mixin::Provides
include Chef::DSL::Universal
+ extend Chef::DSL::Universal
# Bring in `property` and `property_type`
include Chef::Mixin::Properties
@@ -83,7 +83,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.
@@ -97,26 +97,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)
#
@@ -130,21 +110,25 @@ 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
+ @params = {}
@provider = nil
@allowed_actions = self.class.allowed_actions.to_a
@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
+ @skip_docs = 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
@@ -153,7 +137,7 @@ class Chef
@guard_interpreter = nil
@default_guard_interpreter = :default
@elapsed_time = 0
- @sensitive = false
+ @executed_by_runner = false
end
#
@@ -177,10 +161,29 @@ class Chef
end
end
- # Alias for normal assigment syntax.
+ # Alias for normal assignment syntax.
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, run_context.unified_mode))
+ 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.
#
@@ -408,7 +411,6 @@ class Chef
@not_if
end
- #
# The number of times to retry this resource if it fails by throwing an
# exception while running an action. Default: 0
#
@@ -418,40 +420,44 @@ 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.
#
# @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
# data will be displayed in log output.
#
# @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
+ property :sensitive, [ TrueClass, FalseClass ], default: false, desired_state: false
- # ??? TODO unreferenced. Delete?
- attr_reader :not_if_args
- # ??? TODO unreferenced. Delete?
- attr_reader :only_if_args
+ # If this is set the resource will be set to run at compile time and the converge time
+ # action will be set to :nothing.
+ #
+ # @param arg [Boolean] Whether or not to force this resource to run at compile time.
+ # @return [Boolean] Whether or not to force this resource to run at compile time.
+ #
+ property :compile_time, [TrueClass, FalseClass],
+ description: "Determines whether or not the resource is executed during the compile time phase.",
+ default: false, desired_state: false
+ # Set a umask to be used for the duration of converging the resource.
+ # Defaults to `nil`, which means to use the system umask.
#
+ # @param arg [String] The umask to apply while converging the resource.
+ # @return [Boolean] The umask to apply while converging the resource.
+ #
+ property :umask, String,
+ desired_state: false,
+ introduced: "16.2",
+ description: "Set a umask to be used for the duration of converging the resource. Defaults to `nil`, which means to use the system umask. Unsupported on Windows because Windows lacks a direct equivalent to UNIX's umask."
+
# The time it took (in seconds) to run the most recently-run action. Not
# cumulative across actions. This is set to 0 as soon as a new action starts
# running, and set to the elapsed time at the end of the action.
@@ -461,7 +467,10 @@ class Chef
#
attr_reader :elapsed_time
+ # @return [Boolean] If the resource was executed by the runner
#
+ attr_accessor :executed_by_runner
+
# The guard interpreter that will be used to process `only_if` and `not_if`
# statements. If left unset, the #default_guard_interpreter will be used.
#
@@ -479,7 +488,7 @@ class Chef
if arg.nil?
@guard_interpreter || @default_guard_interpreter
else
- set_or_return(:guard_interpreter, arg, :kind_of => Symbol)
+ set_or_return(:guard_interpreter, arg, kind_of: Symbol)
end
end
@@ -496,7 +505,7 @@ class Chef
state = {}
state_properties = self.class.state_properties
state_properties.each do |property|
- if property.identity? || property.is_set?(self)
+ if property.is_set?(self)
state[property.name] = property.sensitive? ? "*sensitive value suppressed*" : send(property.name)
end
end
@@ -504,15 +513,6 @@ class Chef
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.
@@ -528,29 +528,23 @@ class Chef
result[property.name] = send(property.name)
end
return result.values.first if identity_properties.size == 1
+
result
end
#
# 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.
@@ -560,7 +554,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
@@ -584,9 +578,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
@@ -600,15 +594,18 @@ class Chef
begin
return if should_skip?(action)
- provider_for_action(action).run_action
- rescue Exception => e
+
+ with_umask do
+ provider_for_action(action).run_action
+ end
+ 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
@@ -624,12 +621,20 @@ class Chef
events.resource_completed(self)
end
+ def with_umask
+ old_value = ::File.umask(umask.oct) if umask
+ yield
+ ensure
+ ::File.umask(old_value) if umask
+ end
+
#
# If we are currently initializing the resource, this will be true.
#
# Do NOT use this. It may be removed. It is for internal purposes only.
# @api private
attr_reader :resource_initializing
+
def resource_initializing=(value)
if value
@resource_initializing = true
@@ -648,23 +653,41 @@ 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|
+
+ 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
+
+ ivars = instance_variables.map(&: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.key?(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 = instance_variables.map(&:to_sym) - FORBIDDEN_IVARS
ivars.inject("<#{self}") do |str, ivar|
str << " #{ivar}: #{instance_variable_get(ivar).inspect}"
end << ">"
@@ -674,8 +697,8 @@ class Chef
# is loaded. activesupport will call as_json and skip over to_json. This ensure
# json is encoded as expected
def as_json(*a)
- safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
- instance_vars = Hash.new
+ safe_ivars = instance_variables.map(&:to_sym) - FORBIDDEN_IVARS
+ instance_vars = {}
safe_ivars.each do |iv|
instance_vars[iv.to_s.sub(/^@/, "")] = instance_variable_get(iv)
end
@@ -691,29 +714,40 @@ class Chef
Chef::JSONCompat.to_json(results, *a)
end
- def to_hash
+ def to_h
# Grab all current state, then any other ivars (backcompat)
result = {}
self.class.state_properties.each do |p|
result[p.name] = p.get(self)
end
- safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
+ safe_ivars = instance_variables.map(&:to_sym) - FORBIDDEN_IVARS
safe_ivars.each do |iv|
key = iv.to_s.sub(/^@/, "").to_sym
- next if result.has_key?(key)
+ next if result.key?(key)
+
result[key] = instance_variable_get(iv)
end
result
end
- def self.json_create(o)
- resource = self.new(o["instance_vars"]["@name"])
+ alias_method :to_hash, :to_h
+
+ 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)
#
@@ -738,13 +772,12 @@ class Chef
# @see Chef::Resource.action_class
#
def provider(arg = nil)
- klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
+ klass = if arg.is_a?(String) || arg.is_a?(Symbol)
lookup_provider_constant(arg)
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)
@@ -772,7 +805,7 @@ class Chef
# @return [Array<Symbol>] All property names with desired state.
#
def self.state_attrs(*names)
- state_properties(*names).map { |property| property.name }
+ state_properties(*names).map(&:name)
end
#
@@ -802,8 +835,9 @@ class Chef
def self.identity_property(name = nil)
result = identity_properties(*Array(name))
if result.size > 1
- raise Chef::Exceptions::MultipleIdentityError, "identity_property cannot be called on an object with more than one identity property (#{result.map { |r| r.name }.join(", ")})."
+ raise Chef::Exceptions::MultipleIdentityError, "identity_property cannot be called on an object with more than one identity property (#{result.map(&:name).join(", ")})."
end
+
result.first
end
@@ -823,7 +857,8 @@ class Chef
#
def self.identity_attr(name = nil)
property = identity_property(name)
- return nil if !property
+ return nil unless property
+
property.name
end
@@ -849,7 +884,8 @@ 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
@@ -901,19 +937,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]
@@ -925,30 +948,7 @@ 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
+ # A hook called after a resource is created. Meant to be overridden by
# subclasses.
#
def after_created
@@ -956,30 +956,9 @@ 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
- # name.
- #
- # resource_name defaults to your class name.
- #
- # Call `resource_name nil` to remove the resource name (and any
- # corresponding DSL).
+ # Call `resource_name nil` to remove the resource name
#
# @param value [Symbol] The desired name of this resource type (e.g.
# `execute`), or `nil` if this class is abstract and has no resource_name.
@@ -989,20 +968,10 @@ class Chef
def self.resource_name(name = NOT_PASSED)
# Setter
if name != NOT_PASSED
- remove_canonical_dsl
-
- # Set the resource_name and call provides
- if name
- name = name.to_sym
- # If our class is not already providing this name, provide it.
- if !Chef::ResourceResolver.includes_handler?(name, self)
- provides name, canonical: true
- end
- @resource_name = name
- else
- @resource_name = nil
- end
+ @resource_name = name.to_sym rescue nil
end
+
+ @resource_name = nil unless defined?(@resource_name)
@resource_name
end
@@ -1010,40 +979,14 @@ class Chef
resource_name(name)
end
+ # If the resource's action should run in separated compile/converge mode.
#
- # Use the class name as the resource name.
- #
- # Munges the last part of the class name from camel case to snake case,
- # and sets the resource_name to that:
- #
- # A::B::BlahDBlah -> blah_d_blah
- #
- def self.use_automatic_resource_name
- automatic_name = convert_to_snake_case(self.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
+ # @param flag [Boolean] value to set unified_mode to
+ # @return [Boolean] unified_mode value
+ def self.unified_mode(flag = nil)
+ @unified_mode = Chef::Config[:resource_unified_mode_default] if !defined?(@unified_mode) || @unified_mode.nil?
+ @unified_mode = flag unless flag.nil?
+ !!@unified_mode
end
#
@@ -1070,7 +1013,7 @@ class Chef
#
# The action that will be run if no other action is specified.
#
- # Setting default_action will automatially add the action to
+ # Setting default_action will automatically add the action to
# allowed_actions, if it isn't already there.
#
# Defaults to [:nothing].
@@ -1086,7 +1029,7 @@ class Chef
self.allowed_actions |= @default_action
end
- if @default_action
+ if defined?(@default_action) && @default_action
@default_action
elsif superclass.respond_to?(:default_action)
superclass.default_action
@@ -1099,7 +1042,6 @@ class Chef
default_action action_name
end
- #
# Define an action on this resource.
#
# The action is defined as a *recipe* block that will be compiled and then
@@ -1137,7 +1079,6 @@ class Chef
default_action action if Array(default_action) == [:nothing]
end
- #
# Define a method to load up this resource's properties with the current
# actual values.
#
@@ -1148,7 +1089,6 @@ class Chef
define_method(:load_current_value!, &load_block)
end
- #
# Call this in `load_current_value` to indicate that the value does not
# exist and that `current_resource` should therefore be `nil`.
#
@@ -1158,7 +1098,6 @@ class Chef
raise Chef::Exceptions::CurrentValueDoesNotExist
end
- #
# Get the current actual value of this resource.
#
# This does not cache--a new value will be returned each time.
@@ -1171,66 +1110,95 @@ class Chef
if provider.whyrun_mode? && !provider.whyrun_supported?
raise "Cannot retrieve #{self.class.current_resource} in why-run mode: #{provider} does not support why-run"
end
+
provider.load_current_resource
provider.current_resource
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 indicates 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 whatever.
#
# 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
+
+ # Set or return if this resource is in preview mode.
+ #
+ # This only has value in the resource_inspector to mark a resource as being new-to-chef-core.
+ # Its meaning is probably more equivalent to "experimental" in that the API might change even
+ # in minor versions due to bugfixing and is NOT considered "stable" yet.
+ #
+ # @param value [nil, Boolean] If nil, get the current value. If not nil, set
+ # the value of the flag.
+ # @return [Boolean]
+ def self.preview_resource(value = nil)
+ @preview_resource = false unless defined?(@preview_resource)
+ @preview_resource = value unless value.nil?
+ @preview_resource
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 do not show up when the resource is converted to JSON (ie. hidden from data_collector and sending to the chef server via #to_json/to_h/as_json/inspect)
+ FORBIDDEN_IVARS = %i{@run_context @logger @not_if @only_if @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner}.freeze
+ # HIDDEN_IVARS do not show up when the resource is displayed to the user as text (ie. in the error inspector output via #to_text)
+ HIDDEN_IVARS = %i{@allowed_actions @resource_name @source_line @run_context @logger @name @not_if @only_if @elapsed_time @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner}.freeze
include Chef::Mixin::ConvertToClassName
extend Chef::Mixin::ConvertToClassName
@@ -1242,18 +1210,27 @@ class Chef
# @return [Chef::RunContext] The run context for this Resource. This is
# where the context for the current Chef run is stored, including the node
# 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
# @return [String] The recipe this resource was declared in.
+ #
attr_accessor :recipe_name
# @return [Chef::Provider] The provider this resource was declared in (if
# it was declared in an LWRP). When you call methods that do not exist
# on this Resource, Chef will try to call the method on the provider
# as well before giving up.
+ #
attr_accessor :enclosing_provider
# @return [String] The source line where this resource was declared.
@@ -1261,6 +1238,7 @@ class Chef
# of these formats:
# /some/path/to/file.rb:80:in `wombat_tears'
# C:/some/path/to/file.rb:80 in 1`wombat_tears'
+ #
attr_accessor :source_line
# @return [String] The actual name that was used to create this resource.
@@ -1269,37 +1247,40 @@ class Chef
# user will expect to see the thing they wrote, not the type that was
# returned. May be `nil`, in which case callers should read #resource_name.
# See #declared_key.
+ #
attr_accessor :declared_type
- #
# Iterates over all immediate and delayed notifications, calling
# resolve_resource_reference on each in turn, causing them to
# resolve lazy/forward references.
- def resolve_notification_references
+ #
+ def resolve_notification_references(always_raise = false)
run_context.before_notifications(self).each do |n|
- n.resolve_resource_reference(run_context.resource_collection)
+ n.resolve_resource_reference(run_context.resource_collection, true)
end
+
run_context.immediate_notifications(self).each do |n|
- n.resolve_resource_reference(run_context.resource_collection)
+ n.resolve_resource_reference(run_context.resource_collection, always_raise)
end
+
run_context.delayed_notifications(self).each do |n|
- n.resolve_resource_reference(run_context.resource_collection)
+ n.resolve_resource_reference(run_context.resource_collection, always_raise)
end
end
# Helper for #notifies
def notifies_before(action, resource_spec)
- run_context.notifies_before(Notification.new(resource_spec, action, self))
+ run_context.notifies_before(Notification.new(resource_spec, action, self, run_context.unified_mode))
end
# Helper for #notifies
def notifies_immediately(action, resource_spec)
- run_context.notifies_immediately(Notification.new(resource_spec, action, self))
+ run_context.notifies_immediately(Notification.new(resource_spec, action, self, run_context.unified_mode))
end
# Helper for #notifies
def notifies_delayed(action, resource_spec)
- run_context.notifies_delayed(Notification.new(resource_spec, action, self))
+ run_context.notifies_delayed(Notification.new(resource_spec, action, self, run_context.unified_mode))
end
class << self
@@ -1323,18 +1304,12 @@ class Chef
# life as well.
@@sorted_descendants = nil
def self.sorted_descendants
- @@sorted_descendants ||= descendants.sort_by { |x| x.to_s }
+ @@sorted_descendants ||= descendants.sort_by(&:to_s)
end
def self.inherited(child)
super
@@sorted_descendants = nil
- # set resource_name automatically if it's not set
- if child.name && !child.resource_name
- if child.name =~ /^Chef::Resource::(\w+)$/
- child.resource_name(convert_to_snake_case($1))
- end
- end
end
# If an unknown method is invoked, determine whether the enclosing Provider's
@@ -1348,7 +1323,25 @@ class Chef
end
end
+ # This API can be used for backcompat to do:
+ #
+ # chef_version_for_provides "< 14.0" if defined?(:chef_version_for_provides)
+ #
+ # For core chef versions that do not support chef_version: in provides lines.
+ #
+ # Since resource_name calls provides the generally correct way of doing this is
+ # to do `chef_version_for_provides` first, then `resource_name` and then
+ # any additional options `provides` lines.
+ #
+ # Once we no longer care about supporting chef < 14.4 then we can deprecate
+ # this API.
#
+ # @param arg [String] version constraint to match against (e.g. "> 14")
+ #
+ def self.chef_version_for_provides(constraint)
+ @chef_version_for_provides = constraint
+ end
+
# Mark this resource as providing particular DSL.
#
# Resources have an automatic DSL based on their resource_name, equivalent to
@@ -1359,13 +1352,17 @@ class Chef
def self.provides(name, **options, &block)
name = name.to_sym
- # `provides :resource_name, os: 'linux'`) needs to remove the old
- # canonical DSL before adding the new one.
- if @resource_name && name == @resource_name
- remove_canonical_dsl
+ # quell warnings
+ @chef_version_for_provides = nil unless defined?(@chef_version_for_provides)
+
+ # deliberately do not go through the accessor here
+ @resource_name = name if resource_name.nil?
+
+ if @chef_version_for_provides && !options.include?(:chef_version)
+ options[:chef_version] = @chef_version_for_provides
end
- result = Chef.resource_handler_map.set(name, self, options, &block)
+ result = Chef.resource_handler_map.set(name, self, **options, &block)
Chef::DSL::Resources.add_resource_dsl(name)
result
end
@@ -1384,6 +1381,7 @@ class Chef
# the declared key we want to fall back on the old to_s key.
def declared_key
return to_s if declared_type.nil?
+
"#{declared_type}[#{@name}]"
end
@@ -1429,7 +1427,63 @@ 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
+
+ def self.skip_docs(skip_docs = "NOT_PASSED")
+ if skip_docs != "NOT_PASSED"
+ @skip_docs = skip_docs
+ end
+ @skip_docs
+ end
+
+ def self.default_description(default_description = "NOT_PASSED")
+ if default_description != "NOT_PASSED"
+ @default_description = default_description
+ end
+ @default_description
+ end
+
+ # Use a partial code fragment. This can be used for code sharing between multiple resources.
+ #
+ # Do not wrap the code fragment in a class or module. It also does not support the use of super
+ # to subclass any methods defined in the fragment, the methods will just be overwritten.
+ #
+ # @param partial [String] the code fragment to eval against the class
#
+ def self.use(partial)
+ dirname = ::File.dirname(partial)
+ basename = ::File.basename(partial, ".rb")
+ basename = basename[1..] if basename.start_with?("_")
+ class_eval IO.read(::File.expand_path("#{dirname}/_#{basename}.rb", ::File.dirname(caller_locations.first.absolute_path)))
+ end
+
# The cookbook in which this Resource was defined (if any).
#
# @return Chef::CookbookVersion The cookbook in which this Resource was defined.
@@ -1455,25 +1509,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.
#
# @param e [StandardError] An exception with `e.message`
@@ -1508,7 +1543,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
@@ -1525,12 +1560,19 @@ class Chef
def self.resource_for_node(short_name, node)
klass = Chef::ResourceResolver.resolve(short_name, node: node)
raise Chef::Exceptions::NoSuchResourceType.new(short_name, node) if klass.nil?
+
klass
end
- #
# 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)
#
@@ -1538,54 +1580,32 @@ class Chef
# <Chef::Resource>:: returns the proper Chef::Resource class
#
def self.resource_matching_short_name(short_name)
- Chef::ResourceResolver.resolve(short_name, canonical: true)
+ Chef::ResourceResolver.resolve(short_name)
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
- module DeprecatedLWRPClass
-
- # @api private
- def 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)
- Chef::Resource.deprecated_constants[class_name.to_sym] = resource_class
- end
- end
-
- def deprecated_constants
- raise "Deprecated constants should be called only on Chef::Resource" unless self == Chef::Resource
- @deprecated_constants ||= {}
- end
- end
-
- def self.remove_canonical_dsl
- if @resource_name
- remaining = Chef.resource_handler_map.delete_canonical(@resource_name, self)
- if !remaining
- Chef::DSL::Resources.remove_resource_dsl(@resource_name)
- end
- end
+ # This is used to suppress the "(up to date)" message in the doc formatter
+ # for the log resource (where it is nonsensical).
+ #
+ # This is not exactly a private API, but its doubtful there exist many other sane
+ # use cases for this.
+ #
+ def suppress_up_to_date_messages?
+ false
end
- extend DeprecatedLWRPClass
end
end
# Requiring things at the bottom breaks cycles
-require "chef/chef_class"
+require_relative "chef_class"
diff --git a/lib/chef/resource/action_class.rb b/lib/chef/resource/action_class.rb
index 98b4d87ef1..a1b0c4e73e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,78 +16,76 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/dsl/recipe"
+require_relative "../provider"
+require_relative "../exceptions"
+require_relative "../dsl/recipe"
class Chef
class Resource
- module ActionClass
+ class ActionClass < Chef::Provider
include Chef::DSL::Recipe
def to_s
"#{new_resource || "<no resource>"} action #{action ? action.inspect : "<no action>"}"
end
- def whyrun_supported?
- true
- end
-
- #
- # If load_current_value! is defined on the resource, use that.
- #
- def load_current_resource
+ def return_load_current_value
+ resource = nil
if new_resource.respond_to?(:load_current_value!)
- # dup the resource and then reset desired-state properties.
- current_resource = new_resource.dup
+ resource = new_resource.class.new(new_resource.name, new_resource.run_context)
- # 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|
- if property.desired_state? && !property.identity? && !property.name_property?
- property.reset(current_resource)
+ # copy the non-desired state, the identity properties and name property to the new resource
+ # (the desired state values must be loaded by load_current_value)
+ resource.class.properties.each_value do |property|
+ if !property.desired_state? || property.identity? || property.name_property?
+ property.set(resource, new_resource.send(property.name)) if new_resource.class.properties[property.name].is_set?(new_resource)
end
end
- # Call the actual load_current_value! method. If it raises
- # CurrentValueDoesNotExist, set current_resource to `nil`.
+ # we support optionally passing the new_resource as an arg to load_current_value and
+ # load_current_value can raise in order to clear the current_resource to nil
begin
- # If the user specifies load_current_value do |desired_resource|, we
- # pass in the desired resource as well as the current one.
- if current_resource.method(:load_current_value!).arity > 0
- current_resource.load_current_value!(new_resource)
+ if resource.method(:load_current_value!).arity > 0
+ resource.load_current_value!(new_resource)
else
- current_resource.load_current_value!
+ resource.load_current_value!
end
rescue Chef::Exceptions::CurrentValueDoesNotExist
- current_resource = nil
+ resource = nil
end
end
+ resource
+ end
- @current_resource = current_resource
+ # build the before state (current_resource)
+ def load_current_resource
+ @current_resource = return_load_current_value
end
- def self.included(other)
- other.extend(ClassMethods)
- other.use_inline_resources
- other.include_resource_dsl true
+ # build the after state (after_resource)
+ def load_after_resource
+ @after_resource = return_load_current_value
end
- module ClassMethods
+ def self.include_resource_dsl?
+ true
+ end
+
+ 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/alternatives.rb b/lib/chef/resource/alternatives.rb
new file mode 100644
index 0000000000..fe5af6b7b6
--- /dev/null
+++ b/lib/chef/resource/alternatives.rb
@@ -0,0 +1,210 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2016-2020, Virender Khatri
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+
+class Chef
+ class Resource
+ class Alternatives < Chef::Resource
+ unified_mode true
+
+ provides(:alternatives) { true }
+
+ description "The alternatives resource allows for configuration of command alternatives in Linux using the alternatives or update-alternatives packages."
+ introduced "16.0"
+ examples <<~DOC
+ **Install an alternative**:
+
+ ```ruby
+ alternatives 'python install 2' do
+ link_name 'python'
+ path '/usr/bin/python2.7'
+ priority 100
+ action :install
+ end
+ ```
+
+ **Set an alternative**:
+
+ ```ruby
+ alternatives 'python set version 3' do
+ link_name 'python'
+ path '/usr/bin/python3'
+ action :set
+ end
+ ```
+
+ **Set the automatic alternative state**:
+
+ ```ruby
+ alternatives 'python auto' do
+ link_name 'python'
+ action :auto
+ end
+ ```
+
+ **Refresh an alternative**:
+
+ ```ruby
+ alternatives 'python refresh' do
+ link_name 'python'
+ action :refresh
+ end
+ ```
+
+ **Remove an alternative**:
+
+ ```ruby
+ alternatives 'python remove' do
+ link_name 'python'
+ path '/usr/bin/python3'
+ action :remove
+ end
+ ```
+ DOC
+
+ property :link_name, String,
+ name_property: true,
+ description: "The name of the link to create. This will be the command you type on the command line such as `ruby` or `gcc`."
+
+ property :link, String,
+ default: lazy { |n| "/usr/bin/#{n.link_name}" },
+ default_description: "/usr/bin/LINK_NAME",
+ description: "The path to the alternatives link."
+
+ property :path, String,
+ description: "The absolute path to the original application binary such as `/usr/bin/ruby27`."
+
+ property :priority, [String, Integer],
+ coerce: proc { |n| n.to_i },
+ description: "The priority of the alternative."
+
+ def define_resource_requirements
+ requirements.assert(:install) do |a|
+ a.assertion do
+ !new_resource.priority.nil?
+ end
+
+ a.failure_message("Could not set alternatives for #{new_resource.link_name}, you must provide the :priority property")
+ end
+
+ requirements.assert(:install, :set, :remove) do |a|
+ a.assertion do
+ !new_resource.path.nil?
+ end
+
+ a.failure_message("Could not set alternatives for #{new_resource.link_name}, you must provide the :path property")
+ end
+
+ requirements.assert(:install, :set, :remove) do |a|
+ a.assertion do
+ ::File.exist?(new_resource.path)
+ end
+
+ a.whyrun("Assuming file #{new_resource.path} already exists or was created already")
+ a.failure_message("Could not set alternatives for #{new_resource.link_name}, missing #{new_resource.path}")
+ end
+ end
+
+ action :install do
+ if path_priority != new_resource.priority
+ converge_by("adding alternative #{new_resource.link} #{new_resource.link_name} #{new_resource.path} #{new_resource.priority}") do
+ output = shell_out(alternatives_cmd, "--install", new_resource.link, new_resource.link_name, new_resource.path, new_resource.priority)
+ unless output.exitstatus == 0
+ raise "failed to add alternative #{new_resource.link} #{new_resource.link_name} #{new_resource.path} #{new_resource.priority}"
+ end
+ end
+ end
+ end
+
+ action :set do
+ if current_path != new_resource.path
+ converge_by("setting alternative #{new_resource.link_name} #{new_resource.path}") do
+ output = shell_out(alternatives_cmd, "--set", new_resource.link_name, new_resource.path)
+ unless output.exitstatus == 0
+ raise "failed to set alternative #{new_resource.link_name} #{new_resource.path} \n #{output.stdout.strip}"
+ end
+ end
+ end
+ end
+
+ action :remove do
+ if path_exists?
+ converge_by("removing alternative #{new_resource.link_name} #{new_resource.path}") do
+ shell_out(alternatives_cmd, "--remove", new_resource.link_name, new_resource.path)
+ end
+ end
+ end
+
+ action :auto do
+ converge_by("setting auto alternative #{new_resource.link_name}") do
+ shell_out(alternatives_cmd, "--auto", new_resource.link_name)
+ end
+ end
+
+ action :refresh do
+ converge_by("refreshing alternative #{new_resource.link_name}") do
+ shell_out(alternatives_cmd, "--refresh", new_resource.link_name)
+ end
+ end
+
+ action_class do
+ #
+ # @return [String] The appropriate alternatives command based on the platform
+ #
+ def alternatives_cmd
+ if debian?
+ "update-alternatives"
+ else
+ "alternatives"
+ end
+ end
+
+ #
+ # @return [Integer] The current path priority for the link_name alternative
+ #
+ def path_priority
+ # https://rubular.com/r/IcUlEU0mSNaMm3
+ escaped_path = Regexp.new(Regexp.escape("#{new_resource.path} - priority ") + "(.*)")
+ match = shell_out(alternatives_cmd, "--display", new_resource.link_name).stdout.match(escaped_path)
+
+ match.nil? ? nil : match[1].to_i
+ end
+
+ #
+ # @return [String] The current path for the link_name alternative
+ #
+ def current_path
+ # https://rubular.com/r/ylsuvzUtquRPqc
+ match = shell_out(alternatives_cmd, "--display", new_resource.link_name).stdout.match(/link currently points to (.*)/)
+ match[1]
+ end
+
+ #
+ # @return [Boolean] does the path exist for the link_name alternative
+ #
+ def path_exists?
+ # https://rubular.com/r/ogvDdq8h2IKRff
+ escaped_path = Regexp.new(Regexp.escape("#{new_resource.path} - priority"))
+ shell_out(alternatives_cmd, "--display", new_resource.link_name).stdout.match?(escaped_path)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/apt_package.rb b/lib/chef/resource/apt_package.rb
index 069fefcb2b..0a31f89af3 100644
--- a/lib/chef/resource/apt_package.rb
+++ b/lib/chef/resource/apt_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,63 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/apt"
+require_relative "package"
class Chef
class Resource
class AptPackage < Chef::Resource::Package
- resource_name :apt_package
- provides :package, os: "linux", platform_family: [ "debian" ]
+ unified_mode true
- property :default_release, String, desired_state: false
+ provides :apt_package, target_mode: true
+ provides :package, platform_family: "debian", target_mode: true
+ examples <<~DOC
+ **Install a package using package manager**:
+
+ ```ruby
+ apt_package 'name of package' do
+ action :install
+ end
+ ```
+
+ **Install a package without specifying the default action**:
+
+ ```ruby
+ apt_package 'name of package'
+ ```
+
+ **Install multiple packages at once**:
+
+ ```ruby
+ apt_package %(package1 package2 package3)
+ ```
+
+ **Install without using recommend packages as a dependency**:
+
+ ```ruby
+ package 'apache2' do
+ options '--no-install-recommends'
+ end
+ ```
+ DOC
+
+ 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],
+ introduced: "14.0",
+ description: "Overwrite existing configuration files with those supplied by the package, if prompted by APT.",
+ default: false
+
+ property :response_file, String,
+ description: "The direct path to the file used to pre-seed a package.",
+ desired_state: false
+
+ property :response_file_variables, Hash,
+ description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.",
+ default: lazy { {} }, desired_state: 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..fd987466ea
--- /dev/null
+++ b/lib/chef/resource/apt_preference.rb
@@ -0,0 +1,148 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class AptPreference < Chef::Resource
+ unified_mode true
+
+ provides(:apt_preference) { true }
+
+ description "Use the **apt_preference** resource to create APT [preference files](https://wiki.debian.org/AptPreferences). Preference files are used to control which package versions and sources are prioritized during installation."
+ introduced "13.3"
+ examples <<~DOC
+ **Pin libmysqlclient16 to a version 5.1.49-3**:
+
+ ```ruby
+ apt_preference 'libmysqlclient16' do
+ pin 'version 5.1.49-3'
+ pin_priority '700'
+ end
+ ```
+
+ Note: The `pin_priority` of `700` ensures that this version will be preferred over any other available versions.
+
+ **Unpin a libmysqlclient16**:
+
+ ```ruby
+ apt_preference 'libmysqlclient16' do
+ action :remove
+ end
+ ```
+
+ **Pin all packages to prefer the packages.dotdeb.org repository**:
+
+ ```ruby
+ apt_preference 'dotdeb' do
+ glob '*'
+ pin 'origin packages.dotdeb.org'
+ pin_priority '700'
+ end
+ ```
+ DOC
+
+ property :package_name, String,
+ name_property: true,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ regex: [/^([a-z]|[A-Z]|[0-9]|_|-|\.|\*|\+)+$/],
+ validation_message: "The provided package name is not valid. Package names can only contain alphanumeric characters as well as _, -, +, or *!"
+
+ property :glob, String,
+ description: "Pin by a `glob()` expression or with a regular expression surrounded by `/`."
+
+ property :pin, String,
+ description: "The package version or repository to pin.",
+ required: [:add]
+
+ property :pin_priority, [String, Integer],
+ description: "Sets the Pin-Priority for a package. See <https://wiki.debian.org/AptPreferences> for more details.",
+ required: [:add]
+
+ default_action :add
+ allowed_actions :add, :remove
+
+ APT_PREFERENCE_DIR = "/etc/apt/preferences.d".freeze
+
+ action_class do
+ # 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
+
+ action :add do
+ return unless debian?
+
+ preference = build_pref(
+ new_resource.glob || new_resource.package_name,
+ new_resource.pin,
+ new_resource.pin_priority
+ )
+
+ 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}"
+ 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}"
+ file "#{APT_PREFERENCE_DIR}/#{new_resource.package_name}" do
+ action :delete
+ end
+ end
+
+ file "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref" do
+ mode "0644"
+ content preference
+ action :create
+ end
+ end
+
+ action :remove do
+ return unless debian?
+
+ 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}"
+ file "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref" do
+ action :delete
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/resource/apt_repository.rb b/lib/chef/resource/apt_repository.rb
index 8b87371824..da8ca78413 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:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,32 +16,471 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
+require_relative "../http/simple"
+require "tmpdir" unless defined?(Dir.mktmpdir)
+module Addressable
+ autoload :URI, "addressable/uri"
+end
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
+ unified_mode true
+
+ 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"
+
+ examples <<~DOC
+ **Add repository with basic settings**:
+
+ ```ruby
+ apt_repository 'nginx' do
+ uri 'http://nginx.org/packages/ubuntu/'
+ components ['nginx']
+ end
+ ```
+
+ **Enable Ubuntu multiverse repositories**:
+
+ ```ruby
+ apt_repository 'security-ubuntu-multiverse' do
+ uri 'http://security.ubuntu.com/ubuntu'
+ distribution 'xenial-security'
+ components ['multiverse']
+ deb_src true
+ end
+ ```
+
+ **Add the Nginx PPA, autodetect the key and repository url**:
+
+ ```ruby
+ apt_repository 'nginx-php' do
+ uri 'ppa:nginx/stable'
+ end
+ ```
+
+ **Add the JuJu PPA, grab the key from the Ubuntu keyserver, and add source repo**:
+
+ ```ruby
+ apt_repository 'juju' do
+ uri 'ppa:juju/stable'
+ components ['main']
+ distribution 'xenial'
+ key 'C8068B11'
+ action :add
+ deb_src true
+ end
+ ```
+
+ **Add repository that requires multiple keys to authenticate packages**:
+
+ ```ruby
+ apt_repository 'rundeck' do
+ uri 'https://dl.bintray.com/rundeck/rundeck-deb'
+ distribution '/'
+ key ['379CE192D401AB61', 'http://rundeck.org/keys/BUILD-GPG-KEY-Rundeck.org.key']
+ keyserver 'keyserver.ubuntu.com'
+ action :add
+ end
+ ```
+
+ **Add the Cloudera Repo of CDH4 packages for Ubuntu 16.04 on AMD64**:
+
+ ```ruby
+ apt_repository 'cloudera' do
+ uri 'http://archive.cloudera.com/cdh4/ubuntu/xenial/amd64/cdh'
+ arch 'amd64'
+ distribution 'xenial-cdh4'
+ components ['contrib']
+ key 'http://archive.cloudera.com/debian/archive.key'
+ end
+ ```
+
+ **Remove a repository from the list**:
+
+ ```ruby
+ apt_repository 'zenoss' do
+ action :remove
+ end
+ ```
+ DOC
+
+ # 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: [%r{^[^/]+$}],
+ description: "An optional property to set the repository name if it differs from the resource block's name. The value of this setting must not contain spaces.",
+ validation_message: "repo_name property cannot contain a forward slash '/'",
+ introduced: "14.1", 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 `xenial`, `bionic`, or `focal`.",
+ default: lazy { node["lsb"]["codename"] }, default_description: "The LSB codename of the node such as 'focal'."
+
+ property :components, Array,
+ description: "Package groupings, such as 'main' and 'stable'.",
+ default: lazy { [] }, default_description: "`main` if using a PPA repository."
+
+ 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
+
+ action_class do
+ LIST_APT_KEY_FINGERPRINTS = %w{apt-key adv --list-public-keys --with-fingerprint --with-colons}.freeze
+
+ # 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..] 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 [Array<String>] cmd the command to run
+ #
+ # @return [Array] an array of fingerprints
+ def extract_fingerprints_from_cmd(*cmd)
+ so = shell_out(*cmd)
+ so.stdout.split(/\n/).map do |t|
+ if z = t.match(/^fpr:+([0-9A-F]+):/)
+ z[1].split.join
+ end
+ end.compact
+ end
+
+ # 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 = shell_out("apt-key", "list").stdout.each_line.none?(%r{^\/#{key}.*\[expired: .*\]$})
+
+ 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)
+ # 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
+
+ # 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)
+ tmp_dir = Dir.mktmpdir(".gpg")
+ at_exit { FileUtils.remove_entry(tmp_dir) }
+
+ declare_resource(key_type(key), cached_keyfile) do
+ source key
+ mode "0644"
+ sensitive new_resource.sensitive
+ action :create
+ verify "gpg --homedir #{tmp_dir} %{path}"
+ end
+
+ execute "apt-key add #{cached_keyfile}" do
+ command [ "apt-key", "add", cached_keyfile ]
+ default_env true
+ sensitive new_resource.sensitive
+ action :run
+ not_if { no_new_keys?(cached_keyfile) }
+ notifies :run, "execute[apt-cache gencaches]", :immediately
+ end
+ end
+
+ # 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 --no-tty --recv"
+ cmd << " --keyserver-options http-proxy=#{new_resource.key_proxy}" if new_resource.key_proxy
+ cmd << " --keyserver "
+ cmd << if keyserver.start_with?("hkp://")
+ keyserver
+ else
+ "hkp://#{keyserver}:80"
+ 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)
+ execute "install-key #{key}" do
+ command keyserver_install_cmd(key, keyserver)
+ default_env true
+ sensitive new_resource.sensitive
+ not_if do
+ present = extract_fingerprints_from_cmd(*LIST_APT_KEY_FINGERPRINTS).any? do |fp|
+ fp.end_with? key.upcase
+ end
+ 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?(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('"')
+ install_key_from_keyserver(key_id, "keyserver.ubuntu.com")
+ rescue Net::HTTPClientException => e
+ 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)
+ owner, repo = ppa[4..-1].split("/")
+ repo ||= "ppa"
+
+ install_ppa_key(owner, repo)
+ "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)
+
+ uri = Addressable::URI.parse(uri)
+ components = Array(components).join(" ")
+ options = []
+ options << "arch=#{arch}" if arch
+ options << "trusted=yes" if trusted
+ optstr = unless options.empty?
+ "[" + options.join(" ") + "]"
+ end
+ info = [ optstr, uri.normalize.to_s, distribution, components ].compact.join(" ")
+ repo = "deb #{info}\n"
+ 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
+ file legacy_path do
+ action :delete
+ # Not triggering an update since it isn't super likely to be needed.
+ end
+ end
+ end
+ end
+ end
+
+ action :add do
+ return unless debian?
+
+ execute "apt-cache gencaches" do
+ command %w{apt-cache gencaches}
+ default_env true
+ ignore_failure true
+ action :nothing
+ end
+
+ apt_update new_resource.name do
+ ignore_failure true
+ action :nothing
+ end
+
+ 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
+
+ cleanup_legacy_file!
+
+ repo = build_repo(
+ new_resource.uri,
+ new_resource.distribution,
+ repo_components,
+ new_resource.trusted,
+ new_resource.arch,
+ new_resource.deb_src
+ )
+
+ file "/etc/apt/sources.list.d/#{new_resource.repo_name}.list" do
+ owner "root"
+ group "root"
+ mode "0644"
+ content repo
+ sensitive new_resource.sensitive
+ action :create
+ notifies :run, "execute[apt-cache gencaches]", :immediately
+ notifies :update, "apt_update[#{new_resource.name}]", :immediately if new_resource.cache_rebuild
+ end
+ end
+
+ action :remove do
+ return unless debian?
+
+ 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
+ apt_update new_resource.name do
+ ignore_failure true
+ action :nothing
+ end
+
+ 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
+ end
+ end
+ else
+ logger.trace("/etc/apt/sources.list.d/#{new_resource.repo_name}.list does not exist. Nothing to do")
+ end
+ end
+
end
end
end
diff --git a/lib/chef/resource/apt_update.rb b/lib/chef/resource/apt_update.rb
index df2033b063..e5f75143bb 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) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,93 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class AptUpdate < Chef::Resource
- resource_name :apt_update
- provides :apt_update, os: "linux"
+ unified_mode true
- property :frequency, Integer, default: 86_400
+ provides(:apt_update) { true }
+
+ description "Use the **apt_update** resource to manage APT repository updates on Debian and Ubuntu platforms."
+ introduced "12.7"
+ examples <<~DOC
+ **Update the Apt repository at a specified interval**:
+
+ ```ruby
+ apt_update 'all platforms' do
+ frequency 86400
+ action :periodic
+ end
+ ```
+
+ **Update the Apt repository at the start of a Chef Infra Client run**:
+
+ ```ruby
+ apt_update 'update'
+ ```
+ DOC
+
+ # 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
+
+ action_class do
+ APT_CONF_DIR = "/etc/apt/apt.conf.d".freeze
+ STAMP_DIR = "/var/lib/apt/periodic".freeze
+
+ # Determines whether we need to run `apt-get update`
+ #
+ # @return [Boolean]
+ def apt_up_to_date?
+ ::File.exist?("#{STAMP_DIR}/update-success-stamp") &&
+ ::File.mtime("#{STAMP_DIR}/update-success-stamp") > Time.now - new_resource.frequency
+ end
+
+ def do_update
+ [STAMP_DIR, APT_CONF_DIR].each do |d|
+ directory d do
+ recursive true
+ end
+ end
+
+ file "#{APT_CONF_DIR}/15update-stamp" do
+ content "APT::Update::Post-Invoke-Success {\"touch #{STAMP_DIR}/update-success-stamp 2>/dev/null || true\";};\n"
+ action :create_if_missing
+ end
+
+ execute "apt-get -q update" do
+ command [ "apt-get", "-q", "update" ]
+ default_env true
+ end
+ end
+ end
+
+ action :periodic do
+ return unless debian?
+
+ unless apt_up_to_date?
+ converge_by "update new lists of packages" do
+ do_update
+ end
+ end
+ end
+
+ action :update do
+ return unless debian?
+
+ converge_by "force update new lists of packages" do
+ do_update
+ end
+ end
+
end
end
end
diff --git a/lib/chef/resource/archive_file.rb b/lib/chef/resource/archive_file.rb
new file mode 100644
index 0000000000..4d77ee979b
--- /dev/null
+++ b/lib/chef/resource/archive_file.rb
@@ -0,0 +1,205 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Author:: Jamie Winsor (<jamie@vialstudios.com>)
+# Author:: Tim Smith (<tsmith@chef.io>)
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+require "fileutils" unless defined?(FileUtils)
+
+class Chef
+ class Resource
+ class ArchiveFile < Chef::Resource
+ unified_mode true
+
+ provides :archive_file
+ provides :libarchive_file # legacy cookbook name
+
+ introduced "15.0"
+ description "Use the **archive_file** resource to extract archive files to disk. This resource uses the libarchive library to extract multiple archive formats including tar, gzip, bzip, and zip formats."
+ examples <<~DOC
+ **Extract a zip file to a specified directory**:
+
+ ```ruby
+ archive_file 'Precompiled.zip' do
+ path '/tmp/Precompiled.zip'
+ destination '/srv/files'
+ end
+ ```
+
+ **Set specific permissions on the extracted files**:
+
+ ```ruby
+ archive_file 'Precompiled.zip' do
+ owner 'tsmith'
+ group 'staff'
+ mode '700'
+ path '/tmp/Precompiled.zip'
+ destination '/srv/files'
+ end
+ ```
+ DOC
+
+ property :path, String,
+ name_property: true,
+ coerce: proc { |f| ::File.expand_path(f) },
+ description: "An optional property to set the file path to the archive to extract if it differs from the resource block's name."
+
+ property :owner, String,
+ description: "The owner of the extracted files."
+
+ property :group, String,
+ description: "The group of the extracted files."
+
+ property :mode, [String, Integer],
+ description: "The mode of the extracted files. Integer values are deprecated as octal values (ex. 0755) would not be interpreted correctly.",
+ default: "755"
+
+ property :destination, String,
+ description: "The file path to extract the archive file to.",
+ required: true
+
+ property :options, [Array, Symbol],
+ description: "An array of symbols representing extraction flags. Example: `:no_overwrite` to prevent overwriting files on disk. By default, this properly sets `:time` which preserves the modification timestamps of files in the archive when writing them to disk.",
+ default: lazy { [:time] }
+
+ property :overwrite, [TrueClass, FalseClass, :auto],
+ description: "Should the resource overwrite the destination file contents if they already exist? If set to `:auto` the date stamp of files within the archive will be compared to those on disk and disk contents will be overwritten if they differ. This may cause unintended consequences if disk date stamps are changed between runs, which will result in the files being overwritten during each client run. Make sure to properly test any change to this property.",
+ default: false
+
+ # backwards compatibility for the legacy cookbook names
+ alias_method :extract_options, :options
+ alias_method :extract_to, :destination
+
+ action :extract do
+ description "Extract and archive file."
+
+ require_libarchive
+
+ unless ::File.exist?(new_resource.path)
+ raise Errno::ENOENT, "No archive found at #{new_resource.path}! Cannot continue."
+ end
+
+ if !::File.exist?(new_resource.destination)
+ Chef::Log.trace("File or directory does not exist at destination path: #{new_resource.destination}")
+
+ converge_by("create directory #{new_resource.destination}") do
+ # @todo when we remove the ability for mode to be an int we can remove the .to_s below
+ FileUtils.mkdir_p(new_resource.destination, mode: new_resource.mode.to_s.to_i(8))
+ end
+
+ extract(new_resource.path, new_resource.destination, Array(new_resource.options))
+ else
+ Chef::Log.trace("File or directory exists at destination path: #{new_resource.destination}.")
+
+ if new_resource.overwrite == true ||
+ (new_resource.overwrite == :auto && archive_differs_from_disk?(new_resource.path, new_resource.destination))
+ Chef::Log.debug("Overwriting existing content at #{new_resource.destination} due to resource's overwrite property settings.")
+
+ extract(new_resource.path, new_resource.destination, Array(new_resource.options))
+ else
+ Chef::Log.debug("Not extracting archive as #{new_resource.destination} exists and resource not set to overwrite.")
+ end
+ end
+
+ if new_resource.owner || new_resource.group
+ converge_by("set owner of files extracted in #{new_resource.destination} to #{new_resource.owner}:#{new_resource.group}") do
+ archive = Archive::Reader.open_filename(new_resource.path)
+ archive.each_entry do |e|
+ FileUtils.chown(new_resource.owner, new_resource.group, "#{new_resource.destination}/#{e.pathname}")
+ end
+ end
+ end
+ end
+
+ action_class do
+ def require_libarchive
+ require "ffi-libarchive"
+ end
+
+ def define_resource_requirements
+ if new_resource.mode.is_a?(Integer)
+ Chef.deprecated(:archive_file_integer_file_mode, "The mode property should be passed to archive_file resources as a String and not an Integer to ensure the value is properly interpreted.")
+ end
+ end
+
+ # This can't be a constant since we might not have required 'ffi-libarchive' yet.
+ def extract_option_map
+ {
+ owner: Archive::EXTRACT_OWNER,
+ permissions: Archive::EXTRACT_PERM,
+ time: Archive::EXTRACT_TIME,
+ no_overwrite: Archive::EXTRACT_NO_OVERWRITE,
+ acl: Archive::EXTRACT_ACL,
+ fflags: Archive::EXTRACT_FFLAGS,
+ extended_information: Archive::EXTRACT_XATTR,
+ xattr: Archive::EXTRACT_XATTR,
+ no_overwrite_newer: Archive::EXTRACT_NO_OVERWRITE_NEWER,
+ }
+ end
+
+ # try to determine if the resource has updated or not by checking for files that are in the
+ # archive, but not on disk or files with a non-matching mtime
+ #
+ # @param [String] src
+ # @param [String] dest
+ #
+ # @return [Boolean]
+ def archive_differs_from_disk?(src, dest)
+ modified = false
+ Dir.chdir(dest) do
+ archive = Archive::Reader.open_filename(src)
+ Chef::Log.trace("Beginning the comparison of file mtime between contents of #{src} and #{dest}")
+ archive.each_entry do |e|
+ pathname = ::File.expand_path(e.pathname)
+ if ::File.exist?(pathname)
+ Chef::Log.trace("#{pathname} mtime is #{::File.mtime(pathname)} and archive is #{e.mtime}")
+ modified = true unless ::File.mtime(pathname) == e.mtime
+ else
+ Chef::Log.trace("#{pathname} doesn't exist on disk, but exists in the archive")
+ modified = true
+ end
+ end
+ end
+ modified
+ end
+
+ # extract the archive
+ #
+ # @param [String] src
+ # @param [String] dest
+ # @param [Array] options
+ #
+ # @return [void]
+ def extract(src, dest, options = [])
+ converge_by("extract #{src} to #{dest}") do
+ flags = [options].flatten.map { |option| extract_option_map[option] }.compact.reduce(:|)
+
+ Dir.chdir(dest) do
+ archive = Archive::Reader.open_filename(src)
+
+ archive.each_entry do |e|
+ archive.extract(e, flags.to_i)
+ end
+ archive.close
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/bash.rb b/lib/chef/resource/bash.rb
index 1238eedc42..9ed6dc68d5 100644
--- a/lib/chef/resource/bash.rb
+++ b/lib/chef/resource/bash.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,134 @@
# limitations under the License.
#
-require "chef/resource/script"
-require "chef/provider/script"
+require_relative "script"
class Chef
class Resource
class Bash < Chef::Resource::Script
+ unified_mode true
+
+ provides :bash
+
+ 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."
+ examples <<~'DOC'
+ **Compile an application**
+
+ ```ruby
+ bash 'install_something' do
+ user 'root'
+ cwd '/tmp'
+ code <<-EOH
+ wget http://www.example.com/tarball.tar.gz
+ tar -zxf tarball.tar.gz
+ cd tarball
+ ./configure
+ make
+ make install
+ EOH
+ end
+ ```
+
+ **Install a file from a remote location**
+
+ The following is an example of how to install the foo123 module for Nginx. This module adds shell-style functionality to an Nginx configuration file and does the following:
+
+ - Declares three variables
+ - Gets the Nginx file from a remote location
+ - Installs the file using Bash to the path specified by the `src_filepath` variable
+
+ ```ruby
+ src_filename = "foo123-nginx-module-v#{node['nginx']['foo123']['version']}.tar.gz"
+ src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}"
+ extract_path = "#{Chef::Config['file_cache_path']}/nginx_foo123_module/#{node['nginx']['foo123']['checksum']}"
+
+ remote_file 'src_filepath' do
+ source node['nginx']['foo123']['url']
+ checksum node['nginx']['foo123']['checksum']
+ owner 'root'
+ group 'root'
+ mode '0755'
+ end
+
+ bash 'extract_module' do
+ cwd ::File.dirname(src_filepath)
+ code <<-EOH
+ mkdir -p #{extract_path}
+ tar xzf #{src_filename} -C #{extract_path}
+ mv #{extract_path}/*/* #{extract_path}/
+ EOH
+ not_if { ::File.exist?(extract_path) }
+ end
+ ```
+
+ **Install an application from git**
+
+ ```ruby
+ git "#{Chef::Config[:file_cache_path]}/ruby-build" do
+ repository 'git://github.com/rbenv/ruby-build.git'
+ revision 'master'
+ action :sync
+ end
+
+ bash 'install_ruby_build' do
+ cwd "#{Chef::Config[:file_cache_path]}/ruby-build"
+ user 'rbenv'
+ group 'rbenv'
+ code <<-EOH
+ ./install.sh
+ EOH
+ environment 'PREFIX' => '/usr/local'
+ end
+ ```
+
+ **Using Attributes in Bash Code**
+
+ The following recipe shows how an attributes file can be used to store certain settings. An attributes file is located in the `attributes/`` directory in the same cookbook as the recipe which calls the attributes file. In this example, the attributes file specifies certain settings for Python that are then used across all nodes against which this recipe will run.
+
+ Python packages have versions, installation directories, URLs, and checksum files. An attributes file that exists to support this type of recipe would include settings like the following:
+
+ ```ruby
+ default['python']['version'] = '2.7.1'
+
+ if python['install_method'] == 'package'
+ default['python']['prefix_dir'] = '/usr'
+ else
+ default['python']['prefix_dir'] = '/usr/local'
+ end
+
+ default['python']['url'] = 'http://www.python.org/ftp/python'
+ default['python']['checksum'] = '80e387...85fd61'
+ ```
+
+ and then the methods in the recipe may refer to these values. A recipe that is used to install Python will need to do the following:
+
+ - Identify each package to be installed (implied in this example, not shown)
+ - Define variables for the package `version` and the `install_path`
+ - Get the package from a remote location, but only if the package does not already exist on the target system
+ - Use the **bash** resource to install the package on the node, but only when the package is not already installed
+
+ ```ruby
+ version = node['python']['version']
+ install_path = "#{node['python']['prefix_dir']}/lib/python#{version.split(/(^\d+\.\d+)/)[1]}"
+
+ remote_file "#{Chef::Config[:file_cache_path]}/Python-#{version}.tar.bz2" do
+ source "#{node['python']['url']}/#{version}/Python-#{version}.tar.bz2"
+ checksum node['python']['checksum']
+ mode '0755'
+ not_if { ::File.exist?(install_path) }
+ end
+
+ bash 'build-and-install-python' do
+ cwd Chef::Config[:file_cache_path]
+ code <<-EOF
+ tar -jxvf Python-#{version}.tar.bz2
+ (cd Python-#{version} && ./configure #{configure_options})
+ (cd Python-#{version} && make && make install)
+ EOF
+ not_if { ::File.exist?(install_path) }
+ end
+ ```
+ DOC
def initialize(name, run_context = nil)
super
diff --git a/lib/chef/resource/batch.rb b/lib/chef/resource/batch.rb
index 10e96839fb..bbb5e73905 100644
--- a/lib/chef/resource/batch.rb
+++ b/lib/chef/resource/batch.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,21 @@
# limitations under the License.
#
-require "chef/resource/windows_script"
+require_relative "windows_script"
class Chef
class Resource
class Batch < Chef::Resource::WindowsScript
+ unified_mode true
- provides :batch, os: "windows"
+ provides :batch
- def initialize(name, run_context = nil)
- super(name, run_context, nil, "cmd.exe")
+ 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(*args)
+ super
+ @interpreter = "cmd.exe"
+ @default_guard_interpreter = resource_name
end
end
diff --git a/lib/chef/resource/bff_package.rb b/lib/chef/resource/bff_package.rb
index b14591876a..ffe5dfac1b 100644
--- a/lib/chef/resource/bff_package.rb
+++ b/lib/chef/resource/bff_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Deepali Jagtap (<deepali.jagtap@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,46 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/aix"
+require_relative "package"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class BffPackage < Chef::Resource::Package
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ The **bff_package** resource is the default package provider on the AIX platform. The base **package** resource may be used, and then when the platform is AIX, #{ChefUtils::Dist::Infra::PRODUCT} will identify the correct package provider. The following examples show how to install part of the IBM XL C/C++ compiler.
+
+ **Installing using the base package resource**
+
+ ```ruby
+ package 'xlccmp.13.1.0' do
+ source '/var/tmp/IBM_XL_C_13.1.0/usr/sys/inst.images/xlccmp.13.1.0'
+ action :install
+ end
+ ```
+
+ **Installing using the bff_package resource**
+
+ ```ruby
+ bff_package 'xlccmp.13.1.0' do
+ source '/var/tmp/IBM_XL_C_13.1.0/usr/sys/inst.images/xlccmp.13.1.0'
+ action :install
+ end
+ ```
+ DOC
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
end
end
end
diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb
index a5eed0da94..50e2d06391 100644
--- a/lib/chef/resource/breakpoint.rb
+++ b/lib/chef/resource/breakpoint.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,91 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class Breakpoint < Chef::Resource
+ unified_mode true
+
+ provides :breakpoint, target_mode: true
+
+ description "Use the **breakpoint** resource to add breakpoints to recipes. Run the #{ChefUtils::Dist::Infra::SHELL} in #{ChefUtils::Dist::Infra::PRODUCT} mode, and then use those breakpoints to debug recipes. Breakpoints are ignored by the #{ChefUtils::Dist::Infra::CLIENT} during an actual #{ChefUtils::Dist::Infra::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"
+ examples <<~DOC
+ **A recipe without a breakpoint**
+
+ ```ruby
+ yum_key node['yum']['elrepo']['key'] do
+ url node['yum']['elrepo']['key_url']
+ action :add
+ end
+
+ yum_repository 'elrepo' do
+ description 'ELRepo.org Community Enterprise Linux Extras Repository'
+ key node['yum']['elrepo']['key']
+ mirrorlist node['yum']['elrepo']['url']
+ includepkgs node['yum']['elrepo']['includepkgs']
+ exclude node['yum']['elrepo']['exclude']
+ action :create
+ end
+ ```
+
+ **The same recipe with breakpoints**
+
+ In the following example, the name of each breakpoint is an arbitrary string.
+
+ ```ruby
+ breakpoint "before yum_key node['yum']['repo_name']['key']" do
+ action :break
+ end
+
+ yum_key node['yum']['repo_name']['key'] do
+ url node['yum']['repo_name']['key_url']
+ action :add
+ end
+
+ breakpoint "after yum_key node['yum']['repo_name']['key']" do
+ action :break
+ end
+
+ breakpoint "before yum_repository 'repo_name'" do
+ action :break
+ end
+
+ yum_repository 'repo_name' do
+ description 'description'
+ key node['yum']['repo_name']['key']
+ mirrorlist node['yum']['repo_name']['url']
+ includepkgs node['yum']['repo_name']['includepkgs']
+ exclude node['yum']['repo_name']['exclude']
+ action :create
+ end
+
+ breakpoint "after yum_repository 'repo_name'" do
+ action :break
+ end
+ ```
+
+ In the previous examples, the names are used to indicate if the breakpoint is before or after a resource and also to specify which resource it is before or after.
+ DOC
+
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..3039f709c8
--- /dev/null
+++ b/lib/chef/resource/build_essential.rb
@@ -0,0 +1,187 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class BuildEssential < Chef::Resource
+ unified_mode true
+
+ provides(:build_essential) { true }
+
+ description "Use the **build_essential** resource to install the packages required for compiling C software from source."
+ introduced "14.0"
+ examples <<~DOC
+ **Install compilation packages**:
+
+ ```ruby
+ build_essential
+ ```
+
+ **Install compilation packages during the compilation phase**:
+
+ ```ruby
+ build_essential 'Install compilation tools' do
+ compile_time true
+ end
+ ```
+
+ **Upgrade compilation packages on macOS systems**:
+
+ ```ruby
+ build_essential 'Install compilation tools' do
+ action :upgrade
+ end
+ ```
+ DOC
+
+ # this allows us to use build_essential without setting a name
+ property :name, String, default: ""
+
+ property :raise_if_unsupported, [TrueClass, FalseClass],
+ description: "Raise a hard error on platforms where this resource is unsupported.",
+ introduced: "15.5",
+ default: false, desired_state: false # FIXME: make this default to true
+
+ action :install do
+
+ description "Install build essential packages"
+
+ case
+ when debian?
+ package %w{ autoconf binutils-doc bison build-essential flex gettext ncurses-dev }
+ when fedora_derived?
+ package %w{ autoconf bison flex gcc gcc-c++ gettext kernel-devel make m4 ncurses-devel patch }
+ when freebsd?
+ package "devel/gmake"
+ package "devel/autoconf"
+ package "devel/m4"
+ package "devel/gettext"
+ when macos?
+ install_xcode_cli_tools(xcode_cli_package_label) unless xcode_cli_installed?
+ when omnios?
+ package "developer/gcc48"
+ package "developer/object-file"
+ package "developer/linker"
+ package "developer/library/lint"
+ package "developer/build/gnu-make"
+ package "system/header"
+ 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?
+ package "autoconf"
+ package "automake"
+ package "bison"
+ package "gnu-coreutils"
+ package "flex"
+ package "gcc"
+ package "gnu-grep"
+ package "gnu-make"
+ package "gnu-patch"
+ package "gnu-tar"
+ package "make"
+ package "pkg-config"
+ package "ucb"
+ when smartos?
+ package "autoconf"
+ package "binutils"
+ package "build-essential"
+ package "gcc47"
+ package "gmake"
+ package "pkg-config"
+ when suse?
+ package %w{ autoconf bison flex gcc gcc-c++ kernel-default-devel make m4 }
+ else
+ msg = <<-EOH
+ The build_essential resource does not currently support the '#{node["platform_family"]}'
+ platform family. Skipping...
+ EOH
+ if new_resource.raise_if_unsupported
+ raise msg
+ else
+ Chef::Log.warn msg
+ end
+ end
+ end
+
+ action :upgrade do
+ description "Upgrade build essential (Xcode Command Line) tools on macOS"
+
+ if macos?
+ pkg_label = xcode_cli_package_label
+
+ # With upgrade action we should install if it's not installed or if there's an available update.
+ # `pkg_label` will be nil if there's no update.
+ install_xcode_cli_tools(pkg_label) if !xcode_cli_installed? || pkg_label
+ else
+ Chef::Log.info "The build_essential resource :upgrade action is only supported on macOS systems. Skipping..."
+ end
+ end
+
+ action_class do
+ #
+ # Install Xcode Command Line tools via softwareupdate CLI
+ #
+ # @param [String] label The label (package name) to install
+ #
+ def install_xcode_cli_tools(label)
+ # 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
+ bash "install Xcode Command Line Tools" do
+ code <<-EOH
+ # 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
+ # install it
+ softwareupdate -i "#{label}" --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
+
+ #
+ # Determine if the XCode Command Line Tools are installed by checking
+ # for success from `xcode-select -p`
+ #
+ # @return [true, false]
+ def xcode_cli_installed?
+ !shell_out("xcode-select", "-p").error?
+ end
+
+ #
+ # Return to package label of the latest Xcode Command Line Tools update, if available
+ #
+ # @return [String, NilClass]
+ def xcode_cli_package_label
+ available_updates = shell_out("softwareupdate", "--list")
+
+ # raise if we fail to check
+ available_updates.error!
+
+ # https://rubular.com/r/UPEE5P7mZLvXNs
+ # this will return the match or nil
+ available_updates.stdout[/^\s*\* (?:Label: )?(Command Line Tools.*)/, 1]
+ 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..904fe81701
--- /dev/null
+++ b/lib/chef/resource/cab_package.rb
@@ -0,0 +1,81 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "package"
+require_relative "../mixin/uris"
+
+class Chef
+ class Resource
+ class CabPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+ unified_mode true
+
+ provides :cab_package
+
+ description "Use the **cab_package** resource to install or remove Microsoft Windows cabinet (.cab) packages."
+ introduced "12.15"
+ examples <<~'DOC'
+ **Using local path in source**
+
+ ```ruby
+ cab_package 'Install .NET 3.5 sp1 via KB958488' do
+ source 'C:\Users\xyz\AppData\Local\Temp\Windows6.1-KB958488-x64.cab'
+ action :install
+ end
+
+ cab_package 'Remove .NET 3.5 sp1 via KB958488' do
+ source 'C:\Users\xyz\AppData\Local\Temp\Windows6.1-KB958488-x64.cab'
+ action :remove
+ end
+ ```
+
+ **Using URL in source**
+
+ ```ruby
+ cab_package 'Install .NET 3.5 sp1 via KB958488' do
+ source 'https://s3.amazonaws.com/my_bucket/Windows6.1-KB958488-x64.cab'
+ action :install
+ end
+
+ cab_package 'Remove .NET 3.5 sp1 via KB958488' do
+ source 'https://s3.amazonaws.com/my_bucket/Temp\Windows6.1-KB958488-x64.cab'
+ action :remove
+ end
+ ```
+ DOC
+
+ allowed_actions :install, :remove
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ 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 { package_name }, default_description: "The package name."
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_config.rb b/lib/chef/resource/chef_client_config.rb
new file mode 100644
index 0000000000..b3ea86c476
--- /dev/null
+++ b/lib/chef/resource/chef_client_config.rb
@@ -0,0 +1,313 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefClientConfig < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_config
+
+ description "Use the **chef_client_config** resource to create a client.rb file in the #{ChefUtils::Dist::Infra::PRODUCT} configuration directory. See the [client.rb docs](https://docs.chef.io/config_rb_client/) for more details on options available in the client.rb configuration file."
+ introduced "16.6"
+ examples <<~DOC
+ **Bare minimum #{ChefUtils::Dist::Infra::PRODUCT} client.rb**:
+
+ The absolute minimum configuration necessary for a node to communicate with the Infra Server is the URL of the Infra Server. All other configuration options either have values at the server side (Policyfiles, Roles, Environments, etc) or have default values determined at client startup.
+
+ ```ruby
+ chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+ end
+ ```
+
+ **More complex #{ChefUtils::Dist::Infra::PRODUCT} client.rb**:
+
+ ```ruby
+ chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+ log_level :info
+ log_location :syslog
+ http_proxy 'proxy.example.dmz'
+ https_proxy 'proxy.example.dmz'
+ no_proxy %w(internal.example.dmz)
+ end
+ ```
+
+ **Adding additional config content to the client.rb**:
+
+ This resource aims to provide common configuration options. Some configuration options are missing and some users may want to use arbitrary Ruby code within their configuration. For this we offer an `additional_config` property that can be used to add any configuration or code to the bottom of the `client.rb` file. Also keep in mind that within the configuration directory is a `client.d` directory where you can put additional `.rb` files containing configuration options. These can be created using `file` or `template` resources within your cookbooks as necessary.
+
+ ```ruby
+ chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+ additional_config <<~CONFIG
+ # Extra config code to safely load a gem into the client run.
+ # Since the config is Ruby you can run any Ruby code you want via the client.rb.
+ # It's a great way to break things, so be careful
+ begin
+ require 'aws-sdk'
+ rescue LoadError
+ Chef::Log.warn "Failed to load aws-sdk."
+ end
+ CONFIG
+ end
+ ```
+
+ **Setup two report handlers in the client.rb**:
+
+ ```ruby
+ chef_client_config 'Create client.rb' do
+ chef_server_url 'https://chef.example.dmz'
+ report_handlers [
+ {
+ 'class' => 'ReportHandler1Class',
+ 'arguments' => ["'FirstArgument'", "'SecondArgument'"],
+ },
+ {
+ 'class' => 'ReportHandler2Class',
+ 'arguments' => ["'FirstArgument'", "'SecondArgument'"],
+ },
+ ]
+ end
+ ```
+ DOC
+
+ # @todo policy_file or policy_group being set requires the other to be set so enforce that.
+ # @todo all properties for automate report
+ # @todo add all descriptions
+ # @todo validate handler hash structure
+
+ #
+ # @param [String, Symbol] prop_val the value from the property
+ #
+ # @return [Symbol] The symbol form of the symbol-like string, string, or symbol value
+ #
+ def string_to_symbol(prop_val)
+ if prop_val.is_a?(String) && prop_val.start_with?(":")
+ prop_val[1..-1].to_sym
+ else
+ prop_val.to_sym
+ end
+ end
+
+ property :config_directory, String,
+ description: "The directory to store the client.rb in.",
+ default: ChefConfig::Config.etc_chef_dir,
+ default_description: "`/etc/chef/` on *nix-like systems and `C:\\chef\\` on Windows"
+
+ property :user, String,
+ description: "The user that should own the client.rb file and the configuration directory if it needs to be created. Note: The configuration directory will not be created if it already exists, which allows you to further control the setup of that directory outside of this resource."
+
+ property :group, String,
+ description: "The group that should own the client.rb file and the configuration directory if it needs to be created. Note: The configuration directory will not be created if it already exists, which allows you to further control the setup of that directory outside of this resource."
+
+ property :node_name, [String, NilClass], # this accepts nil so people can disable the default
+ description: "The name of the node. This configuration sets the `node.name` value used in cookbooks and the `client_name` value used when authenticating to a #{ChefUtils::Dist::Server::PRODUCT} to determine what configuration to apply. Note: By default this configuration uses the `node.name` value which would be set during bootstrap. Hard coding this value in the `client.rb` config avoids logic within #{ChefUtils::Dist::Server::PRODUCT} that performs DNS lookups and may fail in the event of a DNS outage. To skip this default value and instead use the built-in #{ChefUtils::Dist::Server::PRODUCT} logic, set this property to `nil`",
+ default: lazy { node.name },
+ default_description: "The `node.name` value reported by #{ChefUtils::Dist::Infra::PRODUCT}."
+
+ property :chef_server_url, String,
+ description: "The URL for the #{ChefUtils::Dist::Server::PRODUCT}.",
+ required: true
+
+ # @todo Allow passing this as a string and convert it to the symbol
+ property :ssl_verify_mode, [Symbol, String],
+ equal_to: %i{verify_none verify_peer},
+ coerce: proc { |x| string_to_symbol(x) },
+ description: <<~DESC
+ Set the verify mode for HTTPS requests.
+
+ * Use :verify_none for no validation of SSL certificates.
+ * Use :verify_peer for validation of all SSL certificates, including the #{ChefUtils::Dist::Server::PRODUCT} connections, S3 connections, and any HTTPS remote_file resource URLs used in #{ChefUtils::Dist::Infra::PRODUCT} runs. This is the recommended setting.
+ DESC
+
+ property :formatters, Array,
+ description: "",
+ default: []
+
+ property :event_loggers, Array,
+ description: "",
+ default: []
+
+ property :log_level, Symbol,
+ description: "The level of logging performed by the #{ChefUtils::Dist::Infra::PRODUCT}.",
+ equal_to: %i{auto trace debug info warn fatal}
+
+ property :log_location, [String, Symbol],
+ description: "The location to save logs to. This can either by a path to a log file on disk `:syslog` to log to Syslog, `:win_evt` to log to the Windows Event Log, or `'STDERR'`/`'STDOUT'` to log to the *nix text streams.",
+ callbacks: {
+ "accepts Symbol values of ':win_evt' for Windows Event Log or ':syslog' for Syslog" => lambda { |p|
+ p.is_a?(Symbol) ? %i{win_evt syslog}.include?(p) : true
+ },
+ }
+
+ property :http_proxy, String,
+ description: "The proxy server to use for HTTP connections."
+
+ property :https_proxy, String,
+ description: "The proxy server to use for HTTPS connections."
+
+ property :ftp_proxy, String,
+ description: "The proxy server to use for FTP connections."
+
+ property :no_proxy, [String, Array],
+ description: "A comma-separated list or an array of URLs that do not need a proxy.",
+ coerce: proc { |x| x.is_a?(Array) ? x.join(",") : x },
+ default: []
+
+ # @todo we need to fixup bad plugin naming inputs here
+ property :ohai_disabled_plugins, Array,
+ description: "Ohai plugins that should be disabled in order to speed up the #{ChefUtils::Dist::Infra::PRODUCT} run and reduce the size of node data sent to #{ChefUtils::Dist::Infra::PRODUCT}",
+ coerce: proc { |x| x.map { |v| string_to_symbol(v).capitalize } },
+ default: []
+
+ # @todo we need to fixup bad plugin naming inputs here
+ property :ohai_optional_plugins, Array,
+ description: "Optional Ohai plugins that should be enabled to provide additional Ohai data for use in cookbooks.",
+ coerce: proc { |x| x.map { |v| string_to_symbol(v).capitalize } },
+ default: []
+
+ property :minimal_ohai, [true, false],
+ description: "Run a minimal set of Ohai plugins providing data necessary for the execution of #{ChefUtils::Dist::Infra::PRODUCT}'s built-in resources. Setting this to true will skip many large and time consuming data sets such as `cloud` or `packages`. Setting this this to true may break cookbooks that assume all Ohai data will be present."
+
+ property :start_handlers, Array,
+ description: %q(An array of hashes that contain a report handler class and the arguments to pass to that class on initialization. The hash should include `class` and `argument` keys where `class` is a String and `argument` is an array of quoted String values. For example: `[{'class' => 'MyHandler', %w('"argument1"', '"argument2"')}]`),
+ default: []
+
+ property :report_handlers, Array,
+ description: %q(An array of hashes that contain a report handler class and the arguments to pass to that class on initialization. The hash should include `class` and `argument` keys where `class` is a String and `argument` is an array of quoted String values. For example: `[{'class' => 'MyHandler', %w('"argument1"', '"argument2"')}]`),
+ default: []
+
+ property :exception_handlers, Array,
+ description: %q(An array of hashes that contain a exception handler class and the arguments to pass to that class on initialization. The hash should include `class` and `argument` keys where `class` is a String and `argument` is an array of quoted String values. For example: `[{'class' => 'MyHandler', %w('"argument1"', '"argument2"')}]`),
+ default: []
+
+ property :chef_license, String,
+ description: "Accept the [Chef EULA](https://www.chef.io/end-user-license-agreement/)",
+ equal_to: %w{accept accept-no-persist accept-silent}
+
+ property :policy_name, String,
+ description: "The name of a policy, as identified by the `name` setting in a Policyfile.rb file. `policy_group` when setting this property."
+
+ property :policy_group, String,
+ description: "The name of a `policy group` that exists on the #{ChefUtils::Dist::Server::PRODUCT}. `policy_name` must also be specified when setting this property."
+
+ property :named_run_list, String,
+ description: "A specific named runlist defined in the node's applied Policyfile, which the should be used when running #{ChefUtils::Dist::Infra::PRODUCT}."
+
+ property :pid_file, String,
+ description: "The location in which a process identification number (pid) is saved. An executable, when started as a daemon, writes the pid to the specified file. "
+
+ property :file_cache_path, String,
+ description: "The location in which cookbooks (and other transient data) files are stored when they are synchronized. This value can also be used in recipes to download files with the `remote_file` resource."
+
+ property :file_backup_path, String,
+ description: "The location in which backup files are stored. If this value is empty, backup files are stored in the directory of the target file"
+
+ property :file_staging_uses_destdir, String,
+ description: "How file staging (via temporary files) is done. When `true`, temporary files are created in the directory in which files will reside. When `false`, temporary files are created under `ENV['TMP']`"
+
+ property :additional_config, String,
+ description: "Additional text to add at the bottom of the client.rb config. This can be used to run custom Ruby or to add less common config options"
+
+ action :create do
+ unless ::Dir.exist?(new_resource.config_directory)
+ directory new_resource.config_directory do
+ user new_resource.user unless new_resource.user.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode "0750"
+ recursive true
+ end
+ end
+
+ unless ::Dir.exist?(::File.join(new_resource.config_directory, "client.d"))
+ directory ::File.join(new_resource.config_directory, "client.d") do
+ user new_resource.user unless new_resource.user.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode "0750"
+ recursive true
+ end
+ end
+
+ template ::File.join(new_resource.config_directory, "client.rb") do
+ source ::File.expand_path("support/client.erb", __dir__)
+ user new_resource.user unless new_resource.user.nil?
+ group new_resource.group unless new_resource.group.nil?
+ local true
+ variables(
+ chef_license: new_resource.chef_license,
+ chef_server_url: new_resource.chef_server_url,
+ event_loggers: new_resource.event_loggers,
+ exception_handlers: format_handler(new_resource.exception_handlers),
+ file_backup_path: new_resource.file_backup_path,
+ file_cache_path: new_resource.file_cache_path,
+ file_staging_uses_destdir: new_resource.file_staging_uses_destdir,
+ formatters: new_resource.formatters,
+ http_proxy: new_resource.http_proxy,
+ https_proxy: new_resource.https_proxy,
+ ftp_proxy: new_resource.ftp_proxy,
+ log_level: new_resource.log_level,
+ log_location: new_resource.log_location,
+ minimal_ohai: new_resource.minimal_ohai,
+ named_run_list: new_resource.named_run_list,
+ no_proxy: new_resource.no_proxy,
+ node_name: new_resource.node_name,
+ ohai_disabled_plugins: new_resource.ohai_disabled_plugins,
+ ohai_optional_plugins: new_resource.ohai_optional_plugins,
+ pid_file: new_resource.pid_file,
+ policy_group: new_resource.policy_group,
+ policy_name: new_resource.policy_name,
+ report_handlers: format_handler(new_resource.report_handlers),
+ ssl_verify_mode: new_resource.ssl_verify_mode,
+ start_handlers: format_handler(new_resource.start_handlers),
+ additional_config: new_resource.additional_config
+ )
+ mode "0640"
+ action :create
+ end
+ end
+
+ action :remove do
+ file ::File.join(new_resource.config_directory, "client.rb") do
+ action :delete
+ end
+ end
+
+ action_class do
+ #
+ # Format the handler document in the way we want it presented in the client.rb file
+ #
+ # @param [Hash] a handler property
+ #
+ # @return [Array] Array of handler data
+ #
+ def format_handler(handler_property)
+ handler_data = []
+
+ handler_property.each do |handler|
+ handler_data << "#{handler["class"]}.new(#{handler["arguments"].join(",")})"
+ end
+
+ handler_data
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_cron.rb b/lib/chef/resource/chef_client_cron.rb
new file mode 100644
index 0000000000..003b28d7b3
--- /dev/null
+++ b/lib/chef/resource/chef_client_cron.rb
@@ -0,0 +1,235 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "helpers/cron_validations"
+require "digest/md5" unless defined?(Digest::MD5)
+
+class Chef
+ class Resource
+ class ChefClientCron < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_cron
+
+ description "Use the **chef_client_cron** resource to setup the #{ChefUtils::Dist::Infra::PRODUCT} to run as a cron job. This resource will also create the specified log directory if it doesn't already exist."
+ introduced "16.0"
+ examples <<~DOC
+ **Setup #{ChefUtils::Dist::Infra::PRODUCT} to run using the default 30 minute cadence**:
+
+ ```ruby
+ chef_client_cron 'Run #{ChefUtils::Dist::Infra::PRODUCT} as a cron job'
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} twice a day**:
+
+ ```ruby
+ chef_client_cron 'Run #{ChefUtils::Dist::Infra::PRODUCT} every 12 hours' do
+ minute 0
+ hour '0,12'
+ end
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} with extra options passed to the client**:
+
+ ```ruby
+ chef_client_cron 'Run an override recipe' do
+ daemon_options ['--override-runlist mycorp_base::default']
+ end
+ ```
+ DOC
+
+ extend Chef::ResourceHelpers::CronValidations
+
+ property :job_name, String,
+ default: ChefUtils::Dist::Infra::CLIENT,
+ description: "The name of the cron job to create."
+
+ property :comment, String,
+ description: "A comment to place in the cron.d file."
+
+ property :user, String,
+ description: "The name of the user that #{ChefUtils::Dist::Infra::PRODUCT} runs as.",
+ default: "root"
+
+ property :minute, [Integer, String],
+ description: "The minute at which #{ChefUtils::Dist::Infra::PRODUCT} is to run (0 - 59) or a cron pattern such as '0,30'.",
+ default: "0,30", callbacks: {
+ "should be a valid minute spec" => method(:validate_minute),
+ }
+
+ property :hour, [Integer, String],
+ description: "The hour at which #{ChefUtils::Dist::Infra::PRODUCT} is to run (0 - 23) or a cron pattern such as '0,12'.",
+ default: "*", callbacks: {
+ "should be a valid hour spec" => method(:validate_hour),
+ }
+
+ property :day, [Integer, String],
+ description: "The day of month at which #{ChefUtils::Dist::Infra::PRODUCT} is to run (1 - 31) or a cron pattern such as '1,7,14,21,28'.",
+ default: "*", callbacks: {
+ "should be a valid day spec" => method(:validate_day),
+ }
+
+ property :month, [Integer, String],
+ description: "The month in the year on which #{ChefUtils::Dist::Infra::PRODUCT} is to run (1 - 12, jan-dec, or *).",
+ default: "*", callbacks: {
+ "should be a valid month spec" => method(:validate_month),
+ }
+
+ property :weekday, [Integer, String],
+ description: "The day of the week on which #{ChefUtils::Dist::Infra::PRODUCT} is to run (0-7, mon-sun, or *), where Sunday is both 0 and 7.",
+ default: "*", callbacks: {
+ "should be a valid weekday spec" => method(:validate_dow),
+ }
+
+ property :splay, [Integer, String],
+ default: 300,
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive number" => proc { |v| v > 0 } },
+ description: "A random number of seconds between 0 and X to add to interval so that all #{ChefUtils::Dist::Infra::CLIENT} commands don't execute at the same time."
+
+ property :mailto, String,
+ description: "The e-mail address to e-mail any cron task failures to."
+
+ property :accept_chef_license, [true, false],
+ description: "Accept the Chef Online Master License and Services Agreement. See <https://www.chef.io/online-master-agreement/>",
+ default: false
+
+ property :config_directory, String,
+ default: ChefConfig::Config.etc_chef_dir,
+ description: "The path of the config directory."
+
+ property :log_directory, String,
+ default: lazy { platform?("mac_os_x") ? "/Library/Logs/#{ChefUtils::Dist::Infra::DIR_SUFFIX.capitalize}" : "/var/log/#{ChefUtils::Dist::Infra::DIR_SUFFIX}" },
+ default_description: "/Library/Logs/#{ChefUtils::Dist::Infra::DIR_SUFFIX.capitalize} on macOS and /var/log/#{ChefUtils::Dist::Infra::DIR_SUFFIX} otherwise",
+ description: "The path of the directory to create the log file in."
+
+ property :log_file_name, String,
+ default: "client.log",
+ description: "The name of the log file to use."
+
+ property :append_log_file, [true, false],
+ default: true,
+ description: "Append to the log file instead of overwriting the log file on each run."
+
+ property :chef_binary_path, String,
+ default: "/opt/#{ChefUtils::Dist::Infra::DIR_SUFFIX}/bin/#{ChefUtils::Dist::Infra::CLIENT}",
+ description: "The path to the #{ChefUtils::Dist::Infra::CLIENT} binary."
+
+ property :daemon_options, Array,
+ default: lazy { [] },
+ description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command."
+
+ property :environment, Hash,
+ default: lazy { {} },
+ description: "A Hash containing additional arbitrary environment variables under which the cron job will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`."
+
+ property :nice, [Integer, String],
+ description: "The process priority to run the #{ChefUtils::Dist::Infra::CLIENT} process at. A value of -20 is the highest priority and 19 is the lowest priority.",
+ introduced: "16.5",
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be an Integer between -20 and 19" => proc { |v| v >= -20 && v <= 19 } }
+
+ action :add do
+ # TODO: Replace this with a :create_if_missing action on directory when that exists
+ unless ::Dir.exist?(new_resource.log_directory)
+ directory new_resource.log_directory do
+ owner new_resource.user
+ mode "0750"
+ recursive true
+ end
+ end
+
+ declare_resource(cron_resource_type, new_resource.job_name) do
+ minute new_resource.minute
+ hour new_resource.hour
+ day new_resource.day
+ weekday new_resource.weekday
+ month new_resource.month
+ environment new_resource.environment
+ mailto new_resource.mailto if new_resource.mailto
+ user new_resource.user
+ comment new_resource.comment if new_resource.comment
+ command client_command
+ end
+ end
+
+ action :remove do
+ declare_resource(cron_resource_type, new_resource.job_name) do
+ action :delete
+ end
+ end
+
+ action_class do
+ #
+ # Generate a uniformly distributed unique number to sleep from 0 to the splay time
+ #
+ # @param [Integer] splay The number of seconds to splay
+ #
+ # @return [Integer]
+ #
+ def splay_sleep_time(splay)
+ seed = node["shard_seed"] || Digest::MD5.hexdigest(node.name).to_s.hex
+ random = Random.new(seed.to_i)
+ random.rand(splay)
+ end
+
+ #
+ # The complete cron command to run
+ #
+ # @return [String]
+ #
+ def client_command
+ cmd = ""
+ cmd << "/bin/sleep #{splay_sleep_time(new_resource.splay)}; "
+ cmd << "#{which("nice")} -n #{new_resource.nice} " if new_resource.nice
+ cmd << "#{new_resource.chef_binary_path} "
+ cmd << "#{new_resource.daemon_options.join(" ")} " unless new_resource.daemon_options.empty?
+ cmd << "-c #{::File.join(new_resource.config_directory, "client.rb")} "
+ cmd << "--chef-license accept " if new_resource.accept_chef_license
+ cmd << log_command
+ cmd << " || echo \"#{ChefUtils::Dist::Infra::PRODUCT} execution failed\"" if new_resource.mailto
+ cmd
+ end
+
+ #
+ # The portion of the overall cron job that handles logging based on the append_log_file property
+ #
+ # @return [String]
+ #
+ def log_command
+ if new_resource.append_log_file
+ "-L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}"
+ else
+ "> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1"
+ end
+ end
+
+ #
+ # The type of cron resource to run. Linux systems all support the /etc/cron.d directory
+ # and can use the cron_d resource, but Solaris / AIX / FreeBSD need to use the crontab
+ # via the legacy cron resource.
+ #
+ # @return [Symbol]
+ #
+ def cron_resource_type
+ linux? ? :cron_d : :cron
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_launchd.rb b/lib/chef/resource/chef_client_launchd.rb
new file mode 100644
index 0000000000..0e173050d0
--- /dev/null
+++ b/lib/chef/resource/chef_client_launchd.rb
@@ -0,0 +1,194 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+class Chef
+ class Resource
+ class ChefClientLaunchd < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_launchd
+
+ description "Use the **chef_client_launchd** resource to configure the #{ChefUtils::Dist::Infra::PRODUCT} to run on a schedule."
+ introduced "16.5"
+ examples <<~DOC
+ **Set the #{ChefUtils::Dist::Infra::PRODUCT} to run on a schedule**:
+
+ ```ruby
+ chef_client_launchd 'Setup the #{ChefUtils::Dist::Infra::PRODUCT} to run every 30 minutes' do
+ interval 30
+ action :enable
+ end
+ ```
+
+ **Disable the #{ChefUtils::Dist::Infra::PRODUCT} running on a schedule**:
+
+ ```ruby
+ chef_client_launchd 'Prevent the #{ChefUtils::Dist::Infra::PRODUCT} from running on a schedule' do
+ action :disable
+ end
+ ```
+ DOC
+
+ property :user, String,
+ description: "The name of the user that #{ChefUtils::Dist::Infra::PRODUCT} runs as.",
+ default: "root"
+
+ property :working_directory, String,
+ description: "The working directory to run the #{ChefUtils::Dist::Infra::PRODUCT} from.",
+ default: "/var/root"
+
+ property :interval, [Integer, String],
+ description: "Time in minutes between #{ChefUtils::Dist::Infra::PRODUCT} executions.",
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive number" => proc { |v| v > 0 } },
+ default: 30
+
+ property :splay, [Integer, String],
+ default: 300,
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive number" => proc { |v| v > 0 } },
+ description: "A random number of seconds between 0 and X to add to interval so that all #{ChefUtils::Dist::Infra::CLIENT} commands don't execute at the same time."
+
+ property :accept_chef_license, [true, false],
+ description: "Accept the Chef Online Master License and Services Agreement. See <https://www.chef.io/online-master-agreement/>",
+ default: false
+
+ property :config_directory, String,
+ description: "The path of the config directory.",
+ default: ChefConfig::Config.etc_chef_dir
+
+ property :log_directory, String,
+ description: "The path of the directory to create the log file in.",
+ default: "/Library/Logs/Chef"
+
+ property :log_file_name, String,
+ description: "The name of the log file to use.",
+ default: "client.log"
+
+ property :chef_binary_path, String,
+ description: "The path to the #{ChefUtils::Dist::Infra::CLIENT} binary.",
+ default: "/opt/#{ChefUtils::Dist::Infra::DIR_SUFFIX}/bin/#{ChefUtils::Dist::Infra::CLIENT}"
+
+ property :daemon_options, Array,
+ description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
+ default: lazy { [] }
+
+ property :environment, Hash,
+ description: "A Hash containing additional arbitrary environment variables under which the launchd daemon will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`.",
+ default: lazy { {} }
+
+ property :nice, [Integer, String],
+ description: "The process priority to run the #{ChefUtils::Dist::Infra::CLIENT} process at. A value of -20 is the highest priority and 19 is the lowest priority.",
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be an Integer between -20 and 19" => proc { |v| v >= -20 && v <= 19 } }
+
+ property :low_priority_io, [true, false],
+ description: "Run the #{ChefUtils::Dist::Infra::CLIENT} process with low priority disk IO",
+ default: true
+
+ action :enable do
+ unless ::Dir.exist?(new_resource.log_directory)
+ directory new_resource.log_directory do
+ owner new_resource.user
+ mode "0750"
+ recursive true
+ end
+ end
+
+ launchd "com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}" do
+ username new_resource.user
+ working_directory new_resource.working_directory
+ start_interval new_resource.interval * 60
+ program_arguments ["/bin/bash", "-c", client_command]
+ environment_variables new_resource.environment unless new_resource.environment.empty?
+ nice new_resource.nice
+ low_priority_io true
+ notifies :sleep, "chef_sleep[Sleep before client restart]", :immediately
+ action :create # create only creates the file. No service restart triggering
+ end
+
+ # Launchd doesn't have the concept of a reload aka restart. Instead to update a daemon config you have
+ # to unload it and then reload the new plist. That's usually fine, but not if chef-client is trying
+ # to restart itself. If the chef-client process uses launchd or macosx_service resources to restart itself
+ # we'll end up with a stopped service that will never get started back up. Instead we use this daemon
+ # that triggers when the chef-client plist file is updated, and handles the restart outside the run.
+ launchd "com.#{ChefUtils::Dist::Infra::SHORT}.restarter" do
+ username "root"
+ watch_paths ["/Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist"]
+ standard_out_path ::File.join(new_resource.log_directory, new_resource.log_file_name)
+ standard_error_path ::File.join(new_resource.log_directory, new_resource.log_file_name)
+ program_arguments ["/bin/bash",
+ "-c",
+ "echo; echo #{ChefUtils::Dist::Infra::PRODUCT} launchd daemon config has been updated. Manually unloading and reloading the daemon; echo Now unloading the daemon; launchctl unload /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist; sleep 2; echo Now loading the daemon; launchctl load /Library/LaunchDaemons/com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}.plist"]
+ action :enable # enable creates the plist & triggers service restarts on change
+ end
+
+ # We want to make sure that after we update the chef-client launchd config that we don't move on to another recipe
+ # before the restarter daemon can do its thing. This sleep avoids killing the client while it's doing something like
+ # installing a package, which could be problematic. It also makes it a bit more clear in the log that the killed process
+ # was intentional.
+ chef_sleep "Sleep before client restart" do
+ seconds 10
+ action :nothing
+ end
+ end
+
+ action :disable do
+ service ChefUtils::Dist::Infra::PRODUCT do
+ service_name "com.#{ChefUtils::Dist::Infra::SHORT}.#{ChefUtils::Dist::Infra::CLIENT}"
+ action :disable
+ end
+
+ service "com.#{ChefUtils::Dist::Infra::SHORT}.restarter" do
+ action :disable
+ end
+ end
+
+ action_class do
+ #
+ # Generate a uniformly distributed unique number to sleep from 0 to the splay time
+ #
+ # @param [Integer] splay The number of seconds to splay
+ #
+ # @return [Integer]
+ #
+ def splay_sleep_time(splay)
+ seed = node["shard_seed"] || Digest::MD5.hexdigest(node.name).to_s.hex
+ random = Random.new(seed.to_i)
+ random.rand(splay)
+ end
+
+ #
+ # random sleep time + chef-client + daemon option properties + license acceptance
+ #
+ # @return [String]
+ #
+ def client_command
+ cmd = ""
+ cmd << "/bin/sleep #{splay_sleep_time(new_resource.splay)};"
+ cmd << " #{new_resource.chef_binary_path}"
+ cmd << " #{new_resource.daemon_options.join(" ")}" unless new_resource.daemon_options.empty?
+ cmd << " -c #{::File.join(new_resource.config_directory, "client.rb")}"
+ cmd << " -L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}"
+ cmd << " --chef-license accept" if new_resource.accept_chef_license
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_scheduled_task.rb b/lib/chef/resource/chef_client_scheduled_task.rb
new file mode 100644
index 0000000000..25780afdf4
--- /dev/null
+++ b/lib/chef/resource/chef_client_scheduled_task.rb
@@ -0,0 +1,216 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefClientScheduledTask < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_scheduled_task
+
+ description "Use the **chef_client_scheduled_task** resource to setup the #{ChefUtils::Dist::Infra::PRODUCT} to run as a Windows scheduled task. This resource will also create the specified log directory if it doesn't already exist."
+ introduced "16.0"
+ examples <<~DOC
+ **Setup #{ChefUtils::Dist::Infra::PRODUCT} to run using the default 30 minute cadence**:
+
+ ```ruby
+ chef_client_scheduled_task 'Run #{ChefUtils::Dist::Infra::PRODUCT} as a scheduled task'
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} on system start**:
+
+ ```ruby
+ chef_client_scheduled_task '#{ChefUtils::Dist::Infra::PRODUCT} on start' do
+ frequency 'onstart'
+ end
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} with extra options passed to the client**:
+
+ ```ruby
+ chef_client_scheduled_task 'Run an override recipe' do
+ daemon_options ['--override-runlist mycorp_base::default']
+ end
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} daily at 01:00 am, specifying a named run-list**:
+
+ ```ruby
+ chef_client_scheduled_task 'Run chef-client named run-list daily' do
+ frequency 'daily'
+ start_time '01:00'
+ daemon_options ['-n audit_only']
+ end
+ ```
+ DOC
+
+ resource_name :chef_client_scheduled_task
+
+ property :task_name, String,
+ description: "The name of the scheduled task to create.",
+ default: ChefUtils::Dist::Infra::CLIENT
+
+ property :user, String,
+ description: "The name of the user that #{ChefUtils::Dist::Infra::PRODUCT} runs as.",
+ default: "System", sensitive: true
+
+ property :password, String,
+ description: "The password for the user that #{ChefUtils::Dist::Infra::PRODUCT} runs as.",
+ sensitive: true
+
+ property :frequency, String,
+ description: "Frequency with which to run the task.",
+ default: "minute",
+ equal_to: %w{minute hourly daily monthly once on_logon onstart on_idle}
+
+ property :frequency_modifier, [Integer, String],
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive number" => proc { |v| v > 0 } },
+ description: "Numeric value to go with the scheduled task frequency",
+ default: lazy { frequency == "minute" ? 30 : 1 },
+ default_description: "30 if frequency is 'minute', 1 otherwise"
+
+ property :accept_chef_license, [true, false],
+ description: "Accept the Chef Online Master License and Services Agreement. See <https://www.chef.io/online-master-agreement/>",
+ default: false
+
+ property :start_date, String,
+ description: "The start date for the task in m:d:Y format (ex: 12/17/2020).",
+ regex: [%r{^[0-1][0-9]\/[0-3][0-9]\/\d{4}$}]
+
+ property :start_time, String,
+ description: "The start time for the task in HH:mm format (ex: 14:00). If the frequency is minute default start time will be Time.now plus the frequency_modifier number of minutes.",
+ regex: [/^\d{2}:\d{2}$/]
+
+ property :splay, [Integer, String],
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive number" => proc { |v| v > 0 } },
+ description: "A random number of seconds between 0 and X to add to interval so that all #{ChefUtils::Dist::Infra::CLIENT} commands don't execute at the same time.",
+ default: 300
+
+ property :run_on_battery, [true, false],
+ description: "Run the #{ChefUtils::Dist::Infra::PRODUCT} task when the system is on batteries.",
+ default: true
+
+ property :config_directory, String,
+ description: "The path of the config directory.",
+ default: ChefConfig::Config.etc_chef_dir
+
+ property :log_directory, String,
+ description: "The path of the directory to create the log file in.",
+ default: lazy { |r| "#{r.config_directory}/log" },
+ default_description: "CONFIG_DIRECTORY/log"
+
+ property :log_file_name, String,
+ description: "The name of the log file to use.",
+ default: "client.log"
+
+ property :chef_binary_path, String,
+ description: "The path to the #{ChefUtils::Dist::Infra::CLIENT} binary.",
+ default: "C:/#{ChefUtils::Dist::Org::LEGACY_CONF_DIR}/#{ChefUtils::Dist::Infra::DIR_SUFFIX}/bin/#{ChefUtils::Dist::Infra::CLIENT}"
+
+ property :daemon_options, Array,
+ description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
+ default: lazy { [] }
+
+ action :add do
+ # TODO: Replace this with a :create_if_missing action on directory when that exists
+ unless Dir.exist?(new_resource.log_directory)
+ directory new_resource.log_directory do
+ inherits true
+ recursive true
+ action :create
+ end
+ end
+
+ # According to https://docs.microsoft.com/en-us/windows/desktop/taskschd/schtasks,
+ # the :once, :onstart, :onlogon, and :onidle schedules don't accept schedule modifiers
+
+ windows_task new_resource.task_name do
+ run_level :highest
+ command full_command
+ user new_resource.user
+ password new_resource.password
+ frequency new_resource.frequency.to_sym
+ frequency_modifier new_resource.frequency_modifier if frequency_supports_frequency_modifier?
+ start_time new_resource.start_time
+ start_day new_resource.start_date unless new_resource.start_date.nil?
+ random_delay new_resource.splay if frequency_supports_random_delay?
+ disallow_start_if_on_batteries new_resource.splay unless new_resource.run_on_battery
+ action %i{create enable}
+ end
+ end
+
+ action :remove do
+ windows_task new_resource.task_name do
+ action :delete
+ end
+ end
+
+ action_class do
+ #
+ # The full command to run in the scheduled task
+ #
+ # @return [String]
+ #
+ def full_command
+ # Fetch path of cmd.exe through environment variable comspec
+ cmd_path = ENV["COMSPEC"]
+
+ "#{cmd_path} /c \"#{client_cmd}\""
+ end
+
+ #
+ # Build command line to pass to cmd.exe
+ #
+ # @return [String]
+ #
+ def client_cmd
+ cmd = new_resource.chef_binary_path.dup
+ cmd << " -L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}"
+ cmd << " -c #{::File.join(new_resource.config_directory, "client.rb")}"
+
+ # Add custom options
+ cmd << " #{new_resource.daemon_options.join(" ")}" if new_resource.daemon_options.any?
+ cmd << " --chef-license accept" if new_resource.accept_chef_license
+ cmd
+ end
+
+ #
+ # not all frequencies in the windows_task resource support random_delay
+ #
+ # @return [boolean]
+ #
+ def frequency_supports_random_delay?
+ %w{once minute hourly daily weekly monthly}.include?(new_resource.frequency)
+ end
+
+ #
+ # not all frequencies in the windows_task resource support frequency_modifier
+ #
+ # @return [boolean]
+ #
+ def frequency_supports_frequency_modifier?
+ # these are the only ones that don't
+ !%w{once on_logon onstart on_idle}.include?(new_resource.frequency)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_systemd_timer.rb b/lib/chef/resource/chef_client_systemd_timer.rb
new file mode 100644
index 0000000000..e911fc2cb0
--- /dev/null
+++ b/lib/chef/resource/chef_client_systemd_timer.rb
@@ -0,0 +1,187 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefClientSystemdTimer < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_systemd_timer
+
+ description "Use the **chef_client_systemd_timer** resource to setup the #{ChefUtils::Dist::Infra::PRODUCT} to run as a systemd timer."
+ introduced "16.0"
+ examples <<~DOC
+ **Setup #{ChefUtils::Dist::Infra::PRODUCT} to run using the default 30 minute cadence**:
+
+ ```ruby
+ chef_client_systemd_timer 'Run #{ChefUtils::Dist::Infra::PRODUCT} as a systemd timer'
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} every 1 hour**:
+
+ ```ruby
+ chef_client_systemd_timer 'Run #{ChefUtils::Dist::Infra::PRODUCT} every 1 hour' do
+ interval '1hr'
+ end
+ ```
+
+ **Run #{ChefUtils::Dist::Infra::PRODUCT} with extra options passed to the client**:
+
+ ```ruby
+ chef_client_systemd_timer 'Run an override recipe' do
+ daemon_options ['--override-runlist mycorp_base::default']
+ end
+ ```
+ DOC
+
+ property :job_name, String,
+ description: "The name of the system timer to create.",
+ default: ChefUtils::Dist::Infra::CLIENT
+
+ property :description, String,
+ description: "The description to add to the systemd timer. This will be displayed when running `systemctl status` for the timer.",
+ default: "#{ChefUtils::Dist::Infra::PRODUCT} periodic execution"
+
+ property :user, String,
+ description: "The name of the user that #{ChefUtils::Dist::Infra::PRODUCT} runs as.",
+ default: "root"
+
+ property :delay_after_boot, String,
+ description: "The time to wait after booting before the interval starts. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.",
+ default: "1min"
+
+ property :interval, String,
+ description: "The interval to wait between executions. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.",
+ default: "30min"
+
+ property :splay, String,
+ description: "A interval between 0 and X to add to the interval so that all #{ChefUtils::Dist::Infra::CLIENT} commands don't execute at the same time. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.",
+ default: "5min"
+
+ property :accept_chef_license, [true, false],
+ description: "Accept the Chef Online Master License and Services Agreement. See <https://www.chef.io/online-master-agreement/>",
+ default: false
+
+ property :run_on_battery, [true, false],
+ description: "Run the timer for #{ChefUtils::Dist::Infra::PRODUCT} if the system is on battery.",
+ default: true
+
+ property :config_directory, String,
+ description: "The path of the config directory.",
+ default: ChefConfig::Config.etc_chef_dir
+
+ property :chef_binary_path, String,
+ description: "The path to the #{ChefUtils::Dist::Infra::CLIENT} binary.",
+ default: "/opt/#{ChefUtils::Dist::Infra::DIR_SUFFIX}/bin/#{ChefUtils::Dist::Infra::CLIENT}"
+
+ property :daemon_options, Array,
+ description: "An array of options to pass to the #{ChefUtils::Dist::Infra::CLIENT} command.",
+ default: lazy { [] }
+
+ property :environment, Hash,
+ description: "A Hash containing additional arbitrary environment variables under which the systemd timer will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`.",
+ default: lazy { {} }
+
+ property :cpu_quota, [Integer, String],
+ description: "The systemd CPUQuota to run the #{ChefUtils::Dist::Infra::CLIENT} process with. This is a percentage value of the total CPU time available on the system. If the system has more than 1 core this may be a value greater than 100.",
+ introduced: "16.5",
+ coerce: proc { |x| Integer(x) },
+ callbacks: { "should be a positive Integer" => proc { |v| v > 0 } }
+
+ action :add do
+ systemd_unit "#{new_resource.job_name}.service" do
+ content service_content
+ action :create
+ end
+
+ systemd_unit "#{new_resource.job_name}.timer" do
+ content timer_content
+ action %i{create enable start}
+ end
+ end
+
+ action :remove do
+ systemd_unit "#{new_resource.job_name}.service" do
+ action :delete
+ end
+
+ systemd_unit "#{new_resource.job_name}.timer" do
+ action :delete
+ end
+ end
+
+ action_class do
+ #
+ # The chef-client command to run in the systemd unit.
+ #
+ # @return [String]
+ #
+ def chef_client_cmd
+ cmd = new_resource.chef_binary_path.dup
+ cmd << " #{new_resource.daemon_options.join(" ")}" unless new_resource.daemon_options.empty?
+ cmd << " --chef-license accept" if new_resource.accept_chef_license
+ cmd << " -c #{::File.join(new_resource.config_directory, "client.rb")}"
+ cmd
+ end
+
+ #
+ # The timer content to pass to the systemd_unit
+ #
+ # @return [Hash]
+ #
+ def timer_content
+ {
+ "Unit" => { "Description" => new_resource.description },
+ "Timer" => {
+ "OnBootSec" => new_resource.delay_after_boot,
+ "OnUnitActiveSec" => new_resource.interval,
+ "RandomizedDelaySec" => new_resource.splay,
+ },
+ "Install" => { "WantedBy" => "timers.target" },
+ }
+ end
+
+ #
+ # The service content to pass to the systemd_unit
+ #
+ # @return [Hash]
+ #
+ def service_content
+ unit = {
+ "Unit" => {
+ "Description" => new_resource.description,
+ "After" => "network.target auditd.service",
+ },
+ "Service" => {
+ "Type" => "oneshot",
+ "ExecStart" => chef_client_cmd,
+ "SuccessExitStatus" => [3, 213, 35, 37, 41],
+ },
+ "Install" => { "WantedBy" => "multi-user.target" },
+ }
+
+ unit["Service"]["ConditionACPower"] = "true" unless new_resource.run_on_battery
+ unit["Service"]["CPUQuota"] = new_resource.cpu_quota if new_resource.cpu_quota
+ unit["Service"]["Environment"] = new_resource.environment.collect { |k, v| "\"#{k}=#{v}\"" } unless new_resource.environment.empty?
+ unit
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_client_trusted_certificate.rb b/lib/chef/resource/chef_client_trusted_certificate.rb
new file mode 100644
index 0000000000..b5272fbe01
--- /dev/null
+++ b/lib/chef/resource/chef_client_trusted_certificate.rb
@@ -0,0 +1,101 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefClientTrustedCertificate < Chef::Resource
+ unified_mode true
+
+ provides :chef_client_trusted_certificate
+
+ description "Use the **chef_client_trusted_certificate** resource to add certificates to #{ChefUtils::Dist::Infra::PRODUCT}'s trusted certificate directory. This allows the #{ChefUtils::Dist::Infra::PRODUCT} to communicate with internal encrypted resources without errors."
+ introduced "16.5"
+ examples <<~DOC
+ **Trust a self signed certificate**:
+
+ ```ruby
+ chef_client_trusted_certificate 'self-signed.badssl.com' do
+ certificate <<~CERT
+ -----BEGIN CERTIFICATE-----
+ MIIDeTCCAmGgAwIBAgIJAPziuikCTox4MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
+ c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0x
+ OTEwMDkyMzQxNTJaFw0yMTEwMDgyMzQxNTJaMGIxCzAJBgNVBAYTAlVTMRMwEQYD
+ VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK
+ DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB
+ BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2
+ PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW
+ hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A
+ xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve
+ ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY
+ QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T
+ BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI
+ hvcNAQELBQADggEBAGlwCdbPxflZfYOaukZGCaxYK6gpincX4Lla4Ui2WdeQxE95
+ w7fChXvP3YkE3UYUE7mupZ0eg4ZILr/A0e7JQDsgIu/SRTUE0domCKgPZ8v99k3A
+ vka4LpLK51jHJJK7EFgo3ca2nldd97GM0MU41xHFk8qaK1tWJkfrrfcGwDJ4GQPI
+ iLlm6i0yHq1Qg1RypAXJy5dTlRXlCLd8ufWhhiwW0W75Va5AEnJuqpQrKwl3KQVe
+ wGj67WWRgLfSr+4QG1mNvCZb2CkjZWmxkGPuoP40/y7Yu5OFqxP5tAjj4YixCYTW
+ EVA0pmzIzgBg+JIe3PdRy27T0asgQW/F4TY61Yk=
+ -----END CERTIFICATE-----
+ CERT
+ end
+ ```
+ DOC
+
+ property :cert_name, String, name_property: true,
+ description: "The name to use for the certificate file on disk. If not provided the name of the resource block will be used instead."
+
+ property :certificate, String, required: [:add],
+ description: "The text of the certificate file including the BEGIN/END comment lines."
+
+ action :add do
+ unless ::Dir.exist?(Chef::Config[:trusted_certs_dir])
+ directory Chef::Config[:trusted_certs_dir] do
+ mode "0640"
+ recursive true
+ end
+ end
+
+ file cert_path do
+ content new_resource.certificate
+ mode "0640"
+ end
+ end
+
+ action :remove do
+ file cert_path do
+ action :delete
+ end
+ end
+
+ action_class do
+ #
+ # The path to the string on disk
+ #
+ # @return [String]
+ #
+ def cert_path
+ path = ::File.join(Chef::Config[:trusted_certs_dir], new_resource.cert_name)
+ path << ".pem" unless path.end_with?(".pem")
+ path
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index 4445bf0f89..2c5b342bce 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,36 +16,80 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/resource/gem_package"
+require_relative "package"
+require_relative "gem_package"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class ChefGem < Chef::Resource::Package::GemPackage
- resource_name :chef_gem
-
- property :gem_binary, default: "#{RbConfig::CONFIG['bindir']}/gem",
- 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
-
- 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."
+ unified_mode true
+ provides :chef_gem
+
+ description <<~DESC
+ Use the **chef_gem** resource to install a gem only for the instance of Ruby that is dedicated to the #{ChefUtils::Dist::Infra::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 `#{ChefUtils::Dist::Infra::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.
+
+ Warning: The **chef_gem** and **gem_package** resources are both used to install Ruby gems. For any machine on which #{ChefUtils::Dist::Infra::PRODUCT} is
+ installed, there are two instances of Ruby. One is the standard, system-wide instance of Ruby and the other is a dedicated instance that is
+ available only to #{ChefUtils::Dist::Infra::PRODUCT}.
+ Use the **chef_gem** resource to install gems into the instance of Ruby that is dedicated to #{ChefUtils::Dist::Infra::PRODUCT}.
+ Use the **gem_package** resource to install all other gems (i.e. install gems system-wide).
+ DESC
+
+ examples <<~EXAMPLES
+ **Compile time vs. converge time installation of gems**
+
+ To install a gem while #{ChefUtils::Dist::Infra::PRODUCT} is configuring the node (the converge phase), set the `compile_time` property to `false`:
+ ```ruby
+ chef_gem 'loofah' do
+ compile_time false
+ action :install
+ end
+ ```
+
+ To install a gem while the resource collection is being built (the compile phase), set the `compile_time` property to `true`:
+ ```ruby
+ chef_gem 'loofah' do
+ compile_time true
+ action :install
end
+ ```
+
+ **Install MySQL gem into #{ChefUtils::Dist::Infra::PRODUCT}***
+ ```ruby
+ apt_update
- if compile_time || compile_time.nil?
- Array(action).each do |action|
- self.run_action(action)
- end
- Gem.clear_paths
+ build_essential 'install compilation tools' do
+ compile_time true
end
- end
+
+ chef_gem 'mysql'
+ ```
+ EXAMPLES
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ property :gem_binary, String,
+ default: "#{RbConfig::CONFIG["bindir"]}/gem",
+ default_description: "The `gem` binary included with #{ChefUtils::Dist::Infra::PRODUCT}.",
+ description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by #{ChefUtils::Dist::Infra::PRODUCT} will be used.",
+ 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" },
+ }
end
end
end
diff --git a/lib/chef/resource/chef_handler.rb b/lib/chef/resource/chef_handler.rb
new file mode 100644
index 0000000000..a006b2648a
--- /dev/null
+++ b/lib/chef/resource/chef_handler.rb
@@ -0,0 +1,283 @@
+#
+# Author:: Seth Chisamore <schisamo@chef.io>
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefHandler < Chef::Resource
+ unified_mode true
+
+ provides(:chef_handler) { true }
+
+ description "Use the **chef_handler** resource to enable handlers during a #{ChefUtils::Dist::Infra::PRODUCT} run. The resource allows arguments to be passed to #{ChefUtils::Dist::Infra::PRODUCT}, which then applies the conditions defined by the custom handler to the node attribute data collected during a #{ChefUtils::Dist::Infra::PRODUCT} run, and then processes the handler based on that data.\nThe **chef_handler** resource is typically defined early in a node's run-list (often being the first item). This ensures that all of the handlers will be available for the entire #{ChefUtils::Dist::Infra::PRODUCT} run."
+ introduced "14.0"
+ examples <<~'DOC'
+ **Enable the 'MyHandler' handler**
+
+ The following example shows how to enable a fictional 'MyHandler' handler which is located on disk at `/etc/chef/my_handler.rb`. The handler will be configured to run with Chef Infra Client and will be passed values to the handler's initializer method:
+
+ ```ruby
+ chef_handler 'MyHandler' do
+ source '/etc/chef/my_handler.rb' # the file should already be at this path
+ arguments path: '/var/chef/reports'
+ action :enable
+ end
+ ```
+
+ **Enable handlers during the compile phase**
+
+ ```ruby
+ chef_handler 'Chef::Handler::JsonFile' do
+ source 'chef/handler/json_file'
+ arguments path: '/var/chef/reports'
+ action :enable
+ compile_time true
+ end
+ ```
+
+ **Handle only exceptions**
+
+ ```ruby
+ chef_handler 'Chef::Handler::JsonFile' do
+ source 'chef/handler/json_file'
+ arguments path: '/var/chef/reports'
+ type exception: true
+ action :enable
+ end
+ ```
+
+ **Cookbook Versions (a custom handler)**
+
+ [@juliandunn](https://github.com/juliandunn) created a custom report handler that logs all of the cookbooks and cookbook versions that were used during a Chef Infra Client run, and then reports after the run is complete.
+
+ cookbook_versions.rb:
+
+ The following custom handler defines how cookbooks and cookbook versions that are used during a Chef Infra Client run will be compiled into a report using the `Chef::Log` class in Chef Infra Client:
+
+ ```ruby
+ require 'chef/log'
+
+ module Chef
+ class CookbookVersionsHandler < Chef::Handler
+ def report
+ cookbooks = run_context.cookbook_collection
+ Chef::Log.info('Cookbooks and versions run: #{cookbooks.map {|x| x.name.to_s + ' ' + x.version }}')
+ end
+ end
+ end
+ ```
+
+ default.rb:
+
+ The following recipe is added to the run-list for every node on which a list of cookbooks and versions will be generated as report output after every Chef Infra Client run.
+
+ ```ruby
+ cookbook_file '/etc/chef/cookbook_versions.rb' do
+ source 'cookbook_versions.rb'
+ action :create
+ end
+
+ chef_handler 'Chef::CookbookVersionsHandler' do
+ source '/etc/chef/cookbook_versions.rb'
+ type report: true
+ action :enable
+ end
+ ```
+
+ This recipe will generate report output similar to the following:
+
+ ```
+ [2013-11-26T03:11:06+00:00] INFO: Chef Infra Client Run complete in 0.300029878 seconds
+ [2013-11-26T03:11:06+00:00] INFO: Running report handlers
+ [2013-11-26T03:11:06+00:00] INFO: Cookbooks and versions run: ["cookbook_versions_handler 1.0.0"]
+ [2013-11-26T03:11:06+00:00] INFO: Report handlers complete
+ ```
+
+ **JsonFile Handler**
+
+ The JsonFile handler is available from the `chef_handler` cookbook and can be used with exceptions and reports. It serializes run status data to a JSON file. This handler may be enabled in one of the following ways.
+
+ By adding the following lines of Ruby code to either the client.rb file or the solo.rb file, depending on how Chef Infra Client is being run:
+
+ ```ruby
+ require 'chef/handler/json_file'
+ report_handlers << Chef::Handler::JsonFile.new(path: '/var/chef/reports')
+ exception_handlers << Chef::Handler::JsonFile.new(path: '/var/chef/reports')
+ ```
+
+ By using the `chef_handler` resource in a recipe, similar to the following:
+
+ ```ruby
+ chef_handler 'Chef::Handler::JsonFile' do
+ source 'chef/handler/json_file'
+ arguments path: '/var/chef/reports'
+ action :enable
+ end
+ ```
+
+ After it has run, the run status data can be loaded and inspected via Interactive Ruby (IRb):
+
+ ```
+ irb(main):002:0> require 'json' => true
+ irb(main):003:0> require 'chef' => true
+ irb(main):004:0> r = JSON.parse(IO.read('/var/chef/reports/chef-run-report-20110322060731.json')) => ... output truncated
+ irb(main):005:0> r.keys => ['end_time', 'node', 'updated_resources', 'exception', 'all_resources', 'success', 'elapsed_time', 'start_time', 'backtrace']
+ irb(main):006:0> r['elapsed_time'] => 0.00246
+ ```
+
+ Register the JsonFile handler
+
+ ```ruby
+ chef_handler 'Chef::Handler::JsonFile' do
+ source 'chef/handler/json_file'
+ arguments path: '/var/chef/reports'
+ action :enable
+ end
+ ```
+
+ **ErrorReport Handler**
+
+ The ErrorReport handler is built into Chef Infra Client and can be used for both exceptions and reports. It serializes error report data to a JSON file. This handler may be enabled in one of the following ways.
+
+ By adding the following lines of Ruby code to either the client.rb file or the solo.rb file, depending on how Chef Infra Client is being run:
+
+ ```ruby
+ require 'chef/handler/error_report'
+ report_handlers << Chef::Handler::ErrorReport.new
+ exception_handlers << Chef::Handler::ErrorReport.new
+ ```
+
+ By using the `chef_handler` resource in a recipe, similar to the following:
+
+ ```ruby
+ chef_handler 'Chef::Handler::ErrorReport' do
+ source 'chef/handler/error_report'
+ action :enable
+ end
+ ```
+ DOC
+
+ property :class_name, String,
+ description: "The name of the handler class. This 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 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 handler for the current #{ChefUtils::Dist::Infra::PRODUCT} 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 handler for the current #{ChefUtils::Dist::Infra::PRODUCT} 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'.
+ #
+ # @return [void]
+ 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 hierarchy 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 parameter 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/chef_sleep.rb b/lib/chef/resource/chef_sleep.rb
new file mode 100644
index 0000000000..c6d3e45877
--- /dev/null
+++ b/lib/chef/resource/chef_sleep.rb
@@ -0,0 +1,72 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class ChefSleep < Chef::Resource
+ provides :chef_sleep
+
+ unified_mode true
+
+ description "Use the **chef_sleep** resource to pause (sleep) for a number of seconds during a #{ChefUtils::Dist::Infra::PRODUCT} run. Only use this resource when a command or service exits successfully but is not ready for the next step in a recipe."
+ introduced "15.5"
+ examples <<~DOC
+ **Sleep for 10 seconds**:
+
+ ```ruby
+ chef_sleep '10'
+ ```
+
+ **Sleep for 10 seconds with a descriptive resource name for logging**:
+
+ ```ruby
+ chef_sleep 'wait for the service to start' do
+ seconds 10
+ end
+ ```
+
+ **Use a notification from another resource to sleep only when necessary**:
+
+ ```ruby
+ service 'Service that is slow to start and reports as started' do
+ service_name 'my_database'
+ action :start
+ notifies :sleep, chef_sleep['wait for service start']
+ end
+
+ chef_sleep 'wait for service start' do
+ seconds 30
+ action :nothing
+ end
+ ```
+ DOC
+
+ property :seconds, [String, Integer],
+ description: "The number of seconds to sleep.",
+ coerce: proc { |s| Integer(s) },
+ name_property: true
+
+ action :sleep do
+ converge_by("sleep #{new_resource.seconds} seconds") do
+ sleep(new_resource.seconds)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chef_vault_secret.rb b/lib/chef/resource/chef_vault_secret.rb
new file mode 100644
index 0000000000..1c8fa985f9
--- /dev/null
+++ b/lib/chef/resource/chef_vault_secret.rb
@@ -0,0 +1,135 @@
+#
+# Author:: Joshua Timberman <joshua@chef.io>
+# Copyright:: Copyright (c) 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_relative "../resource"
+autoload :ChefVault, "chef-vault"
+
+class Chef
+ class Resource
+ class ChefVaultSecret < Chef::Resource
+ unified_mode true
+
+ provides :chef_vault_secret
+
+ introduced "16.0"
+ description "Use the **chef_vault_secret** resource to store secrets in Chef Vault items. Where possible and relevant, this resource attempts to map behavior and functionality to the knife vault sub-commands."
+ examples <<~DOC
+ **To create a 'foo' item in an existing 'bar' data bag**:
+
+ ```ruby
+ chef_vault_secret 'foo' do
+ data_bag 'bar'
+ raw_data({'auth' => 'baz'})
+ admins 'jtimberman'
+ search '*:*'
+ end
+ ```
+
+ **To allow multiple admins access to an item**:
+
+ ```ruby
+ chef_vault_secret 'root-password' do
+ admins 'jtimberman,paulmooring'
+ data_bag 'secrets'
+ raw_data({'auth' => 'DoNotUseThisPasswordForRoot'})
+ search '*:*'
+ end
+ ```
+ DOC
+
+ property :id, String, name_property: true,
+ description: "The name of the data bag item if it differs from the name of the resource block"
+
+ property :data_bag, String, required: true, desired_state: false,
+ description: "The data bag that contains the item."
+
+ property :admins, [String, Array], required: true, desired_state: false,
+ description: "A list of admin users who should have access to the item. Corresponds to the 'admin' option when using the chef-vault knife plugin. Can be specified as a comma separated string or an array."
+
+ property :clients, [String, Array], desired_state: false,
+ description: "A search query for the nodes' API clients that should have access to the item."
+
+ property :search, String, default: "*:*", desired_state: false,
+ description: "Search query that would match the same used for the clients, gets stored as a field in the item."
+
+ property :raw_data, [Hash, Mash], default: {},
+ description: "The raw data, as a Ruby Hash, that will be stored in the item."
+
+ property :environment, [String, NilClass], desired_state: false,
+ description: "The Chef environment of the data if storing per environment values."
+
+ load_current_value do
+
+ item = ChefVault::Item.load(data_bag, id)
+ raw_data item.raw_data
+ clients item.get_clients
+ admins item.get_admins
+ search item.search
+ rescue ChefVault::Exceptions::SecretDecryption
+ current_value_does_not_exist!
+ rescue ChefVault::Exceptions::KeysNotFound
+ current_value_does_not_exist!
+ rescue Net::HTTPClientException => e
+ current_value_does_not_exist! if e.response_code == "404"
+
+ end
+
+ action :create do
+ description "Creates the item, or updates it if it already exists."
+
+ converge_if_changed do
+ item = ChefVault::Item.new(new_resource.data_bag, new_resource.id)
+
+ Chef::Log.debug("#{new_resource.id} environment: '#{new_resource.environment}'")
+ item.raw_data = if new_resource.environment.nil?
+ new_resource.raw_data.merge("id" => new_resource.id)
+ else
+ { "id" => new_resource.id, new_resource.environment => new_resource.raw_data }
+ end
+
+ Chef::Log.debug("#{new_resource.id} search query: '#{new_resource.search}'")
+ item.search(new_resource.search)
+ Chef::Log.debug("#{new_resource.id} clients: '#{new_resource.clients}'")
+ item.clients([new_resource.clients].flatten.join(",")) unless new_resource.clients.nil?
+ Chef::Log.debug("#{new_resource.id} admins (users): '#{new_resource.admins}'")
+ item.admins([new_resource.admins].flatten.join(","))
+ item.save
+ end
+ end
+
+ action :create_if_missing do
+ description "Calls the create action unless it exists."
+
+ action_create if current_resource.nil?
+ end
+
+ action :delete do
+ description "Deletes the item and the item's keys ('id'_keys)."
+
+ chef_data_bag_item new_resource.id do
+ data_bag new_resource.data_bag
+ action :delete
+ end
+
+ chef_data_bag_item [new_resource.id, "keys"].join("_") do
+ data_bag new_resource.data_bag
+ action :delete
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chocolatey_config.rb b/lib/chef/resource/chocolatey_config.rb
new file mode 100644
index 0000000000..c4f100c28d
--- /dev/null
+++ b/lib/chef/resource/chocolatey_config.rb
@@ -0,0 +1,102 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+class Chef
+ class Resource
+ class ChocolateyConfig < Chef::Resource
+ unified_mode true
+
+ provides :chocolatey_config
+
+ description "Use the **chocolatey_config** resource to add or remove Chocolatey configuration keys."
+ introduced "14.3"
+ examples <<~DOC
+ **Set the Chocolatey cacheLocation config**:
+
+ ```ruby
+ chocolatey_config 'Set cacheLocation config' do
+ config_key 'cacheLocation'
+ value 'C:\temp\choco'
+ end
+ ```
+
+ **Unset a Chocolatey config**:
+
+ ```ruby
+ chocolatey_config 'BogusConfig' do
+ action :unset
+ end
+ ```
+ DOC
+
+ property :config_key, String, name_property: true,
+ description: "An optional property to set the config key name if it differs from the resource block's name."
+
+ property :value, String,
+ description: "The value to set."
+
+ load_current_value do
+ current_val = fetch_config_element(config_key)
+ current_value_does_not_exist! if current_val.nil?
+
+ config_key config_key
+ value current_val
+ end
+
+ # @param [String] id the config name
+ # @return [String] the element's value field
+ def fetch_config_element(id)
+ require "rexml/document" unless defined?(REXML::Document)
+ config_file = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\config\\chocolatey.config"
+ raise "Could not find the Chocolatey config at #{config_file}!" unless ::File.exist?(config_file)
+
+ contents = REXML::Document.new(::File.read(config_file))
+ data = REXML::XPath.first(contents, "//config/add[@key=\"#{id}\"]")
+ data ? data.attribute("value").to_s : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :set do
+ description "Sets a Chocolatey config value."
+
+ raise "#{new_resource}: When adding a Chocolatey config you must pass the 'value' property!" unless new_resource.value
+
+ converge_if_changed do
+ shell_out!(choco_cmd("set"))
+ end
+ end
+
+ action :unset do
+ description "Unsets a Chocolatey config value."
+
+ if current_resource
+ converge_by("unset Chocolatey config '#{new_resource.config_key}'") do
+ shell_out!(choco_cmd("unset"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco config command string
+ def choco_cmd(action)
+ cmd = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\bin\\choco config #{action} --name #{new_resource.config_key}"
+ cmd << " --value #{new_resource.value}" if action == "set"
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chocolatey_feature.rb b/lib/chef/resource/chocolatey_feature.rb
new file mode 100644
index 0000000000..66752fbd5d
--- /dev/null
+++ b/lib/chef/resource/chocolatey_feature.rb
@@ -0,0 +1,97 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+class Chef
+ class Resource
+ class ChocolateyFeature < Chef::Resource
+ unified_mode true
+ provides :chocolatey_feature
+
+ description "Use the **chocolatey_feature** resource to enable and disable Chocolatey features."
+ introduced "15.1"
+ examples <<~DOC
+ **Enable the checksumFiles Chocolatey feature**
+
+ ```ruby
+ chocolatey_feature 'checksumFiles' do
+ action :enable
+ end
+ ```
+
+ **Disable the checksumFiles Chocolatey feature**
+
+ ```ruby
+ chocolatey_feature 'checksumFiles' do
+ action :disable
+ end
+ ```
+ DOC
+
+ property :feature_name, String, name_property: true,
+ description: "The name of the Chocolatey feature to enable or disable."
+
+ property :feature_state, [TrueClass, FalseClass], default: false, skip_docs: true
+
+ load_current_value do
+ current_state = fetch_feature_element(feature_name)
+ current_value_does_not_exist! if current_state.nil?
+
+ feature_name feature_name
+ feature_state current_state == "true"
+ end
+
+ # @param [String] id the feature name
+ # @return [String] the element's value field
+ def fetch_feature_element(name)
+ require "rexml/document" unless defined?(REXML::Document)
+ config_file = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\config\\chocolatey.config"
+ raise "Could not find the Chocolatey config at #{config_file}!" unless ::File.exist?(config_file)
+
+ contents = REXML::Document.new(::File.read(config_file))
+ data = REXML::XPath.first(contents, "//features/feature[@name=\"#{name}\"]")
+ data ? data.attribute("enabled").to_s : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :enable do
+ description "Enables a named Chocolatey feature."
+
+ if current_resource.feature_state != true
+ converge_by("enable Chocolatey feature '#{new_resource.feature_name}'") do
+ shell_out!(choco_cmd("enable"))
+ end
+ end
+ end
+
+ action :disable do
+ description "Disables a named Chocolatey feature."
+
+ if current_resource.feature_state == true
+ converge_by("disable Chocolatey feature '#{new_resource.feature_name}'") do
+ shell_out!(choco_cmd("disable"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco feature command string
+ def choco_cmd(action)
+ "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\bin\\choco feature #{action} --name #{new_resource.feature_name}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chocolatey_package.rb b/lib/chef/resource/chocolatey_package.rb
index 805d3a3121..e0568e586a 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,24 +16,68 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
class Chef
class Resource
class ChocolateyPackage < Chef::Resource::Package
+ unified_mode true
- provides :chocolatey_package, os: "windows"
+ provides :chocolatey_package
- allowed_actions :install, :upgrade, :remove, :uninstall, :purge, :reconfig
+ description "Use the **chocolatey_package** resource to manage packages using Chocolatey on the Microsoft Windows platform. Note: The Chocolatey package manager is not installed on Windows by default. You will need to install it prior to using this resource by adding the [Chocolatey cookbook](https://supermarket.chef.io/cookbooks/chocolatey/) to your node's run list."
+ introduced "12.7"
+ examples <<~DOC
+ **Install a Chocolatey package**:
- def initialize(name, run_context = nil)
- super
- @resource_name = :chocolatey_package
- end
+ ```ruby
+ chocolatey_package 'name of package' do
+ action :install
+ end
+ ```
- property :package_name, [String, Array], coerce: proc { |x| [x].flatten }
+ **Install a package with options with Chocolatey's `--checksum` option**:
- property :version, [String, Array], coerce: proc { |x| [x].flatten }
+ ```ruby
+ chocolatey_package 'name of package' do
+ options '--checksum 1234567890'
+ action :install
+ end
+ ```
+ DOC
+
+ allowed_actions :install, :upgrade, :remove, :purge, :reconfig
+
+ # windows can't take Array options yet
+ property :options, [String, Array],
+ description: "One (or more) additional options that are passed to the command."
+
+ property :list_options, String,
+ introduced: "15.3",
+ description: "One (or more) additional list options that are passed to the command."
+
+ property :user, String,
+ introduced: "15.3",
+ description: "The username to authenticate feeds."
+
+ property :password, String,
+ introduced: "15.3",
+ description: "The password to authenticate to the source."
+
+ property :package_name, [String, Array],
+ description: "The name of the package. Default value: the name of the resource block.",
+ 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 }
+
+ # In the choco if we have the feature useEnhancedExitCodes turned on, then choco will provide enhanced exit codes(2: no results).
+ # Choco exit codes https://chocolatey.org/docs/commandsinfo#exit-codes
+ property :returns, [Integer, Array],
+ description: "The exit code(s) returned a chocolatey package that indicate success.",
+ default: [ 0, 2 ], desired_state: false,
+ introduced: "12.18"
end
end
end
diff --git a/lib/chef/resource/chocolatey_source.rb b/lib/chef/resource/chocolatey_source.rb
new file mode 100644
index 0000000000..cee4289682
--- /dev/null
+++ b/lib/chef/resource/chocolatey_source.rb
@@ -0,0 +1,148 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+class Chef
+ class Resource
+ class ChocolateySource < Chef::Resource
+ unified_mode true
+ provides :chocolatey_source
+
+ description "Use the **chocolatey_source** resource to add, remove, enable, or disable Chocolatey sources."
+ introduced "14.3"
+ examples <<~DOC
+ **Add a Chocolatey source**
+
+ ```ruby
+ chocolatey_source 'MySource' do
+ source 'http://example.com/something'
+ action :add
+ end
+ ```
+
+ **Remove a Chocolatey source**
+
+ ```ruby
+ chocolatey_source 'MySource' do
+ action :remove
+ end
+ ```
+ DOC
+
+ property :source_name, String, name_property: true,
+ description: "An optional property to set the source name if it differs from the resource block's name."
+
+ property :source, String,
+ description: "The source URL."
+
+ property :bypass_proxy, [TrueClass, FalseClass], default: false,
+ description: "Whether or not to bypass the system's proxy settings to access the source."
+
+ property :admin_only, [TrueClass, FalseClass], default: false,
+ description: "Whether or not to set the source to be accessible to only admins.",
+ introduced: "15.1"
+
+ property :allow_self_service, [TrueClass, FalseClass], default: false,
+ description: "Whether or not to set the source to be used for self service.",
+ introduced: "15.1"
+
+ property :priority, Integer, default: 0,
+ description: "The priority level of the source."
+
+ property :disabled, [TrueClass, FalseClass], default: false, desired_state: false, skip_docs: true
+
+ load_current_value do
+ element = fetch_source_element(source_name)
+ current_value_does_not_exist! if element.nil?
+
+ source_name element["id"]
+ source element["value"]
+ bypass_proxy element["bypassProxy"] == "true"
+ admin_only element["adminOnly"] == "true"
+ allow_self_service element["selfService"] == "true"
+ priority element["priority"].to_i
+ disabled element["disabled"] == "true"
+ end
+
+ # @param [String] id the source name
+ # @return [REXML::Attributes] finds the source element with the
+ def fetch_source_element(id)
+ require "rexml/document" unless defined?(REXML::Document)
+
+ config_file = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\config\\chocolatey.config"
+ raise "Could not find the Chocolatey config at #{config_file}!" unless ::File.exist?(config_file)
+
+ config_contents = REXML::Document.new(::File.read(config_file))
+ data = REXML::XPath.first(config_contents, "//sources/source[@id=\"#{id}\"]")
+ data ? data.attributes : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :add do
+ description "Adds a Chocolatey source."
+
+ raise "#{new_resource}: When adding a Chocolatey source you must pass the 'source' property!" unless new_resource.source
+
+ converge_if_changed do
+ shell_out!(choco_cmd("add"))
+ end
+ end
+
+ action :remove do
+ description "Removes a Chocolatey source."
+
+ if current_resource
+ converge_by("remove Chocolatey source '#{new_resource.source_name}'") do
+ shell_out!(choco_cmd("remove"))
+ end
+ end
+ end
+
+ action :disable do
+ description "Disables a Chocolatey source."
+
+ if current_resource.disabled != true
+ converge_by("disable Chocolatey source '#{new_resource.source_name}'") do
+ shell_out!(choco_cmd("disable"))
+ end
+ end
+ end
+
+ action :enable do
+ description "Enables a Chocolatey source."
+
+ if current_resource.disabled == true
+ converge_by("enable Chocolatey source '#{new_resource.source_name}'") do
+ shell_out!(choco_cmd("enable"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco source command string
+ def choco_cmd(action)
+ cmd = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\bin\\choco source #{action} -n \"#{new_resource.source_name}\""
+ if action == "add"
+ cmd << " -s #{new_resource.source} --priority=#{new_resource.priority}"
+ cmd << " --bypassproxy" if new_resource.bypass_proxy
+ cmd << " --allowselfservice" if new_resource.allow_self_service
+ cmd << " --adminonly" if new_resource.admin_only
+ end
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb
index 452718cae8..76f7637693 100644
--- a/lib/chef/resource/conditional.rb
+++ b/lib/chef/resource/conditional.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/mixin/shell_out"
-require "chef/guard_interpreter"
+require_relative "../mixin/shell_out"
+require_relative "../guard_interpreter"
class Chef
class Resource
diff --git a/lib/chef/resource/cookbook_file.rb b/lib/chef/resource/cookbook_file.rb
index 785cf693be..f1ae195426 100644
--- a/lib/chef/resource/cookbook_file.rb
+++ b/lib/chef/resource/cookbook_file.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,32 +18,31 @@
# limitations under the License.
#
-require "chef/resource/file"
-require "chef/provider/cookbook_file"
-require "chef/mixin/securable"
+require_relative "file"
+require_relative "../provider/cookbook_file"
+require_relative "../mixin/securable"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class CookbookFile < Chef::Resource::File
include Chef::Mixin::Securable
+ unified_mode true
- default_action :create
+ provides :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 #{ChefUtils::Dist::Infra::PRODUCT}. 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 #{ChefUtils::Dist::Infra::PRODUCT} 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 #{ChefUtils::Dist::Server::PRODUCT}. A file is not transferred when the checksums match. Only files that require an update are transferred from the #{ChefUtils::Dist::Server::PRODUCT} 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).",
+ desired_state: false,
+ default_description: "The current cookbook name"
+ default_action :create
end
end
end
diff --git a/lib/chef/resource/cron.rb b/lib/chef/resource/cron.rb
deleted file mode 100644
index a76d454bf0..0000000000
--- a/lib/chef/resource/cron.rb
+++ /dev/null
@@ -1,216 +0,0 @@
-#
-# Author:: Bryan McLellan (btm@loftninjas.org)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2009-2016, Bryan McLellan
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 Cron < Chef::Resource
-
- identity_attr :command
-
- state_attrs :minute, :hour, :day, :month, :weekday, :user
-
- default_action :create
- allowed_actions :create, :delete
-
- def initialize(name, run_context = nil)
- super
- @minute = "*"
- @hour = "*"
- @day = "*"
- @month = "*"
- @weekday = "*"
- @command = nil
- @user = "root"
- @mailto = nil
- @path = nil
- @shell = nil
- @home = nil
- @time = nil
- @environment = {}
- end
-
- def minute(arg = nil)
- if arg.is_a?(Integer)
- converted_arg = arg.to_s
- else
- converted_arg = arg
- end
- begin
- if integerize(arg) > 59 then raise RangeError end
- rescue ArgumentError
- end
- set_or_return(
- :minute,
- converted_arg,
- :kind_of => String
- )
- end
-
- def hour(arg = nil)
- if arg.is_a?(Integer)
- converted_arg = arg.to_s
- else
- converted_arg = arg
- end
- begin
- if integerize(arg) > 23 then raise RangeError end
- rescue ArgumentError
- end
- set_or_return(
- :hour,
- converted_arg,
- :kind_of => String
- )
- end
-
- def day(arg = nil)
- if arg.is_a?(Integer)
- converted_arg = arg.to_s
- else
- converted_arg = arg
- end
- begin
- if integerize(arg) > 31 then raise RangeError end
- rescue ArgumentError
- end
- set_or_return(
- :day,
- converted_arg,
- :kind_of => String
- )
- end
-
- def month(arg = nil)
- if arg.is_a?(Integer)
- converted_arg = arg.to_s
- else
- converted_arg = arg
- end
- begin
- if integerize(arg) > 12 then raise RangeError end
- rescue ArgumentError
- end
- set_or_return(
- :month,
- converted_arg,
- :kind_of => String
- )
- end
-
- def weekday(arg = nil)
- if arg.is_a?(Integer)
- converted_arg = arg.to_s
- else
- converted_arg = arg
- end
- begin
- error_message = "You provided '#{arg}' as a weekday, acceptable values are "
- error_message << Provider::Cron::WEEKDAY_SYMBOLS.map { |sym| ":#{sym}" }.join(", ")
- error_message << " and a string in crontab format"
- if (arg.is_a?(Symbol) && !Provider::Cron::WEEKDAY_SYMBOLS.include?(arg)) ||
- (!arg.is_a?(Symbol) && integerize(arg) > 7) ||
- (!arg.is_a?(Symbol) && integerize(arg) < 0)
- raise RangeError, error_message
- end
- rescue ArgumentError
- end
- set_or_return(
- :weekday,
- converted_arg,
- :kind_of => [String, Symbol]
- )
- 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
-
- private
-
- # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no.
- def integerize(integerish)
- Integer(integerish)
- rescue TypeError
- 0
- end
- end
- end
-end
diff --git a/lib/chef/resource/cron/_cron_shared.rb b/lib/chef/resource/cron/_cron_shared.rb
new file mode 100644
index 0000000000..6d11035862
--- /dev/null
+++ b/lib/chef/resource/cron/_cron_shared.rb
@@ -0,0 +1,99 @@
+unified_mode true
+
+TIMEOUT_OPTS = %w{duration preserve-status foreground kill-after signal}.freeze
+TIMEOUT_REGEX = /\A\S+/.freeze
+WEEKDAYS = {
+ sunday: "0", monday: "1", tuesday: "2", wednesday: "3", thursday: "4", friday: "5", saturday: "6",
+ sun: "0", mon: "1", tue: "2", wed: "3", thu: "4", fri: "5", sat: "6"
+}.freeze
+
+property :minute, [Integer, String],
+ description: "The minute at which the cron entry should run (`0 - 59`).",
+ default: "*", callbacks: {
+ "should be a valid minute spec" => ->(spec) { Chef::ResourceHelpers::CronValidations.validate_numeric(spec, 0, 59) },
+ }
+
+property :hour, [Integer, String],
+ description: "The hour at which the cron entry is to run (`0 - 23`).",
+ default: "*", callbacks: {
+ "should be a valid hour spec" => ->(spec) { Chef::ResourceHelpers::CronValidations.validate_numeric(spec, 0, 23) },
+ }
+
+property :day, [Integer, String],
+ description: "The day of month at which the cron entry should run (`1 - 31`).",
+ default: "*", callbacks: {
+ "should be a valid day spec" => ->(spec) { Chef::ResourceHelpers::CronValidations.validate_numeric(spec, 1, 31) },
+ }
+
+property :month, [Integer, String],
+ description: "The month in the year on which a cron entry is to run (`1 - 12`, `jan-dec`, or `*`).",
+ default: "*", callbacks: {
+ "should be a valid month spec" => ->(spec) { Chef::ResourceHelpers::CronValidations.validate_month(spec) },
+ }
+
+property :weekday, [Integer, String, Symbol],
+ description: "The day of the week on which this entry is to run (`0-7`, `mon-sun`, `monday-sunday`, or `*`), where Sunday is both `0` and `7`.",
+ default: "*", coerce: proc { |day| weekday_in_crontab(day) },
+ callbacks: {
+ "should be a valid weekday spec" => ->(spec) { Chef::ResourceHelpers::CronValidations.validate_dow(spec) },
+ }
+
+property :shell, String,
+ description: "Set the `SHELL` environment variable."
+
+property :path, String,
+ description: "Set the `PATH` environment variable."
+
+property :home, String,
+ description: "Set the `HOME` environment variable."
+
+property :mailto, String,
+ description: "Set the `MAILTO` environment variable."
+
+property :command, String,
+ description: "The command to be run, or the path to a file that contains the command to be run.",
+ identity: true,
+ required: [:create]
+
+property :user, String,
+ description: "The name of the user that runs the command.",
+ default: "root"
+
+property :environment, Hash,
+ description: "A Hash containing additional arbitrary environment variables under which the cron job will be run in the form of `({'ENV_VARIABLE' => 'VALUE'})`. **Note**: These variables must exist for a command to be run successfully.",
+ default: lazy { {} }
+
+property :time_out, Hash,
+ description: "A Hash of timeouts in the form of `({'OPTION' => 'VALUE'})`. Accepted valid options are:
+ - `preserve-status` (BOOL, default: 'false'),
+ - `foreground` (BOOL, default: 'false'),
+ - `kill-after` (in seconds),
+ - `signal` (a name like 'HUP' or a number)",
+ default: lazy { {} },
+ introduced: "15.7",
+ coerce: proc { |h|
+ if h.is_a?(Hash)
+ invalid_keys = h.keys - TIMEOUT_OPTS
+ unless invalid_keys.empty?
+ error_msg = "Key of option time_out must be equal to one of: \"#{TIMEOUT_OPTS.join('", "')}\"! You passed \"#{invalid_keys.join(", ")}\"."
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ unless h.values.all? { |x| x =~ TIMEOUT_REGEX }
+ error_msg = "Values of option time_out should be non-empty strings without any leading whitespace."
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ h
+ elsif h.is_a?(Integer) || h.is_a?(String)
+ { "duration" => h }
+ end
+ }
+
+private
+
+# Convert weekday input value into crontab format that
+# could be written in the crontab
+# @return [Integer, String] A weekday formed as per the user inputs.
+def weekday_in_crontab(day)
+ weekday = day.to_s.downcase.to_sym
+ WEEKDAYS[weekday] || day
+end
diff --git a/lib/chef/resource/cron/cron.rb b/lib/chef/resource/cron/cron.rb
new file mode 100644
index 0000000000..31d6efcfde
--- /dev/null
+++ b/lib/chef/resource/cron/cron.rb
@@ -0,0 +1,46 @@
+#
+# Author:: Bryan McLellan (btm@loftninjas.org)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright 2009-2016, Bryan McLellan
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../../resource"
+require_relative "../helpers/cron_validations"
+require_relative "../../provider/cron" # do not remove. we actually need this below
+
+class Chef
+ class Resource
+ class Cron < Chef::Resource
+ unified_mode true
+
+ use "cron_shared"
+
+ provides :cron
+
+ 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
+
+ default_action :create
+ allowed_actions :create, :delete
+
+ property :time, Symbol,
+ description: "A time interval.",
+ equal_to: Chef::Provider::Cron::SPECIAL_TIME_VALUES
+
+ end
+ end
+end
diff --git a/lib/chef/resource/cron/cron_d.rb b/lib/chef/resource/cron/cron_d.rb
new file mode 100644
index 0000000000..7fea6ac76d
--- /dev/null
+++ b/lib/chef/resource/cron/cron_d.rb
@@ -0,0 +1,188 @@
+#
+# Copyright:: Copyright (c) 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 "../../resource"
+require_relative "../helpers/cron_validations"
+require "shellwords" unless defined?(Shellwords)
+
+class Chef
+ class Resource
+ class CronD < Chef::Resource
+ unified_mode true
+
+ use "cron_shared"
+
+ provides :cron_d
+
+ introduced "14.4"
+ description "Use the **cron_d** resource to manage cron job files in the /etc/cron.d directory. This is similar to the 'cron' resource, but it does not use the monolithic /etc/crontab file."
+ examples <<~DOC
+ **Run a program on the fifth hour of the day**
+
+ ```ruby
+ cron_d 'noop' do
+ hour '5'
+ minute '0'
+ command '/bin/true'
+ end
+ ```
+
+ **Run an entry if a folder exists**
+
+ ```ruby
+ cron_d 'ganglia_tomcat_thread_max' do
+ command "/usr/bin/gmetric
+ -n 'tomcat threads max'
+ -t uint32
+ -v '/usr/local/bin/tomcat-stat
+ --thread-max'"
+ only_if { ::File.exist?('/home/jboss') }
+ end
+ ```
+
+ **Run an entry every Saturday, 8:00 AM**
+
+ ```ruby
+ cron_d 'name_of_cron_entry' do
+ minute '0'
+ hour '8'
+ weekday '6'
+ mailto 'admin@example.com'
+ command '/bin/true'
+ action :create
+ end
+ ```
+
+ **Run an entry at 8:00 PM, every weekday (Monday through Friday), but only in November**
+
+ ```ruby
+ cron_d 'name_of_cron_entry' do
+ minute '0'
+ hour '20'
+ day '*'
+ month '11'
+ weekday '1-5'
+ command '/bin/true'
+ action :create
+ end
+ ```
+
+ **Remove a cron job by name**:
+
+ ```ruby
+ cron_d 'job_to_remove' do
+ action :delete
+ end
+ ```
+ DOC
+
+ property :cron_name, String,
+ description: "An optional property to set the cron name if it differs from the resource block's name.",
+ name_property: true
+
+ property :cookbook, String, desired_state: false, skip_docs: true
+
+ property :predefined_value, String,
+ description: "Schedule your cron job with one of the special predefined value instead of ** * pattern.",
+ equal_to: %w{ @reboot @yearly @annually @monthly @weekly @daily @midnight @hourly }
+
+ property :comment, String,
+ description: "A comment to place in the cron.d file."
+
+ property :mode, [String, Integer],
+ description: "The octal mode of the generated crontab file.",
+ default: "0600"
+
+ property :random_delay, Integer,
+ description: "Set the `RANDOM_DELAY` environment variable in the cron.d file."
+
+ # warn if someone passes the deprecated cookbook property
+ def after_created
+ raise ArgumentError, "The 'cookbook' property for the cron_d resource is no longer supported now that it ships as a core resource." if cookbook
+ end
+
+ action :create do
+ description "Add a cron definition file to /etc/cron.d."
+
+ create_template(:create)
+ end
+
+ action :create_if_missing do
+ description "Add a cron definition file to /etc/cron.d, but do not update an existing file."
+
+ create_template(:create_if_missing)
+ end
+
+ action :delete do
+ description "Remove a cron definition file from /etc/cron.d if it exists."
+
+ # cleanup the legacy named job if it exists
+ file "legacy named cron.d file" do
+ path "/etc/cron.d/#{new_resource.cron_name}"
+ action :delete
+ end
+
+ file "/etc/cron.d/#{sanitized_name}" do
+ action :delete
+ end
+ end
+
+ action_class do
+ # @return [String] cron_name property with . replaced with -
+ def sanitized_name
+ new_resource.cron_name.tr(".", "-")
+ end
+
+ def create_template(create_action)
+ # cleanup the legacy named job if it exists
+ file "#{new_resource.cron_name} legacy named cron.d file" do
+ path "/etc/cron.d/#{new_resource.cron_name}"
+ action :delete
+ only_if { new_resource.cron_name != sanitized_name }
+ end
+
+ # @todo this is Chef 12 era cleanup. Someday we should remove it all
+ template "/etc/cron.d/#{sanitized_name}" do
+ source ::File.expand_path("../support/cron.d.erb", __dir__)
+ local true
+ mode new_resource.mode
+ sensitive new_resource.sensitive
+ variables(
+ name: sanitized_name,
+ predefined_value: new_resource.predefined_value,
+ minute: new_resource.minute,
+ hour: new_resource.hour,
+ day: new_resource.day,
+ month: new_resource.month,
+ weekday: new_resource.weekday,
+ command: new_resource.command,
+ user: new_resource.user,
+ mailto: new_resource.mailto,
+ path: new_resource.path,
+ home: new_resource.home,
+ shell: new_resource.shell,
+ comment: new_resource.comment,
+ random_delay: new_resource.random_delay,
+ environment: new_resource.environment
+ )
+ action create_action
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/cron_access.rb b/lib/chef/resource/cron_access.rb
new file mode 100644
index 0000000000..3ea777ce9c
--- /dev/null
+++ b/lib/chef/resource/cron_access.rb
@@ -0,0 +1,102 @@
+#
+# Author:: Sander Botman <sbotman@schubergphilis.com>
+# Author:: Tim Smith <tsmith@chef.io>
+#
+# Copyright:: 2014-2018, Sander Botman
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class CronAccess < Chef::Resource
+ unified_mode true
+ provides :cron_access
+ provides(:cron_manage) # legacy name @todo in Chef 15 we should { true } this so it wins over the cookbook
+
+ introduced "14.4"
+ description "Use the **cron_access** resource to manage cron's cron.allow and cron.deny files. Note: This resource previously shipped in the `cron` cookbook as `cron_manage`, which it can still be used as for backwards compatibility with existing Chef Infra Client releases."
+ examples <<~DOC
+ **Add the mike user to cron.allow**
+
+ ```ruby
+ cron_access 'mike'
+ ```
+
+ **Add the mike user to cron.deny**
+
+ ```ruby
+ cron_access 'mike' do
+ action :deny
+ end
+ ```
+
+ **Specify the username with the user property**
+
+ ```ruby
+ cron_access 'Deny the jenkins user access to cron for security purposes' do
+ user 'jenkins'
+ action :deny
+ end
+ ```
+ DOC
+
+ property :user, String,
+ description: "An optional property to set the user name if it differs from the resource block's name.",
+ name_property: true
+
+ CRON_PATHS = {
+ "aix" => "/var/adm/cron",
+ "solaris" => "/etc/cron.d",
+ "default" => "/etc",
+ }.freeze
+
+ action :allow do
+ description "Add the user to the cron.allow file."
+ allow_path = ::File.join(value_for_platform_family(CRON_PATHS), "cron.allow")
+
+ with_run_context :root do
+ edit_resource(:template, allow_path) do |new_resource|
+ source ::File.expand_path("support/cron_access.erb", __dir__)
+ local true
+ mode "0600"
+ variables["users"] ||= []
+ variables["users"] << new_resource.user
+ action :nothing
+ delayed_action :create
+ end
+ end
+ end
+
+ action :deny do
+ description "Add the user to the cron.deny file."
+ deny_path = ::File.join(value_for_platform_family(CRON_PATHS), "cron.deny")
+
+ with_run_context :root do
+ edit_resource(:template, deny_path) do |new_resource|
+ source ::File.expand_path("support/cron_access.erb", __dir__)
+ local true
+ mode "0600"
+ variables["users"] ||= []
+ variables["users"] << new_resource.user
+ action :nothing
+ delayed_action :create
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/csh.rb b/lib/chef/resource/csh.rb
index 4e7c22b660..6f0c37d01e 100644
--- a/lib/chef/resource/csh.rb
+++ b/lib/chef/resource/csh.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,21 @@
# limitations under the License.
#
-require "chef/resource/script"
-require "chef/provider/script"
+require_relative "script"
class Chef
class Resource
class Csh < Chef::Resource::Script
+ unified_mode true
+
+ provides :csh
+
+ 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/deploy_revision.rb b/lib/chef/resource/deploy_revision.rb
deleted file mode 100644
index 41046ec288..0000000000
--- a/lib/chef/resource/deploy_revision.rb
+++ /dev/null
@@ -1,31 +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.
-#
-
-class Chef
- class Resource
-
- # Convenience class for using the deploy resource with the revision
- # deployment strategy (provider)
- class DeployRevision < Chef::Resource::Deploy
- end
-
- class DeployBranch < Chef::Resource::DeployRevision
- end
-
- end
-end
diff --git a/lib/chef/resource/directory.rb b/lib/chef/resource/directory.rb
index faad659668..d51fb144f7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,15 +18,22 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/directory"
-require "chef/mixin/securable"
+require_relative "../resource"
+require_relative "../mixin/securable"
class Chef
class Resource
class Directory < Chef::Resource
+ unified_mode true
- identity_attr :path
+ provides :directory
+
+ 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 +42,12 @@ 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,
+ description: "The path to the directory. Using a fully qualified path is recommended, but is not always required."
+ property :recursive, [ TrueClass, FalseClass ],
+ description: "Create or delete parent directories recursively. For the owner, group, and mode properties, the value of this property applies only to the leaf directory.",
+ 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..c6cd04156c
--- /dev/null
+++ b/lib/chef/resource/dmg_package.rb
@@ -0,0 +1,202 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class DmgPackage < Chef::Resource
+ unified_mode true
+
+ provides(:dmg_package) { true }
+
+ description "Use the **dmg_package** resource to install a package from a .dmg file. The resource will retrieve the dmg file from a remote URL, mount it using macOS' `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"
+ examples <<~DOC
+ **Install Google Chrome via the DMG package**:
+
+ ```ruby
+ dmg_package 'Google Chrome' do
+ dmg_name 'googlechrome'
+ source 'https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg'
+ checksum '7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a'
+ action :install
+ end
+ ```
+
+ **Install VirtualBox from the .mpkg**:
+
+ ```ruby
+ dmg_package 'Virtualbox' do
+ source 'http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg'
+ type 'mpkg'
+ end
+ ```
+
+ **Install pgAdmin and automatically accept the EULA**:
+
+ ```ruby
+ dmg_package 'pgAdmin3' do
+ source 'http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg'
+ checksum '9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9'
+ accept_eula true
+ end
+ ```
+ DOC
+
+ property :app, String,
+ description: "The name of the application as it appears in the `/Volumes` directory if it differs from the resource block's name.",
+ name_property: true
+
+ property :source, String,
+ description: "The remote URL that is used to download the `.dmg` file, if specified."
+
+ property :file, String,
+ description: "The absolute path to the `.dmg` file on the local system."
+
+ property :owner, [String, Integer],
+ description: "The user 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` file to download."
+
+ property :volumes_dir, String,
+ description: "The directory under `/Volumes` where the `dmg` is mounted if it differs from the name of the `.dmg` file.",
+ default: lazy { app }, default_description: "The value passed for the application name."
+
+ property :dmg_name, String,
+ description: "The name of the `.dmg` file if it differs from that of the app, or if the name has spaces.",
+ desired_state: false,
+ default: lazy { app }, default_description: "The value passed for the application name."
+
+ property :type, String,
+ description: "The type of package.",
+ equal_to: %w{app pkg mpkg},
+ default: "app", desired_state: false
+
+ property :package_id, String,
+ description: "The package ID that is registered with `pkgutil` when a `pkg` or `mpkg` is installed."
+
+ property :dmg_passphrase, String,
+ description: "Specify a passphrase to be used to decrypt the `.dmg` file during the mount process.",
+ desired_state: false
+
+ property :accept_eula, [TrueClass, FalseClass],
+ description: "Specify whether to accept the EULA. Certain dmg files require acceptance of EULA before mounting.",
+ default: false, desired_state: false
+
+ property :headers, Hash,
+ description: "Allows custom HTTP headers (like cookies) to be set on the `remote_file` resource.",
+ desired_state: false
+
+ property :allow_untrusted, [TrueClass, FalseClass],
+ description: "Allow installation of packages that do not have trusted certificates.",
+ default: false, desired_state: false
+
+ load_current_value do |new_resource|
+ if ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app")
+ Chef::Log.info "#{new_resource.app} is already installed. To upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\""
+ elsif shell_out("pkgutil --pkg-info '#{new_resource.package_id}'").exitstatus == 0
+ Chef::Log.info "#{new_resource.app} is already installed. To upgrade, try \"sudo pkgutil --forget '#{new_resource.package_id}'\""
+ else
+ current_value_does_not_exist! # allows us to check for current_resource.nil? below
+ end
+ end
+
+ action :install do
+ description "Installs the application."
+
+ if current_resource.nil?
+ if new_resource.source
+ remote_file dmg_file do
+ source new_resource.source
+ headers new_resource.headers if new_resource.headers
+ checksum new_resource.checksum if new_resource.checksum
+ end
+ end
+
+ unless dmg_attached?
+ converge_by "attach #{dmg_file}" do
+ raise "This DMG package requires EULA acceptance. Add 'accept_eula true' to dmg_package resource to accept the EULA during installation." if software_license_agreement? && !new_resource.accept_eula
+
+ attach_cmd = new_resource.accept_eula ? "yes | " : ""
+ attach_cmd << "/usr/bin/hdiutil attach #{passphrase_cmd} '#{dmg_file}' -nobrowse -mountpoint '/Volumes/#{new_resource.volumes_dir}'"
+
+ shell_out!(attach_cmd, env: { "PAGER" => "true" })
+ end
+ end
+
+ case new_resource.type
+ when "app"
+ execute "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{new_resource.volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'" do
+ user new_resource.owner if new_resource.owner
+ end
+
+ file "#{new_resource.destination}/#{new_resource.app}.app/Contents/MacOS/#{new_resource.app}" do
+ mode "0755"
+ ignore_failure true
+ end
+ when "mpkg", "pkg"
+ install_cmd = "installation_file=$(ls '/Volumes/#{new_resource.volumes_dir}' | grep '.#{new_resource.type}$') && sudo installer -pkg \"/Volumes/#{new_resource.volumes_dir}/$installation_file\" -target /"
+ install_cmd += " -allowUntrusted" if new_resource.allow_untrusted
+
+ execute install_cmd do
+ # Prevent cfprefsd from holding up hdiutil detach for certain disk images
+ environment("__CFPREFERENCES_AVOID_DAEMON" => "1")
+ end
+ end
+
+ execute "/usr/bin/hdiutil detach '/Volumes/#{new_resource.volumes_dir}' || /usr/bin/hdiutil detach '/Volumes/#{new_resource.volumes_dir}' -force"
+ end
+ end
+
+ action_class do
+ # @return [String] the path to the dmg file
+ 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
+
+ # @return [String] the hdiutil flag for handling DMGs with a password
+ def passphrase_cmd
+ @passphrase_cmd ||= new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : ""
+ end
+
+ # @return [Boolean] does the DMG require a software license agreement
+ def software_license_agreement?
+ # example hdiutil imageinfo output: http://rubular.com/r/0xvOaA6d8B
+ /Software License Agreement: true/.match?(shell_out!("/usr/bin/hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}'").stdout)
+ end
+
+ # @return [Boolean] is the dmg file currently attached?
+ def dmg_attached?
+ # example hdiutil imageinfo output: http://rubular.com/r/CDcqenkixg
+ /image-path.*#{dmg_file}/.match?(shell_out!("/usr/bin/hdiutil info #{passphrase_cmd}").stdout)
+ 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..80727de7d0
--- /dev/null
+++ b/lib/chef/resource/dnf_package.rb
@@ -0,0 +1,79 @@
+#
+# Copyright:: Copyright (c) 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 "package"
+require_relative "../mixin/which"
+require_relative "../mixin/shell_out"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class DnfPackage < Chef::Resource::Package
+ extend Chef::Mixin::Which
+ extend Chef::Mixin::ShellOut
+
+ unified_mode true
+ provides :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
+
+ description "Use the **dnf_package** resource to install, upgrade, and remove packages with DNF for Fedora and RHEL 8+. 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],
+ description: "The architecture of the package to be installed or upgraded. This value can also be passed as part of the package name.",
+ 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,
+ description: "Flush the in-memory cache before or after a DNF operation that installs, upgrades, or removes a package. DNF automatically synchronizes remote metadata to a local cache. The #{ChefUtils::Dist::Infra::CLIENT} creates a copy of the local cache, and then stores it in-memory during the #{ChefUtils::Dist::Infra::CLIENT} run. The in-memory cache allows packages to be installed during the #{ChefUtils::Dist::Infra::CLIENT} run without the need to continue synchronizing the remote metadata to the local cache while the #{ChefUtils::Dist::Infra::CLIENT} run is in-progress.",
+ 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)
+ unless 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
+ true
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/dpkg_package.rb b/lib/chef/resource/dpkg_package.rb
index 9ff3239884..466b17d702 100644
--- a/lib/chef/resource/dpkg_package.rb
+++ b/lib/chef/resource/dpkg_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,27 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
class Chef
class Resource
class DpkgPackage < Chef::Resource::Package
- resource_name :dpkg_package
- provides :dpkg_package, os: "linux"
+ unified_mode true
- property :source, [ String, Array, nil ]
+ 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 ],
+ description: "The path to a package in the local file system."
+
+ property :response_file, String,
+ description: "The direct path to the file used to pre-seed a package.",
+ desired_state: false
+
+ property :response_file_variables, Hash,
+ description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.",
+ default: lazy { {} }, desired_state: false
end
end
end
diff --git a/lib/chef/resource/dsc_resource.rb b/lib/chef/resource/dsc_resource.rb
index 58594cce7b..679deef47b 100644
--- a/lib/chef/resource/dsc_resource.rb
+++ b/lib/chef/resource/dsc_resource.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
@@ -15,12 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/dsl/powershell"
+require_relative "../dsl/powershell"
class Chef
class Resource
class DscResource < Chef::Resource
- provides :dsc_resource, os: "windows"
+ unified_mode true
+
+ provides :dsc_resource
+
+ description "The dsc_resource resource allows any DSC resource to be used in a 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 +34,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
@@ -37,7 +42,7 @@ class Chef
end
"#{property}=>#{obj_text}"
end
- "{#{descriptions.join(', ')}}"
+ "{#{descriptions.join(", ")}}"
end
end
@@ -49,7 +54,6 @@ class Chef
super
@properties = ToTextHash.new
@resource = nil
- @reboot_action = :nothing
end
def resource(value = nil)
@@ -68,8 +72,12 @@ class Chef
end
end
+ property :module_version, String,
+ introduced: "12.21",
+ description: "The version number of the module to use. PowerShell 5.0.10018.0 (or higher) supports having multiple versions of a module installed. This should be specified along with the module_name."
+
def property(property_name, value = nil)
- if not property_name.is_a?(Symbol)
+ unless 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"
end
@@ -91,21 +99,14 @@ 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: %i{nothing reboot_now request_reboot},
+ introduced: "12.6",
+ description: "Use to request an immediate reboot or to queue a reboot using the :reboot_now (immediate reboot) or :request_reboot (queued reboot) actions built into the reboot resource."
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :timeout, Integer,
+ introduced: "12.6",
+ description: "The amount of time (in seconds) a command is to wait before timing out.",
+ desired_state: false
private
diff --git a/lib/chef/resource/dsc_script.rb b/lib/chef/resource/dsc_script.rb
index 7da29a651a..3a14da15f0 100644
--- a/lib/chef/resource/dsc_script.rb
+++ b/lib/chef/resource/dsc_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,27 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/dsl/powershell"
+require_relative "../resource"
+require_relative "../exceptions"
+require_relative "../dsl/powershell"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class DscScript < Chef::Resource
include Chef::DSL::Powershell
- provides :dsc_script, os: "windows"
+ unified_mode true
+ provides :dsc_script
+
+ description <<~DESC
+ Many DSC resources are comparable to built-in #{ChefUtils::Dist::Infra::PRODUCT} resources. For example, both DSC and #{ChefUtils::Dist::Infra::PRODUCT}
+ 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 #{ChefUtils::Dist::Infra::PRODUCT}, 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 #{ChefUtils::Dist::Infra::PRODUCT} recipe.
+
+ Warning: The **dsc_script** resource may not be used with the 32 bit Chef Infra client. It must be executed from a 64 bit Chef Infra client.
+ DESC
default_action :run
@@ -35,59 +47,64 @@ class Chef
def code(arg = nil)
if arg && command
- raise ArgumentError, "Only one of 'code' and 'command' attributes may be specified"
+ raise ArgumentError, "Only one of 'code' and 'command' properties may be specified"
end
if arg && configuration_name
- raise ArgumentError, "The 'code' and 'command' attributes may not be used together"
+ raise ArgumentError, "The 'code' and 'command' properties may not be used together"
end
+
set_or_return(
:code,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
def configuration_name(arg = nil)
if arg && code
- raise ArgumentError, "Attribute `configuration_name` may not be set if `code` is set"
+ raise ArgumentError, "Property `configuration_name` may not be set if `code` is set"
end
+
set_or_return(
:configuration_name,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
def command(arg = nil)
if arg && code
- raise ArgumentError, "The 'code' and 'command' attributes may not be used together"
+ raise ArgumentError, "The 'code' and 'command' properties may not be used together"
end
+
set_or_return(
:command,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
def configuration_data(arg = nil)
if arg && configuration_data_script
- raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together"
+ raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' properties may not be used together"
end
+
set_or_return(
:configuration_data,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
def configuration_data_script(arg = nil)
if arg && configuration_data
- raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together"
+ raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' properties may not be used together"
end
+
set_or_return(
:configuration_data_script,
arg,
- :kind_of => [ String ]
+ kind_of: [ String ]
)
end
@@ -104,37 +121,18 @@ class Chef
end
end
- def flags(arg = nil)
- set_or_return(
- :flags,
- arg,
- :kind_of => [ Hash ]
- )
- end
+ property :flags, Hash,
+ description: "Pass parameters to the DSC script that is specified by the command property. Parameters are defined as key-value pairs, where the value of each key is the parameter to pass. This property may not be used in the same recipe as the code property."
- def cwd(arg = nil)
- set_or_return(
- :cwd,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :cwd, String,
+ description: "The current working directory."
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
+ property :environment, Hash,
+ description: "A Hash of environment variables in the form of ({'ENV_VARIABLE' => 'VALUE'}). (These variables must exist for a command to be run successfully)."
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :timeout, Integer,
+ description: "The amount of time (in seconds) a command is to wait before timing out.",
+ desired_state: false
end
end
end
diff --git a/lib/chef/resource/easy_install_package.rb b/lib/chef/resource/easy_install_package.rb
deleted file mode 100644
index dc5073a6f7..0000000000
--- a/lib/chef/resource/easy_install_package.rb
+++ /dev/null
@@ -1,32 +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/resource/package"
-
-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
-
- end
- end
-end
diff --git a/lib/chef/resource/env.rb b/lib/chef/resource/env.rb
deleted file mode 100644
index 7fac8af40b..0000000000
--- a/lib/chef/resource/env.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# 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.
-#
-
-class Chef
- class Resource
- class Env < Chef::Resource
-
- identity_attr :key_name
-
- state_attrs :value
-
- provides :env, os: "windows"
-
- 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
- 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..5a78160642 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,14 +17,486 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/execute"
+require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class Execute < Chef::Resource
+ unified_mode true
- identity_attr :command
+ provides :execute, target_mode: true
+
+ 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. Note: Use the **script** resource to execute a script using a specific interpreter (Ruby, Python, Perl, csh, or Bash)."
+
+ examples <<~EXAMPLES
+ **Run a command upon notification**:
+
+ ```ruby
+ execute 'slapadd' do
+ command 'slapadd < /tmp/something.ldif'
+ creates '/var/lib/slapd/uid.bdb'
+
+ action :nothing
+ end
+
+ template '/tmp/something.ldif' do
+ source 'something.ldif'
+
+ notifies :run, 'execute[slapadd]', :immediately
+ end
+ ```
+
+ **Run a touch file only once while running a command**:
+
+ ```ruby
+ execute 'upgrade script' do
+ command 'php upgrade-application.php && touch /var/application/.upgraded'
+
+ creates '/var/application/.upgraded'
+ action :run
+ end
+ ```
+
+ **Run a command which requires an environment variable**:
+
+ ```ruby
+ execute 'slapadd' do
+ command 'slapadd < /tmp/something.ldif'
+ creates '/var/lib/slapd/uid.bdb'
+
+ action :run
+ environment ({'HOME' => '/home/my_home'})
+ end
+ ```
+
+ **Delete a repository using yum to scrub the cache**:
+
+ ```ruby
+ # the following code sample thanks to gaffneyc @ https://gist.github.com/918711
+ execute 'clean-yum-cache' do
+ command 'yum clean all'
+ action :nothing
+ end
+
+ file '/etc/yum.repos.d/bad.repo' do
+ action :delete
+ notifies :run, 'execute[clean-yum-cache]', :immediately
+ end
+ ```
+
+ **Prevent restart and reconfigure if configuration is broken**:
+
+ Use the `:nothing` action (common to all resources) to prevent the test from
+ starting automatically, and then use the `subscribes` notification to run a
+ configuration test when a change to the template is detected.
+
+ ```ruby
+ execute 'test-nagios-config' do
+ command 'nagios3 --verify-config'
+ action :nothing
+ subscribes :run, 'template[/etc/nagios3/configures-nagios.conf]', :immediately
+ end
+ ```
+
+ **Notify in a specific order**:
+
+ To notify multiple resources, and then have these resources run in a certain
+ order, do something like the following.
+
+ ```ruby
+ execute 'foo' do
+ command '...'
+ notifies :create, 'template[baz]', :immediately
+ notifies :install, 'package[bar]', :immediately
+ notifies :run, 'execute[final]', :immediately
+ end
+
+ template 'baz' do
+ #...
+ notifies :run, 'execute[restart_baz]', :immediately
+ end
+
+ package 'bar'
+ execute 'restart_baz'
+ execute 'final' do
+ command '...'
+ end
+ ```
+
+ where the sequencing will be in the same order as the resources are listed in
+ the recipe: `execute 'foo'`, `template 'baz'`, `execute [restart_baz]`,
+ `package 'bar'`, and `execute 'final'`.
+
+ **Execute a command using a template**:
+
+ The following example shows how to set up IPv4 packet forwarding using the
+ **execute** resource to run a command named `forward_ipv4` that uses a template
+ defined by the **template** resource.
+
+ ```ruby
+ execute 'forward_ipv4' do
+ command 'echo > /proc/.../ipv4/ip_forward'
+ action :nothing
+ end
+
+ template '/etc/file_name.conf' do
+ source 'routing/file_name.conf.erb'
+
+ notifies :run, 'execute[forward_ipv4]', :delayed
+ end
+ ```
+
+ where the `command` property for the **execute** resource contains the command
+ that is to be run and the `source` property for the **template** resource
+ specifies which template to use. The `notifies` property for the **template**
+ specifies that the `execute[forward_ipv4]` (which is defined by the **execute**
+ resource) should be queued up and run at the end of a Chef Infra Client run.
+
+ **Add a rule to an IP table**:
+
+ The following example shows how to add a rule named `test_rule` to an IP table
+ using the **execute** resource to run a command using a template that is defined
+ by the **template** resource:
+
+ ```ruby
+ execute 'test_rule' do
+ command "command_to_run
+ --option value
+ --option value
+ --source \#{node[:name_of_node][:ipsec][:local][:subnet]}
+ -j test_rule"
+
+ action :nothing
+ end
+
+ template '/etc/file_name.local' do
+ source 'routing/file_name.local.erb'
+ notifies :run, 'execute[test_rule]', :delayed
+ end
+ ```
+
+ where the `command` property for the **execute** resource contains the command
+ that is to be run and the `source` property for the **template** resource
+ specifies which template to use. The `notifies` property for the **template**
+ specifies that the `execute[test_rule]` (which is defined by the **execute**
+ resource) should be queued up and run at the end of a Chef Infra Client run.
+
+ **Stop a service, do stuff, and then restart it**:
+
+ The following example shows how to use the **execute**, **service**, and
+ **mount** resources together to ensure that a node running on Amazon EC2 is
+ running MySQL. This example does the following:
+
+ - Checks to see if the Amazon EC2 node has MySQL
+ - If the node has MySQL, stops MySQL
+ - Installs MySQL
+ - Mounts the node
+ - Restarts MySQL
+
+ ```ruby
+ # the following code sample comes from the ``server_ec2``
+ # recipe in the following cookbook:
+ # https://github.com/chef-cookbooks/mysql
+
+ if (node.attribute?('ec2') && !FileTest.directory?(node['mysql']['ec2_path']))
+ service 'mysql' do
+ action :stop
+ end
+
+ execute 'install-mysql' do
+ command "mv \#{node['mysql']['data_dir']} \#{node['mysql']['ec2_path']}"
+ not_if { ::File.directory?(node['mysql']['ec2_path']) }
+ end
+
+ [node['mysql']['ec2_path'], node['mysql']['data_dir']].each do |dir|
+ directory dir do
+ owner 'mysql'
+ group 'mysql'
+ end
+ end
+
+ mount node['mysql']['data_dir'] do
+ device node['mysql']['ec2_path']
+ fstype 'none'
+ options 'bind,rw'
+ action [:mount, :enable]
+ end
+
+ service 'mysql' do
+ action :start
+ end
+ end
+ ```
+
+ where
+
+ - the two **service** resources are used to stop, and then restart the MySQL service
+ - the **execute** resource is used to install MySQL
+ - the **mount** resource is used to mount the node and enable MySQL
+
+ **Use the platform_family? method**:
+
+ The following is an example of using the `platform_family?` method in the Recipe
+ DSL to create a variable that can be used with other resources in the same
+ recipe. In this example, `platform_family?` is being used to ensure that a
+ specific binary is used for a specific platform before using the **remote_file**
+ resource to download a file from a remote location, and then using the
+ **execute** resource to install that file by running a command.
+
+ ```ruby
+ if platform_family?('rhel')
+ pip_binary = '/usr/bin/pip'
+ else
+ pip_binary = '/usr/local/bin/pip'
+ end
+
+ remote_file "\#{Chef::Config[:file_cache_path]}/distribute_setup.py" do
+ source 'http://python-distribute.org/distribute_setup.py'
+ mode '0755'
+ not_if { ::File.exist?(pip_binary) }
+ end
+
+ execute 'install-pip' do
+ cwd Chef::Config[:file_cache_path]
+ command <<~EOF
+ # command for installing Python goes here
+ EOF
+ not_if { ::File.exist?(pip_binary) }
+ end
+ ```
+
+ where a command for installing Python might look something like:
+
+ ```ruby
+ \#{node['python']['binary']} distribute_setup.py \#{::File.dirname(pip_binary)}/easy_install pip
+ ```
+
+ **Control a service using the execute resource**:
+
+ <div class="admonition-warning">
+ <p class="admonition-warning-title">Warning</p>
+ <div class="admonition-warning-text">
+ This is an example of something that should NOT be done. Use the **service**
+ resource to control a service, not the **execute** resource.
+ </div>
+ </div>
+
+ Do something like this:
+
+ ```ruby
+ service 'tomcat' do
+ action :start
+ end
+ ```
+
+ and NOT something like this:
+
+ ```ruby
+ execute 'start-tomcat' do
+ command '/etc/init.d/tomcat start'
+ action :run
+ end
+ ```
+
+ There is no reason to use the **execute** resource to control a service because
+ the **service** resource exposes the `start_command` property directly, which
+ gives a recipe full control over the command issued in a much cleaner, more
+ direct manner.
+
+ **Use the search recipe DSL method to find users**:
+
+ The following example shows how to use the `search` method in the Recipe DSL to
+ search for users:
+
+ ```ruby
+ # the following code sample comes from the openvpn cookbook:
+
+ search("users", "*:*") do |u|
+ execute "generate-openvpn-\#{u['id']}" do
+ command "./pkitool \#{u['id']}"
+ cwd '/etc/openvpn/easy-rsa'
+ end
+
+ %w{ conf ovpn }.each do |ext|
+ template "\#{node['openvpn']['key_dir']}/\#{u['id']}.\#{ext}" do
+ source 'client.conf.erb'
+ variables :username => u['id']
+ end
+ end
+ end
+ ```
+
+ where
+
+ - the search data will be used to create **execute** resources
+ - the **template** resource tells Chef Infra Client which template to use
+
+ **Enable remote login for macOS**:
+
+ ```ruby
+ execute 'enable ssh' do
+ command '/usr/sbin/systemsetup -setremotelogin on'
+ not_if '/usr/sbin/systemsetup -getremotelogin | /usr/bin/grep On'
+ action :run
+ end
+ ```
+
+ **Execute code immediately, based on the template resource**:
+
+ By default, notifications are `:delayed`, that is they are queued up as they are
+ triggered, and then executed at the very end of a Chef Infra Client run. To run
+ kan action immediately, use `:immediately`:
+
+ ```ruby
+ template '/etc/nagios3/configures-nagios.conf' do
+ # other parameters
+ notifies :run, 'execute[test-nagios-config]', :immediately
+ end
+ ```
+
+ and then Chef Infra Client would immediately run the following:
+
+ ```ruby
+ execute 'test-nagios-config' do
+ command 'nagios3 --verify-config'
+ action :nothing
+ end
+ ```
+
+ **Sourcing a file**:
+
+ The **execute** resource cannot be used to source a file (e.g. `command 'source
+ filename'`). The following example will fail because `source` is not an
+ executable:
+
+ ```ruby
+ execute 'foo' do
+ command 'source /tmp/foo.sh'
+ end
+ ```
+
+
+ Instead, use the **script** resource or one of the **script**-based resources
+ (**bash**, **csh**, **perl**, **python**, or **ruby**). For example:
+
+ ```ruby
+ bash 'foo' do
+ code 'source /tmp/foo.sh'
+ end
+ ```
+
+ **Run a Knife command**:
+
+ ```ruby
+ execute 'create_user' do
+ command <<~EOM
+ knife user create \#{user}
+ --admin
+ --password password
+ --disable-editing
+ --file /home/vagrant/.chef/user.pem
+ --config /tmp/knife-admin.rb
+ EOM
+ end
+ ```
+
+ **Run install command into virtual environment**:
+
+ The following example shows how to install a lightweight JavaScript framework
+ into Vagrant:
+
+ ```ruby
+ execute "install q and zombiejs" do
+ cwd "/home/vagrant"
+ user "vagrant"
+ environment ({'HOME' => '/home/vagrant', 'USER' => 'vagrant'})
+ command "npm install -g q zombie should mocha coffee-script"
+ action :run
+ end
+ ```
+
+ **Run a command as a named user**:
+
+ The following example shows how to run `bundle install` from a Chef Infra Client
+ run as a specific user. This will put the gem into the path of the user
+ (`vagrant`) instead of the root user (under which the Chef Infra Client runs):
+
+ ```ruby
+ execute '/opt/chefdk/embedded/bin/bundle install' do
+ cwd node['chef_workstation']['bundler_path']
+ user node['chef_workstation']['user']
+
+ environment ({
+ 'HOME' => "/home/\#{node['chef_workstation']['user']}",
+ 'USER' => node['chef_workstation']['user']
+ })
+ not_if 'bundle check'
+ end
+ ```
+
+ **Run a command as an alternate user**:
+
+ *Note*: When Chef is running as a service, this feature requires that the user
+ that Chef runs as has 'SeAssignPrimaryTokenPrivilege' (aka
+ 'SE_ASSIGNPRIMARYTOKEN_NAME') user right. By default only LocalSystem and
+ NetworkService have this right when running as a service. This is necessary
+ even if the user is an Administrator.
+
+ This right can be added and checked in a recipe using this example:
+
+ ```ruby
+ # Add 'SeAssignPrimaryTokenPrivilege' for the user
+ Chef::ReservedNames::Win32::Security.add_account_right('<user>', 'SeAssignPrimaryTokenPrivilege')
+
+ # Check if the user has 'SeAssignPrimaryTokenPrivilege' rights
+ Chef::ReservedNames::Win32::Security.get_account_right('<user>').include?('SeAssignPrimaryTokenPrivilege')
+ ```
+
+ The following example shows how to run `mkdir test_dir` from a Chef Infra Client
+ run as an alternate user.
+
+ ```ruby
+ # Passing only username and password
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "username"
+ password "password"
+ end
+
+ # Passing username and domain
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ domain "domain-name"
+ user "user"
+ password "password"
+ end
+
+ # Passing username = 'domain-name\\username'. No domain is passed
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "domain-name\\username"
+ password "password"
+ end
+
+ # Passing username = 'username@domain-name'. No domain is passed
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "username@domain-name"
+ password "password"
+ end
+ ```
+
+ **Run a command with an external input file**:
+
+ execute 'md5sum' do
+ input File.read(__FILE__)
+ end
+ EXAMPLES
# 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
@@ -37,111 +509,70 @@ class Chef
def initialize(name, run_context = nil)
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
+ property :command, [ String, Array ],
+ name_property: true,
+ description: "An optional property to set the command to be executed if it differs from the resource block's name."
- def command(arg = nil)
- set_or_return(
- :command,
- arg,
- :kind_of => [ String, Array ]
- )
- end
+ property :umask, [ String, Integer ],
+ description: "The file mode creation mask, or umask."
- def creates(arg = nil)
- set_or_return(
- :creates,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :creates, String,
+ description: "Prevent a command from creating a file when that file already exists."
- def cwd(arg = nil)
- set_or_return(
- :cwd,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :cwd, String,
+ description: "The current working directory from which the command will be run."
- def environment(arg = nil)
- set_or_return(
- :environment,
- arg,
- :kind_of => [ Hash ]
- )
- end
+ property :environment, Hash,
+ description: "A Hash of environment variables in the form of `({'ENV_VARIABLE' => 'VALUE'})`. **Note**: These variables must exist for a command to be run successfully."
- alias :env :environment
+ property :group, [ String, Integer ],
+ description: "The group name or group ID that must be changed before running a command."
- def group(arg = nil)
- set_or_return(
- :group,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :live_stream, [ TrueClass, FalseClass ], default: false,
+ description: "Send the output of the command run by this execute resource block to the #{ChefUtils::Dist::Infra::PRODUCT} event stream."
- def live_stream(arg = nil)
- set_or_return(
- :live_stream,
- arg,
- :kind_of => [ TrueClass, FalseClass ])
- end
+ # default_env defaults to `false` so that the command execution more exactly matches what the user gets on the command line without magic
+ property :default_env, [ TrueClass, FalseClass ], desired_state: false, default: false,
+ introduced: "14.2",
+ description: "When true this enables ENV magic to add path_sanity to the PATH and force the locale to English+UTF-8 for parsing output"
- 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."
+ property :returns, [ Integer, Array ], default: 0,
+ description: "The return value for a command. This may be an array of accepted values. An exception is raised when the return value(s) do not match."
- set_or_return(
- :path,
- arg,
- :kind_of => [ Array ]
- )
- end
+ property :timeout, [ Integer, String, Float ],
+ default: 3600,
+ description: "The amount of time (in seconds) a command is to wait before timing out.",
+ desired_state: false
- def returns(arg = nil)
- set_or_return(
- :returns,
- arg,
- :kind_of => [ Integer, Array ]
- )
- end
+ property :user, [ String, Integer ],
+ description: "The user name of the user identity with which to launch the new process. The user name may optionally be specified with a domain, i.e. `domainuser` 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 domain property. On Windows only, if this property is specified, the password property must be specified."
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => [ Integer, Float ]
- )
- end
+ property :domain, String,
+ introduced: "12.21",
+ description: "Windows only: The domain of the user user specified by the user property. If not specified, the username and password specified by the `user` and `password` properties will be used to resolve that user against the domain in which the system running #{ChefUtils::Dist::Infra::PRODUCT} is joined, or if that system is not joined to a domain it will resolve 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 user property."
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :password, String, sensitive: true,
+ introduced: "12.21",
+ description: "Windows only: The password of the user specified by the user property. This property is mandatory if user is specified on Windows and may only be specified if user is specified. The sensitive property for this resource will automatically be set to true if password is specified."
+
+ # lazy used to set default value of sensitive to true if password is set
+ property :sensitive, [ TrueClass, FalseClass ],
+ description: "Ensure that sensitive resource data is not logged by the #{ChefUtils::Dist::Infra::PRODUCT}.",
+ default: lazy { password ? true : false }, default_description: "True if the password property is set. False otherwise."
+
+ property :elevated, [ TrueClass, FalseClass ], default: false,
+ description: "Determines whether the script will run with elevated permissions to circumvent User Access Control (UAC) from interactively blocking the process.\nThis will cause the process to be run under a batch login instead of an interactive login. The user running #{ChefUtils::Dist::Infra::CLIENT} needs the 'Replace a process level token' and 'Adjust Memory Quotas for a process' permissions. The user that is running the command needs the 'Log on as a batch job' permission.\nBecause this requires a login, the user and password properties are required.",
+ introduced: "13.3"
+
+ property :input, [String],
+ introduced: "16.2",
+ description: "An optional property to set the input sent to the command as STDIN."
+
+ alias :env :environment
def self.set_guard_inherited_attributes(*inherited_attributes)
@class_inherited_attributes = inherited_attributes
@@ -156,13 +587,84 @@ class Chef
ancestor_attributes = superclass.guard_inherited_attributes
end
- ancestor_attributes.concat(@class_inherited_attributes ? @class_inherited_attributes : []).uniq
+ ancestor_attributes.concat(@class_inherited_attributes || []).uniq
+ end
+
+ # post resource creation validation
+ #
+ # @return [void]
+ 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 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.is_a?(String) && ((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.is_a?(String) && 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,
+ :domain,
:environment,
:group,
+ :password,
:user,
:umask
)
diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb
index 207de63778..b2bba06185 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,18 +17,24 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/platform/query_helpers"
-require "chef/mixin/securable"
-require "chef/resource/file/verification"
-require "pathname"
+require_relative "../resource"
+require_relative "../platform/query_helpers"
+require_relative "../mixin/securable"
+require_relative "file/verification"
+require "pathname" unless defined?(Pathname)
+require "chef-utils" unless defined?(ChefUtils::CANARY)
class Chef
class Resource
class File < Chef::Resource
include Chef::Mixin::Securable
+ unified_mode true
- if Platform.windows?
+ provides :file
+
+ description "Use the **file** resource to manage files directly on a node. Note: Use the **cookbook_file** resource to copy a file from a cookbook's `/files` directory. Use the **template** resource to create a file based on a template in a cookbook's `/templates` directory. And use the **remote_file** resource to transfer a file to a node from a remote location."
+
+ if ChefUtils.windows?
# Use Windows rights instead of standard *nix permissions
state_attrs :checksum, :rights, :deny_rights
else
@@ -43,24 +49,42 @@ 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 { |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 :path, String, name_property: true,
+ description: "The full path to the file, including the file name and its extension. For example: /files/file.txt. Default value: the name of the resource block. Microsoft Windows: A path that begins with a forward slash `/` will point to the root of the current working directory of the #{ChefUtils::Dist::Infra::PRODUCT} process. This path can vary from system to system. Therefore, using a path that begins with a forward slash `/` is not recommended."
+
+ property :atomic_update, [ TrueClass, FalseClass ], desired_state: false, default: lazy { docker? && special_docker_files?(path) ? false : Chef::Config[:file_atomic_update] },
+ default_description: "False if modifying /etc/hosts, /etc/hostname, or /etc/resolv.conf within Docker containers. Otherwise default to the client.rb 'file_atomic_update' config value.",
+ description: "Perform atomic file updates on a per-resource basis. Set to true for atomic file updates. Set to false for non-atomic file updates. This setting overrides `file_atomic_update`, which is a global setting found in the `client.rb` file."
+
+ property :backup, [ Integer, FalseClass ], desired_state: false, default: 5,
+ description: "The number of backups to be kept in `/var/chef/backup` (for UNIX- and Linux-based platforms) or `C:/chef/backup` (for the Microsoft Windows platform). Set to `false` to prevent backups from being kept."
+
+ property :checksum, [ String, nil ],
+ regex: /^\h{64}$/,
+ coerce: lambda { |s| s.is_a?(String) ? s.downcase : s },
+ description: "The SHA-256 checksum of the file. Use to ensure that a specific file is used. If the checksum does not match, the file is not used."
+
+ property :content, [ String, nil ], desired_state: false,
+ description: "A string that is written to the file. The contents of this property replace any previous content when this property has something other than the default value. The default behavior will not modify content."
+
+ property :diff, [ String, nil ], desired_state: false, skip_docs: true
+
+ property :force_unlink, [ TrueClass, FalseClass ], desired_state: false, default: false,
+ description: "How #{ChefUtils::Dist::Infra::PRODUCT} handles certain situations when the target file turns out not to be a file. For example, when a target file is actually a symlink. Set to `true` for #{ChefUtils::Dist::Infra::PRODUCT} to delete the non-file target and replace it with the specified file. Set to `false` for #{ChefUtils::Dist::Infra::PRODUCT} to raise an error."
+
+ property :manage_symlink_source, [ TrueClass, FalseClass ], desired_state: false,
+ description: "Change the behavior of the file resource if it is pointed at a symlink. When this value is set to true, #{ChefUtils::Dist::Infra::PRODUCT} will manage the symlink's permissions or will replace the symlink with a normal file if the resource has content. When this value is set to false, #{ChefUtils::Dist::Infra::PRODUCT} will follow the symlink and will manage the permissions and content of symlink's target file. The default behavior is true but emits a warning that the default value will be changed to false in a future version; setting this explicitly to true or false suppresses this warning."
+
property :verifications, Array, default: lazy { [] }
def verify(command = nil, opts = {}, &block)
- if ! (command.nil? || [String, Symbol].include?(command.class))
+ unless command.nil? || [String, Symbol].include?(command.class)
raise ArgumentError, "verify requires either a string, symbol, or a block"
end
diff --git a/lib/chef/resource/file/verification.rb b/lib/chef/resource/file/verification.rb
index e11035d33f..7c5299af5a 100644
--- a/lib/chef/resource/file/verification.rb
+++ b/lib/chef/resource/file/verification.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/guard_interpreter"
-require "chef/mixin/descendants_tracker"
+require_relative "../../exceptions"
+require_relative "../../guard_interpreter"
+require_relative "../../mixin/descendants_tracker"
class Chef
class Resource
@@ -36,7 +36,7 @@ class Chef
# ruby block, which will be called, or a string, which will be
# executed as a Shell command.
#
- # Additonally, Chef or third-party verifications can ship
+ # Additionally, Chef or third-party verifications can ship
# "registered verifications" that the user can use by specifying
# a :symbol as the command name.
#
@@ -63,6 +63,7 @@ class Chef
class Verification
extend Chef::Mixin::DescendantsTracker
+ attr_reader :output
def self.provides(name)
@provides = name
@@ -77,9 +78,14 @@ class Chef
if c.nil?
raise Chef::Exceptions::VerificationNotFound.new "No file verification for #{name} found."
end
+
c
end
+ def logger
+ @parent_resource.logger
+ end
+
def initialize(parent_resource, command, opts, &block)
@command, @command_opts = command, opts
@block = block
@@ -87,7 +93,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,15 +112,15 @@ 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
+ ret = interpreter.evaluate
+ @output = interpreter.output
+ ret
end
def verify_registered_verification(path, opts)
@@ -122,6 +128,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..bfc8881522
--- /dev/null
+++ b/lib/chef/resource/file/verification/systemd_unit.rb
@@ -0,0 +1,68 @@
+#
+# Author:: Mal Graty (<mal.graty@googlemail.com>)
+# Copyright:: Copyright (c) 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 "../../../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 a94dd0a928..081adaf702 100644
--- a/lib/chef/resource/freebsd_package.rb
+++ b/lib/chef/resource/freebsd_package.rb
@@ -1,7 +1,7 @@
#
# Authors:: AJ Christensen (<aj@chef.io>)
# Richard Manyanza (<liseki@nyikacraftsmen.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2014-2016, Richard Manyanza.
# License:: Apache License, Version 2.0
#
@@ -18,43 +18,34 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/freebsd/port"
-require "chef/provider/package/freebsd/pkg"
-require "chef/provider/package/freebsd/pkgng"
-require "chef/mixin/shell_out"
+require_relative "package"
+require_relative "../provider/package/freebsd/port"
+require_relative "../provider/package/freebsd/pkgng"
class Chef
class Resource
class FreebsdPackage < Chef::Resource::Package
- include Chef::Mixin::ShellOut
-
- resource_name :freebsd_package
+ unified_mode true
+ provides :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
- def supports_pkgng?
- ships_with_pkgng? || !!shell_out!("make -V WITH_PKGNG", :env => nil).stdout.match(/yes/i)
- end
-
private
- 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[:os_version].to_i >= 1000017
- end
-
def assign_provider
- @provider = if source.to_s =~ /^ports$/i
+ @provider = if /^ports$/i.match?(source.to_s)
Chef::Provider::Package::Freebsd::Port
- elsif supports_pkgng?
- Chef::Provider::Package::Freebsd::Pkgng
else
- Chef::Provider::Package::Freebsd::Pkg
+ Chef::Provider::Package::Freebsd::Pkgng
end
end
end
diff --git a/lib/chef/resource/gem_package.rb b/lib/chef/resource/gem_package.rb
index e095115356..a3ad5f614b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,85 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class GemPackage < Chef::Resource::Package
- resource_name :gem_package
+ unified_mode true
+ provides :gem_package
- property :source, [ String, Array ]
- property :clear_sources, [ true, false ], default: false, desired_state: false
- # Sets a custom gem_binary to run for gem commands.
- property :gem_binary, String, desired_state: false
+ description <<~DESC
+ Use the **gem_package** resource to manage gem packages that are only included in recipes.
+ When a gem is installed from a local file, it must be added to the node using the **remote_file** or **cookbook_file** resources.
- ##
- # 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
+ Note: The **gem_package** resource must be specified as `gem_package` and cannot be shortened to `package` in a recipe.
+ Warning: The **chef_gem** and **gem_package** resources are both used to install Ruby gems. For any machine on which #{ChefUtils::Dist::Infra::PRODUCT} is
+ installed, there are two instances of Ruby. One is the standard, system-wide instance of Ruby and the other is a dedicated instance that is
+ available only to #{ChefUtils::Dist::Infra::PRODUCT}.
+ Use the **chef_gem** resource to install gems into the instance of Ruby that is dedicated to #{ChefUtils::Dist::Infra::PRODUCT}.
+ Use the **gem_package** resource to install all other gems (i.e. install gems system-wide).
+ DESC
+
+ examples <<~EXAMPLES
+ The following examples demonstrate various approaches for using the **gem_package** resource in recipes:
+
+ **Install a gem file from the local file system**
+
+ ```ruby
+ gem_package 'loofah' do
+ source '/tmp/loofah-2.7.0.gem'
+ action :install
+ end
+ ```
+
+ **Use the `ignore_failure` common attribute**
+
+ ```ruby
+ gem_package 'syntax' do
+ action :install
+ ignore_failure true
+ end
+ ```
+ EXAMPLES
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ # 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://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 ],
+ description: "Optional. The URL, or list of URLs, at which the gem package is located. This list is added to the source configured in `Chef::Config[:rubygems_url]` (see also include_default_source) to construct the complete list of rubygems sources. Users in an 'airgapped' environment should set Chef::Config[:rubygems_url] to their local RubyGems mirror."
+
+ property :clear_sources, [ TrueClass, FalseClass, nil ],
+ description: "Set to `true` to download a gem from the path specified by the `source` property (and not from RubyGems).",
+ default: lazy { Chef::Config[:clear_gem_sources] }, desired_state: false
+
+ property :gem_binary, String, desired_state: false,
+ description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by #{ChefUtils::Dist::Infra::PRODUCT} will be used."
+
+ property :include_default_source, [ TrueClass, FalseClass, nil ],
+ description: "Set to `false` to not include `Chef::Config[:rubygems_url]` in the sources.",
+ default: nil, introduced: "13.0"
+
+ property :options, [ String, Hash, Array, nil ],
+ description: "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.",
+ desired_state: false
end
end
end
diff --git a/lib/chef/resource/git.rb b/lib/chef/resource/git.rb
deleted file mode 100644
index 4799b54d3d..0000000000
--- a/lib/chef/resource/git.rb
+++ /dev/null
@@ -1,44 +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/resource/scm"
-
-class Chef
- class Resource
- class Git < Chef::Resource::Scm
-
- 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
-
- alias :branch :revision
- alias :reference :revision
-
- alias :repo :repository
- end
- end
-end
diff --git a/lib/chef/resource/group.rb b/lib/chef/resource/group.rb
index d3a4a1ce89..3a129592d0 100644
--- a/lib/chef/resource/group.rb
+++ b/lib/chef/resource/group.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,83 +20,45 @@
class Chef
class Resource
class Group < Chef::Resource
+ unified_mode true
+ state_attrs :members
- identity_attr :group_name
+ description "Use the **group** resource to manage a local group."
- state_attrs :members
+ provides :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
+ property :group_name, String,
+ name_property: true,
+ description: "The name of the group."
- def group_name(arg = nil)
- set_or_return(
- :group_name,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :gid, [ String, Integer ],
+ description: "The identifier for the group."
- def gid(arg = nil)
- set_or_return(
- :gid,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :members, [String, Array], default: lazy { [] },
+ coerce: proc { |arg| arg.is_a?(String) ? arg.split(/\s*,\s*/) : arg },
+ description: "Which users should be set or appended to a group. When more than one group member is identified, the list of members should be an array: members ['user1', 'user2']."
- def members(arg = nil)
- converted_members = arg.is_a?(String) ? arg.split(",") : arg
- set_or_return(
- :members,
- converted_members,
- :kind_of => [ Array ]
- )
- end
+ property :excluded_members, [String, Array], default: lazy { [] },
+ coerce: proc { |arg| arg.is_a?(String) ? arg.split(/\s*,\s*/) : arg },
+ description: "Remove users from a group. May only be used when append is set to true."
- alias_method :users, :members
+ property :append, [ TrueClass, FalseClass ], default: false,
+ description: "How members should be appended and/or removed from a group. When true, members are appended and excluded_members are removed. When false, group members are reset to the value of the members property."
- def excluded_members(arg = nil)
- converted_members = arg.is_a?(String) ? arg.split(",") : arg
- set_or_return(
- :excluded_members,
- converted_members,
- :kind_of => [ Array ]
- )
- end
+ property :system, [ TrueClass, FalseClass ], default: false,
+ description: "Set if a group belongs to a system group. Set to true if the group belongs to a system group."
- def append(arg = nil)
- set_or_return(
- :append,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :non_unique, [ TrueClass, FalseClass ], default: false,
+ description: "Allow gid duplication. May only be used with the Groupadd provider."
- def system(arg = nil)
- set_or_return(
- :system,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :comment, String,
+ introduced: "14.9",
+ description: "Specifies a comment to associate with the local group."
- def non_unique(arg = nil)
- set_or_return(
- :non_unique,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ alias_method :users, :members
end
end
end
diff --git a/lib/chef/resource/helpers/cron_validations.rb b/lib/chef/resource/helpers/cron_validations.rb
new file mode 100644
index 0000000000..60861be617
--- /dev/null
+++ b/lib/chef/resource/helpers/cron_validations.rb
@@ -0,0 +1,101 @@
+#
+# Copyright:: Copyright (c) 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 ResourceHelpers
+ # a collection of methods for validating cron times. Used in the various cron-like resources
+ module CronValidations
+ # validate a provided value is between two other provided values
+ # we also allow * as a valid input
+ # @param spec the value to validate
+ # @param min the lowest value allowed
+ # @param max the highest value allowed
+ # @return [Boolean] valid or not?
+ def validate_numeric(spec, min, max)
+ return true if spec == "*"
+
+ if spec.respond_to? :to_int
+ return spec >= min && spec <= max
+ end
+
+ # Lists of individual values, ranges, and step values all share the validity range for type
+ spec.split(%r{\/|-|,}).each do |x|
+ next if x == "*"
+ return false unless /^\d+$/.match?(x)
+
+ x = x.to_i
+ return false unless x >= min && x <= max
+ end
+ true
+ end
+
+ # validate the provided month value to be jan - dec, 1 - 12, or *
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def validate_month(spec)
+ return true if spec == "*"
+
+ if spec.respond_to? :to_int
+ validate_numeric(spec, 1, 12)
+ elsif spec.respond_to? :to_str
+ # Named abbreviations are permitted but not as part of a range or with stepping
+ return true if %w{jan feb mar apr may jun jul aug sep oct nov dec}.include? spec.downcase
+
+ # 1-12 are legal for months
+ validate_numeric(spec, 1, 12)
+ else
+ false
+ end
+ end
+
+ # validate the provided day of the week is sun-sat, sunday-saturday, 0-7, or *
+ # Added crontab param to check cron resource
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def validate_dow(spec)
+ spec = spec.to_s
+ spec == "*" ||
+ validate_numeric(spec, 0, 7) ||
+ %w{sun mon tue wed thu fri sat}.include?(spec.downcase) ||
+ %w{sunday monday tuesday wednesday thursday friday saturday}.include?(spec.downcase)
+ end
+
+ # validate the day of the month is 1-31
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def validate_day(spec)
+ validate_numeric(spec, 1, 31)
+ end
+
+ # validate the hour is 0-23
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def validate_hour(spec)
+ validate_numeric(spec, 0, 23)
+ end
+
+ # validate the minute is 0-59
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def validate_minute(spec)
+ validate_numeric(spec, 0, 59)
+ end
+
+ extend self
+ 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..4c68afaab0
--- /dev/null
+++ b/lib/chef/resource/homebrew_cask.rb
@@ -0,0 +1,104 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require_relative "../mixin/homebrew_user"
+
+class Chef
+ class Resource
+ class HomebrewCask < Chef::Resource
+ unified_mode true
+
+ 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: "An optional property to set the cask name if it differs from the resource block's name.",
+ regex: %r{^[\w/-]+$},
+ validation_message: "The provided Homebrew cask name is not valid. Cask names can contain alphanumeric characters, _, -, or / only!",
+ name_property: true
+
+ property :options, String,
+ description: "Options to pass to the brew command during installation."
+
+ property :install_cask, [TrueClass, FalseClass],
+ description: "Automatically install the Homebrew 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, Integer],
+ 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 "homebrew/cask" if new_resource.install_cask
+
+ unless casked?
+ converge_by("install cask #{new_resource.cask_name} #{new_resource.options}") do
+ shell_out!("#{new_resource.homebrew_path} install --cask #{new_resource.cask_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 "homebrew/cask" if new_resource.install_cask
+
+ if casked?
+ converge_by("uninstall cask #{new_resource.cask_name}") do
+ shell_out!("#{new_resource.homebrew_path} uninstall --cask #{new_resource.cask_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
+
+ # Is the desired cask already casked?
+ #
+ # @return [Boolean]
+ def casked?
+ unscoped_name = new_resource.cask_name.split("/").last
+ shell_out!("#{new_resource.homebrew_path} list --cask 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..3874622005 100644
--- a/lib/chef/resource/homebrew_package.rb
+++ b/lib/chef/resource/homebrew_package.rb
@@ -2,8 +2,7 @@
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Graeme Mathieson (<mathie@woss.name>)
#
-# Copyright 2011-2016, Chef Software Inc.
-# Copyright 2014-2016, Chef Software, Inc <legal@chef.io>
+# Copyright:: Copyright (c) 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.
@@ -18,16 +17,52 @@
# limitations under the License.
#
-require "chef/provider/package"
-require "chef/resource/package"
+require_relative "../provider/package"
+require_relative "package"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class HomebrewPackage < Chef::Resource::Package
- resource_name :homebrew_package
+ unified_mode true
+
+ provides :homebrew_package
provides :package, os: "darwin"
- property :homebrew_user, [ String, Integer ]
+ description "Use the **homebrew_package** resource to manage packages for the macOS platform. Note: Starting with #{ChefUtils::Dist::Infra::PRODUCT} 16 the homebrew resource now accepts an array of packages for installing multiple packages at once."
+ introduced "12.0"
+ examples <<~DOC
+ **Install a package**:
+
+ ```ruby
+ homebrew_package 'git'
+ ```
+
+ **Install multiple packages at once**:
+
+ ```ruby
+ homebrew_package %w(git fish ruby)
+ ```
+
+ **Specify the Homebrew user with a UUID**
+
+ ```ruby
+ homebrew_package 'git' do
+ homebrew_user 1001
+ end
+ ```
+
+ **Specify the Homebrew user with a string**:
+
+ ```ruby
+ homebrew_package 'vim' do
+ homebrew_user 'user1'
+ end
+ ```
+ DOC
+
+ property :homebrew_user, [ String, Integer ],
+ description: "The name or uid of the Homebrew owner to be used by #{ChefUtils::Dist::Infra::PRODUCT} when executing a command."
end
end
diff --git a/lib/chef/resource/homebrew_tap.rb b/lib/chef/resource/homebrew_tap.rb
new file mode 100644
index 0000000000..937a9ab420
--- /dev/null
+++ b/lib/chef/resource/homebrew_tap.rb
@@ -0,0 +1,91 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require_relative "../mixin/homebrew_user"
+
+class Chef
+ class Resource
+ class HomebrewTap < Chef::Resource
+ unified_mode true
+
+ 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: "An optional property to set the tap name if it differs from the resource block's name.",
+ validation_message: "Homebrew tap names must be in the form REPO/TAP format!",
+ regex: %r{^[\w-]+(?:\/[\w-]+)+$},
+ name_property: true
+
+ property :url, String,
+ description: "The URL of the tap."
+
+ property :full, [TrueClass, FalseClass],
+ description: "Perform a full clone on the tap, as opposed to a shallow clone.",
+ 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.tap_name)
+ converge_by("tap #{new_resource.tap_name}") do
+ shell_out!("#{new_resource.homebrew_path} tap #{new_resource.full ? "--full" : ""} #{new_resource.tap_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.tap_name)
+ converge_by("untap #{new_resource.tap_name}") do
+ shell_out!("#{new_resource.homebrew_path} untap #{new_resource.tap_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
+
+ # Is the passed tap already tapped
+ #
+ # @return [Boolean]
+ 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/homebrew_update.rb b/lib/chef/resource/homebrew_update.rb
new file mode 100644
index 0000000000..27b352bfb6
--- /dev/null
+++ b/lib/chef/resource/homebrew_update.rb
@@ -0,0 +1,110 @@
+#
+# Author:: Joshua Timberman (<jtimberman@chef.io>)
+# Author:: Dan Webb (<dan@webb-agile-solutions.ltd>)
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: Copyright (c) Webb Agile Solutions Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+require_relative "../mixin/homebrew_user"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class HomebrewUpdate < Chef::Resource
+ include Chef::Mixin::HomebrewUser
+
+ unified_mode true
+
+ provides(:homebrew_update) { true }
+
+ description "Use the **homebrew_update** resource to manage Homebrew repository updates on macOS."
+ introduced "16.2"
+ examples <<~DOC
+ **Update the homebrew repository data at a specified interval**:
+ ```ruby
+ homebrew_update 'all platforms' do
+ frequency 86400
+ action :periodic
+ end
+ ```
+ **Update the Homebrew repository at the start of a #{ChefUtils::Dist::Infra::PRODUCT} run**:
+ ```ruby
+ homebrew_update 'update'
+ ```
+ DOC
+
+ # allow bare homebrew_update with no name
+ property :name, String, default: ""
+
+ property :frequency, Integer,
+ description: "Determines how frequently (in seconds) Homebrew updates are made. Use this property when the `:periodic` action is specified.",
+ default: 86_400
+
+ default_action :periodic
+ allowed_actions :update, :periodic
+
+ action_class do
+ BREW_STAMP_DIR = "/var/lib/homebrew/periodic".freeze
+ BREW_STAMP = "#{BREW_STAMP_DIR}/update-success-stamp".freeze
+
+ # Determines whether we need to run `homebrew update`
+ #
+ # @return [Boolean]
+ def brew_up_to_date?
+ ::File.exist?(BREW_STAMP) &&
+ ::File.mtime(BREW_STAMP) > Time.now - new_resource.frequency
+ end
+
+ def do_update
+ directory BREW_STAMP_DIR do
+ recursive true
+ end
+
+ file BREW_STAMP do
+ content "BREW::Update::Post-Invoke-Success\n"
+ action :create_if_missing
+ end
+
+ execute "brew update" do
+ command %w{brew update}
+ default_env true
+ user find_homebrew_uid
+ notifies :touch, "file[#{BREW_STAMP}]", :immediately
+ end
+ end
+ end
+
+ action :periodic do
+ return unless macos?
+
+ unless brew_up_to_date?
+ converge_by "update new lists of packages" do
+ do_update
+ end
+ end
+ end
+
+ action :update do
+ return unless macos?
+
+ converge_by "force update new lists of packages" do
+ do_update
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/hostname.rb b/lib/chef/resource/hostname.rb
new file mode 100644
index 0000000000..e63c0be54e
--- /dev/null
+++ b/lib/chef/resource/hostname.rb
@@ -0,0 +1,260 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ # Sets the hostname and updates /etc/hosts on *nix systems
+ # @since 14.0.0
+ class Hostname < Chef::Resource
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ **Set the hostname using the IP address, as detected by Ohai**:
+
+ ```ruby
+ hostname 'example'
+ ```
+
+ **Manually specify the hostname and IP address**:
+
+ ```ruby
+ hostname 'statically_configured_host' do
+ hostname 'example'
+ ipaddress '198.51.100.2'
+ end
+ ```
+ DOC
+
+ property :hostname, String,
+ description: "An optional property to set the hostname if it differs from the resource block's name.",
+ name_property: true
+
+ property :ipaddress, String,
+ description: "The IP address to use when configuring the hosts file.",
+ default: lazy { node["ipaddress"] }, default_description: "The node's IP address as determined by Ohai."
+
+ property :aliases, [ Array, nil ],
+ description: "An array of hostname aliases to use when configuring the hosts file.",
+ default: nil
+
+ # override compile_time property to be true by default
+ property :compile_time, [ TrueClass, FalseClass ],
+ description: "Determines whether or not the resource should be run at compile time.",
+ default: true, desired_state: false
+
+ property :windows_reboot, [ TrueClass, FalseClass ],
+ description: "Determines whether or not Windows should be reboot after changing the hostname, as this is required for the change to 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" unless defined?(REXML::Document)
+ config = REXML::Document.new(::File.read(WINDOWS_EC2_CONFIG))
+ # 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 !windows?
+ ohai "reload hostname" do
+ plugin "hostname"
+ action :nothing
+ end
+
+ # set the hostname via /bin/hostname
+ 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 persist on a reboot
+ case
+ when darwin?
+ # darwin
+ 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
+ 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[/[^\.]*/]
+ 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 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
+ 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}\s*$/ }
+ 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)
+ 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
+ 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
+ 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}")
+
+ file "/etc/myname" do
+ content "#{new_resource.hostname}\n"
+ owner "root"
+ group node["root_group"]
+ mode "0644"
+ end
+ when ::File.exist?("/usr/sbin/svccfg") # solaris 5.11
+ 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 =~ %r{config/nodename\s+astring\s+#{new_resource.hostname}} }
+ end
+ execute "svcadm refresh" do
+ command "svcadm refresh system/identity:node"
+ action :nothing
+ end
+ 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
+ WINDOWS_EC2_CONFIG = 'C:\Program Files\Amazon\Ec2ConfigService\Settings\config.xml'.freeze
+
+ raise "Windows hostnames cannot contain a period." if new_resource.hostname.include?(".")
+
+ # suppress EC2 config service from setting our hostname
+ if ::File.exist?(WINDOWS_EC2_CONFIG)
+ 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
+ file WINDOWS_EC2_CONFIG do
+ content xml_contents
+ end
+ end
+ end
+
+ unless Socket.gethostbyname(Socket.gethostname).first == new_resource.hostname
+ converge_by "set hostname to #{new_resource.hostname}" do
+ powershell_exec! <<~EOH
+ $sysInfo = Get-WmiObject -Class Win32_ComputerSystem
+ $sysInfo.Rename("#{new_resource.hostname}")
+ EOH
+ end
+
+ # reboot because $windows
+ reboot "setting hostname" do
+ reason "#{ChefUtils::Dist::Infra::PRODUCT} updated system hostname"
+ only_if { new_resource.windows_reboot }
+ action :request_reboot
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/http_request.rb b/lib/chef/resource/http_request.rb
index fcc48470bc..f53d3e731f 100644
--- a/lib/chef/resource/http_request.rb
+++ b/lib/chef/resource/http_request.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,31 +17,29 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/http_request"
+require_relative "../resource"
class Chef
class Resource
class HttpRequest < Chef::Resource
+ unified_mode true
- identity_attr :url
+ provides :http_request
+
+ 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,
+ description: "The URL to which an HTTP request is sent."
+
+ property :headers, Hash, default: lazy { {} },
+ description: "A Hash of custom headers."
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)
@@ -49,15 +47,7 @@ class Chef
set_or_return(
:message,
args,
- :kind_of => Object
- )
- end
-
- def headers(args = nil)
- set_or_return(
- :headers,
- args,
- :kind_of => Hash
+ kind_of: Object
)
end
diff --git a/lib/chef/resource/ifconfig.rb b/lib/chef/resource/ifconfig.rb
index fd523d9580..2c5262fa97 100644
--- a/lib/chef/resource/ifconfig.rb
+++ b/lib/chef/resource/ifconfig.rb
@@ -17,131 +17,138 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class Ifconfig < Chef::Resource
+ unified_mode true
- identity_attr :device
+ provides :ifconfig
+
+ description "Use the **ifconfig** resource to manage interfaces on Unix and Linux systems. Note: This resource requires the ifconfig binary to be present on the system and may require additional packages to be installed first. On Ubuntu 18.04 or later you will need to install the `ifupdown` package, which disables the built in Netplan functionality. Warning: This resource will not work with Fedora release 33 or later."
+ examples <<~DOC
+ **Configure a network interface with a static IP**
+
+ ```ruby
+ ifconfig '33.33.33.80' do
+ device 'eth1'
+ end
+ ```
+
+ will create the following interface configuration:
+
+ ```
+ iface eth1 inet static
+ address 33.33.33.80
+ ```
+
+ **Configure an interface to use DHCP**
+
+ ```ruby
+ ifconfig 'Set eth1 to DHCP' do
+ device 'eth1'
+ bootproto 'dhcp'
+ end
+ ```
+
+ will create the following interface configuration:
+
+ ```
+ iface eth1 inet dhcp
+ ```
+
+ **Update a static IP address with a boot protocol**
+
+ ```ruby
+ ifconfig "33.33.33.80" do
+ bootproto "dhcp"
+ device "eth1"
+ end
+ ```
+
+ will update the interface configuration from static to dhcp:
+
+ ```
+ iface eth1 inet dhcp
+ address 33.33.33.80
+ ```
+ DOC
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
+ property :target, String,
+ name_property: true,
+ description: "The IP address that is to be assigned to the network interface. If not specified we'll use the resource's name."
- def target(arg = nil)
- set_or_return(
- :target,
- arg,
- :kind_of => String
- )
- end
+ property :hwaddr, String,
+ description: "The hardware address for the network interface."
- def device(arg = nil)
- set_or_return(
- :device,
- arg,
- :kind_of => String
- )
- end
+ property :mask, String,
+ description: "The decimal representation of the network mask. For example: `255.255.255.0`."
- def hwaddr(arg = nil)
- set_or_return(
- :hwaddr,
- arg,
- :kind_of => String
- )
- end
+ property :family, String, default: "inet",
+ introduced: "14.0",
+ description: "Networking family option for Debian-based systems; for example: `inet` or `inet6`."
- def inet_addr(arg = nil)
- set_or_return(
- :inet_addr,
- arg,
- :kind_of => String
- )
- end
+ property :inet_addr, String,
+ description: "The Internet host address for the network interface."
- def bcast(arg = nil)
- set_or_return(
- :bcast,
- arg,
- :kind_of => String
- )
- end
+ property :bcast, String,
+ description: "The broadcast address for a network interface. On some platforms this property is not set using ifconfig, but instead is added to the startup configuration file for the network interface."
- def mask(arg = nil)
- set_or_return(
- :mask,
- arg,
- :kind_of => String
- )
- end
+ property :mtu, String,
+ description: "The maximum transmission unit (MTU) for the network interface."
- def mtu(arg = nil)
- set_or_return(
- :mtu,
- arg,
- :kind_of => String
- )
- end
+ property :metric, String,
+ description: "The routing metric for the interface."
- def metric(arg = nil)
- set_or_return(
- :metric,
- arg,
- :kind_of => String
- )
- end
+ property :device, String,
+ identity: true,
+ description: "The network interface to be configured."
- def onboot(arg = nil)
- set_or_return(
- :onboot,
- arg,
- :kind_of => String
- )
- end
+ property :onboot, String,
+ description: "Bring up the network interface on boot."
- def network(arg = nil)
- set_or_return(
- :network,
- arg,
- :kind_of => String
- )
- end
+ property :network, String,
+ description: "The address for the network interface."
- def bootproto(arg = nil)
- set_or_return(
- :bootproto,
- arg,
- :kind_of => String
- )
- end
+ property :bootproto, String,
+ description: "The boot protocol used by a network interface."
- def onparent(arg = nil)
- set_or_return(
- :onparent,
- arg,
- :kind_of => String
- )
- end
- end
+ property :onparent, String,
+ description: "Bring up the network interface when its parent interface is brought up."
+
+ property :ethtool_opts, String,
+ introduced: "13.4",
+ description: "Options to be passed to ethtool(8). For example: `-A eth0 autoneg off rx off tx off`."
+
+ property :bonding_opts, String,
+ introduced: "13.4",
+ description: "Bonding options to pass via `BONDING_OPTS` on RHEL and CentOS. For example: `mode=active-backup miimon=100`."
+
+ property :master, String,
+ introduced: "13.4",
+ description: "Specifies the channel bonding interface to which the Ethernet interface is linked."
+ property :slave, String,
+ introduced: "13.4",
+ description: "When set to `yes`, this device is controlled by the channel bonding interface that is specified via the `master` property."
+
+ property :vlan, String,
+ introduced: "14.4",
+ description: "The VLAN to assign the interface to."
+
+ property :gateway, String,
+ introduced: "14.4",
+ description: "The gateway to use for the interface."
+
+ property :bridge, String,
+ introduced: "16.7",
+ description: "The bridge interface this interface is a member of on Red Hat based systems."
+ end
end
end
diff --git a/lib/chef/resource/ips_package.rb b/lib/chef/resource/ips_package.rb
index 4d2c957e17..a579a5a5a2 100644
--- a/lib/chef/resource/ips_package.rb
+++ b/lib/chef/resource/ips_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Jason Williams (<williamsjj@digitar.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,31 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/ips"
+require_relative "package"
+require_relative "../provider/package/ips"
class Chef
class Resource
class IpsPackage < ::Chef::Resource::Package
- resource_name :ips_package
+ unified_mode true
+
+ provides :ips_package
provides :package, os: "solaris2"
- provides :ips_package, os: "solaris2"
+
+ 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 :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ property :accept_license, [TrueClass, FalseClass],
+ default: false, desired_state: false,
+ description: "Accept an end-user license agreement, automatically."
end
end
end
diff --git a/lib/chef/resource/kernel_module.rb b/lib/chef/resource/kernel_module.rb
new file mode 100644
index 0000000000..485511470e
--- /dev/null
+++ b/lib/chef/resource/kernel_module.rb
@@ -0,0 +1,227 @@
+#
+# Resource:: kernel_module
+#
+# The MIT License (MIT)
+#
+# Copyright:: 2016-2018, Shopify Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
+
+require_relative "../resource"
+
+class Chef
+ class Resource
+ class KernelModule < Chef::Resource
+ unified_mode true
+
+ provides :kernel_module
+
+ description "Use the **kernel_module** resource to manage kernel modules on Linux systems. This resource can load, unload, blacklist, disable, install, and uninstall modules."
+ introduced "14.3"
+ examples <<~DOC
+ Install and load a kernel module, and ensure it loads on reboot.
+
+ ```ruby
+ kernel_module 'loop'
+ ```
+
+ Install and load a kernel with a specific set of options, and ensure it loads on reboot. Consult kernel module
+ documentation for specific options that are supported.
+
+ ```ruby
+ kernel_module 'loop' do
+ options [
+ 'max_loop=4',
+ 'max_part=8',
+ ]
+ end
+ ```
+
+ Load a kernel module.
+
+ ```ruby
+ kernel_module 'loop' do
+ action :load
+ end
+ ```
+
+ Unload a kernel module and remove module config, so it doesn't load on reboot.
+
+ ```ruby
+ kernel_module 'loop' do
+ action :uninstall
+ end
+ ```
+
+ Unload kernel module.
+
+ ```ruby
+ kernel_module 'loop' do
+ action :unload
+ end
+ ```
+
+ Blacklist a module from loading.
+
+ ```ruby
+ kernel_module 'loop' do
+ action :blacklist
+ end
+ ```
+
+ Disable a kernel module.
+
+ ```ruby
+ kernel_module 'loop' do
+ action :disable
+ end
+ ```
+ DOC
+
+ property :modname, String,
+ description: "An optional property to set the kernel module name if it differs from the resource block's name.",
+ name_property: true
+
+ property :options, Array,
+ description: "An optional property to set options for the kernel module.",
+ introduced: "15.4"
+
+ property :load_dir, String,
+ description: "The directory to load modules from.",
+ default: "/etc/modules-load.d"
+
+ property :unload_dir, String,
+ description: "The modprobe.d directory.",
+ default: "/etc/modprobe.d"
+
+ action :install do
+ description "Load kernel module, and ensure it loads on reboot."
+
+ with_run_context :root do
+ find_resource(:execute, "update initramfs") do
+ command initramfs_command
+ action :nothing
+ end
+ end
+
+ # create options file before loading the module
+ unless new_resource.options.nil?
+ file "#{new_resource.unload_dir}/options_#{new_resource.modname}.conf" do
+ content "options #{new_resource.modname} #{new_resource.options.join(" ")}\n"
+ end
+ end
+
+ # load the module first before installing
+ action_load
+
+ directory new_resource.load_dir do
+ recursive true
+ end
+
+ file "#{new_resource.load_dir}/#{new_resource.modname}.conf" do
+ content "#{new_resource.modname}\n"
+ notifies :run, "execute[update initramfs]", :delayed
+ end
+ end
+
+ action :uninstall do
+ description "Unload a kernel module and remove module config, so it doesn't load on reboot."
+ with_run_context :root do
+ find_resource(:execute, "update initramfs") do
+ command initramfs_command
+ action :nothing
+ end
+ end
+
+ file "#{new_resource.load_dir}/#{new_resource.modname}.conf" do
+ action :delete
+ notifies :run, "execute[update initramfs]", :delayed
+ end
+
+ file "#{new_resource.unload_dir}/blacklist_#{new_resource.modname}.conf" do
+ action :delete
+ notifies :run, "execute[update initramfs]", :delayed
+ end
+
+ file "#{new_resource.unload_dir}/options_#{new_resource.modname}.conf" do
+ action :delete
+ end
+
+ action_unload
+ end
+
+ action :blacklist do
+ description "Blacklist a kernel module."
+
+ with_run_context :root do
+ find_resource(:execute, "update initramfs") do
+ command initramfs_command
+ action :nothing
+ end
+ end
+
+ file "#{new_resource.unload_dir}/blacklist_#{new_resource.modname}.conf" do
+ content "blacklist #{new_resource.modname}"
+ notifies :run, "execute[update initramfs]", :delayed
+ end
+
+ action_unload
+ end
+
+ action :disable do
+ description "Disable a kernel module."
+
+ with_run_context :root do
+ find_resource(:execute, "update initramfs") do
+ command initramfs_command
+ action :nothing
+ end
+ end
+
+ file "#{new_resource.unload_dir}/disable_#{new_resource.modname}.conf" do
+ content "install #{new_resource.modname} /bin/false"
+ notifies :run, "execute[update initramfs]", :delayed
+ end
+
+ action_unload
+ end
+
+ action :load do
+ description "Load a kernel module."
+
+ unless module_loaded?
+ converge_by("load kernel module #{new_resource.modname}") do
+ shell_out!("modprobe #{new_resource.modname}")
+ end
+ end
+ end
+
+ action :unload do
+ description "Unload kernel module."
+
+ if module_loaded?
+ converge_by("unload kernel module #{new_resource.modname}") do
+ shell_out!("modprobe -r #{new_resource.modname}")
+ end
+ end
+ end
+
+ action_class do
+ # determine the correct command to regen the initramfs based on platform
+ # @return [String]
+ def initramfs_command
+ if platform_family?("debian")
+ "update-initramfs -u"
+ else
+ "dracut -f"
+ end
+ end
+
+ # see if the module is listed in /proc/modules or not
+ # @return [Boolean]
+ def module_loaded?
+ /^#{new_resource.modname}/.match?(::File.read("/proc/modules"))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/ksh.rb b/lib/chef/resource/ksh.rb
index 3097156329..28d71970e7 100644
--- a/lib/chef/resource/ksh.rb
+++ b/lib/chef/resource/ksh.rb
@@ -1,6 +1,6 @@
#
# Author:: Nolan Davidson (<nolan.davidson@gmail.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,22 @@
# limitations under the License.
#
-require "chef/resource/script"
+require_relative "script"
class Chef
class Resource
class Ksh < Chef::Resource::Script
+ unified_mode true
+
+ provides :ksh
+
+ 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 8dca90ef0e..c8e3d93afc 100644
--- a/lib/chef/resource/launchd.rb
+++ b/lib/chef/resource/launchd.rb
@@ -16,89 +16,242 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/launchd"
+require_relative "../resource"
class Chef
class Resource
class Launchd < Chef::Resource
- provides :launchd, os: "darwin"
+ unified_mode true
+ 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
-
- 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 :group, [String, Integer]
- property :hash, Hash
- property :mode, [String, Integer]
- property :owner, [String, Integer]
- property :path, String
- property :source, String
- property :session_type, String
-
- property :type, String, default: "daemon", coerce: proc { |type|
- type = type ? type.downcase : "daemon"
- types = %w{daemon agent}
-
- unless types.include?(type)
- error_msg = "type must be daemon or agent"
- raise Chef::Exceptions::ValidationFailed, error_msg
- end
- type
- }
+ allowed_actions :create, :create_if_missing, :delete, :enable, :disable, :restart
+
+ property :label, String,
+ name_property: true,
+ description: "The unique identifier for the job."
+
+ property :backup, [Integer, FalseClass],
+ desired_state: false,
+ description: "The number of backups to be kept in /var/chef/backup. Set to false to prevent backups from being kept."
+
+ property :cookbook, String,
+ desired_state: false,
+ description: "The name of the cookbook in which the source files are located."
+
+ property :group, [String, Integer],
+ description: "When launchd is run as the root user, the group to run the job as. If the username property is specified and this property is not, this value is set to the default group for the user."
+
+ property :plist_hash, Hash,
+ introduced: "12.19",
+ description: "A Hash of key value pairs used to create the launchd property list."
+
+ property :mode, [String, Integer],
+ description: "A quoted 3-5 character string that defines the octal mode. For example: '755', '0755', or 00755."
+
+ property :owner, [String, Integer],
+ description: "A string or ID that identifies the group owner by user name, including fully qualified user names such as domain_user or user@domain. If this value is not specified, existing owners remain unchanged and new owner assignments use the current user (when necessary)."
+
+ property :path, String,
+ description: "The path to the directory. Using a fully qualified path is recommended, but is not always required."
+
+ property :source, String,
+ description: "The path to the launchd property list."
+
+ property :session_type, String,
+ description: "The type of launchd plist to be created. Possible values: system (default) or user."
+
+ # 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, launchctl 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],
+ description: "A Hash (similar to crontab) that defines the calendar frequency at which a job is started or an 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,
+ description: "The type of resource. Possible values: daemon (default), agent.",
+ default: "daemon", coerce: proc { |type|
+ type = type ? type.downcase : "daemon"
+ types = %w{daemon agent}
+
+ unless types.include?(type)
+ error_msg = "type must be daemon or agent"
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ type
+ }
# Apple LaunchD Keys
- property :abandon_process_group, [ TrueClass, FalseClass ]
- property :debug, [ TrueClass, FalseClass ]
- property :disabled, [ TrueClass, FalseClass ], default: false
- property :enable_globbing, [ TrueClass, FalseClass ]
- property :enable_transactions, [ TrueClass, FalseClass ]
- property :environment_variables, Hash
- property :exit_timeout, Integer
- property :hard_resource_limits, Hash
- property :inetd_compatibility, Hash
- property :init_groups, [ 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 :low_priority_io, [ TrueClass, FalseClass ]
- property :mach_services, Hash
- property :nice, Integer
- property :on_demand, [ TrueClass, FalseClass ]
- property :process_type, String
- property :program, String
- property :program_arguments, Array
- property :queue_directories, Array
- property :root_directory, String
- property :run_at_load, [ TrueClass, FalseClass ]
- property :sockets, Hash
- property :soft_resource_limits, Array
- 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
- property :time_out, Integer
- property :umask, Integer
- property :username, String
- property :wait_for_debugger, [ TrueClass, FalseClass ]
- property :watch_paths, Array
- property :working_directory, String
+ property :abandon_process_group, [ TrueClass, FalseClass ],
+ description: "If a job dies, all remaining processes with the same process ID may be kept running. Set to true to kill all remaining processes."
+
+ property :debug, [ TrueClass, FalseClass ],
+ description: "Sets the log mask to `LOG_DEBUG` for this job."
+
+ property :disabled, [ TrueClass, FalseClass ], default: false,
+ description: "Hints to `launchctl` to not submit this job to launchd."
+
+ property :enable_globbing, [ TrueClass, FalseClass ],
+ description: "Update program arguments before invocation."
+
+ property :enable_transactions, [ TrueClass, FalseClass ],
+ description: "Track in-progress transactions; if none, then send the `SIGKILL` signal."
+
+ property :environment_variables, Hash,
+ description: "Additional environment variables to set before running a job."
+
+ property :exit_timeout, Integer,
+ description: "The amount of time (in seconds) launchd waits before sending a `SIGKILL` signal."
+
+ property :hard_resource_limits, Hash,
+ description: "A Hash of resource limits to be imposed on a job."
+
+ property :inetd_compatibility, Hash,
+ description: "Specifies if a daemon expects to be run as if it were launched from inetd. Set to `wait => true` to pass standard input, output, and error file descriptors. Set to `wait => false` to call the accept system call on behalf of the job, and then pass standard input, output, and error file descriptors."
+
+ property :init_groups, [ TrueClass, FalseClass ],
+ description: "Specify if `initgroups` is called before running a job."
+
+ property :keep_alive, [ TrueClass, FalseClass, Hash ],
+ introduced: "12.14",
+ description: "Keep a job running continuously (true) or allow demand and conditions on the node to determine if the job keeps running (`false`)."
+
+ property :launch_events, [ Hash ],
+ introduced: "15.1",
+ description: "Specify higher-level event types to be used as launch-on-demand event sources."
+
+ property :launch_only_once, [ TrueClass, FalseClass ],
+ description: "Specify if a job can be run only one time. Set this value to true if a job cannot be restarted without a full machine reboot."
+
+ property :ld_group, String,
+ description: "The group name."
+
+ property :limit_load_from_hosts, Array,
+ description: "An array of hosts to which this configuration file does not apply, i.e. 'apply this configuration file to all hosts not specified in this array'."
+
+ property :limit_load_to_hosts, Array,
+ description: "An array of hosts to which this configuration file applies."
+
+ property :limit_load_to_session_type, [ Array, String ],
+ description: "The session type(s) to which this configuration file applies."
+
+ property :low_priority_io, [ TrueClass, FalseClass ],
+ description: "Specify if the kernel on the node should consider this daemon to be low priority during file system I/O."
+
+ property :mach_services, Hash,
+ description: "Specify services to be registered with the bootstrap subsystem."
+
+ property :nice, Integer,
+ description: "The program scheduling priority value in the range -20 to 19.",
+ callbacks: { "should be a Integer between -20 and 19" => proc { |v| v >= -20 && v <= 19 } }
+
+ property :on_demand, [ TrueClass, FalseClass ],
+ description: "Keep a job alive. Only applies to macOS version 10.4 (and earlier); use `keep_alive` instead for newer versions."
+
+ property :process_type, String,
+ description: "The intended purpose of the job: `Adaptive`, `Background`, `Interactive`, or `Standard`."
+
+ property :program, String,
+ description: "The first argument of execvp, typically the file name associated with the file to be executed. This value must be specified if program_arguments is not specified, and vice-versa."
+
+ property :program_arguments, Array,
+ description: "The second argument of execvp. If program is not specified, this property must be specified and will be handled as if it were the first argument."
+
+ property :queue_directories, Array,
+ description: "An array of non-empty directories which, if any are modified, will cause a job to be started."
+
+ property :root_directory, String,
+ description: "`chroot` to this directory, and then run the job."
+
+ property :run_at_load, [ TrueClass, FalseClass ],
+ description: "Launch a job once (at the time it is loaded)."
+
+ property :sockets, Hash,
+ description: "A Hash of on-demand sockets that notify launchd when a job should be run."
+
+ property :soft_resource_limits, Array,
+ description: "A Hash of resource limits to be imposed on a job."
+
+ property :standard_error_path, String,
+ description: "The file to which standard error (`stderr`) is sent."
+
+ property :standard_in_path, String,
+ description: "The file to which standard input (`stdin`) is sent."
+
+ property :standard_out_path, String,
+ description: "The file to which standard output (`stdout`) is sent."
+
+ property :start_interval, Integer,
+ description: "The frequency (in seconds) at which a job is started."
+
+ property :start_on_mount, [ TrueClass, FalseClass ],
+ description: "Start a job every time a file system is mounted."
+
+ property :throttle_interval, Integer,
+ description: "The frequency (in seconds) at which jobs are allowed to spawn."
+
+ property :time_out, Integer,
+ description: "The amount of time (in seconds) a job may be idle before it times out. If no value is specified, the default timeout value for launchd will be used."
+
+ property :umask, Integer,
+ description: "A decimal value to pass to `umask` before running a job."
+
+ property :username, String,
+ description: "When launchd is run as the root user, the user to run the job as."
+
+ property :wait_for_debugger, [ TrueClass, FalseClass ],
+ description: "Specify if launchd has a job wait for a debugger to attach before executing code."
+
+ property :watch_paths, Array,
+ description: "An array of paths which, if any are modified, will cause a job to be started."
+
+ property :working_directory, String,
+ description: "`chdir` to this directory, and then run the job."
end
end
end
diff --git a/lib/chef/resource/link.rb b/lib/chef/resource/link.rb
index 5717ec7bad..e1422d0d61 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,91 +17,56 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/mixin/securable"
+require_relative "../resource"
+require_relative "../mixin/securable"
class Chef
class Resource
class Link < Chef::Resource
include Chef::Mixin::Securable
+ unified_mode true
- identity_attr :target_file
+ provides :link
- state_attrs :to, :owner, :group
+ 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 :owner # required since it's not a property below
default_action :create
allowed_actions :create, :delete
- 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: "An optional property to set the target file if it differs from the resource block's name.",
+ name_property: 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.is_a?(String) ? arg.to_sym : arg },
+ equal_to: %i{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 group name or ID number 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
target_file
end
-
- private
-
- def verify_links_supported!
- # On certain versions of windows links are not supported. Make
- # sure we are not on such a platform.
-
- if Chef::Platform.windows?
- require "chef/win32/file"
- begin
- Chef::ReservedNames::Win32::File.verify_links_supported!
- rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
- Chef::Log.fatal("Link resource is not supported on this version of Windows")
- raise e
- end
- end
- end
end
end
end
diff --git a/lib/chef/resource/locale.rb b/lib/chef/resource/locale.rb
new file mode 100644
index 0000000000..fafa1a5caa
--- /dev/null
+++ b/lib/chef/resource/locale.rb
@@ -0,0 +1,184 @@
+#
+# Copyright:: 2011-2016, Heavy Water Software Inc.
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class Locale < Chef::Resource
+ unified_mode true
+ provides :locale
+
+ description "Use the **locale** resource to set the system's locale on Debian and Windows systems. Windows support was added in Chef Infra Client 16.0"
+ introduced "14.5"
+
+ examples <<~DOC
+ Set the lang to 'en_US.UTF-8'
+
+ ```ruby
+ locale 'set system locale' do
+ lang 'en_US.UTF-8'
+ end
+ ```
+ DOC
+
+ LC_VARIABLES ||= %w{LC_ADDRESS LC_COLLATE LC_CTYPE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME}.freeze
+ LOCALE_CONF ||= "/etc/locale.conf".freeze
+ LOCALE_REGEX ||= /\A\S+/.freeze
+ LOCALE_PLATFORM_FAMILIES ||= %w{debian windows}.freeze
+
+ property :lang, String,
+ description: "Sets the default system language.",
+ regex: [LOCALE_REGEX],
+ validation_message: "The provided lang is not valid. It should be a non-empty string without any leading whitespace."
+
+ property :lc_env, Hash,
+ description: "A Hash of LC_* env variables in the form of `({ 'LC_ENV_VARIABLE' => 'VALUE' })`.",
+ default: lazy { {} },
+ coerce: proc { |h|
+ if h.respond_to?(:keys)
+ invalid_keys = h.keys - LC_VARIABLES
+ unless invalid_keys.empty?
+ error_msg = "Key of option lc_env must be equal to one of: \"#{LC_VARIABLES.join('", "')}\"! You passed \"#{invalid_keys.join(", ")}\"."
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ end
+ unless h.values.all? { |x| x =~ LOCALE_REGEX }
+ error_msg = "Values of option lc_env should be non-empty string without any leading whitespace."
+ raise Chef::Exceptions::ValidationFailed, error_msg
+ end
+ h
+ }
+
+ # @deprecated Use {#lc_env} instead of this property.
+ # {#lc_env} uses Hash with specific LC var as key.
+ # @raise [Chef::Deprecated]
+ #
+ def lc_all(arg = nil)
+ unless arg.nil?
+ Chef.deprecated(:locale_lc_all, "Changing LC_ALL can break #{ChefUtils::Dist::Infra::PRODUCT}'s parsing of command output in unexpected ways.\n Use one of the more specific LC_ properties as needed.")
+ end
+ end
+
+ load_current_value do
+ if windows?
+ lang get_system_locale_windows
+ else
+ begin
+ old_content = ::File.read(LOCALE_CONF)
+ locale_values = Hash[old_content.split("\n").map { |v| v.split("=") }]
+ lang locale_values["LANG"]
+ rescue Errno::ENOENT => e
+ false
+ end
+ end
+ end
+
+ # Gets the System-locale setting for the current computer.
+ # @see https://docs.microsoft.com/en-us/powershell/module/international/get-winsystemlocale
+ # @return [String] the current value of the System-locale setting.
+ #
+ def get_system_locale_windows
+ powershell_exec("Get-WinSystemLocale").result["Name"]
+ end
+
+ action :update do
+ description "Update the system's locale."
+ converge_if_changed do
+ set_system_locale
+ end
+ end
+
+ action_class do
+ # Avoid running this resource on platforms that don't use /etc/locale.conf
+ #
+ def define_resource_requirements
+ requirements.assert(:all_actions) do |a|
+ a.assertion { LOCALE_PLATFORM_FAMILIES.include?(node[:platform_family]) }
+ a.failure_message(Chef::Exceptions::ProviderNotFound, "The locale resource is not supported on platform family: #{node[:platform_family]}")
+ end
+
+ requirements.assert(:all_actions) do |a|
+ # RHEL/CentOS type platforms don't have locale-gen
+ a.assertion { shell_out("locale-gen") }
+ a.failure_message(Chef::Exceptions::ProviderNotFound, "The locale resource requires the locale-gen tool")
+ end
+ end
+
+ # Generates the localization files from templates using locale-gen.
+ # @see http://manpages.ubuntu.com/manpages/cosmic/man8/locale-gen.8.html
+ # @raise [Mixlib::ShellOut::ShellCommandFailed] not a supported language or locale
+ #
+ def generate_locales
+ shell_out!("locale-gen #{unavailable_locales.join(" ")}", timeout: 1800)
+ end
+
+ # Sets the system locale for the current computer.
+ #
+ def set_system_locale
+ if windows?
+ # Sets the system locale for the current computer.
+ # @see https://docs.microsoft.com/en-us/powershell/module/internationalcmdlets/set-winsystemlocale
+ #
+ response = powershell_exec("Set-WinSystemLocale -SystemLocale #{new_resource.lang}")
+ raise response.errors.join(" ") if response.error?
+ else
+ generate_locales unless unavailable_locales.empty?
+ update_locale
+ end
+ end
+
+ # Updates system locale by appropriately writing them in /etc/locale.conf
+ # @note This locale change won't affect the current run. At this time it is an exercise
+ # left to the user to restart or reboot if the locale change is required at
+ # later part of the client run.
+ # @see https://wiki.archlinux.org/index.php/locale#Setting_the_system_locale
+ #
+ def update_locale
+ file "Updating system locale" do
+ path LOCALE_CONF
+ content new_content
+ end
+ end
+
+ # @return [Array<String>] Locales that user wants to set but are not available on
+ # the system. They are required to be generated.
+ #
+ def unavailable_locales
+ @unavailable_locales ||= begin
+ available = shell_out!("locale -a").stdout.split("\n")
+ required = [new_resource.lang, new_resource.lc_env.values].flatten.compact.uniq
+ required - available
+ end
+ end
+
+ # @return [String] Contents that are required to be
+ # updated in /etc/locale.conf
+ #
+ def new_content
+ @new_content ||= begin
+ content = {}
+ content = new_resource.lc_env.dup if new_resource.lc_env
+ content["LANG"] = new_resource.lang if new_resource.lang
+ content.sort.map { |t| t.join("=") }.join("\n") + "\n"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/log.rb b/lib/chef/resource/log.rb
index 8f7879872f..e9f8bc315a 100644
--- a/lib/chef/resource/log.rb
+++ b/lib/chef/resource/log.rb
@@ -1,7 +1,7 @@
#
# Author:: Cary Penniman (<cary@rightscale.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,60 +17,50 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/log"
+require_relative "../resource"
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
+ unified_mode true
- identity_attr :message
+ provides :log, target_mode: true
- default_action :write
+ 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.)"
- # 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 }
- #
+ property :message, String,
+ name_property: true, identity: true,
+ description: "The message to be added to a log file. If not specified we'll use the resource's name instead."
- # 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
+ property :level, Symbol,
+ equal_to: %i{debug info warn error fatal}, default: :info,
+ description: "The logging level to display this message at."
- def message(arg = nil)
- set_or_return(
- :message,
- arg,
- :kind_of => String
- )
- end
+ allowed_actions :write
+ default_action :write
- # <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 ]
- )
+ def suppress_up_to_date_messages?
+ true
end
+ # Write the log to Chef's log
+ #
+ # @return [true] Always returns true
+ action :write do
+ 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
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 7dfe147341..2cf891d530 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,14 +18,14 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/resource_resolver"
-require "chef/node"
-require "chef/log"
-require "chef/exceptions"
-require "chef/mixin/convert_to_class_name"
-require "chef/mixin/from_file"
-require "chef/mixin/params_validate" # for DelayedEvaluator
+require_relative "../resource"
+require_relative "../resource_resolver"
+require_relative "../node"
+require_relative "../log"
+require_relative "../exceptions"
+require_relative "../mixin/convert_to_class_name"
+require_relative "../mixin/from_file"
+require_relative "../mixin/params_validate" # for DelayedEvaluator
class Chef
class Resource
@@ -41,19 +41,15 @@ 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
resource_name = filename_to_qualified_string(cookbook_name, filename)
- # We load the class first to give it a chance to set its own name
resource_class = Class.new(self)
- resource_class.resource_name resource_name.to_sym
resource_class.run_context = run_context
resource_class.class_from_file(filename)
@@ -65,17 +61,17 @@ 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})")
+
+ # wire up the default resource name after the class is parsed only if we haven't declared one.
+ # (this ordering is important for MapCollision deprecation warnings)
+ resource_class.provides resource_name.to_sym unless Chef::ResourceResolver.includes_handler?(resource_name.to_sym, self)
- LWRPBase.loaded_lwrps[filename] = true
+ LWRPBase.loaded_lwrps[filename] = resource_class
- # Create the deprecated Chef::Resource::LwrpFoo class
- Chef::Resource.register_deprecated_lwrp_class(resource_class, convert_to_class_name(resource_name))
resource_class
end
- alias :attribute :property
-
# Adds +action_names+ to the list of valid actions for this resource.
# Does not include superclass's action list when appending.
def actions(*action_names)
@@ -90,7 +86,7 @@ class Chef
# @deprecated
def valid_actions(*args)
- Chef::Log.warn("`valid_actions' is deprecated, please use allowed_actions `instead'!")
+ Chef::Log.warn("`valid_actions` is deprecated, please use `allowed_actions` instead!")
allowed_actions(*args)
end
@@ -104,6 +100,8 @@ class Chef
protected
+ attr_writer :loaded_lwrps
+
def loaded_lwrps
@loaded_lwrps ||= {}
end
@@ -116,6 +114,7 @@ class Chef
# +default_action+ and other DSL-y methods when extending LWRP::Base.
def from_superclass(m, default = nil)
return default if superclass == Chef::Resource::LWRPBase
+
superclass.respond_to?(m) ? superclass.send(m) : default
end
end
diff --git a/lib/chef/resource/macos_userdefaults.rb b/lib/chef/resource/macos_userdefaults.rb
new file mode 100644
index 0000000000..a150aeb9ed
--- /dev/null
+++ b/lib/chef/resource/macos_userdefaults.rb
@@ -0,0 +1,256 @@
+#
+# Copyright:: 2011-2018, Joshua Timberman
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+autoload :Plist, "plist"
+
+class Chef
+ class Resource
+ class MacosUserDefaults < Chef::Resource
+ unified_mode true
+
+ # align with apple's marketing department
+ provides(:macos_userdefaults) { true }
+ provides(:mac_os_x_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"
+ examples <<~DOC
+ **Specify a global domain value**
+
+ ```ruby
+ macos_userdefaults 'Full keyboard access to all controls' do
+ key 'AppleKeyboardUIMode'
+ value 2
+ end
+ ```
+
+ **Setting a value on a specific domain**
+
+ ```ruby
+ macos_userdefaults 'Enable macOS firewall' do
+ domain '/Library/Preferences/com.apple.alf'
+ key 'globalstate'
+ value 1
+ end
+ ```
+
+ **Specifying the type of a key to skip automatic type detection**
+
+ ```ruby
+ macos_userdefaults 'Finder expanded save dialogs' do
+ key 'NSNavPanelExpandedStateForSaveMode'
+ value 'TRUE'
+ type 'bool'
+ end
+ ```
+ DOC
+
+ property :domain, String,
+ description: "The domain that the user defaults belong to.",
+ default: "NSGlobalDomain",
+ default_description: "NSGlobalDomain: the global domain.",
+ desired_state: false
+
+ property :global, [TrueClass, FalseClass],
+ description: "Determines whether or not the domain is global.",
+ deprecated: true,
+ default: false,
+ desired_state: false
+
+ property :key, String,
+ description: "The preference key.",
+ required: true
+
+ property :host, [String, Symbol],
+ description: "Set either :current or a hostname to set the user default at the host level.",
+ desired_state: false,
+ introduced: "16.3"
+
+ property :value, [Integer, Float, String, TrueClass, FalseClass, Hash, Array],
+ description: "The value of the key. Note: With the `type` property set to `bool`, `String` forms of Boolean true/false values that Apple accepts in the defaults command will be coerced: 0/1, 'TRUE'/'FALSE,' 'true'/false', 'YES'/'NO', or 'yes'/'no'.",
+ required: [:write],
+ coerce: proc { |v| v.is_a?(Hash) ? v.transform_keys(&:to_s) : v } # make sure keys are all strings for comparison
+
+ property :type, String,
+ description: "The value type of the preference key.",
+ equal_to: %w{bool string int float array dict},
+ desired_state: false
+
+ property :user, String,
+ description: "The system user that the default will be applied to.",
+ desired_state: false
+
+ property :sudo, [TrueClass, FalseClass],
+ description: "Set to true if the setting you wish to modify requires privileged access. This requires passwordless sudo for the '/usr/bin/defaults' command to be setup for the user running #{ChefUtils::Dist::Infra::PRODUCT}.",
+ default: false,
+ desired_state: false
+
+ load_current_value do |desired|
+ Chef::Log.debug "#load_current_value: shelling out \"#{defaults_export_cmd(desired).join(" ")}\" to determine state"
+ state = shell_out(defaults_export_cmd(desired), user: desired.user)
+
+ if state.error? || state.stdout.empty?
+ Chef::Log.debug "#load_current_value: #{defaults_export_cmd(desired).join(" ")} returned stdout: #{state.stdout} and stderr: #{state.stderr}"
+ current_value_does_not_exist!
+ end
+
+ plist_data = ::Plist.parse_xml(state.stdout)
+
+ # handle the situation where the key doesn't exist in the domain
+ if plist_data.key?(desired.key)
+ key desired.key
+ else
+ current_value_does_not_exist!
+ end
+
+ value plist_data[desired.key]
+ end
+
+ #
+ # The defaults command to export a domain
+ #
+ # @return [Array] defaults command
+ #
+ def defaults_export_cmd(resource)
+ state_cmd = ["/usr/bin/defaults"]
+
+ if resource.host == "current"
+ state_cmd.concat(["-currentHost"])
+ elsif resource.host # they specified a non-nil value, which is a hostname
+ state_cmd.concat(["-host", resource.host])
+ end
+
+ state_cmd.concat(["export", resource.domain, "-"])
+ state_cmd
+ end
+
+ action :write do
+ description "Write the value to the specified domain/key."
+
+ converge_if_changed do
+ cmd = defaults_modify_cmd
+ Chef::Log.debug("Updating defaults value by shelling out: #{cmd.join(" ")}")
+
+ shell_out!(cmd, user: new_resource.user)
+ end
+ end
+
+ action :delete do
+ description "Delete a key from a domain."
+
+ # if it's not there there's nothing to remove
+ return unless current_resource
+
+ converge_by("delete domain:#{new_resource.domain} key:#{new_resource.key}") do
+
+ cmd = defaults_modify_cmd
+ Chef::Log.debug("Removing defaults key by shelling out: #{cmd.join(" ")}")
+
+ shell_out!(cmd, user: new_resource.user)
+ end
+ end
+
+ action_class do
+ #
+ # The command used to write or delete delete values from domains
+ #
+ # @return [Array] Array representation of defaults command to run
+ #
+ def defaults_modify_cmd
+ cmd = ["/usr/bin/defaults"]
+
+ if new_resource.host == :current
+ cmd.concat(["-currentHost"])
+ elsif new_resource.host # they specified a non-nil value, which is a hostname
+ cmd.concat(["-host", new_resource.host])
+ end
+
+ cmd.concat([action.to_s, new_resource.domain, new_resource.key])
+ cmd.concat(processed_value) if action == :write
+ cmd.prepend("sudo") if new_resource.sudo
+ cmd
+ end
+
+ #
+ # convert the provided value into the format defaults expects
+ #
+ # @return [array] array of values starting with the type if applicable
+ #
+ def processed_value
+ type = new_resource.type || value_type(new_resource.value)
+
+ # when dict this creates an array of values ["Key1", "Value1", "Key2", "Value2" ...]
+ cmd_values = ["-#{type}"]
+
+ case type
+ when "dict"
+ cmd_values.concat(new_resource.value.flatten)
+ when "array"
+ cmd_values.concat(new_resource.value)
+ when "bool"
+ cmd_values.concat(bool_to_defaults_bool(new_resource.value))
+ else
+ cmd_values.concat([new_resource.value])
+ end
+
+ cmd_values
+ end
+
+ #
+ # defaults booleans on the CLI must be 'TRUE' or 'FALSE' so convert various inputs to that
+ #
+ # @param [String, Integer, Boolean] input <description>
+ #
+ # @return [String] TRUE or FALSE
+ #
+ def bool_to_defaults_bool(input)
+ return ["TRUE"] if [true, "TRUE", "1", "true", "YES", "yes"].include?(input)
+ return ["FALSE"] if [false, "FALSE", "0", "false", "NO", "no"].include?(input)
+
+ # make sure it's very clear bad input was given
+ raise ArgumentError, "#{input} cannot be converted to a boolean value for use with Apple's defaults command. Acceptable values are: 'TRUE', 'YES', 'true, 'yes', '0', true, 'FALSE', 'false', 'NO', 'no', '1', or false."
+ end
+
+ #
+ # convert ruby type to defaults type
+ #
+ # @param [Integer, Float, String, TrueClass, FalseClass, Hash, Array] value The value being set
+ #
+ # @return [string, nil] the type value used by defaults or nil if not applicable
+ #
+ def value_type(value)
+ case value
+ when true, false
+ "bool"
+ when Integer
+ "int"
+ when Float
+ "float"
+ when Hash
+ "dict"
+ when Array
+ "array"
+ when String
+ "string"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/macosx_service.rb b/lib/chef/resource/macosx_service.rb
index 08c748bead..693b8a4b87 100644
--- a/lib/chef/resource/macosx_service.rb
+++ b/lib/chef/resource/macosx_service.rb
@@ -16,42 +16,22 @@
# limitations under the License.
#
-require "chef/resource/service"
+require_relative "service"
class Chef
class Resource
class MacosxService < Chef::Resource::Service
+ unified_mode true
- provides :macosx_service, os: "darwin"
+ provides :macosx_service
provides :service, os: "darwin"
- identity_attr :service_name
+ description "Use the **macosx_service** resource to manage services on the macOS platform."
- state_attrs :enabled, :running
+ property :plist, String,
+ description: "A plist to use in the case where the filename and label for the service do not match."
- 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 :session_type, String
end
end
diff --git a/lib/chef/resource/macports_package.rb b/lib/chef/resource/macports_package.rb
index 3685334c17..70fa84dec7 100644
--- a/lib/chef/resource/macports_package.rb
+++ b/lib/chef/resource/macports_package.rb
@@ -1,6 +1,6 @@
#
# Author:: David Balatero (<dbalatero@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,22 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
class Chef
class Resource
class MacportsPackage < Chef::Resource::Package
- resource_name :macports_package
+ unified_mode true
+ provides :macports_package
+
+ description "Use the **macports_package** resource to manage packages for the macOS platform using the MacPorts package management system."
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
end
end
end
diff --git a/lib/chef/resource/mdadm.rb b/lib/chef/resource/mdadm.rb
index df6e705f15..f2e610c1cf 100644
--- a/lib/chef/resource/mdadm.rb
+++ b/lib/chef/resource/mdadm.rb
@@ -17,94 +17,109 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class Mdadm < Chef::Resource
+ unified_mode true
- identity_attr :raid_device
+ provides :mdadm
- 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
+ property :chunk, Integer,
+ default: 16,
+ description: "The chunk size. This property should not be used for a RAID 1 mirrored pair (i.e. when the `level` property is set to `1`)."
- def chunk(arg = nil)
- set_or_return(
- :chunk,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :devices, Array,
+ default: lazy { [] },
+ description: "The devices to be part of a RAID array."
- def devices(arg = nil)
- set_or_return(
- :devices,
- arg,
- :kind_of => [ Array ]
- )
- end
+ # @todo this should get refactored away
+ property :exists, [ TrueClass, FalseClass ],
+ default: false,
+ skip_docs: true
- def exists(arg = nil)
- set_or_return(
- :exists,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :level, Integer,
+ default: 1,
+ description: "The RAID level."
- def level(arg = nil)
- set_or_return(
- :level,
- arg,
- :kind_of => [ Integer ]
- )
- end
+ property :metadata, String,
+ default: "0.90",
+ description: "The superblock type for RAID metadata."
+
+ property :bitmap, String,
+ description: "The path to a file in which a write-intent bitmap is stored."
+
+ property :raid_device, String,
+ name_property: true,
+ description: "An optional property to specify the name of the RAID device if it differs from the resource block's name."
+
+ property :layout, String,
+ description: "The RAID5 parity algorithm. Possible values: `left-asymmetric` (or `la`), `left-symmetric` (or ls), `right-asymmetric` (or `ra`), or `right-symmetric` (or `rs`)."
+
+ action_class do
+ def load_current_resource
+ @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}")
- def metadata(arg = nil)
- set_or_return(
- :metadata,
- arg,
- :kind_of => [ String ]
- )
+ device_not_found = 4
+ mdadm = shell_out!("mdadm", "--detail", "--test", new_resource.raid_device, returns: [0, device_not_found])
+ exists = (mdadm.status == 0)
+ current_resource.exists(exists)
+ end
end
- def bitmap(arg = nil)
- set_or_return(
- :bitmap,
- arg,
- :kind_of => [ String ]
- )
+ action :create do
+ 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(" ")}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
+ shell_out!(command)
+ logger.info("#{new_resource} created raid device (#{new_resource.raid_device})")
+ end
+ else
+ logger.trace("#{new_resource} raid device already exists, skipping create (#{new_resource.raid_device})")
+ end
end
- def raid_device(arg = nil)
- set_or_return(
- :raid_device,
- arg,
- :kind_of => [ String ]
- )
+ action :assemble do
+ 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(" ")}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
+ shell_out!(command)
+ logger.info("#{new_resource} assembled raid device (#{new_resource.raid_device})")
+ end
+ else
+ logger.trace("#{new_resource} raid device already exists, skipping assemble (#{new_resource.raid_device})")
+ end
end
- def layout(arg = nil)
- set_or_return(
- :layout,
- arg,
- :kind_of => [ String ]
- )
+ action :stop do
+ if current_resource.exists
+ converge_by("stop RAID device #{new_resource.raid_device}") do
+ command = "yes | mdadm --stop #{new_resource.raid_device}"
+ logger.trace("#{new_resource} mdadm command: #{command}")
+ shell_out!(command)
+ logger.info("#{new_resource} stopped raid device (#{new_resource.raid_device})")
+ end
+ else
+ logger.trace("#{new_resource} raid device doesn't exist (#{new_resource.raid_device}) - not stopping")
+ end
end
end
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index 2d85b3897c..c23ba9bbee 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,171 +17,89 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class Mount < Chef::Resource
+ description "Use the **mount** resource to manage a mounted file system."
+ unified_mode true
- identity_attr :device
-
- state_attrs :mount_point, :device_type, :fstype, :username, :password, :domain
+ provides :mount
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
+ allowed_actions :mount, :umount, :unmount, :remount, :enable, :disable
- def mount_point(arg = nil)
- set_or_return(
- :mount_point,
- arg,
- :kind_of => [ String ]
- )
- end
+ # this is a poor API please do not re-use this pattern
+ property :supports, [Array, Hash],
+ description: "Specify a Hash of supported mount features.",
+ default: lazy { { remount: false } },
+ coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x }
- def device(arg = nil)
- set_or_return(
- :device,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :password, String,
+ description: "Windows only:. Use to specify the password for username.",
+ sensitive: true
- 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
+ property :mount_point, String, name_property: true,
+ coerce: proc { |arg| arg.chomp("/") }, # Removed "/" from the end of str, because it was causing idempotency issue.
+ description: "The directory (or path) in which the device is to be mounted. Defaults to the name of the resource block if not provided."
- def fsck_device(arg = nil)
- set_or_return(
- :fsck_device,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :device, String, identity: true,
+ description: "Required for :umount and :remount actions (for the purpose of checking the mount command output for presence). The special block device or remote node, a label, or a uuid to be mounted."
- def fstype(arg = nil)
- set_or_return(
- :fstype,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :device_type, [String, Symbol],
+ description: "The type of device: :device, :label, or :uuid",
+ coerce: proc { |arg| arg.is_a?(String) ? arg.to_sym : arg },
+ default: :device,
+ equal_to: RUBY_PLATFORM.match?(/solaris/i) ? %i{ device } : %i{ device label uuid }
- def options(arg = nil)
- ret = set_or_return(
- :options,
- arg,
- :kind_of => [ Array, String ]
- )
-
- if ret.is_a? String
- ret.tr(",", " ").split(/ /)
- else
- ret
- end
- end
+ # @todo this should get refactored away: https://github.com/chef/chef/issues/7621
+ property :mounted, [TrueClass, FalseClass], default: false, skip_docs: true
- def dump(arg = nil)
- set_or_return(
- :dump,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
+ property :fsck_device, String,
+ description: "Solaris only: The fsck device.",
+ default: "-"
- def pass(arg = nil)
- set_or_return(
- :pass,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
+ property :fstype, [String, nil],
+ description: "The file system type (fstype) of the device.",
+ default: "auto"
- def mounted(arg = nil)
- set_or_return(
- :mounted,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :options, [Array, String, nil],
+ description: "An array or comma separated list of options for the mount.",
+ coerce: proc { |arg| mount_options(arg) }, # Please see #mount_options method.
+ default: %w{defaults}
- def enabled(arg = nil)
- set_or_return(
- :enabled,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :dump, [Integer, FalseClass],
+ description: "The dump frequency (in days) used while creating a file systems table (fstab) entry.",
+ default: 0
- def supports(args = {})
- if args.is_a? Array
- args.each { |arg| @supports[arg] = true }
- elsif args.any?
- @supports = args
- else
- @supports
- end
- end
+ property :pass, [Integer, FalseClass],
+ description: "The pass number used by the file system check (fsck) command while creating a file systems table (fstab) entry.",
+ default: 2
- def username(arg = nil)
- set_or_return(
- :username,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :enabled, [TrueClass, FalseClass],
+ description: "Use to specify if a mounted file system is enabled.",
+ default: false
- def password(arg = nil)
- set_or_return(
- :password,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :username, String,
+ description: "Windows only: Use to specify the user name."
- def domain(arg = nil)
- set_or_return(
- :domain,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :domain, String,
+ description: "Windows only: Use to specify the domain in which the `username` and `password` are located."
private
# Used by the AIX provider to set fstype to nil.
- # TODO use property to make nil a valid value for fstype
+ # @todo use property to make nil a valid value for fstype
def clear_fstype
@fstype = nil
end
+ # Returns array of string without leading and trailing whitespace.
+ def mount_options(options)
+ (options.is_a?(String) ? options.split(",") : options).collect(&:strip)
+ end
+
end
end
end
diff --git a/lib/chef/resource/msu_package.rb b/lib/chef/resource/msu_package.rb
new file mode 100644
index 0000000000..23e92b2dd1
--- /dev/null
+++ b/lib/chef/resource/msu_package.rb
@@ -0,0 +1,66 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "package"
+require_relative "../mixin/uris"
+
+class Chef
+ class Resource
+ class MsuPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+ unified_mode true
+
+ 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 :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ # This is the same property as the main package resource except it has the skip docs set to true
+ # This resource abuses the package resource by storing the versions of all the cabs in the MSU file
+ # in the version attribute from load current value even though those aren't technically the version of the
+ # msu. Since the user wouldn't actually set this we don't want it on the docs site.
+ property :version, [String, Array],
+ skip_docs: true,
+ description: "The version of a package to be installed or upgraded."
+
+ property :source, String,
+ description: "The local file path or URL for the MSU package.",
+ coerce: (proc do |s|
+ unless s.nil?
+ uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
+ end
+ end),
+ default: lazy { package_name }
+
+ property :checksum, String, desired_state: false,
+ description: "SHA-256 digest used to verify the checksum of the downloaded MSU package."
+
+ property :timeout, [String, Integer],
+ default: 3600,
+ description: "The amount of time (in seconds) to wait before timing out.",
+ desired_state: false
+ end
+ end
+end
diff --git a/lib/chef/resource/notify_group.rb b/lib/chef/resource/notify_group.rb
new file mode 100644
index 0000000000..9a1edf3eb8
--- /dev/null
+++ b/lib/chef/resource/notify_group.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class NotifyGroup < Chef::Resource
+ provides :notify_group
+
+ unified_mode true
+
+ description "The notify_group resource does nothing, and always fires notifications which are set on it. Use it to DRY blocks of notifications that are common to multiple resources, and provide a single target for other resources to notify. Unlike most resources, its default action is :nothing."
+ introduced "15.8"
+ examples <<~DOC
+ Wire up a notification from a service resource to stop and start the service with a 60 second delay.
+
+ ```ruby
+ service "crude" do
+ action [ :enable, :start ]
+ end
+
+ chef_sleep "60" do
+ action :nothing
+ end
+
+ # Example code for a hypothetical badly behaved service that requires
+ # 60 seconds between a stop and start in order to restart the service
+ # (due to race conditions, bleeding connections down, resources that only
+ # slowly unlock in the background, or other poor software behaviors that
+ # are sometimes encountered).
+ #
+ notify_group "crude_stop_and_start" do
+ notifies :stop, "service[crude]", :immediately
+ notifies :sleep, "chef_sleep[60]", :immediately
+ notifies :start, "service[crude]", :immediately
+ end
+
+ template "/etc/crude/crude.conf" do
+ source "crude.conf.erb"
+ variables node["crude"]
+ notifies :run, "notify_group[crude_stop_and_start]", :immediately
+ end
+ ```
+ DOC
+
+ action :run do
+ new_resource.updated_by_last_action(true)
+ end
+
+ # This is deliberate. Users should be sending a single notification to a notify_group resource which then
+ # distributes multiple notifications. Having a notify_group run by default is unnecessary indirection and
+ # should be replaced by just running the resources in order. If resources need to be batch run multiple times
+ # per invocation then the resources themselves are not properly declarative idempotent resources, and the user
+ # is attempting to turn Chef into an imperative language using this construct and/or the batch of resources
+ # need to be turned into a custom resource.
+ #
+ default_action :nothing
+ end
+ end
+end
diff --git a/lib/chef/resource/ohai.rb b/lib/chef/resource/ohai.rb
index 09cd22efc5..560a15353a 100644
--- a/lib/chef/resource/ohai.rb
+++ b/lib/chef/resource/ohai.rb
@@ -2,6 +2,7 @@
# Author:: Michael Leinartas (<mleinartas@gmail.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
# Copyright:: Copyright 2010-2016, Michael Leinartas
+# Copyright:: Copyright (c) Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,36 +18,81 @@
# limitations under the License.
#
+require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "ohai" unless defined?(Ohai::System)
+
class Chef
class Resource
class Ohai < Chef::Resource
+ unified_mode true
+
+ 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 #{ChefUtils::Dist::Infra::PRODUCT} run."
- state_attrs :plugin
+ examples <<~DOC
+ Reload All Ohai Plugins
+
+ ```ruby
+ ohai 'reload' do
+ action :reload
+ end
+ ```
+
+ Reload A Single Ohai Plugin
+
+ ```ruby
+ ohai 'reload' do
+ plugin 'ipaddress'
+ action :reload
+ end
+ ```
- default_action :reload
+ Reload Ohai after a new user is created
- def initialize(name, run_context = nil)
- super
- @name = name
- @plugin = nil
+ ```ruby
+ ohai 'reload_passwd' do
+ action :nothing
+ plugin 'etc'
end
- def plugin(arg = nil)
- set_or_return(
- :plugin,
- arg,
- :kind_of => [ String ]
- )
+ user 'daemon_user' do
+ home '/dev/null'
+ shell '/sbin/nologin'
+ system true
+ notifies :reload, 'ohai[reload_passwd]', :immediately
end
- def name(arg = nil)
- set_or_return(
- :name,
- arg,
- :kind_of => [ String ]
- )
+ ruby_block 'just an example' do
+ block do
+ # These variables will now have the new values
+ puts node['etc']['passwd']['daemon_user']['uid']
+ puts node['etc']['passwd']['daemon_user']['gid']
+ end
+ end
+ ```
+ DOC
+
+ property :plugin, String,
+ description: "Specific Ohai attribute data to reload. This property behaves similar to specifying attributes when running Ohai on the command line and takes the attribute that you wish to reload instead of the actual plugin name. For instance, you can pass `ipaddress` to reload `node['ipaddress']` even though that data comes from the `Network` plugin. If this property is not specified, #{ChefUtils::Dist::Infra::PRODUCT} will reload all plugins."
+
+ def load_current_resource
+ true
+ end
+
+ 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
+ # 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
+ node.automatic_attrs.merge! ohai.data
+ logger.info("#{new_resource} reloaded")
+ end
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..88ea02c809
--- /dev/null
+++ b/lib/chef/resource/ohai_hint.rb
@@ -0,0 +1,123 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class OhaiHint < Chef::Resource
+ unified_mode true
+
+ provides(:ohai_hint) { true }
+
+ description "Use the **ohai_hint** resource to aid in configuration detection by passing hint data to Ohai."
+ introduced "14.0"
+ examples <<~DOC
+ **Create a hint file**
+
+ ```ruby
+ ohai_hint 'example' do
+ content a: 'test_content'
+ end
+ ```
+
+ **Create a hint file with a name that does not match the resource name**
+
+ ```ruby
+ ohai_hint 'example' do
+ hint_name 'custom'
+ end
+ ```
+
+ **Create a hint file that is not loaded at compile time**
+
+ ```ruby
+ ohai_hint 'example' do
+ compile_time false
+ end
+ ```
+
+ **Delete a hint file**
+
+ ```ruby
+ ohai_hint 'example' do
+ action :delete
+ end
+ ```
+ DOC
+
+ property :hint_name, String,
+ description: "An optional property to set the hint name if it differs from the resource block's name.",
+ name_property: true
+
+ property :content, Hash,
+ description: "Values to include in the hint file."
+
+ # override compile_time property to default to true
+ property :compile_time, [TrueClass, FalseClass],
+ description: "Determines whether or not the resource is executed during the compile time phase.",
+ default: true, desired_state: false
+
+ action :create do
+ description "Create an Ohai hint file."
+
+ directory ::Ohai::Config.ohai.hints_path.first do
+ action :create
+ recursive true
+ end
+
+ 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."
+
+ file ohai_hint_file_path(new_resource.hint_name) do
+ action :delete
+ notifies :reload, ohai[reload ohai post hint removal]
+ end
+
+ 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
+ end
+ end
+end
diff --git a/lib/chef/resource/openbsd_package.rb b/lib/chef/resource/openbsd_package.rb
index d0f9fe877f..af632ebb57 100644
--- a/lib/chef/resource/openbsd_package.rb
+++ b/lib/chef/resource/openbsd_package.rb
@@ -2,7 +2,7 @@
# Authors:: AJ Christensen (<aj@chef.io>)
# Richard Manyanza (<liseki@nyikacraftsmen.com>)
# Scott Bonds (<scott@ggr.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2014-2016, Richard Manyanza, Scott Bonds
# License:: Apache License, Version 2.0
#
@@ -19,17 +19,25 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/openbsd"
-require "chef/mixin/shell_out"
+require_relative "package"
+require_relative "../provider/package/openbsd"
class Chef
class Resource
class OpenbsdPackage < Chef::Resource::Package
- include Chef::Mixin::ShellOut
-
- resource_name :openbsd_package
+ unified_mode true
+ provides :openbsd_package
provides :package, os: "openbsd"
+
+ description "Use the **openbsd_package** resource to manage packages for the OpenBSD platform."
+ introduced "12.1"
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
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..3d20b1b439
--- /dev/null
+++ b/lib/chef/resource/openssl_dhparam.rb
@@ -0,0 +1,110 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class OpensslDhparam < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ 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.pem` file, it will be overwritten."
+ introduced "14.0"
+ examples <<~DOC
+ **Create a dhparam file**
+
+ ```ruby
+ openssl_dhparam '/etc/httpd/ssl/dhparam.pem'
+ ```
+
+ **Create a dhparam file with a specific key length**
+
+ ```ruby
+ openssl_dhparam '/etc/httpd/ssl/dhparam.pem' do
+ key_length 4096
+ end
+ ```
+
+ **Create a dhparam file with specific user/group ownership**
+
+ ```ruby
+ openssl_dhparam '/etc/httpd/ssl/dhparam.pem' do
+ owner 'www-data'
+ group 'www-data'
+ end
+ ```
+
+ **Manually specify the dhparam file path**
+
+ ```ruby
+ openssl_dhparam 'httpd_dhparam' do
+ path '/etc/httpd/ssl/dhparam.pem'
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's 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, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource.",
+ default: "0640"
+
+ action :create do
+ description "Create the dhparam file."
+ dhparam_content = nil
+ unless dhparam_pem_valid?(new_resource.path)
+ dhparam_content = gen_dhparam(new_resource.key_length, new_resource.generator).to_pem
+ Chef::Log.debug("Valid dhparam content not found at #{new_resource.path}, creating new")
+ end
+
+ file new_resource.path do
+ action :create
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+ sensitive true
+ content dhparam_content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_ec_private_key.rb b/lib/chef/resource/openssl_ec_private_key.rb
new file mode 100644
index 0000000000..7625b5ea6e
--- /dev/null
+++ b/lib/chef/resource/openssl_ec_private_key.rb
@@ -0,0 +1,119 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Author:: Julien Huon
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+
+class Chef
+ class Resource
+ class OpensslEcPrivateKey < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides :openssl_ec_private_key
+
+ description "Use the **openssl_ec_private_key** resource to generate an elliptic curve (EC) private key file. If a valid EC key file can be opened at the specified location, no new file will be created. If the EC key file cannot be opened, either because it does not exist or because the password to the EC key file does not match the password in the recipe, then it will be overwritten."
+ introduced "14.4"
+ examples <<~DOC
+ Generate a new ec privatekey with prime256v1 key curve and default des3 cipher
+
+ ```ruby
+ openssl_ec_private_key '/etc/ssl_files/eckey_prime256v1_des3.pem' do
+ key_curve 'prime256v1'
+ key_pass 'something'
+ action :create
+ end
+ ```
+
+ Generate a new ec private key with prime256v1 key curve and aes-128-cbc cipher
+
+ ```ruby
+ openssl_ec_private_key '/etc/ssl_files/eckey_prime256v1_des3.pem' do
+ key_curve 'prime256v1'
+ key_cipher 'aes-128-cbc'
+ key_pass 'something'
+ action :create
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's name.",
+ name_property: true
+
+ property :key_curve, String,
+ equal_to: %w{secp384r1 secp521r1 prime256v1 secp224r1 secp256k1},
+ description: "The desired curve of the generated key (if key_type is equal to 'ec'). Run openssl ecparam -list_curves to see available options.",
+ default: "prime256v1"
+
+ property :key_pass, String,
+ description: "The desired passphrase for the key."
+
+ property :key_cipher, String,
+ description: "The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options.",
+ default: lazy { "des3" },
+ default_description: "des3",
+ callbacks: {
+ "key_cipher must be a cipher known to openssl. Run `openssl list-cipher-algorithms` to see available options." =>
+ proc { |v| OpenSSL::Cipher.ciphers.include?(v) },
+ }
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource.",
+ default: "0600"
+
+ property :force, [TrueClass, FalseClass],
+ description: "Force creation of the key even if the same key already exists on the node.",
+ default: false, desired_state: false
+
+ action :create do
+ description "Generate the ec private key"
+
+ unless new_resource.force || priv_key_file_valid?(new_resource.path, new_resource.key_pass)
+ converge_by("Create an EC private key #{new_resource.path}") do
+ log "Generating an #{new_resource.key_curve} "\
+ "EC key file at #{new_resource.path}, this may take some time"
+
+ if new_resource.key_pass
+ unencrypted_ec_key = gen_ec_priv_key(new_resource.key_curve)
+ ec_key_content = encrypt_ec_key(unencrypted_ec_key, new_resource.key_pass, new_resource.key_cipher)
+ else
+ ec_key_content = gen_ec_priv_key(new_resource.key_curve).to_pem
+ end
+
+ 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 ec_key_content
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_ec_public_key.rb b/lib/chef/resource/openssl_ec_public_key.rb
new file mode 100644
index 0000000000..44441eb72d
--- /dev/null
+++ b/lib/chef/resource/openssl_ec_public_key.rb
@@ -0,0 +1,96 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Author:: Julien Huon
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+
+class Chef
+ class Resource
+ class OpensslEcPublicKey < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides :openssl_ec_public_key
+
+ description "Use the **openssl_ec_public_key** resource to generate elliptic curve (EC) public key files from a given EC private key."
+ introduced "14.4"
+ examples <<~DOC
+ **Generate new EC public key from a private key on disk**
+
+ ```ruby
+ openssl_ec_public_key '/etc/ssl_files/eckey_prime256v1_des3.pub' do
+ private_key_path '/etc/ssl_files/eckey_prime256v1_des3.pem'
+ private_key_pass 'something'
+ action :create
+ end
+ ```
+
+ **Generate new EC public key by passing in a private key**
+
+ ```ruby
+ openssl_ec_public_key '/etc/ssl_files/eckey_prime256v1_des3_2.pub' do
+ private_key_content "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEII2VAU9re44mAUzYPWCg+qqwdmP8CplsEg0b/DYPXLg2oAoGCCqGSM49\nAwEHoUQDQgAEKkpMCbIQ2C6Qlp/B+Odp1a9Y06Sm8yqPvCVIkWYP7M8PX5+RmoIv\njGBVf/+mVBx77ji3NpTilMUt2KPZ87lZ3w==\n-----END EC PRIVATE KEY-----\n"
+ action :create
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's name.",
+ name_property: true
+
+ property :private_key_path, String,
+ description: "The path to the private key file."
+
+ property :private_key_content, String,
+ description: "The content of the private key including new lines. This property is used in place of private_key_path in instances where you want to avoid having to first write the private key to disk"
+
+ property :private_key_pass, String,
+ description: "The passphrase of the provided private key."
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource.",
+ default: "0640"
+
+ action :create do
+ description "Generate the ec public key from a private 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 EC 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)
+
+ ec_key_content = gen_ec_pub_key((new_resource.private_key_path || new_resource.private_key_content), new_resource.private_key_pass)
+
+ 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 ec_key_content
+ 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..e9e6ef24ca
--- /dev/null
+++ b/lib/chef/resource/openssl_rsa_private_key.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class OpensslRsaPrivateKey < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ Generate new 2048bit key with the default des3 cipher
+
+ ```ruby
+ openssl_rsa_private_key '/etc/ssl_files/rsakey_des3.pem' do
+ key_length 2048
+ action :create
+ end
+ ```
+
+ Generate new 1024bit key with the aes-128-cbc cipher
+
+ ```ruby
+ openssl_rsa_key '/etc/ssl_files/rsakey_aes128cbc.pem' do
+ key_length 1024
+ key_cipher 'aes-128-cbc'
+ action :create
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's 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,
+ description: "The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options.",
+ default: lazy { "des3" },
+ default_description: "des3",
+ callbacks: {
+ "key_cipher must be a cipher known to openssl. Run `openssl list-cipher-algorithms` to see available options." =>
+ proc { |v| OpenSSL::Cipher.ciphers.include?(v) },
+ }
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource.",
+ default: "0600"
+
+ property :force, [TrueClass, FalseClass],
+ description: "Force creation of the key even if the same key already exists on the node.",
+ default: false, desired_state: false
+
+ action :create do
+ description "Create the RSA private key."
+
+ 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
+
+ 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..8fd8ab558e
--- /dev/null
+++ b/lib/chef/resource/openssl_rsa_public_key.rb
@@ -0,0 +1,97 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class OpensslRsaPublicKey < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides(:openssl_rsa_public_key) { true }
+
+ examples <<~DOC
+ Generate new public key from a private key on disk
+
+ ```ruby
+ openssl_rsa_public_key '/etc/ssl_files/rsakey_des3.pub' do
+ private_key_path '/etc/ssl_files/rsakey_des3.pem'
+ private_key_pass 'something'
+ action :create
+ end
+ ```
+
+ Generate new public key by passing in a private key
+
+ ```ruby
+ openssl_rsa_public_key '/etc/ssl_files/rsakey_2.pub' do
+ private_key_pass 'something'
+ private_key_content "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,5EE0AE9A5FE3342E\n\nyb930kj5/4/nd738dPx6XdbDrMCvqkldaz0rHNw8xsWvwARrl/QSPwROG3WY7ROl\nEUttVlLaeVaqRPfQbmTUfzGI8kTMmDWKjw52gJUx2YJTYRgMHAB0dzYIRjeZAaeS\nypXnEfouVav+jKTmmehr1WuVKbzRhQDBSalzeUwsPi2+fb3Bfuo1dRW6xt8yFuc4\nAkv1hCglymPzPHE2L0nSGjcgA2DZu+/S8/wZ4E63442NHPzO4VlLvpNvJrYpEWq9\nB5mJzcdXPeOTjqd13olNTlOZMaKxu9QShu50GreCTVsl8VRkK8NtwbWuPGBZlIFa\njzlS/RaLuzNzfajaKMkcIYco9t7gN2DwnsACHKqEYT8248Ii3NQ+9/M5YcmpywQj\nWGr0UFCSAdCky1lRjwT+zGQKohr+dVR1GaLem+rSZH94df4YBxDYw4rjsKoEhvXB\nv2Vlx+G7Vl2NFiZzxUKh3MvQLr/NDElpG1pYWDiE0DIG13UqEG++cS870mcEyfFh\nSF2SXYHLWyAhDK0viRDChJyFMduC4E7a2P9DJhL3ZvM0KZ1SLMwROc1XuZ704GwO\nYUqtCX5OOIsTti1Z74jQm9uWFikhgWByhVtu6sYL1YTqtiPJDMFhA560zp/k/qLO\nFKiM4eUWV8AI8AVwT6A4o45N2Ru8S48NQyvh/ADFNrgJbVSeDoYE23+DYKpzbaW9\n00BD/EmUQqaQMc670vmI+CIdcdE7L1zqD6MZN7wtPaRIjx4FJBGsFoeDShr+LoTD\nrwbadwrbc2Rf4DWlvFwLJ4pvNvdtY3wtBu79UCOol0+t8DVVSPVASsh+tp8XncDE\nKRljj88WwBjX7/YlRWvQpe5y2UrsHI0pNy8TA1Xkf6GPr6aS2TvQD5gOrAVReSse\n/kktCzZQotjmY1odvo90Zi6A9NCzkI4ZLgAuhiKDPhxZg61IeLppnfFw0v3H4331\nV9SMYgr1Ftov0++x7q9hFPIHwZp6NHHOhdHNI80XkHqtY/hEvsh7MhFMYCgSY1pa\nK/gMcZ/5Wdg9LwOK6nYRmtPtg6fuqj+jB3Rue5/p9dt4kfom4etCSeJPdvP1Mx2I\neNmyQ/7JN9N87FsfZsIj5OK9OB0fPdj0N0m1mlHM/mFt5UM5x39u13QkCt7skEF+\nyOptXcL629/xwm8eg4EXnKFk330WcYSw+sYmAQ9ZTsBxpCMkz0K4PBTPWWXx63XS\nc4J0r88kbCkMCNv41of8ceeGzFrC74dG7i3IUqZzMzRP8cFeps8auhweUHD2hULs\nXwwtII0YQ6/Fw4hgGQ5//0ASdvAicvH0l1jOQScHzXC2QWNg3GttueB/kmhMeGGm\nsHOJ1rXQ4oEckFvBHOvzjP3kuRHSWFYDx35RjWLAwLCG9odQUApHjLBgFNg9yOR0\njW9a2SGxRvBAfdjTa9ZBBrbjlaF57hq7mXws90P88RpAL+xxCAZUElqeW2Rb2rQ6\nCbz4/AtPekV1CYVodGkPutOsew2zjNqlNH+M8XzfonA60UAH20TEqAgLKwgfgr+a\nc+rXp1AupBxat4EHYJiwXBB9XcVwyp5Z+/dXsYmLXzoMOnp8OFyQ9H8R7y9Y0PEu\n-----END RSA PRIVATE KEY-----\n"
+ action :create
+ end
+ ```
+ DOC
+
+ description "Use the **openssl_rsa_public_key** resource to generate RSA public key files for a given RSA private key."
+ introduced "14.0"
+
+ property :path, String,
+ description: "An optional property for specifying the path to the public key if it differs from the resource block's name.",
+ name_property: true
+
+ property :private_key_path, String,
+ description: "The path to the private key file."
+
+ property :private_key_content, String,
+ description: "The content of the private key, including new lines. This property is used in place of private_key_path in instances where you want to avoid having to first write the private key to disk."
+
+ property :private_key_pass, String,
+ description: "The passphrase of the provided private key."
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to 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)
+
+ 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/openssl_x509_certificate.rb b/lib/chef/resource/openssl_x509_certificate.rb
new file mode 100644
index 0000000000..c723f47d61
--- /dev/null
+++ b/lib/chef/resource/openssl_x509_certificate.rb
@@ -0,0 +1,267 @@
+#
+# License:: Apache License, Version 2.0
+# Author:: Julien Huon
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class OpensslX509Certificate < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides :openssl_x509_certificate
+ provides(:openssl_x509) { true } # legacy cookbook name.
+
+ description "Use the **openssl_x509_certificate** resource to generate signed or self-signed, PEM-formatted x509 certificates. If no existing key is specified, the resource will automatically generate a passwordless key with the certificate. If a CA private key and certificate are provided, the certificate will be signed with them. Note: This resource was renamed from openssl_x509 to openssl_x509_certificate. The legacy name will continue to function, but cookbook code should be updated for the new resource name."
+ introduced "14.4"
+ examples <<~DOC
+ Create a simple self-signed certificate file
+
+ ```ruby
+ openssl_x509_certificate '/etc/httpd/ssl/mycert.pem' do
+ common_name 'www.f00bar.com'
+ org 'Foo Bar'
+ org_unit 'Lab'
+ country 'US'
+ end
+ ```
+
+ Create a certificate using additional options
+
+ ```ruby
+ openssl_x509_certificate '/etc/ssl_files/my_signed_cert.crt' do
+ common_name 'www.f00bar.com'
+ ca_key_file '/etc/ssl_files/my_ca.key'
+ ca_cert_file '/etc/ssl_files/my_ca.crt'
+ expire 365
+ extensions(
+ 'keyUsage' => {
+ 'values' => %w(
+ keyEncipherment
+ digitalSignature),
+ 'critical' => true,
+ },
+ 'extendedKeyUsage' => {
+ 'values' => %w(serverAuth),
+ 'critical' => false,
+ }
+ )
+ subject_alt_name ['IP:127.0.0.1', 'DNS:localhost.localdomain']
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's name.",
+ name_property: true
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :expire, Integer,
+ description: "Value representing the number of days from now through which the issued certificate cert will remain valid. The certificate will expire after this period.",
+ default: 365
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource."
+
+ property :country, String,
+ description: "Value for the `C` certificate field."
+
+ property :state, String,
+ description: "Value for the `ST` certificate field."
+
+ property :city, String,
+ description: "Value for the `L` certificate field."
+
+ property :org, String,
+ description: "Value for the `O` certificate field."
+
+ property :org_unit, String,
+ description: "Value for the `OU` certificate field."
+
+ property :common_name, String,
+ description: "Value for the `CN` certificate field."
+
+ property :email, String,
+ description: "Value for the `email` certificate field."
+
+ property :extensions, Hash,
+ description: "Hash of X509 Extensions entries, in format `{ 'keyUsage' => { 'values' => %w( keyEncipherment digitalSignature), 'critical' => true } }`.",
+ default: lazy { {} }
+
+ property :subject_alt_name, Array,
+ description: "Array of Subject Alternative Name entries, in format `DNS:example.com` or `IP:1.2.3.4`.",
+ default: lazy { [] }
+
+ property :key_file, String,
+ description: "The path to a certificate key file on the filesystem. If the key_file property is specified, the resource will attempt to source a key from this location. If no key file is found, the resource will generate a new key file at this location. If the key_file property is not specified, the resource will generate a key file in the same directory as the generated certificate, with the same name as the generated certificate."
+
+ property :key_pass, String,
+ description: "The passphrase for an existing key's passphrase."
+
+ property :key_type, String,
+ equal_to: %w{rsa ec},
+ description: "The desired type of the generated key.",
+ default: "rsa"
+
+ property :key_length, Integer,
+ equal_to: [1024, 2048, 4096, 8192],
+ description: "The desired bit length of the generated key (if key_type is equal to 'rsa').",
+ default: 2048
+
+ property :key_curve, String,
+ description: "The desired curve of the generated key (if key_type is equal to 'ec'). Run `openssl ecparam -list_curves` to see available options.",
+ equal_to: %w{secp384r1 secp521r1 prime256v1},
+ default: "prime256v1"
+
+ property :csr_file, String,
+ description: "The path to a X509 Certificate Request (CSR) on the filesystem. If the `csr_file` property is specified, the resource will attempt to source a CSR from this location. If no CSR file is found, the resource will generate a Self-Signed Certificate and the certificate fields must be specified (common_name at last)."
+
+ property :ca_cert_file, String,
+ description: "The path to the CA X509 Certificate on the filesystem. If the `ca_cert_file` property is specified, the `ca_key_file` property must also be specified, the certificate will be signed with them."
+
+ property :ca_key_file, String,
+ description: "The path to the CA private key on the filesystem. If the `ca_key_file` property is specified, the `ca_cert_file` property must also be specified, the certificate will be signed with them."
+
+ property :ca_key_pass, String,
+ description: "The passphrase for CA private key's passphrase."
+
+ property :renew_before_expiry, Integer,
+ description: "The number of days before the expiry. The certificate will be automatically renewed when the value is reached.",
+ introduced: "15.7"
+
+ action :create do
+ description "Generate a certificate"
+
+ file new_resource.path do
+ action :create_if_missing
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode unless new_resource.mode.nil?
+ sensitive true
+ content cert.to_pem
+ end
+
+ if !new_resource.renew_before_expiry.nil? && cert_need_renewal?(new_resource.path, new_resource.renew_before_expiry)
+ 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 unless new_resource.mode.nil?
+ sensitive true
+ content cert.to_pem
+ end
+ end
+
+ if new_resource.csr_file.nil?
+ file key_file do
+ action :create_if_missing
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode unless new_resource.mode.nil?
+ sensitive true
+ content key.to_pem
+ end
+ end
+ end
+
+ action_class do
+ def key_file
+ @key_file ||=
+ if new_resource.key_file
+ new_resource.key_file
+ else
+ path, file = ::File.split(new_resource.path)
+ filename = ::File.basename(file, ::File.extname(file))
+ path + "/" + filename + ".key"
+ end
+ end
+
+ def key
+ @key ||= if priv_key_file_valid?(key_file, new_resource.key_pass)
+ OpenSSL::PKey.read ::File.read(key_file), new_resource.key_pass
+ elsif new_resource.key_type == "rsa"
+ gen_rsa_priv_key(new_resource.key_length)
+ else
+ gen_ec_priv_key(new_resource.key_curve)
+ end
+ end
+
+ def request
+ if new_resource.csr_file.nil?
+ gen_x509_request(subject, key)
+ else
+ OpenSSL::X509::Request.new ::File.read(new_resource.csr_file)
+ end
+ end
+
+ def subject
+ OpenSSL::X509::Name.new.tap do |csr_subject|
+ csr_subject.add_entry("C", new_resource.country) unless new_resource.country.nil?
+ csr_subject.add_entry("ST", new_resource.state) unless new_resource.state.nil?
+ csr_subject.add_entry("L", new_resource.city) unless new_resource.city.nil?
+ csr_subject.add_entry("O", new_resource.org) unless new_resource.org.nil?
+ csr_subject.add_entry("OU", new_resource.org_unit) unless new_resource.org_unit.nil?
+ csr_subject.add_entry("CN", new_resource.common_name)
+ csr_subject.add_entry("emailAddress", new_resource.email) unless new_resource.email.nil?
+ end
+ end
+
+ def ca_private_key
+ if new_resource.csr_file.nil?
+ key
+ else
+ OpenSSL::PKey.read ::File.read(new_resource.ca_key_file), new_resource.ca_key_pass
+ end
+ end
+
+ def ca_info
+ # Will contain issuer (if any) & expiration
+ ca_info = {}
+
+ unless new_resource.ca_cert_file.nil?
+ ca_info["issuer"] = OpenSSL::X509::Certificate.new ::File.read(new_resource.ca_cert_file)
+ end
+ ca_info["validity"] = new_resource.expire
+
+ ca_info
+ end
+
+ def extensions
+ extensions = gen_x509_extensions(new_resource.extensions)
+
+ unless new_resource.subject_alt_name.empty?
+ extensions += gen_x509_extensions("subjectAltName" => { "values" => new_resource.subject_alt_name, "critical" => false })
+ end
+
+ extensions
+ end
+
+ def cert
+ gen_x509_cert(request, extensions, ca_info, ca_private_key)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_x509_crl.rb b/lib/chef/resource/openssl_x509_crl.rb
new file mode 100644
index 0000000000..6e7f905084
--- /dev/null
+++ b/lib/chef/resource/openssl_x509_crl.rb
@@ -0,0 +1,152 @@
+#
+# License:: Apache License, Version 2.0
+# Author:: Julien Huon
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class OpensslX509Crl < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides :openssl_x509_crl
+
+ description "Use the **openssl_x509_crl** resource to generate PEM-formatted x509 certificate revocation list (CRL) files."
+ introduced "14.4"
+ examples <<~DOC
+ **Create a certificate revocation file**
+
+ ```ruby
+ openssl_x509_crl '/etc/ssl_test/my_ca.crl' do
+ ca_cert_file '/etc/ssl_test/my_ca.crt'
+ ca_key_file '/etc/ssl_test/my_ca.key'
+ end
+ ```
+
+ **Create a certificate revocation file for a particular serial**
+
+ ```ruby
+ openssl_x509_crl '/etc/ssl_test/my_ca.crl' do
+ ca_cert_file '/etc/ssl_test/my_ca.crt'
+ ca_key_file '/etc/ssl_test/my_ca.key'
+ serial_to_revoke C7BCB6602A2E4251EF4E2827A228CB52BC0CEA2F
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's name.",
+ name_property: true
+
+ property :serial_to_revoke, [Integer, String],
+ description: "Serial of the X509 Certificate to revoke."
+
+ property :revocation_reason, Integer,
+ description: "Reason for the revocation.",
+ default: 0
+
+ property :expire, Integer,
+ description: "Value representing the number of days from now through which the issued CRL will remain valid. The CRL will expire after this period.",
+ default: 8
+
+ property :renewal_threshold, Integer,
+ description: "Number of days before the expiration. It this threshold is reached, the CRL will be renewed.",
+ default: 1
+
+ property :ca_cert_file, String,
+ description: "The path to the CA X509 Certificate on the filesystem. If the `ca_cert_file` property is specified, the `ca_key_file` property must also be specified, the CRL will be signed with them.",
+ required: true
+
+ property :ca_key_file, String,
+ description: "The path to the CA private key on the filesystem. If the `ca_key_file` property is specified, the `ca_cert_file` property must also be specified, the CRL will be signed with them.",
+ required: true
+
+ property :ca_key_pass, String,
+ description: "The passphrase for CA private key's passphrase."
+
+ property :owner, [String, Integer],
+ description: "The owner permission for the CRL file."
+
+ property :group, [String, Integer],
+ description: "The group permission for the CRL file."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of the CRL file."
+
+ action :create do
+ description "Create the CRL file."
+
+ file new_resource.path do
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode unless new_resource.mode.nil?
+ content crl.to_pem
+ action :create
+ end
+ end
+
+ action_class do
+ def crl_info
+ # Will contain issuer & expiration
+ crl_info = {}
+
+ crl_info["issuer"] = ::OpenSSL::X509::Certificate.new ::File.read(new_resource.ca_cert_file)
+ crl_info["validity"] = new_resource.expire
+
+ crl_info
+ end
+
+ def revoke_info
+ # Will contain Serial to revoke & reason
+ revoke_info = {}
+
+ revoke_info["serial"] = new_resource.serial_to_revoke
+ revoke_info["reason"] = new_resource.revocation_reason
+
+ revoke_info
+ end
+
+ def ca_private_key
+ ::OpenSSL::PKey.read ::File.read(new_resource.ca_key_file), new_resource.ca_key_pass
+ end
+
+ def crl
+ if crl_file_valid?(new_resource.path)
+ crl = ::OpenSSL::X509::CRL.new ::File.read(new_resource.path)
+ else
+ log "Creating a CRL #{new_resource.path} for CA #{new_resource.ca_cert_file}"
+ crl = gen_x509_crl(ca_private_key, crl_info)
+ end
+
+ if !new_resource.serial_to_revoke.nil? && serial_revoked?(crl, new_resource.serial_to_revoke) == false
+ log "Revoking serial #{new_resource.serial_to_revoke} in CRL #{new_resource.path}"
+ crl = revoke_x509_crl(revoke_info, crl, ca_private_key, crl_info)
+ elsif crl.next_update <= Time.now + 3600 * 24 * new_resource.renewal_threshold
+ log "Renewing CRL for CA #{new_resource.ca_cert_file}"
+ crl = renew_x509_crl(crl, ca_private_key, crl_info)
+ end
+
+ crl
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_x509_request.rb b/lib/chef/resource/openssl_x509_request.rb
new file mode 100644
index 0000000000..0e68337b05
--- /dev/null
+++ b/lib/chef/resource/openssl_x509_request.rb
@@ -0,0 +1,187 @@
+#
+# License:: Apache License, Version 2.0
+# Author:: Julien Huon
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class OpensslX509Request < Chef::Resource
+ require_relative "../mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ unified_mode true
+
+ provides :openssl_x509_request
+
+ description "Use the **openssl_x509_request** resource to generate PEM-formatted x509 certificates requests. If no existing key is specified, the resource will automatically generate a passwordless key with the certificate."
+ introduced "14.4"
+ examples <<~DOC
+ **Generate new EC key and CSR file**
+
+ ```ruby
+ openssl_x509_request '/etc/ssl_files/my_ec_request.csr' do
+ common_name 'myecrequest.example.com'
+ org 'Test Kitchen Example'
+ org_unit 'Kitchens'
+ country 'UK'
+ end
+ ```
+
+ **Generate a new CSR file from an existing EC key**
+
+ ```ruby
+ openssl_x509_request '/etc/ssl_files/my_ec_request2.csr' do
+ common_name 'myecrequest2.example.com'
+ org 'Test Kitchen Example'
+ org_unit 'Kitchens'
+ country 'UK'
+ key_file '/etc/ssl_files/my_ec_request.key'
+ end
+ ```
+
+ **Generate new RSA key and CSR file**
+
+ ```ruby
+ openssl_x509_request '/etc/ssl_files/my_rsa_request.csr' do
+ common_name 'myrsarequest.example.com'
+ org 'Test Kitchen Example'
+ org_unit 'Kitchens'
+ country 'UK'
+ key_type 'rsa'
+ end
+ ```
+ DOC
+
+ property :path, String, name_property: true,
+ description: "An optional property for specifying the path to write the file to if it differs from the resource block's name."
+
+ property :owner, [String, Integer],
+ description: "The owner applied to all files created by the resource."
+
+ property :group, [String, Integer],
+ description: "The group ownership applied to all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode applied to all files created by the resource."
+
+ property :country, String,
+ description: "Value for the `C` certificate field."
+
+ property :state, String,
+ description: "Value for the `ST` certificate field."
+
+ property :city, String,
+ description: "Value for the `L` certificate field."
+
+ property :org, String,
+ description: "Value for the `O` certificate field."
+
+ property :org_unit, String,
+ description: "Value for the `OU` certificate field."
+
+ property :common_name, String,
+ required: true,
+ description: "Value for the `CN` certificate field."
+
+ property :email, String,
+ description: "Value for the `email` certificate field."
+
+ property :key_file, String,
+ description: "The path to a certificate key file on the filesystem. If the `key_file` property is specified, the resource will attempt to source a key from this location. If no key file is found, the resource will generate a new key file at this location. If the `key_file` property is not specified, the resource will generate a key file in the same directory as the generated certificate, with the same name as the generated certificate."
+
+ property :key_pass, String,
+ description: "The passphrase for an existing key's passphrase."
+
+ property :key_type, String,
+ equal_to: %w{rsa ec}, default: "ec",
+ description: "The desired type of the generated key."
+
+ property :key_length, Integer,
+ equal_to: [1024, 2048, 4096, 8192], default: 2048,
+ description: "The desired bit length of the generated key (if key_type is equal to `rsa`)."
+
+ property :key_curve, String,
+ equal_to: %w{secp384r1 secp521r1 prime256v1}, default: "prime256v1",
+ description: "The desired curve of the generated key (if key_type is equal to `ec`). Run `openssl ecparam -list_curves` to see available options."
+
+ action :create do
+ description "Generate a certificate request."
+
+ unless ::File.exist? new_resource.path
+ converge_by("Create CSR #{@new_resource}") do
+ file new_resource.path do
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode unless new_resource.mode.nil?
+ content csr.to_pem
+ action :create
+ end
+
+ file key_file do
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode unless new_resource.mode.nil?
+ content key.to_pem
+ sensitive true
+ action :create_if_missing
+ end
+ end
+ end
+ end
+
+ action_class do
+ def key_file
+ @key_file ||=
+ if new_resource.key_file
+ new_resource.key_file
+ else
+ path, file = ::File.split(new_resource.path)
+ filename = ::File.basename(file, ::File.extname(file))
+ path + "/" + filename + ".key"
+ end
+ end
+
+ def key
+ @key ||= if priv_key_file_valid?(key_file, new_resource.key_pass)
+ OpenSSL::PKey.read ::File.read(key_file), new_resource.key_pass
+ elsif new_resource.key_type == "rsa"
+ gen_rsa_priv_key(new_resource.key_length)
+ else
+ gen_ec_priv_key(new_resource.key_curve)
+ end
+ end
+
+ def subject
+ OpenSSL::X509::Name.new.tap do |csr_subject|
+ csr_subject.add_entry("C", new_resource.country) unless new_resource.country.nil?
+ csr_subject.add_entry("ST", new_resource.state) unless new_resource.state.nil?
+ csr_subject.add_entry("L", new_resource.city) unless new_resource.city.nil?
+ csr_subject.add_entry("O", new_resource.org) unless new_resource.org.nil?
+ csr_subject.add_entry("OU", new_resource.org_unit) unless new_resource.org_unit.nil?
+ csr_subject.add_entry("CN", new_resource.common_name)
+ csr_subject.add_entry("emailAddress", new_resource.email) unless new_resource.email.nil?
+ end
+ end
+
+ def csr
+ gen_x509_request(subject, key)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/osx_profile.rb b/lib/chef/resource/osx_profile.rb
index 8142e1fd96..491f30be43 100644
--- a/lib/chef/resource/osx_profile.rb
+++ b/lib/chef/resource/osx_profile.rb
@@ -16,59 +16,320 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
+require_relative "../log"
+require_relative "../resource/file"
+autoload :UUIDTools, "uuidtools"
+autoload :Plist, "plist"
class Chef
class Resource
class OsxProfile < Chef::Resource
- provides :osx_profile, os: "darwin"
- provides :osx_config_profile, os: "darwin"
+ unified_mode true
- identity_attr :profile_name
+ provides :osx_profile
+ provides :osx_config_profile
- default_action :install
- allowed_actions :install, :remove
+ 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"
+ examples <<~DOC
+ **Install a profile from a cookbook file**
- def initialize(name, run_context = nil)
- super
- @profile_name = name
- @profile = nil
- @identifier = nil
- @path = nil
+ ```ruby
+ osx_profile 'com.company.screensaver.mobileconfig'
+ ```
+
+ **Install profile from a hash**
+
+ ```ruby
+ profile_hash = {
+ 'PayloadIdentifier' => 'com.company.screensaver',
+ 'PayloadRemovalDisallowed' => false,
+ 'PayloadScope' => 'System',
+ 'PayloadType' => 'Configuration',
+ 'PayloadUUID' => '1781fbec-3325-565f-9022-8aa28135c3cc',
+ 'PayloadOrganization' => 'Chef',
+ 'PayloadVersion' => 1,
+ 'PayloadDisplayName' => 'Screensaver Settings',
+ 'PayloadContent'=> [
+ {
+ 'PayloadType' => 'com.apple.ManagedClient.preferences',
+ 'PayloadVersion' => 1,
+ 'PayloadIdentifier' => 'com.company.screensaver',
+ 'PayloadUUID' => '73fc30e0-1e57-0131-c32d-000c2944c108',
+ 'PayloadEnabled' => true,
+ 'PayloadDisplayName' => 'com.apple.screensaver',
+ 'PayloadContent' => {
+ 'com.apple.screensaver' => {
+ 'Forced' => [
+ {
+ 'mcx_preference_settings' => {
+ 'idleTime' => 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ ],
+ }
+
+ osx_profile 'Install screensaver profile' do
+ profile profile_hash
+ end
+ ```
+
+ **Remove profile using identifier in resource name**
+
+ ```ruby
+ osx_profile 'com.company.screensaver' do
+ action :remove
end
+ ```
+
+ **Remove profile by identifier and user friendly resource name**
+
+ ```ruby
+ osx_profile 'Remove screensaver profile' do
+ identifier 'com.company.screensaver'
+ action :remove
+ end
+ ```
+ DOC
+
+ property :profile_name, String,
+ description: "Use to specify the name of the profile, if different from the name of the resource block.",
+ name_property: true
+
+ property :profile, [ String, Hash ],
+ description: "Use to specify a profile. This may be the name of a profile contained in a cookbook or a Hash that contains the contents of the profile."
- def profile_name(arg = nil)
- set_or_return(
- :profile_name,
- arg,
- :kind_of => [ String ]
- )
+ property :identifier, String,
+ description: "Use to specify the identifier for the profile, such as `com.company.screensaver`."
+
+ # this is not a property it is necessary for the tempfile this resource uses to work (FIXME: this is terrible)
+ #
+ # @api private
+ #
+ def path(path = nil)
+ @path ||= path
+ @path
end
- def profile(arg = nil)
- set_or_return(
- :profile,
- arg,
- :kind_of => [ String, Hash ]
- )
+ action_class do
+ def load_current_resource
+ @current_resource = Chef::Resource::OsxProfile.new(new_resource.name)
+ current_resource.profile_name(new_resource.profile_name)
+
+ if new_profile_hash
+ new_profile_hash["PayloadUUID"] = config_uuid(new_profile_hash)
+ end
+
+ current_resource.profile(current_profile)
+ end
+
+ def current_profile
+ all_profiles = get_installed_profiles
+
+ if all_profiles && all_profiles.key?("_computerlevel")
+ return all_profiles["_computerlevel"].find do |item|
+ item["ProfileIdentifier"] == new_profile_identifier
+ end
+ end
+ nil
+ end
+
+ def invalid_profile_name?(name_or_identifier)
+ name_or_identifier.end_with?(".mobileconfig") || !/^\w+(?:(\.| )\w+)+$/.match(name_or_identifier)
+ end
+
+ def check_resource_semantics!
+ if action == :remove
+ if new_profile_identifier
+ if invalid_profile_name?(new_profile_identifier)
+ raise "when removing using the identifier property, it must match the profile identifier"
+ end
+ else
+ if invalid_profile_name?(new_resource.profile_name)
+ raise "When removing by resource name, it must match the profile identifier"
+ end
+ end
+ end
+
+ if action == :install
+ # we only do this check for the install action so that profiles can still be removed on macOS 11+
+ if mac? && node["platform_version"] =~ ">= 11.0"
+ raise "The osx_profile resource is not available on macOS Big Sur or above due to Apple's removal of support for CLI profile installation"
+ end
+
+ if new_profile_hash.is_a?(Hash) && !new_profile_hash.include?("PayloadIdentifier")
+ raise "The specified profile does not seem to be valid"
+ end
+ if new_profile_hash.is_a?(String) && !new_profile_hash.end_with?(".mobileconfig")
+ raise "#{new_profile_hash}' is not a valid profile"
+ end
+ end
+ end
end
- def identifier(arg = nil)
- set_or_return(
- :identifier,
- arg,
- :kind_of => [ String ]
- )
+ action :install do
+ unless profile_installed?
+ converge_by("install profile #{new_profile_identifier}") do
+ profile_path = write_profile_to_disk
+ install_profile(profile_path)
+ get_installed_profiles(true)
+ end
+ end
end
- def path(arg = nil)
- set_or_return(
- :path,
- arg,
- :kind_of => [ String ]
- )
+ action :remove do
+ # Clean up profile after removing it
+ if profile_installed?
+ converge_by("remove profile #{new_profile_identifier}") do
+ remove_profile
+ get_installed_profiles(true)
+ end
+ end
end
+ action_class do
+ private
+
+ def profile
+ @profile ||= new_resource.profile || new_resource.profile_name
+ end
+
+ def new_profile_hash
+ @new_profile_hash ||= get_profile_hash(profile)
+ end
+
+ def new_profile_identifier
+ @new_profile_identifier ||= if new_profile_hash
+ new_profile_hash["PayloadIdentifier"]
+ else
+ new_resource.identifier || new_resource.profile_name
+ end
+ end
+
+ def load_profile_hash(new_profile)
+ # file must exist in cookbook
+ return nil unless new_profile.end_with?(".mobileconfig")
+
+ unless cookbook_file_available?(new_profile)
+ raise Chef::Exceptions::FileNotFound, "#{self}: '#{new_profile}' not found in cookbook"
+ end
+
+ cookbook_profile = cache_cookbook_profile(new_profile)
+ ::Plist.parse_xml(cookbook_profile)
+ end
+
+ def cookbook_file_available?(cookbook_file)
+ run_context.has_cookbook_file_in_cookbook?(
+ new_resource.cookbook_name, cookbook_file
+ )
+ end
+
+ def get_cache_dir
+ Chef::FileCache.create_cache_path(
+ "profiles/#{new_resource.cookbook_name}"
+ )
+ end
+
+ def cache_cookbook_profile(cookbook_file)
+ Chef::FileCache.create_cache_path(
+ ::File.join(
+ "profiles",
+ new_resource.cookbook_name,
+ ::File.dirname(cookbook_file)
+ )
+ )
+
+ path = ::File.join( get_cache_dir, "#{cookbook_file}.remote")
+
+ cookbook_file path do
+ cookbook_name = new_resource.cookbook_name
+ source(cookbook_file)
+ backup(false)
+ run_action(:create)
+ end
+
+ path
+ end
+
+ def get_profile_hash(new_profile)
+ if new_profile.is_a?(Hash)
+ new_profile
+ elsif new_profile.is_a?(String)
+ load_profile_hash(new_profile)
+ end
+ end
+
+ def config_uuid(profile)
+ # Make a UUID of the profile contents and return as string
+ UUIDTools::UUID.sha1_create(
+ UUIDTools::UUID_DNS_NAMESPACE,
+ profile.to_s
+ ).to_s
+ end
+
+ def write_profile_to_disk
+ # FIXME: this is kind of terrible, the resource needs a tempfile to use and
+ # wants it created similarly to the file providers (with all the magic necessary
+ # for determining if it should go in the cwd or into a tmpdir), but it abuses
+ # the Chef::FileContentManagement::Tempfile API to do that, which requires setting
+ # a `path` method on the resource because of tight-coupling to the file provider
+ # pattern. We don't just want to use a file here because the point is to get
+ # at the tempfile pattern from the file provider, but to feed that into a shell
+ # command rather than deploying the file to somewhere on disk. There's some
+ # better API that needs extracting here.
+ 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
+ end
+
+ def install_profile(profile_path)
+ cmd = [ "/usr/bin/profiles", "-I", "-F", profile_path ]
+ logger.trace("cmd: #{cmd.join(" ")}")
+ shell_out!(*cmd)
+ end
+
+ def remove_profile
+ cmd = [ "/usr/bin/profiles", "-R", "-p", new_profile_identifier ]
+ logger.trace("cmd: #{cmd.join(" ")}")
+ shell_out!(*cmd)
+ end
+
+ #
+ # FIXME FIXME FIXME
+ # The node object should not be used for caching state like this and this is not a public API and may break.
+ # FIXME FIXME FIXME
+ #
+
+ def get_installed_profiles(update = nil)
+ logger.trace("Saving profile data to node.run_state")
+ if update
+ node.run_state[:config_profiles] = query_installed_profiles
+ else
+ node.run_state[:config_profiles] ||= query_installed_profiles
+ end
+ end
+
+ def query_installed_profiles
+ logger.trace("Running /usr/bin/profiles -P -o stdout-xml to determine profile state")
+ so = shell_out( "/usr/bin/profiles", "-P", "-o", "stdout-xml" )
+ ::Plist.parse_xml(so.stdout)
+ end
+
+ def profile_installed?
+ # Profile Identifier and UUID must match a currently installed profile
+ return false if current_resource.profile.nil? || current_resource.profile.empty?
+ return true if action == :remove
+
+ current_resource.profile["ProfileUUID"] == new_profile_hash["PayloadUUID"]
+ end
+ end
end
end
end
diff --git a/lib/chef/resource/package.rb b/lib/chef/resource/package.rb
index 32339e1a24..db3b12d289 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,15 +17,24 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class Package < Chef::Resource
- resource_name :package
+ unified_mode true
+ provides :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
@@ -33,14 +42,24 @@ class Chef
super
end
- property :package_name, [ String, Array ], identity: true
+ property :package_name, [ String, Array ],
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, [ String, Array ],
+ description: "The version of a package to be installed or upgraded."
+
+ property :options, [ String, Array ],
+ description: "One (or more) additional command options that are passed to the command.",
+ coerce: proc { |x| x.is_a?(String) ? x.shellsplit : x }
+
+ property :source, String,
+ description: "The optional path to a package on the local file system.",
+ desired_state: false
- property :version, [ String, Array ]
- property :options, String
- property :response_file, String, desired_state: false
- property :response_file_variables, Hash, default: lazy { {} }, desired_state: false
- property :source, String, desired_state: false
- property :timeout, [ String, Integer ], desired_state: false
+ property :timeout, [ String, Integer ],
+ description: "The amount of time (in seconds) to wait before timing out.",
+ desired_state: false
end
end
diff --git a/lib/chef/resource/pacman_package.rb b/lib/chef/resource/pacman_package.rb
index 66b39d164d..14856de7dd 100644
--- a/lib/chef/resource/pacman_package.rb
+++ b/lib/chef/resource/pacman_package.rb
@@ -16,13 +16,16 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
class Chef
class Resource
class PacmanPackage < Chef::Resource::Package
- resource_name :pacman_package
- provides :pacman_package, os: "linux"
+ unified_mode true
+
+ 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..385922c940 100644
--- a/lib/chef/resource/paludis_package.rb
+++ b/lib/chef/resource/paludis_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Vasiliy Tolstov (<v.tolstov@selfip.ru>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,32 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/paludis"
+require_relative "package"
+require_relative "../provider/package/paludis"
class Chef
class Resource
class PaludisPackage < Chef::Resource::Package
- resource_name :paludis_package
- provides :paludis_package, os: "linux"
+ unified_mode true
+
+ provides :paludis_package
+
+ description "Use the **paludis_package** resource to manage packages for the Paludis platform."
+ introduced "12.1"
allowed_actions :install, :remove, :upgrade
- property :timeout, default: 3600
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ property :timeout, [String, Integer],
+ description: "The amount of time (in seconds) to wait before timing out.",
+ default: 3600,
+ desired_state: false
end
end
end
diff --git a/lib/chef/resource/perl.rb b/lib/chef/resource/perl.rb
index 60af0e92da..ac98c69a8c 100644
--- a/lib/chef/resource/perl.rb
+++ b/lib/chef/resource/perl.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,26 @@
# limitations under the License.
#
-require "chef/resource/script"
-require "chef/provider/script"
+require_relative "script"
class Chef
class Resource
class Perl < Chef::Resource::Script
+ unified_mode true
+
+ provides :perl
+
def initialize(name, run_context = nil)
super
@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/plist.rb b/lib/chef/resource/plist.rb
new file mode 100644
index 0000000000..a7cb88ef57
--- /dev/null
+++ b/lib/chef/resource/plist.rb
@@ -0,0 +1,222 @@
+#
+# Copyright:: Copyright 2017-2020, Microsoft Corporation
+# Copyright:: Copyright (c) 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 "../resource"
+autoload :Plist, "plist"
+
+class Chef
+ class Resource
+
+ class PlistResource < Chef::Resource # we name this PlistResource to avoid confusion with Plist from the plist gem
+ unified_mode true
+
+ provides :plist
+
+ description "Use the **plist** resource to set config values in plist files on macOS systems."
+ introduced "16.0"
+ examples <<~DOC
+ **Show hidden files in finder**:
+
+ ```ruby
+ plist 'show hidden files' do
+ path '/Users/vagrant/Library/Preferences/com.apple.finder.plist'
+ entry 'AppleShowAllFiles'
+ value true
+ end
+ ```
+ DOC
+
+ property :path, String, name_property: true,
+ description: "The path on disk to the plist file."
+
+ property :entry, String
+ property :value, [TrueClass, FalseClass, String, Integer, Float, Hash]
+ property :encoding, String, default: "binary"
+
+ property :owner, String, default: "root",
+ description: "The owner of the plist file."
+
+ property :group, String, default: "wheel",
+ description: "The group of the plist file."
+
+ property :mode, [String, Integer],
+ description: "The file mode of the plist file. Ex: '644'"
+
+ PLISTBUDDY_EXECUTABLE = "/usr/libexec/PlistBuddy".freeze
+ DEFAULTS_EXECUTABLE = "/usr/bin/defaults".freeze
+ PLUTIL_EXECUTABLE = "/usr/bin/plutil".freeze
+ PLUTIL_FORMAT_MAP = { "us-ascii" => "xml1",
+ "text/xml" => "xml1",
+ "utf-8" => "xml1",
+ "binary" => "binary1" }.freeze
+
+ load_current_value do |desired|
+ current_value_does_not_exist! unless ::File.exist? desired.path
+ entry desired.entry if entry_in_plist? desired.entry, desired.path
+
+ setting = setting_from_plist desired.entry, desired.path
+ value convert_to_data_type_from_string(setting[:key_type], setting[:key_value])
+
+ file_type_cmd = shell_out "/usr/bin/file", "--brief", "--mime-encoding", "--preserve-date", desired.path
+ encoding file_type_cmd.stdout.chomp
+
+ file_owner_cmd = shell_out("/usr/bin/stat", "-f", "%Su", desired.path)
+ owner file_owner_cmd.stdout.chomp
+
+ file_group_cmd = shell_out("/usr/bin/stat", "-f", "%Sg", desired.path)
+ group file_group_cmd.stdout.chomp
+ end
+
+ action :set do
+ converge_if_changed :path do
+ converge_by "create new plist: '#{new_resource.path}'" do
+ file new_resource.path do
+ content {}.to_plist
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode if property_is_set?(:mode)
+ end
+ end
+ end
+
+ plist_file_name = ::File.basename(new_resource.path)
+
+ converge_if_changed :entry do
+ converge_by "add entry \"#{new_resource.entry}\" to #{plist_file_name}" do
+ shell_out!(plistbuddy_command(:add, new_resource.entry, new_resource.path, new_resource.value))
+ end
+ end
+
+ converge_if_changed :value do
+ converge_by "#{plist_file_name}: set #{new_resource.entry} to #{new_resource.value}" do
+ shell_out!(plistbuddy_command(:set, new_resource.entry, new_resource.path, new_resource.value))
+ end
+ end
+
+ converge_if_changed :encoding do
+ converge_by "change format" do
+ unless PLUTIL_FORMAT_MAP.key?(new_resource.encoding)
+ Chef::Application.fatal!(
+ "Option encoding must be equal to one of: #{PLUTIL_FORMAT_MAP.keys}! You passed \"#{new_resource.encoding}\"."
+ )
+ end
+ shell_out!(PLUTIL_EXECUTABLE, "-convert", PLUTIL_FORMAT_MAP[new_resource.encoding], new_resource.path)
+ end
+ end
+
+ converge_if_changed :owner do
+ converge_by "update owner to #{new_resource.owner}" do
+ file new_resource.path do
+ owner new_resource.owner
+ end
+ end
+ end
+
+ converge_if_changed :group do
+ converge_by "update group to #{new_resource.group}" do
+ file new_resource.path do
+ group new_resource.group
+ end
+ end
+ end
+ end
+
+ ### Question: Should I refactor these methods into an action_class?
+ ### Answer: NO
+ ### Why: We need them in both the action and in load_current_value. If you put them in the
+ ### action class then they're only in the Provider class and are not available to load_current_value
+
+ def convert_to_data_type_from_string(type, value)
+ case type
+ when "boolean"
+ # Since we've determined this is a boolean data type, we can assume that:
+ # If the value as an int is 1, return true
+ # If the value as an int is 0 (not 1), return false
+ value.to_i == 1
+ when "integer"
+ value.to_i
+ when "float"
+ value.to_f
+ when "string", "dictionary"
+ value
+ when nil
+ ""
+ else
+ raise "Unknown or unsupported data type: #{type.class}"
+ end
+ end
+
+ def type_to_commandline_string(value)
+ case value
+ when Array
+ "array"
+ when Integer
+ "integer"
+ when FalseClass, TrueClass
+ "bool"
+ when Hash
+ "dict"
+ when String
+ "string"
+ when Float
+ "float"
+ else
+ raise "Unknown or unsupported data type: #{value} of #{value.class}"
+ end
+ end
+
+ def entry_in_plist?(entry, path)
+ print_entry = plistbuddy_command :print, entry, path
+ cmd = shell_out print_entry
+ cmd.exitstatus == 0
+ end
+
+ def plistbuddy_command(subcommand, entry, path, value = nil)
+ sep = " "
+ arg = case subcommand.to_s
+ when "add"
+ type_to_commandline_string(value)
+ when "set"
+ if value.is_a?(Hash)
+ sep = ":"
+ value.map { |k, v| "#{k} #{v}" }
+ else
+ value
+ end
+ else
+ ""
+ end
+ entry_with_arg = ["\"#{entry}\"", arg].join(sep).strip
+ subcommand = "#{subcommand.capitalize} :#{entry_with_arg}"
+ [PLISTBUDDY_EXECUTABLE, "-c", "\'#{subcommand}\'", "\"#{path}\""].join(" ")
+ end
+
+ def setting_from_plist(entry, path)
+ defaults_read_type_output = shell_out(DEFAULTS_EXECUTABLE, "read-type", path, entry).stdout
+ data_type = defaults_read_type_output.split.last
+
+ if value.class == Hash
+ plutil_output = shell_out(PLUTIL_EXECUTABLE, "-extract", entry, "xml1", "-o", "-", path).stdout.chomp
+ { key_type: data_type, key_value: ::Plist.parse_xml(plutil_output) }
+ else
+ defaults_read_output = shell_out(DEFAULTS_EXECUTABLE, "read", path, entry).stdout
+ { key_type: data_type, key_value: defaults_read_output.strip }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/portage_package.rb b/lib/chef/resource/portage_package.rb
index ad66c7b42b..05f54c9d21 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,28 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
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
+ unified_mode true
+ provides :portage_package
+
+ description "Use the **portage_package** resource to manage packages for the Gentoo platform."
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
+
+ property :timeout, [String, Integer],
+ default: 3600,
+ description: "The amount of time (in seconds) to wait before timing out.",
+ desired_state: false
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..7d013eac4c
--- /dev/null
+++ b/lib/chef/resource/powershell_package.rb
@@ -0,0 +1,50 @@
+# Author:: Dheeraj Dubey(dheeraj.dubey@msystechnologies.com)
+# Copyright:: Copyright (c) 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 "package"
+
+class Chef
+ class Resource
+ class PowershellPackage < Chef::Resource::Package
+ unified_mode true
+
+ 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 powershell_package_source resource."
+ introduced "12.16"
+
+ allowed_actions :install, :remove
+
+ property :package_name, [String, Array],
+ description: "The name of the package. Default value: the name of the resource block.",
+ 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 :source, String,
+ description: "Specify the source of the package.",
+ introduced: "14.0"
+
+ property :skip_publisher_check, [true, false],
+ description: "Skip validating module author.",
+ default: false, introduced: "14.3", desired_state: false
+
+ end
+ end
+end
diff --git a/lib/chef/resource/powershell_package_source.rb b/lib/chef/resource/powershell_package_source.rb
new file mode 100644
index 0000000000..066efc6a72
--- /dev/null
+++ b/lib/chef/resource/powershell_package_source.rb
@@ -0,0 +1,171 @@
+# Author:: Tor Magnus Rakvåg (tm@intility.no)
+# Copyright:: 2018, Intility AS
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+
+class Chef
+ class Resource
+ class PowershellPackageSource < Chef::Resource
+ unified_mode true
+
+ provides :powershell_package_source
+
+ description "Use the **powershell_package_source** resource to register a PowerShell package repository."
+ introduced "14.3"
+
+ property :source_name, String,
+ description: "The name of the package source.",
+ name_property: true
+
+ property :url, String,
+ description: "The URL to the package source.",
+ required: [:register]
+
+ property :trusted, [TrueClass, FalseClass],
+ description: "Whether or not to trust packages from this source.",
+ default: false
+
+ property :provider_name, String,
+ equal_to: %w{ Programs msi NuGet msu PowerShellGet psl chocolatey },
+ validation_message: "The following providers are supported: 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl' or 'chocolatey'",
+ description: "The package management provider for the source.",
+ default: "NuGet"
+
+ property :publish_location, String,
+ description: "The URL where modules will be published to for this source. Only valid if the provider is `PowerShellGet`."
+
+ property :script_source_location, String,
+ description: "The URL where scripts are located for this source. Only valid if the provider is `PowerShellGet`."
+
+ property :script_publish_location, String,
+ description: "The location where scripts will be published to for this source. Only valid if the provider is `PowerShellGet`."
+
+ load_current_value do
+ cmd = load_resource_state_script(source_name)
+ repo = powershell_exec!(cmd)
+ if repo.result.empty?
+ current_value_does_not_exist!
+ else
+ status = repo.result
+ end
+ url status["url"]
+ trusted status["trusted"]
+ provider_name status["provider_name"]
+ publish_location status["publish_location"]
+ script_source_location status["script_source_location"]
+ script_publish_location status["script_publish_location"]
+ end
+
+ action :register do
+ description "Registers and updates the powershell package source."
+ # TODO: Ensure package provider is installed?
+ if psrepository_cmdlet_appropriate?
+ if package_source_exists?
+ converge_if_changed :url, :trusted, :publish_location, :script_source_location, :script_publish_location do
+ update_cmd = build_ps_repository_command("Set", new_resource)
+ res = powershell_exec(update_cmd)
+ raise "Failed to update #{new_resource.source_name}: #{res.errors}" if res.error?
+ end
+ else
+ converge_by("register source: #{new_resource.source_name}") do
+ register_cmd = build_ps_repository_command("Register", new_resource)
+ res = powershell_exec(register_cmd)
+ raise "Failed to register #{new_resource.source_name}: #{res.errors}" if res.error?
+ end
+ end
+ else
+ if package_source_exists?
+ converge_if_changed :url, :trusted, :provider_name do
+ update_cmd = build_package_source_command("Set", new_resource)
+ res = powershell_exec(update_cmd)
+ raise "Failed to update #{new_resource.source_name}: #{res.errors}" if res.error?
+ end
+ else
+ converge_by("register source: #{new_resource.source_name}") do
+ register_cmd = build_package_source_command("Register", new_resource)
+ res = powershell_exec(register_cmd)
+ raise "Failed to register #{new_resource.source_name}: #{res.errors}" if res.error?
+ end
+ end
+ end
+ end
+
+ action :unregister do
+ description "Unregisters the powershell package source."
+ if package_source_exists?
+ unregister_cmd = "Get-PackageSource -Name '#{new_resource.source_name}' | Unregister-PackageSource"
+ converge_by("unregister source: #{new_resource.source_name}") do
+ res = powershell_exec(unregister_cmd)
+ raise "Failed to unregister #{new_resource.source_name}: #{res.errors}" if res.error?
+ end
+ end
+ end
+
+ action_class do
+ def package_source_exists?
+ cmd = powershell_exec!("(Get-PackageSource -Name '#{new_resource.source_name}' -ErrorAction SilentlyContinue).Name")
+ !cmd.result.empty? && cmd.result.to_s.downcase.strip == new_resource.source_name.downcase
+ end
+
+ def psrepository_cmdlet_appropriate?
+ new_resource.provider_name == "PowerShellGet"
+ end
+
+ def build_ps_repository_command(cmdlet_type, new_resource)
+ cmd = "#{cmdlet_type}-PSRepository -Name '#{new_resource.source_name}'"
+ cmd << " -SourceLocation '#{new_resource.url}'" if new_resource.url
+ cmd << " -InstallationPolicy '#{new_resource.trusted ? "Trusted" : "Untrusted"}'"
+ cmd << " -PublishLocation '#{new_resource.publish_location}'" if new_resource.publish_location
+ cmd << " -ScriptSourceLocation '#{new_resource.script_source_location}'" if new_resource.script_source_location
+ cmd << " -ScriptPublishLocation '#{new_resource.script_publish_location}'" if new_resource.script_publish_location
+ cmd << " | Out-Null"
+ cmd
+ end
+
+ def build_package_source_command(cmdlet_type, new_resource)
+ cmd = "#{cmdlet_type}-PackageSource -Name '#{new_resource.source_name}'"
+ cmd << " -Location '#{new_resource.url}'" if new_resource.url
+ cmd << " -Trusted:#{new_resource.trusted ? "$true" : "$false"}"
+ cmd << " -ProviderName '#{new_resource.provider_name}'" if new_resource.provider_name
+ cmd << " | Out-Null"
+ cmd
+ end
+ end
+ end
+
+ private
+
+ def load_resource_state_script(name)
+ <<-EOH
+ $PSDefaultParameterValues = @{
+ "*:WarningAction" = "SilentlyContinue"
+ }
+ if(Get-PackageSource -Name '#{name}' -ErrorAction SilentlyContinue) {
+ if ((Get-PackageSource -Name '#{name}').ProviderName -eq 'PowerShellGet') {
+ (Get-PSRepository -Name '#{name}') | Select @{n='source_name';e={$_.Name}}, @{n='url';e={$_.SourceLocation}},
+ @{n='trusted';e={$_.Trusted}}, @{n='provider_name';e={$_.PackageManagementProvider}}, @{n='publish_location';e={$_.PublishLocation}},
+ @{n='script_source_location';e={$_.ScriptSourceLocation}}, @{n='script_publish_location';e={$_.ScriptPublishLocation}}
+ }
+ else {
+ (Get-PackageSource -Name '#{name}') | Select @{n='source_name';e={$_.Name}}, @{n='url';e={$_.Location}},
+ @{n='provider_name';e={$_.ProviderName}}, @{n='trusted';e={$_.IsTrusted}}
+ }
+ }
+ EOH
+ end
+ end
+end
diff --git a/lib/chef/resource/powershell_script.rb b/lib/chef/resource/powershell_script.rb
index a530a9116c..eb72518009 100644
--- a/lib/chef/resource/powershell_script.rb
+++ b/lib/chef/resource/powershell_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,24 +15,58 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/resource/windows_script"
+require_relative "windows_script"
class Chef
class Resource
class PowershellScript < Chef::Resource::WindowsScript
+ unified_mode true
+
+ set_guard_inherited_attributes(:interpreter)
+
provides :powershell_script, os: "windows"
- def initialize(name, run_context = nil)
- super(name, run_context, nil, "powershell.exe")
- @convert_boolean_return = false
- end
+ description <<~DESC
+ 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** resource is specific to the Microsoft Windows platform, but may use both the the Windows PowerShell interpreter or the PowerShell Core (pwsh) interpreter as of Chef Infra Client 16.6 and later.
+
+ The **powershell_script** resource creates and executes a temporary file 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` conditionals to guard this resource for idempotence.
+ DESC
+
+ property :flags, String,
+ description: "A string that is passed to the Windows PowerShell command"
+
+ property :interpreter, String,
+ default: "powershell",
+ equal_to: %w{powershell pwsh},
+ description: "The interpreter type, `powershell` or `pwsh` (PowerShell Core)"
+
+ property :convert_boolean_return, [true, false],
+ default: false,
+ description: <<~DESC
+ Return `0` if the last line of a command is evaluated to be true or to return `1` if the last line is evaluated to be false.
+
+ When the `guard_interpreter` common attribute is set to `:powershell_script`, a string command will be evaluated as if this value were set to `true`. This is because the behavior of this attribute is similar to the value of the `"$?"` expression common in UNIX interpreters. For example, this:
+
+ ```ruby
+ powershell_script 'make_safe_backup' do
+ guard_interpreter :powershell_script
+ code 'cp ~/data/nodes.json ~/data/nodes.bak'
+ not_if 'test-path ~/data/nodes.bak'
+ end
+ ```
+
+ is similar to:
+ ```ruby
+ bash 'make_safe_backup' do
+ code 'cp ~/data/nodes.json ~/data/nodes.bak'
+ not_if 'test -e ~/data/nodes.bak'
+ end
+ ```
+ DESC
- def convert_boolean_return(arg = nil)
- set_or_return(
- :convert_boolean_return,
- arg,
- :kind_of => [ FalseClass, TrueClass ]
- )
+ def initialize(*args)
+ super
+ @default_guard_interpreter = resource_name
end
# Allow callers evaluating guards to request default
@@ -42,8 +76,8 @@ class Chef
# default for this resource, this method can be removed since
# guard context and recipe resource context will have the
# same behavior.
- def self.get_default_attributes(opts)
- { :convert_boolean_return => true }
+ def self.get_default_attributes
+ { convert_boolean_return: true }
end
end
end
diff --git a/lib/chef/resource/python.rb b/lib/chef/resource/python.rb
index bcad3d090b..0b36ca0bbd 100644
--- a/lib/chef/resource/python.rb
+++ b/lib/chef/resource/python.rb
@@ -1,5 +1,5 @@
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,17 +15,25 @@
# limitations under the License.
#
-require "chef/resource/script"
-require "chef/provider/script"
+require_relative "script"
class Chef
class Resource
class Python < Chef::Resource::Script
+ unified_mode true
+
+ provides :python
+
def initialize(name, run_context = nil)
super
@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..6ac19e299b 100644
--- a/lib/chef/resource/reboot.rb
+++ b/lib/chef/resource/reboot.rb
@@ -1,6 +1,6 @@
#
# Author:: Chris Doherty <cdoherty@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef, Inc.
+# Copyright:: Copyright 2014-2019, Chef, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,32 +16,76 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-# 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
+ unified_mode true
- def initialize(name, run_context = nil)
- super
- @provider = Chef::Provider::Reboot
+ provides :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.
+ property :reason, String,
+ description: "A string that describes the reboot action.",
+ default: "Reboot by #{ChefUtils::Dist::Infra::PRODUCT}"
+
+ property :delay_mins, Integer,
+ description: "The amount of time (in minutes) to delay a reboot request.",
+ default: 0
+
+ action :request_reboot do
+ description "Reboot a node at the end of a chef-client run."
+
+ converge_by("request a system reboot to occur if the run succeeds") do
+ logger.warn "Reboot requested:'#{new_resource.name}'"
+ request_reboot
+ end
+ end
+
+ action :reboot_now do
+ description "Reboot a node so that the chef-client may continue the installation process."
+
+ converge_by("rebooting the system immediately") do
+ logger.warn "Rebooting system immediately, requested by '#{new_resource.name}'"
+ request_reboot
+ throw :end_client_run_early
+ end
end
- def reason(arg = nil)
- set_or_return(:reason, arg, :kind_of => String)
+ action :cancel do
+ description "Cancel a pending reboot request."
+
+ converge_by("cancel any existing end-of-run reboot request") do
+ logger.warn "Reboot canceled: '#{new_resource.name}'"
+ node.run_context.cancel_reboot
+ end
end
- def delay_mins(arg = nil)
- set_or_return(:delay_mins, arg, :kind_of => Fixnum)
+ # make sure people are quite clear what they want
+ # we have to define this below the actions since setting default_action to :nothing is a no-op
+ # and doesn't actually override the first action in the resource
+ default_action :nothing
+
+ action_class do
+ # 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,
+ timestamp: Time.now,
+ requested_by: new_resource.name
+ )
+ end
end
end
end
diff --git a/lib/chef/resource/registry_key.rb b/lib/chef/resource/registry_key.rb
index d11f826c38..6c17146fcb 100644
--- a/lib/chef/resource/registry_key.rb
+++ b/lib/chef/resource/registry_key.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 (c) 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.
@@ -15,14 +15,109 @@
# 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"
+
+require_relative "../resource"
+require_relative "../digester"
class Chef
class Resource
class RegistryKey < Chef::Resource
- identity_attr :key
+ unified_mode true
+
+ provides(:registry_key) { true }
+
+ description "Use the **registry_key** resource to create and delete registry keys in Microsoft Windows."
+ examples <<~'DOC'
+ **Create a registry key**
+
+ ```ruby
+ registry_key 'HKEY_LOCAL_MACHINE\\path-to-key\\Policies\\System' do
+ values [{
+ name: 'EnableLUA',
+ type: :dword,
+ data: 0
+ }]
+ action :create
+ end
+ ```
+
+ **Create a registry key with binary data: "\x01\x02\x03"**:
+
+ ```ruby
+ registry_key 'HKEY_CURRENT_USER\ChefTest' do
+ values [{
+ :name => "test",
+ :type => :binary,
+ :data => [0, 1, 2].map(&:chr).join
+ }]
+ action :create
+ end
+ ```
+
+ **Create 32-bit key in redirected wow6432 tree**
+
+ In 64-bit versions of Microsoft Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Example is a re-directed key. In the following examples, because HKEY_LOCAL_MACHINE\SOFTWARE\Example is a 32-bit key, the output will be “Found 32-bit key” if they are run on a version of Microsoft Windows that is 64-bit:
+
+ ```ruby
+ registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\Example' do
+ architecture :i386
+ recursive true
+ action :create
+ end
+ ```
+
+ **Set proxy settings to be the same as those used by Chef Infra Client**
+
+ ```ruby
+ proxy = URI.parse(Chef::Config[:http_proxy])
+ registry_key 'HKCU\Software\Microsoft\path\to\key\Internet Settings' do
+ values [{name: 'ProxyEnable', type: :reg_dword, data: 1},
+ {name: 'ProxyServer', data: "#{proxy.host}:#{proxy.port}"},
+ {name: 'ProxyOverride', type: :reg_string, data: <local>},
+ ]
+ action :create
+ end
+ ```
+
+ **Set the name of a registry key to "(Default)"**
+
+ ```ruby
+ registry_key 'Set (Default) value' do
+ key 'HKLM\Software\Test\Key\Path'
+ values [
+ {name: '', type: :string, data: 'test'},
+ ]
+ action :create
+ end
+ ```
+
+ **Delete a registry key value**
+
+ ```ruby
+ registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\path\to\key\AU' do
+ values [{
+ name: 'NoAutoRebootWithLoggedOnUsers',
+ type: :dword,
+ data: ''
+ }]
+ action :delete
+ end
+ ```
+
+ Note: If data: is not specified, you get an error: Missing data key in RegistryKey values hash
+
+ **Delete a registry key and its subkeys, recursively**
+
+ ```ruby
+ registry_key 'HKCU\SOFTWARE\Policies\path\to\key\Themes' do
+ recursive true
+ action :delete_key
+ end
+ ```
+
+ Note: Be careful when using the :delete_key action with the recursive attribute. This will delete the registry key, all of its values and all of the names, types, and data associated with them. This cannot be undone by Chef Infra Client.
+ DOC
+
state_attrs :values
default_action :create
@@ -61,61 +156,46 @@ 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
+
+ VALID_VALUE_HASH_KEYS = %i{name type data}.freeze
def values(arg = nil)
if not arg.nil?
if arg.is_a?(Hash)
- @values = [ arg ]
+ @values = [ Mash.new(arg).symbolize_keys ]
elsif arg.is_a?(Array)
- @values = arg
+ @values = []
+ arg.each do |value|
+ @values << Mash.new(value).symbolize_keys
+ end
else
raise ArgumentError, "Bad type for RegistryKey resource, use Hash or Array"
end
@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)
+ raise ArgumentError, "Missing name key in RegistryKey values hash" unless v.key?(:name)
+
v.each_key do |key|
- raise ArgumentError, "Bad key #{key} in RegistryKey values hash" unless [:name, :type, :data].include?(key)
+ raise ArgumentError, "Bad key #{key} in RegistryKey values hash" unless VALID_VALUE_HASH_KEYS.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: %i{machine x86_64 i386}
private
@@ -135,7 +215,7 @@ class Chef
# Some data types may raise errors when sent as json. Returns true if this
# value's data may need to be converted to a checksum.
def needs_checksum?(value)
- unsafe_types = [:binary, :dword, :dword_big_endian, :qword]
+ unsafe_types = %i{binary dword dword_big_endian qword}
unsafe_types.include?(value[:type])
end
diff --git a/lib/chef/resource/remote_directory.rb b/lib/chef/resource/remote_directory.rb
index 6e2928f3eb..b87fe8c085 100644
--- a/lib/chef/resource/remote_directory.rb
+++ b/lib/chef/resource/remote_directory.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,106 +17,71 @@
# limitations under the License.
#
-require "chef/resource/directory"
-require "chef/provider/remote_directory"
-require "chef/mixin/securable"
+require_relative "directory"
+require_relative "../provider/remote_directory"
+require_relative "../mixin/securable"
class Chef
class Resource
class RemoteDirectory < Chef::Resource::Directory
include Chef::Mixin::Securable
+ unified_mode true
- identity_attr :path
+ provides :remote_directory
- state_attrs :files_owner, :files_group, :files_mode
+ description "Use the **remote_directory** resource to incrementally transfer a directory from a cookbook to a node. The directory that is copied from the cookbook should be located under `COOKBOOK_NAME/files/default/REMOTE_DIRECTORY`. The `remote_directory` resource will obey file specificity."
default_action :create
allowed_actions :create, :create_if_missing, :delete
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?
- # create a second instance of the 'rights' attribute
+ if ChefUtils.windows?
+ # create a second instance of the 'rights' attribute (property)
rights_attribute(:files_rights)
end
- def source(args = nil)
- set_or_return(
- :source,
- args,
- :kind_of => String
- )
- end
+ # This same property exists in the directory resource, but we need to change the default to true here.
+ property :recursive, [ TrueClass, FalseClass ],
+ description: "Create or delete parent directories recursively. For the owner, group, and mode properties, the value of this attribute applies only to the leaf directory.",
+ default: true, desired_state: false
- def files_backup(arg = nil)
- set_or_return(
- :files_backup,
- arg,
- :kind_of => [ Integer, FalseClass ]
- )
- end
+ property :source, String,
+ description: "The base name of the source file (and inferred from the path property).",
+ default_description: "The base portion of the 'path' property. For example '/some/path/' would be 'path'.",
+ default: lazy { ::File.basename(path) }, desired_state: false
- def purge(arg = nil)
- set_or_return(
- :purge,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :files_backup, [ Integer, FalseClass ],
+ description: "The number of backup copies to keep for files in the directory.",
+ default: 5, desired_state: false
- def files_group(arg = nil)
- set_or_return(
- :files_group,
- arg,
- :regex => Chef::Config[:group_valid_regex]
- )
- end
+ property :purge, [ TrueClass, FalseClass ],
+ description: "Purge extra files found in the target directory.",
+ default: false, desired_state: false
- def files_mode(arg = nil)
- set_or_return(
- :files_mode,
- arg,
- :regex => /^\d{3,4}$/
- )
- end
+ property :overwrite, [ TrueClass, FalseClass ],
+ description: "Overwrite a file when it is different.",
+ default: true, desired_state: false
- def files_owner(arg = nil)
- set_or_return(
- :files_owner,
- arg,
- :regex => Chef::Config[:user_valid_regex]
- )
- end
+ property :cookbook, String,
+ description: "The cookbook in which a file is located (if it is not located in the current cookbook). The default value is the current cookbook.",
+ desired_state: false
- def overwrite(arg = nil)
- set_or_return(
- :overwrite,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :files_group, [String, Integer],
+ description: "Configure group permissions for files. A string or ID that identifies the group owner by group name, including fully qualified group names such as domain\\group or group@domain. If this value is not specified, existing groups remain unchanged and new group assignments use the default POSIX group (if available).",
+ regex: Chef::Config[:group_valid_regex]
- def cookbook(args = nil)
- set_or_return(
- :cookbook,
- args,
- :kind_of => String
- )
- end
+ property :files_mode, [String, Integer, nil],
+ description: "The octal mode for a file.\n UNIX- and Linux-based systems: A quoted 3-5 character string that defines the octal mode that is passed to chmod. For example: '755', '0755', or 00755. If the value is specified as a quoted string, it works exactly as if the chmod command was passed. If the value is specified as an integer, prepend a zero (0) to the value to ensure that it is interpreted as an octal number. For example, to assign read, write, and execute rights for all users, use '0777' or '777'; for the same rights, plus the sticky bit, use 01777 or '1777'.\n Microsoft Windows: A quoted 3-5 character string that defines the octal mode that is translated into rights for Microsoft Windows security. For example: '755', '0755', or 00755. Values up to '0777' are allowed (no sticky bits) and mean the same in Microsoft Windows as they do in UNIX, where 4 equals GENERIC_READ, 2 equals GENERIC_WRITE, and 1 equals GENERIC_EXECUTE. This property cannot be used to set :full_control. This property has no effect if not specified, but when it and rights are both specified, the effects are cumulative.",
+ default_description: "0644 on *nix systems",
+ regex: /^\d{3,4}$/, default: lazy { 0644 unless Chef::Platform.windows? }
+ property :files_owner, [String, Integer],
+ description: "Configure owner permissions for files. A string or ID that identifies the group owner by user name, including fully qualified user names such as domain\\user or user@domain. If this value is not specified, existing owners remain unchanged and new owner assignments use the current user (when necessary).",
+ regex: Chef::Config[:user_valid_regex]
end
end
end
diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb
index 4a1d1c6cff..ac0b2fe6a7 100644
--- a/lib/chef/resource/remote_file.rb
+++ b/lib/chef/resource/remote_file.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,25 +17,26 @@
# limitations under the License.
#
-require "uri"
-require "chef/resource/file"
-require "chef/provider/remote_file"
-require "chef/mixin/securable"
-require "chef/mixin/uris"
+require "uri" unless defined?(URI)
+require_relative "file"
+require_relative "../provider/remote_file"
+require_relative "../mixin/securable"
+require_relative "../mixin/uris"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class RemoteFile < Chef::Resource::File
include Chef::Mixin::Securable
+ unified_mode true
+
+ provides :remote_file
+
+ 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. Note: Fetching files from the `files/` directory in a cookbook should be done with the **cookbook_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
@@ -49,10 +50,10 @@ class Chef
def source(*args)
arg = parse_source_args(args)
ret = set_or_return(:source,
- arg,
- { :callbacks => {
- :validate_source => method(:validate_source),
- } })
+ arg,
+ { callbacks: {
+ validate_source: method(:validate_source),
+ } })
if ret.is_a? String
Array(ret)
else
@@ -72,13 +73,8 @@ class Chef
end
end
- def checksum(args = nil)
- set_or_return(
- :checksum,
- args,
- :kind_of => String
- )
- end
+ property :checksum, String,
+ description: "Optional, see `use_conditional_get`. The SHA-256 checksum of the file. Use to prevent a file from being re-downloaded. When the local file matches the checksum, #{ChefUtils::Dist::Infra::PRODUCT} does not download it."
# Disable or enable ETag and Last Modified conditional GET. Equivalent to
# use_etag(true_or_false)
@@ -88,47 +84,92 @@ 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,
+ description: "Enable ETag headers. Set to false to disable ETag headers. To use this setting, `use_conditional_get` must also be set to 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,
+ description: "Enable `If-Modified-Since` headers. Set to `false` to disable `If-Modified-Since` headers. To use this setting, `use_conditional_get` must also be set to `true`."
+
+ property :ftp_active_mode, [ TrueClass, FalseClass ], default: false,
+ description: "Whether #{ChefUtils::Dist::Infra::PRODUCT} uses active or passive FTP. Set to `true` to use active FTP."
+
+ property :headers, Hash, default: lazy { {} },
+ description: "A Hash of custom HTTP headers."
+
+ property :show_progress, [ TrueClass, FalseClass ], default: false
+
+ property :ssl_verify_mode, Symbol, equal_to: %i{verify_none verify_peer},
+ introduced: "16.2",
+ description: "Optional property to override SSL policy. If not specified, uses the SSL policy from `config.rb`."
+
+ property :remote_user, String,
+ introduced: "13.4",
+ description: '**Windows only** The name of a user with access to the remote file specified by the source property. The user name may optionally be specified with a domain, such as: `domain\user` or `user@my.dns.domain.com` via Universal Principal Name (UPN) format. The domain may also be set using the `remote_domain` property. Note that this property is ignored if source is not a UNC path. If this property is specified, the `remote_password` property is required.'
+
+ property :remote_domain, String,
+ introduced: "13.4",
+ description: "**Windows only** The domain of the user specified by the `remote_user` property. By default the resource will authenticate against the domain of the remote system, or as a local account if the remote system is not joined to a domain. If the remote system is not part of a domain, it is necessary to authenticate as a local user on the remote system by setting the domain to `.`, for example: remote_domain '.'. The domain may also be specified as part of the `remote_user` property."
+
+ property :remote_password, String, sensitive: true,
+ introduced: "13.4",
+ description: "**Windows only** The password of the user specified by the `remote_user` property. This property is required 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."
+
+ property :authentication, Symbol, equal_to: %i{remote local}, default: :remote
- def ftp_active_mode(args = nil)
- set_or_return(
- :ftp_active_mode,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
+ 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 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
@@ -138,6 +179,7 @@ class Chef
def validate_source(source)
source = Array(source).flatten
raise ArgumentError, "#{resource_name} has an empty source" if source.empty?
+
source.each do |src|
unless absolute_uri?(src)
raise Exceptions::InvalidRemoteFileURI,
@@ -148,7 +190,7 @@ class Chef
end
def absolute_uri?(source)
- Chef::Provider::RemoteFile::Fetcher.network_share?(source) || (source.kind_of?(String) && as_uri(source).absolute?)
+ Chef::Provider::RemoteFile::Fetcher.network_share?(source) || (source.is_a?(String) && as_uri(source).absolute?)
rescue URI::InvalidURIError
false
end
diff --git a/lib/chef/resource/resource_notification.rb b/lib/chef/resource/resource_notification.rb
index ee90064a17..dfd2546f43 100644
--- a/lib/chef/resource/resource_notification.rb
+++ b/lib/chef/resource/resource_notification.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +16,29 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../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
+ attr_accessor :resource, :action, :notifying_resource, :unified_mode
- def initialize(resource, action, notifying_resource)
+ def initialize(resource, action, notifying_resource, unified_mode = false)
@resource = resource
- @action = action
+ @action = action&.to_sym
@notifying_resource = notifying_resource
+ @unified_mode = unified_mode
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,21 +50,27 @@ 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.
- def resolve_resource_reference(resource_collection)
- return resource if resource.kind_of?(Chef::Resource) && notifying_resource.kind_of?(Chef::Resource)
+ # @param [ResourceCollection] resource_collection
+ #
+ # @return [void]
+ def resolve_resource_reference(resource_collection, always_raise = false)
+ return resource if resource.is_a?(Chef::Resource) && notifying_resource.is_a?(Chef::Resource)
- if not(resource.kind_of?(Chef::Resource))
- fix_resource_reference(resource_collection)
+ unless resource.is_a?(Chef::Resource)
+ fix_resource_reference(resource_collection, always_raise)
end
- if not(notifying_resource.kind_of?(Chef::Resource))
+ unless notifying_resource.is_a?(Chef::Resource)
fix_notifier_reference(resource_collection)
end
end
# 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.
- def fix_resource_reference(resource_collection)
+ # @param [ResourceCollection] resource_collection
+ #
+ # @return [void]
+ def fix_resource_reference(resource_collection, always_raise = false)
matching_resource = resource_collection.find(resource)
if Array(matching_resource).size > 1
msg = "Notification #{self} from #{notifying_resource} was created with a reference to multiple resources, "\
@@ -65,18 +80,21 @@ class Chef
self.resource = matching_resource
rescue Chef::Exceptions::ResourceNotFound => e
- err = Chef::Exceptions::ResourceNotFound.new(<<-FAIL)
-resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
-but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \
-#{notifying_resource.source_line}
- FAIL
- err.set_backtrace(e.backtrace)
- raise err
+ # in unified mode we allow lazy notifications to resources not yet declared
+ if !unified_mode || always_raise
+ err = Chef::Exceptions::ResourceNotFound.new(<<~FAIL)
+ resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
+ but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \
+ #{notifying_resource.source_line}
+ FAIL
+ err.set_backtrace(e.backtrace)
+ raise err
+ end
rescue Chef::Exceptions::InvalidResourceSpecification => e
- err = Chef::Exceptions::InvalidResourceSpecification.new(<<-F)
-Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
-but #{resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \
-is defined near #{notifying_resource.source_line}
+ err = Chef::Exceptions::InvalidResourceSpecification.new(<<~F)
+ Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
+ but #{resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \
+ is defined near #{notifying_resource.source_line}
F
err.set_backtrace(e.backtrace)
raise err
@@ -84,6 +102,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
@@ -95,18 +116,18 @@ is defined near #{notifying_resource.source_line}
self.notifying_resource = matching_notifier
rescue Chef::Exceptions::ResourceNotFound => e
- err = Chef::Exceptions::ResourceNotFound.new(<<-FAIL)
-Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \
-but #{notifying_resource} cannot be found in the resource collection. #{resource} is defined in \
-#{resource.source_line}
+ err = Chef::Exceptions::ResourceNotFound.new(<<~FAIL)
+ Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \
+ but #{notifying_resource} cannot be found in the resource collection. #{resource} is defined in \
+ #{resource.source_line}
FAIL
err.set_backtrace(e.backtrace)
raise err
rescue Chef::Exceptions::InvalidResourceSpecification => e
- err = Chef::Exceptions::InvalidResourceSpecification.new(<<-F)
-Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \
-but #{notifying_resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \
-is defined near #{resource.source_line}
+ err = Chef::Exceptions::InvalidResourceSpecification.new(<<~F)
+ Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \
+ but #{notifying_resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \
+ is defined near #{resource.source_line}
F
err.set_backtrace(e.backtrace)
raise err
@@ -114,6 +135,7 @@ is defined near #{resource.source_line}
def ==(other)
return false unless other.is_a?(self.class)
+
other.resource == resource && other.action == action && other.notifying_resource == notifying_resource
end
diff --git a/lib/chef/resource/rhsm_errata.rb b/lib/chef/resource/rhsm_errata.rb
new file mode 100644
index 0000000000..7a1e3df325
--- /dev/null
+++ b/lib/chef/resource/rhsm_errata.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class RhsmErrata < Chef::Resource
+ unified_mode true
+ 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 it differs from the resource block'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 "#{package_manager_command} update --advisory #{new_resource.errata_id} -y"
+ default_env true
+ action :run
+ end
+ end
+
+ action_class do
+ def package_manager_command
+ node["platform_version"].to_i >= 8 ? "dnf" : "yum"
+ 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..9ac3944153
--- /dev/null
+++ b/lib/chef/resource/rhsm_errata_level.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class RhsmErrataLevel < Chef::Resource
+ unified_mode true
+ 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 Subscription 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: "An optional property for specifying the errata level of packages to install if it differs from the resource block's name.",
+ name_property: true
+
+ action :install do
+ description "Install all packages of the specified errata level."
+
+ if rhel6?
+ yum_package "yum-plugin-security"
+ end
+
+ execute "Install any #{new_resource.errata_level} errata" do
+ command "#{package_manager_command} update --sec-severity=#{new_resource.errata_level.capitalize} -y"
+ default_env true
+ action :run
+ end
+ end
+
+ action_class do
+ def package_manager_command
+ node["platform_version"].to_i >= 8 ? "dnf" : "yum"
+ 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..07c4dbc8d7
--- /dev/null
+++ b/lib/chef/resource/rhsm_register.rb
@@ -0,0 +1,195 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require "shellwords" unless defined?(Shellwords)
+
+class Chef
+ class Resource
+ class RhsmRegister < Chef::Resource
+ unified_mode true
+ 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 activation keys to use when registering; you must also specify the 'organization' property when using this property."
+
+ property :satellite_host, String,
+ description: "The FQDN of the Satellite host to register with. If this property is not specified, the host will register with Red Hat's public RHSM service."
+
+ property :organization, String,
+ description: "The organization to use when registering; required when using the 'activation_key' property."
+
+ property :environment, String,
+ description: "The environment to use when registering; required when using the username and password properties."
+
+ property :username, String,
+ description: "The username to use when registering. This property is not applicable if using an activation key. If specified, password and environment properties are also required."
+
+ property :password, String,
+ description: "The password to use when registering. This property is not applicable if using an activation key. If specified, username and environment are also required."
+
+ property :system_name, String,
+ description: "The name of the system to register, defaults to the hostname.",
+ introduced: "16.5"
+
+ 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 has already been registered.",
+ default: false, desired_state: false
+
+ property :https_for_ca_consumer, [TrueClass, FalseClass],
+ description: "If true, #{ChefUtils::Dist::Infra::PRODUCT} will fetch the katello-ca-consumer-latest.noarch.rpm from the satellite_host using HTTPS.",
+ default: false, desired_state: false,
+ introduced: "15.9"
+
+ action :register do
+ description "Register the node with RHSM."
+
+ package "subscription-manager"
+
+ unless new_resource.satellite_host.nil? || registered_with_rhsm?
+ declare_resource(package_resource, "katello-ca-consumer-latest") do
+ options "--nogpgcheck"
+ source "#{Chef::Config[:file_cache_path]}/katello-package.rpm"
+ action :nothing
+ end
+
+ remote_file "#{Chef::Config[:file_cache_path]}/katello-package.rpm" do
+ source ca_consumer_package_source
+ action :create
+ notifies :install, "#{package_resource}[katello-ca-consumer-latest]", :immediately
+ not_if { katello_cert_rpm_installed? }
+ 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
+ default_env true
+ action :run
+ not_if { registered_with_rhsm? } unless new_resource.force
+ end
+
+ if new_resource.install_katello_agent && !new_resource.satellite_host.nil?
+ package "katello-agent"
+ end
+ end
+
+ action :unregister do
+ description "Unregister the node from RHSM."
+
+ execute "Unregister from RHSM" do
+ command "subscription-manager unregister"
+ default_env true
+ action :run
+ only_if { registered_with_rhsm? }
+ notifies :run, "execute[Clean RHSM Config]", :immediately
+ end
+
+ execute "Clean RHSM Config" do
+ command "subscription-manager clean"
+ default_env true
+ action :nothing
+ end
+ end
+
+ action_class do
+ #
+ # @return [Symbol] dnf_package or yum_package depending on OS release
+ #
+ def package_resource
+ node["platform_version"].to_i >= 8 ? :dnf_package : :yum_package
+ end
+
+ #
+ # @return [Boolean] is the node registered with RHSM
+ #
+ def registered_with_rhsm?
+ @registered ||= !shell_out("subscription-manager status").stdout.include?("Overall Status: Unknown")
+ end
+
+ #
+ # @return [Boolean] is katello-ca-consumer installed
+ #
+ def katello_cert_rpm_installed?
+ shell_out("rpm -qa").stdout.include?("katello-ca-consumer")
+ end
+
+ #
+ # @return [String] The URI to fetch katello-ca-consumer-latest.noarch.rpm from
+ #
+ def ca_consumer_package_source
+ protocol = new_resource.https_for_ca_consumer ? "https" : "http"
+ "#{protocol}://#{new_resource.satellite_host}/pub/katello-ca-consumer-latest.noarch.rpm"
+ 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 << "--name=#{Shellwords.shellescape(new_resource.system_name)}" if new_resource.system_name
+ 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 << "--name=#{Shellwords.shellescape(new_resource.system_name)}" if new_resource.system_name
+ 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..d8959695cf
--- /dev/null
+++ b/lib/chef/resource/rhsm_repo.rb
@@ -0,0 +1,66 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class RhsmRepo < Chef::Resource
+ unified_mode true
+
+ 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 it differs from the resource block'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}"
+ default_env true
+ 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}"
+ default_env true
+ action :run
+ only_if { repo_enabled?(new_resource.repo_name) }
+ end
+ end
+
+ action_class do
+ def repo_enabled?(repo)
+ # FIXME: use shell_out()
+ cmd = Mixlib::ShellOut.new("subscription-manager repos --list-enabled", env: { LANG: "en_US" })
+ cmd.run_command
+ repo == "*" || !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..15a4822ecd
--- /dev/null
+++ b/lib/chef/resource/rhsm_subscription.rb
@@ -0,0 +1,99 @@
+#
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class RhsmSubscription < Chef::Resource
+ unified_mode true
+
+ 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 it differs from the resource block'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}"
+ default_env true
+ 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)}"
+ default_env true
+ action :run
+ only_if { subscription_attached?(new_resource.pool_id) }
+ end
+ end
+
+ action_class do
+ def subscription_attached?(subscription)
+ # FIXME: use shell_out
+ 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
+
+ # FIXME: use shell_out
+ 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..82f961679b 100644
--- a/lib/chef/resource/route.rb
+++ b/lib/chef/resource/route.rb
@@ -17,121 +17,44 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class Route < Chef::Resource
- identity_attr :target
+ unified_mode true
- state_attrs :netmask, :gateway
+ provides :route
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
+ description "Use the **route** resource to manage the system routing table in a Linux environment."
- def networking(arg = nil)
- set_or_return(
- :networking,
- arg,
- :kind_of => String
- )
- end
+ property :target, String,
+ description: "The IP address of the target route.",
+ name_property: true
- def networking_ipv6(arg = nil)
- set_or_return(
- :networking_ipv6,
- arg,
- :kind_of => String
- )
- end
+ property :comment, [String, nil],
+ description: "Add a comment for the route.",
+ introduced: "14.0"
- def hostname(arg = nil)
- set_or_return(
- :hostname,
- arg,
- :kind_of => String
- )
- end
+ property :metric, [Integer, nil],
+ description: "The route metric value."
- def domainname(arg = nil)
- set_or_return(
- :domainname,
- arg,
- :kind_of => String
- )
- end
+ property :netmask, [String, nil],
+ description: "The decimal representation of the network mask. For example: `255.255.255.0`."
- def domain(arg = nil)
- set_or_return(
- :domain,
- arg,
- :kind_of => String
- )
- end
+ property :gateway, [String, nil],
+ description: "The gateway for the route."
- def target(arg = nil)
- set_or_return(
- :target,
- arg,
- :kind_of => String
- )
- end
+ property :device, [String, nil],
+ description: "The network interface to which the route applies.",
+ desired_state: false # Has a partial default in the provider of eth0.
- 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
+ property :route_type, [Symbol, String],
+ description: "",
+ equal_to: %i{host net}, default: :host, desired_state: false
end
end
end
diff --git a/lib/chef/resource/rpm_package.rb b/lib/chef/resource/rpm_package.rb
index c93dfecaf5..3f65e32cef 100644
--- a/lib/chef/resource/rpm_package.rb
+++ b/lib/chef/resource/rpm_package.rb
@@ -16,17 +16,28 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/rpm"
+require_relative "package"
class Chef
class Resource
class RpmPackage < Chef::Resource::Package
- resource_name :rpm_package
- provides :rpm_package, os: %w{linux aix}
+ unified_mode true
- property :allow_downgrade, [ true, false ], default: false, desired_state: false
+ provides :rpm_package
+ description "Use the **rpm_package** resource to manage packages using the RPM Package Manager."
+
+ property :allow_downgrade, [ TrueClass, FalseClass ],
+ description: "Allow downgrading a package to satisfy requested version requirements.",
+ default: true,
+ desired_state: false
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
end
end
end
diff --git a/lib/chef/resource/ruby.rb b/lib/chef/resource/ruby.rb
index 91805a1db6..2c0e65e9da 100644
--- a/lib/chef/resource/ruby.rb
+++ b/lib/chef/resource/ruby.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,17 @@
# limitations under the License.
#
-require "chef/resource/script"
-require "chef/provider/script"
+require_relative "script"
class Chef
class Resource
class Ruby < Chef::Resource::Script
+ unified_mode true
+
+ provides :ruby
+
+ 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..2d7d2fe8b6 100644
--- a/lib/chef/resource/ruby_block.rb
+++ b/lib/chef/resource/ruby_block.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,21 +17,21 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/provider/ruby_block"
+require_relative "../resource"
+require_relative "../provider/ruby_block"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class RubyBlock < Chef::Resource
- default_action :run
- allowed_actions :create, :run
+ unified_mode true
- identity_attr :block_name
+ provides :ruby_block, target_mode: true
- def initialize(name, run_context = nil)
- super
- @block_name = name
- end
+ description "Use the **ruby_block** resource to execute Ruby code during a #{ChefUtils::Dist::Infra::PRODUCT} 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
def block(&block)
if block_given? && block
@@ -41,13 +41,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
end
end
end
diff --git a/lib/chef/resource/scm.rb b/lib/chef/resource/scm.rb
deleted file mode 100644
index 1e8c71e59d..0000000000
--- a/lib/chef/resource/scm.rb
+++ /dev/null
@@ -1,185 +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/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
-
- # 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
-
- alias :env :environment
- end
- end
-end
diff --git a/lib/chef/resource/scm/_scm.rb b/lib/chef/resource/scm/_scm.rb
new file mode 100644
index 0000000000..573d8b9b94
--- /dev/null
+++ b/lib/chef/resource/scm/_scm.rb
@@ -0,0 +1,50 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 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.
+#
+
+unified_mode true
+
+default_action :sync
+allowed_actions :checkout, :export, :sync, :diff, :log
+
+property :destination, String,
+ description: "The location path to which the source is to be cloned, checked out, or exported. Default value: the name of the resource block.",
+ name_property: true
+
+property :repository, String,
+ description: "The URI of the code repository."
+
+property :revision, String,
+ description: "The revision to checkout.",
+ default: "HEAD"
+
+property :user, [String, Integer],
+ description: "The system user that will own the checked-out code.",
+ default_description: "`HOME` environment variable of the user running #{ChefUtils::Dist::Infra::CLIENT}"
+
+property :group, [String, Integer],
+ description: "The system group that will own the checked-out code."
+
+property :timeout, Integer,
+ description: "The amount of time (in seconds) to wait before timing out.",
+ desired_state: false
+
+property :environment, [Hash, nil],
+ description: "A Hash of environment variables in the form of ({'ENV_VARIABLE' => 'VALUE'}).",
+ default: nil
+
+alias :env :environment
diff --git a/lib/chef/resource/scm/git.rb b/lib/chef/resource/scm/git.rb
new file mode 100644
index 0000000000..8293d1ed4a
--- /dev/null
+++ b/lib/chef/resource/scm/git.rb
@@ -0,0 +1,144 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 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 "../../resource"
+
+class Chef
+ class Resource
+ class Git < Chef::Resource
+ use "scm"
+
+ unified_mode true
+
+ provides :git
+
+ 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."
+ examples <<~DOC
+ **Use the git mirror**
+
+ ```ruby
+ git '/opt/my_sources/couch' do
+ repository 'git://git.apache.org/couchdb.git'
+ revision 'master'
+ action :sync
+ end
+ ```
+
+ **Use different branches**
+
+ To use different branches, depending on the environment of the node:
+
+ ```ruby
+ branch_name = if node.chef_environment == 'QA'
+ 'staging'
+ else
+ 'master'
+ end
+
+ git '/home/user/deployment' do
+ repository 'git@github.com:git_site/deployment.git'
+ revision branch_name
+ action :sync
+ user 'user'
+ group 'test'
+ end
+ ```
+
+ Where the `branch_name` variable is set to staging or master, depending on the environment of the node. Once this is determined, the `branch_name` variable is used to set the revision for the repository. If the git status command is used after running the example above, it will return the branch name as `deploy`, as this is the default value. Run Chef Infra Client in debug mode to verify that the correct branches are being checked out:
+
+ ```
+ sudo chef-client -l debug
+ ```
+
+ **Install an application from git using bash**
+
+ The following example shows how Bash can be used to install a plug-in for rbenv named ruby-build, which is located in git version source control. First, the application is synchronized, and then Bash changes its working directory to the location in which ruby-build is located, and then runs a command.
+
+ ```ruby
+ git "#{Chef::Config[:file_cache_path]}/ruby-build" do
+ repository 'git://github.com/rbenv/ruby-build.git'
+ revision 'master'
+ action :sync
+ end
+
+ bash 'install_ruby_build' do
+ cwd "#{Chef::Config[:file_cache_path]}/ruby-build"
+ user 'rbenv'
+ group 'rbenv'
+ code <<-EOH
+ ./install.sh
+ EOH
+ environment 'PREFIX' => '/usr/local'
+ end
+ ```
+
+ **Notify a resource post-checkout**
+
+ ```ruby
+ git "#{Chef::Config[:file_cache_path]}/my_app" do
+ repository node['my_app']['git_repository']
+ revision node['my_app']['git_revision']
+ action :sync
+ notifies :run, 'bash[compile_my_app]', :immediately
+ end
+ ```
+
+ **Pass in environment variables**
+
+ ```ruby
+ git '/opt/my_sources/couch' do
+ repository 'git://git.apache.org/couchdb.git'
+ revision 'master'
+ environment 'VAR' => 'whatever'
+ action :sync
+ end
+ ```
+ DOC
+
+ property :additional_remotes, Hash,
+ description: "A Hash of additional remotes that are added to the git repository configuration.",
+ default: lazy { {} }
+
+ property :depth, Integer,
+ description: "The number of past revisions to be included in the git shallow clone. Unless specified the default behavior will do a full clone."
+
+ property :enable_submodules, [TrueClass, FalseClass],
+ description: "Perform a sub-module initialization and update.",
+ default: false
+
+ property :enable_checkout, [TrueClass, FalseClass],
+ description: "Check out a repo from master. Set to `false` when using the `checkout_branch` attribute to prevent the git resource from attempting to check out `master` from `master`.",
+ default: true
+
+ property :remote, String,
+ description: "The remote repository to use when synchronizing an existing clone.",
+ default: "origin"
+
+ property :ssh_wrapper, String,
+ desired_state: false,
+ description: "The path to the wrapper script used when running SSH with git. The `GIT_SSH` environment variable is set to this."
+
+ property :checkout_branch, String,
+ description: "Set this to use a local branch to avoid checking SHAs or tags to a detached head state."
+
+ alias :branch :revision
+ alias :reference :revision
+ alias :repo :repository
+ end
+ end
+end
diff --git a/lib/chef/resource/scm/subversion.rb b/lib/chef/resource/scm/subversion.rb
new file mode 100644
index 0000000000..db20787aa5
--- /dev/null
+++ b/lib/chef/resource/scm/subversion.rb
@@ -0,0 +1,73 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 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-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class Subversion < Chef::Resource
+ use "scm"
+
+ unified_mode true
+
+ provides :subversion
+
+ description "Use the **subversion** resource to manage source control resources that exist in a Subversion repository."
+ examples <<~DOC
+ **Get the latest version of an application**
+
+ ```ruby
+ subversion 'CouchDB Edge' do
+ repository 'http://svn.apache.org/repos/asf/couchdb/trunk'
+ revision 'HEAD'
+ destination '/opt/my_sources/couch'
+ action :sync
+ end
+ ```
+ DOC
+
+ allowed_actions :force_export
+
+ 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 #{ChefUtils::Dist::Infra::PRODUCT} 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."
+
+ property :svn_username, String,
+ description: "The user name for a user that has access to the Subversion repository."
+
+ property :svn_password, String,
+ description: "The password for a user that has access to the Subversion repository.",
+ sensitive: true, desired_state: false
+
+ # 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
+ end
+ end
+end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 5173a76542..54468a534d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,58 +17,43 @@
# limitations under the License.
#
-require "chef/resource/execute"
-require "chef/provider/script"
+require_relative "execute"
class Chef
class Resource
class Script < Chef::Resource::Execute
- # Chef-13: go back to using :name as the identity attr
- identity_attr :command
+ unified_mode true
+
+ provides :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)
+ super
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 property on a #{resource_name} resource, use the 'code' property 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
+ property :code, String, required: true,
+ description: "A quoted string of code to be executed."
- def flags(arg = nil)
- set_or_return(
- :flags,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :interpreter, String
+ property :flags, String,
+ description: "One or more command line flags that are passed to the interpreter when a command is invoked."
end
end
end
diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb
index 1ca4b84af0..63674a3c93 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,140 +17,89 @@
# limitations under the License.
#
-require "chef/resource"
+require "chef-utils/dsl/service" unless defined?(ChefUtils::DSL::Service)
+require_relative "../resource"
+require "shellwords" unless defined?(Shellwords)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class Service < Chef::Resource
- identity_attr :service_name
+ include Chef::Platform::ServiceHelpers
+ extend Chef::Platform::ServiceHelpers
+ unified_mode true
- state_attrs :enabled, :running, :masked
+ provides :service, target_mode: true
+
+ description "Use the **service** resource to manage a service."
default_action :nothing
allowed_actions :enable, :disable, :start, :stop, :restart, :reload,
- :mask, :unmask
-
- def initialize(name, run_context = nil)
- super
- @service_name = name
- @enabled = nil
- @running = nil
- @masked = nil
- @parameters = nil
- @pattern = service_name
- @start_command = nil
- @stop_command = nil
- @status_command = nil
- @restart_command = nil
- @reload_command = nil
- @init_command = nil
- @priority = nil
- @timeout = nil
- @run_levels = nil
- @user = nil
- @supports = { :restart => nil, :reload => nil, :status => nil }
- end
-
- def service_name(arg = nil)
- set_or_return(
- :service_name,
- arg,
- :kind_of => [ String ]
- )
- end
+ :mask, :unmask
+
+ # this is a poor API please do not re-use this pattern
+ property :supports, Hash, default: { restart: nil, reload: nil, status: nil },
+ description: "A list of properties that controls how #{ChefUtils::Dist::Infra::PRODUCT} is to attempt to manage a service: :restart, :reload, :status. For :restart, the init script or other service provider can use a restart command; if :restart is not specified, the #{ChefUtils::Dist::Infra::CLIENT} attempts to stop and then start a service. For :reload, the init script or other service provider can use a reload command. For :status, the init script or other service provider can use a status command to determine if the service is running; if :status is not specified, the #{ChefUtils::Dist::Infra::CLIENT} attempts to match the service_name against the process table as a regular expression, unless a pattern is specified as a parameter property. Default value: { restart: false, reload: false, status: false } for all platforms (except for the Red Hat platform family, which defaults to { restart: false, reload: false, status: true }.)",
+ coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x }
+
+ property :service_name, String,
+ description: "An optional property to set the service name if it differs from the resource block's name.",
+ name_property: true
# regex for match against ps -ef when !supports[:has_status] && status == nil
- def pattern(arg = nil)
- set_or_return(
- :pattern,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :pattern, String,
+ description: "The pattern to look for in the process table.",
+ default_description: "The value provided to 'service_name' or the resource block's name",
+ default: lazy { service_name }, desired_state: false
# command to call to start service
- def start_command(arg = nil)
- set_or_return(
- :start_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :start_command, [ String, nil, FalseClass ],
+ description: "The command used to start a service.",
+ desired_state: false
# command to call to stop service
- def stop_command(arg = nil)
- set_or_return(
- :stop_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :stop_command, [ String, nil, FalseClass ],
+ description: "The command used to stop a service.",
+ desired_state: false
# command to call to get status of service
- def status_command(arg = nil)
- set_or_return(
- :status_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :status_command, [ String, nil, FalseClass ],
+ description: "The command used to check the run status for a service.",
+ desired_state: false
# command to call to restart service
- def restart_command(arg = nil)
- set_or_return(
- :restart_command,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def reload_command(arg = nil)
- set_or_return(
- :reload_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ property :restart_command, [ String, nil, FalseClass ],
+ description: "The command used to restart a service.",
+ desired_state: false
+
+ property :reload_command, [ String, nil, FalseClass ],
+ description: "The command used to tell a service to reload its configuration.",
+ desired_state: false
# The path to the init script associated with the service. On many
# distributions this is '/etc/init.d/SERVICE_NAME' by default. In
# non-standard configurations setting this value will save having to
# specify overrides for the start_command, stop_command and
- # restart_command attributes.
- def init_command(arg = nil)
- set_or_return(
- :init_command,
- arg,
- :kind_of => [ String ]
- )
- end
+ # restart_command properties.
+ property :init_command, String,
+ description: "The path to the init script that is associated with the service. Use init_command to prevent the need to specify overrides for the start_command, stop_command, and restart_command properties. When this property is not specified, the #{ChefUtils::Dist::Infra::PRODUCT} will use the default init command for the service provider being used.",
+ desired_state: false
# if the service is enabled or not
- def enabled(arg = nil)
- set_or_return(
- :enabled,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :enabled, [ TrueClass, FalseClass ], skip_docs: true
# if the service is running or not
- def running(arg = nil)
- set_or_return(
- :running,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :running, [ TrueClass, FalseClass ], skip_docs: true
# if the service is masked or not
- def masked(arg = nil)
- set_or_return(
- :masked,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :masked, [ TrueClass, FalseClass ], skip_docs: true
+
+ # if the service is indirect or not
+ property :indirect, [ TrueClass, FalseClass ], skip_docs: true
+
+ property :options, [ Array, String ],
+ description: "Solaris platform only. Options to pass to the service command. See the svcadm manual for details of possible options.",
+ coerce: proc { |x| x.respond_to?(:split) ? x.shellsplit : x }
# Priority arguments can have two forms:
#
@@ -162,56 +111,23 @@ class Chef
# runlevel 2, stopped in 3 with priority 55 and no symlinks or
# similar for other runlevels
#
- def priority(arg = nil)
- set_or_return(
- :priority,
- arg,
- :kind_of => [ Integer, String, Hash ]
- )
- end
-
- # timeout only applies to the windows service manager
- def timeout(arg = nil)
- set_or_return(
- :timeout,
- arg,
- :kind_of => Integer
- )
- end
-
- def parameters(arg = nil)
- set_or_return(
- :parameters,
- arg,
- :kind_of => [ Hash ]
- )
- end
-
- def run_levels(arg = nil)
- set_or_return(
- :run_levels,
- arg,
- :kind_of => [ Array ] )
- end
-
- def user(arg = nil)
- set_or_return(
- :user,
- arg,
- :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
+ property :priority, [ Integer, String, Hash ],
+ description: "Debian platform only. The relative priority of the program for start and shutdown ordering. May be an integer or a Hash. An integer is used to define the start run levels; stop run levels are then 100-integer. A Hash is used to define values for specific run levels. For example, { 2 => [:start, 20], 3 => [:stop, 55] } will set a priority of twenty for run level two and a priority of fifty-five for run level three."
+
+ property :timeout, Integer,
+ description: "The amount of time (in seconds) to wait before timing out.",
+ default: 900,
+ desired_state: false
+
+ property :parameters, Hash,
+ description: "Upstart only: A hash of parameters to pass to the service command for use in the service definition."
+
+ property :run_levels, Array,
+ description: "RHEL platforms only: Specific run_levels the service will run under."
+ property :user, String,
+ description: "systemd only: A username to run the service under.",
+ introduced: "12.21"
end
end
end
diff --git a/lib/chef/resource/smartos_package.rb b/lib/chef/resource/smartos_package.rb
index 87173ccfa9..08b2f0d82f 100644
--- a/lib/chef/resource/smartos_package.rb
+++ b/lib/chef/resource/smartos_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,24 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/smartos"
+require_relative "package"
class Chef
class Resource
class SmartosPackage < Chef::Resource::Package
- resource_name :smartos_package
- provides :package, os: "solaris2", platform_family: "smartos"
+ unified_mode true
+
+ provides :smartos_package
+ provides :package, platform_family: "smartos"
+
+ description "Use the **smartos_package** resource to manage packages for the SmartOS platform."
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
end
end
end
diff --git a/lib/chef/resource/snap_package.rb b/lib/chef/resource/snap_package.rb
new file mode 100644
index 0000000000..c211b03555
--- /dev/null
+++ b/lib/chef/resource/snap_package.rb
@@ -0,0 +1,38 @@
+#
+# Author:: S.Cavallo (<smcavallo@hotmail.com>)
+# Copyright:: Copyright (c) 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 "package"
+
+class Chef
+ class Resource
+ class SnapPackage < Chef::Resource::Package
+ unified_mode true
+
+ provides :snap_package
+
+ description "Use the **snap_package** resource to manage snap packages on Debian and Ubuntu platforms."
+ introduced "15.0"
+
+ property :channel, String,
+ description: "The default channel. For example: stable.",
+ default: "stable",
+ equal_to: %w{edge beta candidate stable},
+ desired_state: false
+ end
+ end
+end
diff --git a/lib/chef/resource/solaris_package.rb b/lib/chef/resource/solaris_package.rb
index d0f8c144af..381c62a45b 100644
--- a/lib/chef/resource/solaris_package.rb
+++ b/lib/chef/resource/solaris_package.rb
@@ -1,7 +1,7 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,15 +17,23 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/solaris"
+require_relative "package"
class Chef
class Resource
class SolarisPackage < Chef::Resource::Package
- resource_name :solaris_package
- provides :package, os: "solaris2", platform_family: "nexentacore"
- provides :package, os: "solaris2", platform_family: "solaris2", platform_version: "<= 5.10"
+ unified_mode true
+
+ provides :solaris_package
+
+ description "Use the **solaris_package** resource to manage packages on the Solaris platform."
+
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ property :version, String,
+ description: "The version of a package to be installed or upgraded."
end
end
end
diff --git a/lib/chef/resource/ssh_known_hosts_entry.rb b/lib/chef/resource/ssh_known_hosts_entry.rb
new file mode 100644
index 0000000000..1db811978c
--- /dev/null
+++ b/lib/chef/resource/ssh_known_hosts_entry.rb
@@ -0,0 +1,164 @@
+#
+# Author:: Seth Vargo (<sethvargo@gmail.com>)
+#
+# Copyright:: 2013-2018, Seth Vargo
+# Copyright:: Copyright (c) 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 "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class SshKnownHostsEntry < Chef::Resource
+ unified_mode true
+
+ provides :ssh_known_hosts_entry
+
+ description "Use the **ssh_known_hosts_entry** resource to add an entry for the specified host in /etc/ssh/ssh_known_hosts or a user's known hosts file if specified."
+ introduced "14.3"
+ examples <<~DOC
+ **Add a single entry for github.com with the key auto detected**
+
+ ```ruby
+ ssh_known_hosts_entry 'github.com'
+ ```
+
+ **Add a single entry with your own provided key**
+
+ ```ruby
+ ssh_known_hosts_entry 'github.com' do
+ key 'node.example.com ssh-rsa ...'
+ end
+ ```
+ DOC
+
+ property :host, String,
+ description: "The host to add to the known hosts file.",
+ name_property: true
+
+ property :key, String,
+ description: "An optional key for the host. If not provided this will be automatically determined."
+
+ property :key_type, String,
+ description: "The type of key to store.",
+ default: "rsa"
+
+ property :port, Integer,
+ description: "The server port that the ssh-keyscan command will use to gather the public key.",
+ default: 22
+
+ property :timeout, Integer,
+ description: "The timeout in seconds for ssh-keyscan.",
+ default: 30,
+ desired_state: false
+
+ property :mode, String,
+ description: "The file mode for the ssh_known_hosts file.",
+ default: "0644"
+
+ property :owner, [String, Integer],
+ description: "The file owner for the ssh_known_hosts file.",
+ default: "root"
+
+ property :group, [String, Integer],
+ description: "The file group for the ssh_known_hosts file.",
+ default: lazy { node["root_group"] }
+
+ property :hash_entries, [TrueClass, FalseClass],
+ description: "Hash the hostname and addresses in the ssh_known_hosts file for privacy.",
+ default: false
+
+ property :file_location, String,
+ description: "The location of the ssh known hosts file. Change this to set a known host file for a particular user.",
+ default: "/etc/ssh/ssh_known_hosts"
+
+ action :create do
+ description "Create an entry in the ssh_known_hosts file."
+
+ key =
+ if new_resource.key
+ hoststr = (new_resource.port != 22) ? "[#{new_resource.host}]:#{new_resource.port}" : new_resource.host
+ "#{hoststr} #{type_string(new_resource.key_type)} #{new_resource.key}"
+ else
+ keyscan_cmd = ["ssh-keyscan", "-t#{new_resource.key_type}", "-p #{new_resource.port}"]
+ keyscan_cmd << "-H" if new_resource.hash_entries
+ keyscan_cmd << new_resource.host
+ keyscan = shell_out!(keyscan_cmd.join(" "), timeout: new_resource.timeout)
+ keyscan.stdout
+ end
+
+ key.sub!(/^#{new_resource.host}/, "[#{new_resource.host}]:#{new_resource.port}") if new_resource.port != 22
+
+ comment = key.split("\n").first || ""
+
+ r = with_run_context :root do
+ find_resource(:template, "update ssh known hosts file #{new_resource.file_location}") do
+ source ::File.expand_path("support/ssh_known_hosts.erb", __dir__)
+ local true
+ path new_resource.file_location
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+ action :nothing
+ delayed_action :create
+ backup false
+ variables(entries: [])
+ end
+ end
+
+ keys = r.variables[:entries].reject(&:empty?)
+
+ if key_exists?(keys, key, comment)
+ Chef::Log.debug "Known hosts key for #{new_resource.host} already exists - skipping"
+ else
+ r.variables[:entries].push(key)
+ end
+ end
+
+ # all this does is send an immediate run_action(:create) to the template resource
+ action :flush do
+ description "Immediately flush the entries to the config file. Without this the actual writing of the file is delayed in the #{ChefUtils::Dist::Infra::PRODUCT} run so all entries can be accumulated before writing the file out."
+
+ with_run_context :root do
+ # if you haven't ever called ssh_known_hosts_entry before you're definitely doing it wrong so we blow up hard.
+ find_resource!(:template, "update ssh known hosts file #{new_resource.file_location}").run_action(:create)
+ # it is the user's responsibility to only call this *after* all the ssh_known_hosts_entry resources have been called.
+ # if you call this too early in your run_list you will get a partial known_host file written to disk, and the resource
+ # behavior will not be idempotent (template resources will flap and never show 0 resources updated on converged boxes).
+ Chef::Log.warn "flushed ssh_known_hosts entries to file, later ssh_known_hosts_entry resources will not have been written"
+ end
+ end
+
+ action_class do
+ def key_exists?(keys, key, comment)
+ keys.any? do |line|
+ line.match(/#{Regexp.escape(comment)}|#{Regexp.escape(key)}/)
+ end
+ end
+
+ def type_string(key_type)
+ type_map = {
+ "rsa" => "ssh-rsa",
+ "dsa" => "ssh-dss",
+ "ecdsa" => "ecdsa-sha2-nistp256",
+ "ed25519" => "ssh-ed25519",
+ }
+ type_map[key_type] || key_type
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb
deleted file mode 100644
index 9966614eeb..0000000000
--- a/lib/chef/resource/subversion.rb
+++ /dev/null
@@ -1,44 +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.
-#
-
-require "chef/resource/scm"
-
-class Chef
- class Resource
- class Subversion < Chef::Resource::Scm
- 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
-
- # 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..d6587bd441
--- /dev/null
+++ b/lib/chef/resource/sudo.rb
@@ -0,0 +1,267 @@
+#
+# 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:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class Sudo < Chef::Resource
+ unified_mode true
+
+ 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. Chef-supported releases of Ubuntu, SuSE, Debian,"\
+ " and RHEL (6+) all support this feature."
+ introduced "14.0"
+ examples <<~DOC
+ **Grant a user sudo privileges for any command**
+
+ ```ruby
+ sudo 'admin' do
+ user 'admin'
+ end
+ ```
+
+ **Grant a user and groups sudo privileges for any command**
+
+ ```ruby
+ sudo 'admins' do
+ users 'bob'
+ groups 'sysadmins, superusers'
+ end
+ ```
+
+ **Grant passwordless sudo privileges for specific commands**
+
+ ```ruby
+ sudo 'passwordless-access' do
+ commands ['/bin/systemctl restart httpd', '/bin/systemctl restart mysql']
+ nopasswd true
+ end
+ ```
+ DOC
+
+ # According 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 if it differs from the name of the resource block",
+ name_property: true,
+ coerce: proc { |x| x.gsub(/[\.~]/, "__") }
+
+ property :users, [String, Array],
+ description: "User(s) to provide sudo privileges to. This property accepts either an array or a comma separated list.",
+ 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 property 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 full paths to commands this sudoer can execute.",
+ default: ["ALL"]
+
+ property :host, String,
+ description: "The host to set in the sudo configuration.",
+ default: "ALL"
+
+ property :runas, String,
+ description: "User that the command(s) can be run as.",
+ default: "ALL"
+
+ property :nopasswd, [TrueClass, FalseClass],
+ description: "Allow sudo to be run without specifying a password.",
+ 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. This property is 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 configuration.",
+ default: lazy { [] }
+
+ property :setenv, [TrueClass, FalseClass],
+ description: "Determines whether or not to permit preservation of the 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,
+ deprecated: true
+
+ property :visudo_binary, String,
+ description: "The path to visudo for configuration verification.",
+ default: "/usr/sbin/visudo"
+
+ property :config_prefix, String,
+ description: "The directory that contains the sudoers configuration file.",
+ default: lazy { platform_config_prefix }, default_description: "Prefix values based on the node's platform"
+
+ # 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 absolute 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
+ package "sudo" do
+ not_if "which sudo"
+ end
+ end
+
+ target = "#{new_resource.config_prefix}/sudoers.d/"
+ directory(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?
+ file_path = "#{target}#{new_resource.filename}"
+
+ if new_resource.template
+ logger.trace("Template property provided, all other properties ignored.")
+
+ template file_path do
+ source new_resource.template
+ mode "0440"
+ variables new_resource.variables
+ verify visudo_content(file_path) if visudo_present?
+ action :create
+ end
+ else
+ template file_path do
+ source ::File.expand_path("support/sudoer.erb", __dir__)
+ 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 visudo_content(file_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
+
+ def visudo_content(path)
+ if ::File.exist?(path)
+ "cat #{new_resource.config_prefix}/sudoers | #{new_resource.visudo_binary} -cf - && #{new_resource.visudo_binary} -cf %{path}"
+ else
+ "cat #{new_resource.config_prefix}/sudoers %{path} | #{new_resource.visudo_binary} -cf -"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/support/client.erb b/lib/chef/resource/support/client.erb
new file mode 100644
index 0000000000..6507c74440
--- /dev/null
+++ b/lib/chef/resource/support/client.erb
@@ -0,0 +1,64 @@
+<% %w(@node_name
+ @chef_license
+ @chef_server_url
+ @event_loggers
+ @file_backup_path
+ @file_cache_path
+ @file_staging_uses_destdir
+ @formatters
+ @http_proxy
+ @https_proxy
+ @ftp_proxy
+ @log_level
+ @minimal_ohai
+ @named_run_list
+ @no_proxy
+ @ohai_disabled_plugins
+ @ohai_optional_plugins
+ @pid_file
+ @policy_group
+ @policy_name
+ @ssl_verify_mode).each do |prop| -%>
+<% next if instance_variable_get(prop).nil? || instance_variable_get(prop).empty? -%>
+<%=prop.delete_prefix("@") %> <%= instance_variable_get(prop).inspect %>
+<% end -%>
+<%# log_location is special due to STDOUT/STDERR from String -> IO Object -%>
+<% unless @log_location.nil? %>
+ <% if @log_location.is_a?(String) && %w(STDOUT STDERR).include?(@log_location) -%>
+log_location <%= @log_location %>
+ <% else -%>
+log_location <%= @log_location.inspect %>
+ <% end -%>
+<% end -%>
+<%# The code below is not DRY on purpose to improve readability -%>
+<% unless @start_handlers.empty? -%>
+ # Do not crash if a start handler is missing / not installed yet
+ begin
+ <% @start_handlers.each do |handler| -%>
+ start_handlers << <%= @handler %>
+ <% end -%>
+ rescue NameError => e
+ Chef::Log.error e
+ end
+<% end -%>
+<% unless @report_handlers.empty? -%>
+ # Do not crash if a report handler is missing / not installed yet
+ begin
+ <% @report_handlers.each do |handler| -%>
+ report_handlers << <%= @handler %>
+ <% end -%>
+ rescue NameError => e
+ Chef::Log.error e
+ end
+<% end -%>
+<% unless @exception_handlers.empty? -%>
+ # Do not crash if an exception handler is missing / not installed yet
+ begin
+ <% @exception_handlers.each do |handler| -%>
+ exception_handlers << <%= @handler %>
+ <% end -%>
+ rescue NameError => e
+ Chef::Log.error e
+ end
+<% end -%>
+<%= @additional_config -%>
diff --git a/lib/chef/resource/support/cron.d.erb b/lib/chef/resource/support/cron.d.erb
new file mode 100644
index 0000000000..579e64f405
--- /dev/null
+++ b/lib/chef/resource/support/cron.d.erb
@@ -0,0 +1,28 @@
+# Generated by <%= ChefUtils::Dist::Infra::PRODUCT %>. Changes will be overwritten.
+<% if @mailto -%>
+MAILTO=<%= @mailto %>
+<% end -%>
+<% if @path -%>
+PATH=<%= @path %>
+<% end -%>
+<% if @shell -%>
+SHELL=<%= @shell %>
+<% end -%>
+<% if @home -%>
+HOME=<%= @home %>
+<% end -%>
+<% if @random_delay -%>
+RANDOM_DELAY=<%= @random_delay %>
+<% end -%>
+<% @environment.each do |key, val| -%>
+<%= key %>=<%= val.to_s.shellescape %>
+<% end -%>
+
+<% if @comment -%>
+# <%= @comment %>
+<% end -%>
+<% if @predefined_value -%>
+<%= @predefined_value %> <%= @user %> <%= @command %>
+<% else -%>
+<%= @minute %> <%= @hour %> <%= @day %> <%= @month %> <%= @weekday %> <%= @user %> <%= @command %>
+<% end -%>
diff --git a/lib/chef/resource/support/cron_access.erb b/lib/chef/resource/support/cron_access.erb
new file mode 100644
index 0000000000..1f4e74ab23
--- /dev/null
+++ b/lib/chef/resource/support/cron_access.erb
@@ -0,0 +1,4 @@
+# Generated by <%= ChefUtils::Dist::Infra::PRODUCT %>. Changes will be overwritten.
+<% @users.sort.uniq.each do |user| -%>
+<%= user %>
+<% end -%>
diff --git a/lib/chef/resource/support/ssh_known_hosts.erb b/lib/chef/resource/support/ssh_known_hosts.erb
new file mode 100644
index 0000000000..0073b250ff
--- /dev/null
+++ b/lib/chef/resource/support/ssh_known_hosts.erb
@@ -0,0 +1,3 @@
+<% @entries.sort.each do |entry| -%>
+<%= entry %>
+<% end -%>
diff --git a/lib/chef/resource/support/sudoer.erb b/lib/chef/resource/support/sudoer.erb
new file mode 100644
index 0000000000..f8c9760d94
--- /dev/null
+++ b/lib/chef/resource/support/sudoer.erb
@@ -0,0 +1,17 @@
+# This file is managed by <%= ChefUtils::Dist::Infra::PRODUCT %>. Changes will be overwritten.
+
+<% @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/support/ulimit.erb b/lib/chef/resource/support/ulimit.erb
new file mode 100644
index 0000000000..25ac0fde5c
--- /dev/null
+++ b/lib/chef/resource/support/ulimit.erb
@@ -0,0 +1,41 @@
+# Generated by <%= ChefUtils::Dist::Infra::PRODUCT %>. Changes will be overwritten.
+
+# Limits settings for <%= @ulimit_user %>
+
+<% unless @filehandle_limit.nil? -%>
+<%= @ulimit_user -%> - nofile <%= @filehandle_limit %>
+<% else -%><% unless @filehandle_soft_limit.nil? -%><%= @ulimit_user -%> soft nofile <%= @filehandle_soft_limit %><% end -%>
+<% unless @filehandle_hard_limit.nil? -%><%= @ulimit_user -%> hard nofile <%= @filehandle_hard_limit %><% end -%>
+<% end -%>
+
+<% unless @process_limit.nil? -%>
+<%= @ulimit_user -%> - nproc <%= @process_limit %>
+<% else -%><% unless @process_soft_limit.nil? -%><%= @ulimit_user -%> soft nproc <%= @process_soft_limit %><% end -%>
+<% unless @process_hard_limit.nil? -%><%= @ulimit_user -%> hard nproc <%= @process_hard_limit %><% end -%>
+<% end -%>
+
+<% unless @memory_limit.nil? -%>
+<%= @ulimit_user -%> - memlock <%= @memory_limit %>
+<% end -%>
+
+<% unless @core_limit.nil? -%>
+<%= @ulimit_user -%> - core <%= @core_limit %>
+<% else -%><% unless @core_soft_limit.nil? -%><%= @ulimit_user -%> soft core <%= @core_soft_limit %><% end -%>
+<% unless @core_hard_limit.nil? -%><%= @ulimit_user -%> hard core <%= @core_hard_limit %><% end -%>
+<% end -%>
+
+<% unless @stack_limit.nil? -%>
+<%= @ulimit_user -%> - stack <%= @stack_limit %>
+<% else -%><% unless @stack_soft_limit.nil? -%><%= @ulimit_user -%> soft stack <%= @stack_soft_limit %><% end -%>
+<% unless @stack_hard_limit.nil? -%><%= @ulimit_user -%> hard stack <%= @stack_hard_limit %><% end -%>
+<% end -%>
+
+<% unless @rtprio_limit.nil? -%>
+<%= @ulimit_user -%> - rtprio <%= @rtprio_limit %>
+<% else -%><% unless @rtprio_soft_limit.nil? -%><%= @ulimit_user -%> soft rtprio <%= @rtprio_soft_limit %><% end -%>
+<% unless @rtprio_hard_limit.nil? -%><%= @ulimit_user -%> hard rtprio <%= @rtprio_hard_limit %><% end -%>
+<% end -%>
+
+<% unless @virt_limit.nil? -%>
+ <%= @ulimit_user -%> - as <%= @virt_limit %>
+<% end -%>
diff --git a/lib/chef/resource/swap_file.rb b/lib/chef/resource/swap_file.rb
new file mode 100644
index 0000000000..3d8f31de48
--- /dev/null
+++ b/lib/chef/resource/swap_file.rb
@@ -0,0 +1,228 @@
+#
+# Copyright:: 2012-2018, Seth Vargo
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class SwapFile < Chef::Resource
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ **Create a swap file**
+
+ ```ruby
+ swap_file '/dev/sda1' do
+ size 1024
+ end
+ ```
+
+ **Remove a swap file**
+
+ ```ruby
+ swap_file '/dev/sda1' do
+ action :remove
+ end
+ ```
+ DOC
+
+ property :path, String,
+ description: "The path where the swap file will be created on the system if it differs from the resource block's name.",
+ name_property: true
+
+ property :size, Integer,
+ description: "The size (in MBs) of the swap file."
+
+ property :persist, [TrueClass, FalseClass],
+ description: "Persist the swapon.",
+ default: false
+
+ property :timeout, Integer,
+ description: "Timeout for `dd` / `fallocate` commands.",
+ default: 600,
+ desired_state: false
+
+ 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
+ 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..bf9424f35f
--- /dev/null
+++ b/lib/chef/resource/sysctl.rb
@@ -0,0 +1,233 @@
+#
+# Copyright:: 2018, Webb Agile Solutions Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class Sysctl < Chef::Resource
+ unified_mode true
+
+ provides(:sysctl) { true }
+ provides(:sysctl_param) { true }
+
+ description "Use the **sysctl** resource to set or remove 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`."
+ examples <<~DOC
+ **Set vm.swappiness**:
+
+ ```ruby
+ sysctl 'vm.swappiness' do
+ value 19
+ end
+ ```
+
+ **Remove kernel.msgmax**:
+
+ **Note**: This only removes the sysctl.d config for kernel.msgmax. The value will be set back to the kernel default value.
+
+ ```ruby
+ sysctl 'kernel.msgmax' do
+ action :remove
+ end
+ ```
+
+ **Adding Comments to sysctl configuration files**:
+
+ ```ruby
+ sysctl 'vm.swappiness' do
+ value 19
+ comment "define how aggressively the kernel will swap memory pages."
+ end
+ ```
+
+ This produces /etc/sysctl.d/99-chef-vm.swappiness.conf as follows:
+
+ ```
+ # define how aggressively the kernel will swap memory pages.
+ vm.swappiness = 1
+ ```
+
+ **Converting sysctl settings from shell scripts**:
+
+ Example of existing settings:
+
+ ```bash
+ fs.aio-max-nr = 1048576 net.ipv4.ip_local_port_range = 9000 65500 kernel.sem = 250 32000 100 128
+ ```
+
+ Converted to sysctl resources:
+
+ ```ruby
+ sysctl 'fs.aio-max-nr' do
+ value '1048576'
+ end
+
+ sysctl 'net.ipv4.ip_local_port_range' do
+ value '9000 65500'
+ end
+
+ sysctl 'kernel.sem' do
+ value '250 32000 100 128'
+ end
+ ```
+ DOC
+
+ introduced "14.0"
+
+ property :key, String,
+ description: "The kernel parameter key in dotted format if it differs from the resource block's name.",
+ 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: [:apply]
+
+ property :comment, [Array, String],
+ description: "Comments, placed above the resource setting in the generated file. For multi-line comments, use an array of strings, one per line.",
+ default: [],
+ introduced: "15.8"
+
+ property :conf_dir, String,
+ description: "The configuration directory to write the config to.",
+ default: "/etc/sysctl.d"
+
+ def after_created
+ raise "The sysctl resource requires Linux as it needs sysctl and the sysctl.d directory functionality." unless node["os"] == "linux"
+ end
+
+ def coerce_value(v)
+ case v
+ when Array
+ v.join(" ")
+ else
+ v.to_s
+ end
+ end
+
+ load_current_value do
+
+ value get_sysctl_value(key)
+ rescue
+ current_value_does_not_exist!
+
+ end
+
+ action :apply do
+ description "Apply a sysctl value."
+
+ 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.tr("/", ".")}.conf" do
+ content contruct_sysctl_content
+ end
+
+ execute "Load sysctl values" do
+ command "sysctl #{"-e " if new_resource.ignore_error}-p"
+ default_env true
+ action :run
+ end
+ end
+ end
+
+ action :remove do
+ description "Remove a sysctl value."
+
+ # only converge the resource if the file actually exists to delete
+ if ::File.exist?("#{new_resource.conf_dir}/99-chef-#{new_resource.key.tr("/", ".")}.conf")
+ converge_by "removing sysctl config at #{new_resource.conf_dir}/99-chef-#{new_resource.key.tr("/", ".")}.conf" do
+ file "#{new_resource.conf_dir}/99-chef-#{new_resource.key.tr("/", ".")}.conf" do
+ action :delete
+ end
+
+ execute "Load sysctl values" do
+ default_env true
+ command "sysctl -p"
+ action :run
+ end
+ end
+ end
+ end
+
+ action_class do
+ #
+ # Shell out to set the sysctl value
+ #
+ # @param [String] key The sysctl key
+ # @param [String] value The value of the sysctl key
+ #
+ def set_sysctl_param(key, value)
+ shell_out!("sysctl #{"-e " if new_resource.ignore_error}-w \"#{key}=#{value}\"")
+ end
+
+ #
+ # construct a string, joining members of new_resource.comment and new_resource.value
+ #
+ # @return [String] The text file content
+ #
+ def contruct_sysctl_content
+ sysctl_lines = Array(new_resource.comment).map { |c| "# #{c.strip}" }
+
+ sysctl_lines << "#{new_resource.key} = #{new_resource.value}"
+
+ sysctl_lines.join("\n")
+ end
+ end
+
+ private
+
+ # shellout to sysctl to get the current value
+ # ignore missing keys by using '-e'
+ # convert tabs to spaces since sysctl tab deliminates multivalue parameters
+ # strip the newline off the end of the output as well
+ #
+ # Chef creates a file in sysctld with parameter configuration
+ # Thus this config will persists even after rebooting the system
+ # User can be in a half configured state, where he has already updated the value
+ # which he wants to be configured from the resource
+ # Therefore we need an extra check with sysctld to ensure a correct idempotency
+ #
+ def get_sysctl_value(key)
+ val = shell_out!("sysctl -n -e #{key}").stdout.tr("\t", " ").strip
+ raise unless val == get_sysctld_value(key)
+
+ val
+ end
+
+ # Check if chef has already configured a value for the given key and
+ # return the value. Raise in case this conf file needs to be created
+ # or updated
+ def get_sysctld_value(key)
+ raise unless ::File.exist?("/etc/sysctl.d/99-chef-#{key.tr("/", ".")}.conf")
+
+ k, v = ::File.read("/etc/sysctl.d/99-chef-#{key.tr("/", ".")}.conf").match(/(.*) = (.*)/).captures
+ raise "Unknown sysctl key!" if k.nil?
+ raise "Unknown sysctl value!" if v.nil?
+
+ v
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/systemd_unit.rb b/lib/chef/resource/systemd_unit.rb
index 688f2e9dcd..b028214441 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");
@@ -16,31 +16,97 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
require "iniparse"
class Chef
class Resource
class SystemdUnit < Chef::Resource
- resource_name :systemd_unit
+ unified_mode true
+
+ provides(:systemd_unit) { true }
+
+ description "Use the **systemd_unit** resource to create, manage, and run [systemd units](https://www.freedesktop.org/software/systemd/man/systemd.html#Concepts)."
+ introduced "12.11"
+ examples <<~DOC
+ **Create systemd service unit file from a Hash**
+
+ ```ruby
+ systemd_unit 'etcd.service' do
+ content({Unit: {
+ Description: 'Etcd',
+ Documentation: ['https://coreos.com/etcd', 'man:etcd(1)'],
+ After: 'network.target',
+ },
+ Service: {
+ Type: 'notify',
+ ExecStart: '/usr/local/etcd',
+ Restart: 'always',
+ },
+ Install: {
+ WantedBy: 'multi-user.target',
+ }})
+ action [:create, :enable]
+ end
+ ```
+
+ **Create systemd service unit file from a String**
+
+ ```ruby
+ systemd_unit 'sysstat-collect.timer' do
+ content <<~EOU
+ [Unit]
+ Description=Run system activity accounting tool every 10 minutes
+
+ [Timer]
+ OnCalendar=*:00/10
+
+ [Install]
+ WantedBy=sysstat.service
+ EOU
+
+ action [:create, :enable]
+ end
+ ```
+ DOC
default_action :nothing
allowed_actions :create, :delete,
- :enable, :disable,
- :mask, :unmask,
- :start, :stop,
- :restart, :reload,
- :try_restart, :reload_or_restart,
- :reload_or_try_restart
-
- property :enabled, [TrueClass, FalseClass]
- property :active, [TrueClass, FalseClass]
- property :masked, [TrueClass, FalseClass]
- property :static, [TrueClass, FalseClass]
- property :user, String, desired_state: false
- property :content, [String, Hash]
+ :preset, :revert,
+ :enable, :disable, :reenable,
+ :mask, :unmask,
+ :start, :stop,
+ :restart, :reload,
+ :try_restart, :reload_or_restart,
+ :reload_or_try_restart
+
+ # Internal provider-managed properties
+ property :enabled, [TrueClass, FalseClass], skip_docs: true
+ property :active, [TrueClass, FalseClass], skip_docs: true
+ property :masked, [TrueClass, FalseClass], skip_docs: true
+ property :static, [TrueClass, FalseClass], skip_docs: true
+ property :indirect, [TrueClass, FalseClass], skip_docs: true
+
+ # User-provided properties
+ property :user, String, desired_state: false,
+ description: "The user account that the systemd unit process is run under. The path to the unit for that user would be something like '/etc/systemd/user/sshd.service'. If no user account is specified, the systemd unit will run under a 'system' account, with the path to the unit being something like '/etc/systemd/system/sshd.service'."
+
+ property :content, [String, Hash],
+ description: "A string or hash that contains a systemd [unit file](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) definition that describes the properties of systemd-managed entities, such as services, sockets, devices, and so on. In #{ChefUtils::Dist::Infra::PRODUCT} 14.4 or later, repeatable options can be implemented with an array."
+
property :triggers_reload, [TrueClass, FalseClass],
- default: true, desired_state: false
+ description: "Specifies whether to trigger a daemon reload when creating or deleting a unit.",
+ default: true, desired_state: false
+
+ property :verify, [TrueClass, FalseClass],
+ default: true, desired_state: false,
+ description: "Specifies if the unit will be verified before installation. Systemd can be overly strict when verifying units, so in certain cases it is preferable not to verify the unit."
+
+ property :unit_name, String, desired_state: false,
+ name_property: true,
+ description: "The name of the unit file if it differs from the resource block's name.",
+ introduced: "13.7"
def to_ini
case content
@@ -49,7 +115,9 @@ class Chef
content.each_pair do |sect, opts|
doc.section(sect) do |section|
opts.each_pair do |opt, val|
- section.option(opt, val)
+ [val].flatten.each do |v|
+ section.option(opt, v)
+ end
end
end
end
diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb
index 896aa71340..e3f266740d 100644
--- a/lib/chef/resource/template.rb
+++ b/lib/chef/resource/template.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,13 +18,26 @@
# limitations under the License.
#
-require "chef/resource/file"
-require "chef/provider/template"
-require "chef/mixin/securable"
+require_relative "file"
+require_relative "../mixin/securable"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
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
+ unified_mode true
+
+ provides :template
+
include Chef::Mixin::Securable
attr_reader :inline_helper_blocks
@@ -33,9 +46,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 = []
@@ -45,33 +55,21 @@ class Chef
set_or_return(
:source,
file,
- :kind_of => [ String, Array ]
+ kind_of: [ String, Array ]
)
end
- def variables(args = nil)
- set_or_return(
- :variables,
- args,
- :kind_of => [ Hash ]
- )
- end
+ property :variables, Hash,
+ description: "The variables property of the template resource can be used to reference a partial template file by using a Hash.",
+ default: lazy { {} }
- def cookbook(args = nil)
- set_or_return(
- :cookbook,
- args,
- :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). The default value is the current cookbook.",
+ desired_state: false
- def local(args = nil)
- set_or_return(
- :local,
- args,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
+ property :local, [ TrueClass, FalseClass ],
+ default: false, desired_state: false,
+ description: "Load a template from a local path. By default, the #{ChefUtils::Dist::Infra::CLIENT} loads templates from a cookbook's /templates directory. When this property is set to true, use the source property to specify the path to a template on the local node."
# Declares a helper method to be defined in the template context when
# rendering.
@@ -112,7 +110,7 @@ class Chef
"`helper(:method)` requires a block argument (e.g., `helper(:method) { code }`)"
end
- unless method_name.kind_of?(Symbol)
+ unless method_name.is_a?(Symbol)
raise Exceptions::ValidationFailed,
"method_name argument to `helper(method_name)` must be a symbol (e.g., `helper(:method) { code }`)"
end
@@ -166,13 +164,13 @@ class Chef
"Passing both a module and block to #helpers is not supported. Call #helpers multiple times instead"
elsif block_given?
@inline_helper_modules << block
- elsif module_name.kind_of?(::Module)
+ elsif module_name.is_a?(::Module)
@helper_modules << module_name
elsif module_name.nil?
raise Exceptions::ValidationFailed,
"#helpers requires either a module name or inline module code as a block.\n" +
- "e.g.: helpers do; helper_code; end;\n" +
- "OR: helpers(MyHelpersModule)"
+ "e.g.: helpers do; helper_code; end;\n" +
+ "OR: helpers(MyHelpersModule)"
else
raise Exceptions::ValidationFailed,
"Argument to #helpers must be a module. You gave #{module_name.inspect} (#{module_name.class})"
diff --git a/lib/chef/resource/timestamped_deploy.rb b/lib/chef/resource/timestamped_deploy.rb
deleted file mode 100644
index 1d6b07a719..0000000000
--- a/lib/chef/resource/timestamped_deploy.rb
+++ /dev/null
@@ -1,26 +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.
-#
-
-class Chef
- class Resource
- # Convenience class for using the deploy resource with the timestamped
- # deployment strategy (provider)
- class TimestampedDeploy < Chef::Resource::Deploy
- end
- end
-end
diff --git a/lib/chef/resource/timezone.rb b/lib/chef/resource/timezone.rb
new file mode 100644
index 0000000000..04e5884b88
--- /dev/null
+++ b/lib/chef/resource/timezone.rb
@@ -0,0 +1,178 @@
+#
+# Author:: Kirill Kouznetsov <agon.smith@gmail.com>
+#
+# Copyright:: 2018, Kirill Kouznetsov.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class Timezone < Chef::Resource
+ unified_mode true
+
+ provides :timezone
+
+ description "Use the **timezone** resource to change the system timezone on Windows, Linux, and macOS hosts. Timezones are specified in tz database format, with a complete list of available TZ values for Linux and macOS here: <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>. On Windows systems run `tzutil /l` for a complete list of valid timezones."
+ introduced "14.6"
+ examples <<~DOC
+ **Set the timezone to UTC**
+
+ ```ruby
+ timezone 'UTC'
+ ```
+
+ **Set the timezone to America/Los_Angeles with a friendly resource name on Linux/macOS**
+
+ ```ruby
+ timezone 'Set the host's timezone to America/Los_Angeles' do
+ timezone 'America/Los_Angeles'
+ end
+ ```
+
+ **Set the timezone to PST with a friendly resource name on Windows**
+
+ ```ruby
+ timezone 'Set the host's timezone to PST' do
+ timezone 'Pacific Standard time'
+ end
+ ```
+ DOC
+
+ property :timezone, String,
+ description: "An optional property to set the timezone value if it differs from the resource block's name.",
+ name_property: true
+
+ # detect the current TZ on darwin hosts
+ #
+ # @since 14.7
+ # @return [String] TZ database value
+ def current_macos_tz
+ tz_shellout = shell_out!(["systemsetup", "-gettimezone"])
+ if /You need administrator access/.match?(tz_shellout.stdout)
+ raise "The timezone resource requires administrative privileges to run on macOS hosts!"
+ else
+ /Time Zone: (.*)/.match(tz_shellout.stdout)[1]
+ end
+ end
+
+ # detect the current timezone on windows hosts
+ #
+ # @since 14.7
+ # @return [String] timezone id
+ def current_windows_tz
+ tz_shellout = shell_out("tzutil /g")
+ raise "There was an error running the tzutil command" if tz_shellout.error?
+
+ tz_shellout.stdout.strip
+ end
+
+ # detect the current timezone on systemd hosts
+ #
+ # @since 16.5
+ # @return [String] timezone id
+ def current_systemd_tz
+ tz_shellout = shell_out(["/usr/bin/timedatectl", "status"])
+ raise "There was an error running the timedatectl command" if tz_shellout.error?
+
+ # https://rubular.com/r/eV68MX9XXbyG4k
+ /Time zone: (.*) \(.*/.match(tz_shellout.stdout)[1]
+ end
+
+ # detect the current timezone on non-systemd RHEL-ish hosts
+ #
+ # @since 16.5
+ # @return [String] timezone id
+ def current_rhel_tz
+ return nil unless ::File.exist?("/etc/sysconfig/clock")
+
+ # https://rubular.com/r/aoj01L3bKBM7wh
+ /ZONE="(.*)"/.match(::File.read("/etc/sysconfig/clock"))[1]
+ end
+
+ load_current_value do
+ if systemd?
+ timezone current_systemd_tz
+ else
+ case node["platform_family"]
+ # Old version of RHEL < 7 and Amazon 201X
+ when "rhel", "amazon"
+ timezone current_rhel_tz
+ when "mac_os_x"
+ timezone current_macos_tz
+ when "windows"
+ timezone current_windows_tz
+ end
+ end
+ end
+
+ action :set do
+ description "Set the timezone."
+
+ # we have to check windows first since the value isn't case sensitive here
+ if windows?
+ unless current_windows_tz.casecmp?(new_resource.timezone)
+ converge_by("setting timezone to '#{new_resource.timezone}'") do
+ shell_out!(["tzutil", "/s", new_resource.timezone])
+ end
+ end
+ else # linux / macos
+ converge_if_changed(:timezone) do
+ # Modern SUSE, Amazon, Fedora, RHEL, Ubuntu & Debian
+ if systemd?
+ # make sure we have the tzdata files
+ package suse? ? "timezone" : "tzdata"
+
+ shell_out!(["/usr/bin/timedatectl", "--no-ask-password", "set-timezone", new_resource.timezone])
+ else
+ case node["platform_family"]
+ # Old version of RHEL < 7 and Amazon 201X
+ when "rhel", "amazon"
+ # make sure we have the tzdata files
+ package "tzdata"
+
+ file "/etc/sysconfig/clock" do
+ owner "root"
+ group "root"
+ mode "0644"
+ action :create
+ content <<~CONTENT
+ ZONE="#{new_resource.timezone}"
+ UTC="true"
+ CONTENT
+ end
+
+ execute "tzdata-update" do
+ command "/usr/sbin/tzdata-update"
+ action :nothing
+ only_if { ::File.executable?("/usr/sbin/tzdata-update") }
+ subscribes :run, "file[/etc/sysconfig/clock]", :immediately
+ end
+
+ link "/etc/localtime" do
+ to "/usr/share/zoneinfo/#{new_resource.timezone}"
+ not_if { ::File.executable?("/usr/sbin/tzdata-update") }
+ end
+ when "mac_os_x"
+ shell_out!(["sudo", "systemsetup", "-settimezone", new_resource.timezone])
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 06dfe95bd4..408932175d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,159 +16,63 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class User < Chef::Resource
- resource_name :user_resource_abstract_base_class # this prevents magickal class name DSL wiring
- identity_attr :username
+ unified_mode true
- state_attrs :uid, :gid, :home
+ description "Use the **user** resource to add users, update existing users, remove users, and to lock/unlock user passwords."
default_action :create
allowed_actions :create, :remove, :modify, :manage, :lock, :unlock
- def initialize(name, run_context = nil)
- super
- @username = name
- @comment = nil
- @uid = nil
- @gid = nil
- @home = nil
- @shell = nil
- @password = nil
- @system = false
- @manage_home = false
- @force = false
- @non_unique = false
- @supports = {
- manage_home: false,
- non_unique: false,
- }
- @iterations = 27855
- @salt = nil
- end
-
- def username(arg = nil)
- set_or_return(
- :username,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def comment(arg = nil)
- set_or_return(
- :comment,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def uid(arg = nil)
- set_or_return(
- :uid,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
-
- def gid(arg = nil)
- set_or_return(
- :gid,
- arg,
- :kind_of => [ String, Integer ]
- )
- end
+ property :username, String,
+ description: "An optional property to set the username value if it differs from the resource block's name.",
+ name_property: true
- alias_method :group, :gid
+ property :comment, String,
+ description: "The contents of the user comments field."
+
+ property :home, String,
+ description: "The location of the home directory."
+
+ property :salt, String,
+ description: "A SALTED-SHA512-PBKDF2 hash.",
+ desired_state: false
+
+ property :shell, String,
+ description: "The login shell."
+
+ property :password, String,
+ description: "The password shadow hash",
+ sensitive: true,
+ desired_state: false
+
+ property :non_unique, [ TrueClass, FalseClass ],
+ description: "Create a duplicate (non-unique) user account.",
+ default: false, desired_state: false
- 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 password(arg = nil)
- set_or_return(
- :password,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def salt(arg = nil)
- set_or_return(
- :salt,
- arg,
- :kind_of => [ String ]
- )
- end
-
- def iterations(arg = nil)
- set_or_return(
- :iterations,
- arg,
- :kind_of => [ Integer ]
- )
- end
-
- def system(arg = nil)
- set_or_return(
- :system,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def manage_home(arg = nil)
- set_or_return(
- :manage_home,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def force(arg = nil)
- set_or_return(
- :force,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def non_unique(arg = nil)
- set_or_return(
- :non_unique,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def supports(args = {})
- if args.key?(:manage_home)
- Chef.log_deprecation "supports { manage_home: #{args[:manage_home]} } on the user resource is deprecated and will be removed in Chef 13, set manage_home: #{args[:manage_home]} instead"
- end
- if args.key?(:non_unique)
- Chef.log_deprecation "supports { non_unique: #{args[:non_unique]} } on the user resource is deprecated and will be removed in Chef 13, set non_unique: #{args[:non_unique]} instead"
- end
- super
- end
-
- def supports=(args)
- supports(args)
- end
+ property :manage_home, [ TrueClass, FalseClass ],
+ description: "Manage a user's home directory.\nWhen used with the :create action, a user's home directory is created based on HOME_DIR. If the home directory is missing, it is created unless CREATE_HOME in /etc/login.defs is set to no. When created, a skeleton set of files and subdirectories are included within the home directory.\nWhen used with the :modify action, a user's home directory is moved to HOME_DIR. If the home directory is missing, it is created unless CREATE_HOME in /etc/login.defs is set to no. The contents of the user's home directory are moved to the new location.",
+ default: false, desired_state: false
+
+ property :force, [ TrueClass, FalseClass ],
+ description: "Force the removal of a user. May be used only with the :remove action.",
+ default: false, desired_state: false
+
+ property :system, [ TrueClass, FalseClass ],
+ description: "Create a system user. This property may be used with useradd as the provider to create a system user which passes the -r flag to useradd.",
+ default: false
+
+ property :uid, [ String, Integer, NilClass ], # nil for backwards compat
+ description: "The numeric user identifier."
+
+ property :gid, [ String, Integer, NilClass ], # nil for backwards compat
+ description: "The numeric group identifier."
+
+ alias_method :group, :gid
end
end
end
diff --git a/lib/chef/resource/user/aix_user.rb b/lib/chef/resource/user/aix_user.rb
index 7c07db2e25..c0e4c01e20 100644
--- a/lib/chef/resource/user/aix_user.rb
+++ b/lib/chef/resource/user/aix_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,13 +15,13 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class AixUser < Chef::Resource::User
- resource_name :aix_user
+ unified_mode true
provides :aix_user
provides :user, os: "aix"
diff --git a/lib/chef/resource/user/dscl_user.rb b/lib/chef/resource/user/dscl_user.rb
index 61517d8b44..91efd657de 100644
--- a/lib/chef/resource/user/dscl_user.rb
+++ b/lib/chef/resource/user/dscl_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,16 +15,20 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class DsclUser < Chef::Resource::User
- resource_name :dscl_user
+ unified_mode true
provides :dscl_user
- provides :user, os: "darwin"
+ provides :user, platform: "mac_os_x", platform_version: "< 10.14"
+
+ property :iterations, Integer,
+ description: "macOS platform only. The number of iterations for a password with a SALTED-SHA512-PBKDF2 shadow hash.",
+ default: 27855, desired_state: false
end
end
end
diff --git a/lib/chef/resource/user/linux_user.rb b/lib/chef/resource/user/linux_user.rb
index ec60ac89bf..24406e5079 100644
--- a/lib/chef/resource/user/linux_user.rb
+++ b/lib/chef/resource/user/linux_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,26 +15,17 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class LinuxUser < Chef::Resource::User
- resource_name :linux_user
+ unified_mode true
provides :linux_user
provides :user, os: "linux"
- def initialize(name, run_context = nil)
- super
- @supports = {
- manage_home: false,
- non_unique: false,
- }
- @manage_home = false
- end
-
end
end
end
diff --git a/lib/chef/resource/user/mac_user.rb b/lib/chef/resource/user/mac_user.rb
new file mode 100644
index 0000000000..2331283bbd
--- /dev/null
+++ b/lib/chef/resource/user/mac_user.rb
@@ -0,0 +1,122 @@
+#
+# Author:: Ryan Cragun (<ryan@chef.io>)
+# Copyright:: Copyright (c) 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 "../user"
+
+class Chef
+ class Resource
+ class User
+ # Provide a user resource that is compatible with default TCC restrictions
+ # that were introduced in macOS 10.14.
+ #
+ # Changes:
+ #
+ # * This resource and the corresponding provider have been modified to
+ # work with default macOS TCC policies. Direct access to user binary
+ # plists are no longer permitted by default, thus we've chosen to use
+ # a combination of newer utilities for managing user lifecycles and older
+ # utilities for managing passwords.
+ #
+ # * Due to tooling changes that were necessitated by the new policy
+ # restrictions the mac_user resource is only suitable for use on macOS
+ # >= 10.14. Support for older platforms has been removed.
+ #
+ # New Features:
+ #
+ # * Primary group management is now included.
+ #
+ # * 'admin' is now a boolean property that configures a user to an admin.
+ #
+ # * 'admin_username' and 'admin_password' are new properties that define the
+ # admin user credentials required for toggling SecureToken for a user.
+ #
+ # The value of 'admin_username' must correspond to a system user that
+ # is part of the 'admin' with SecureToken enabled in order to toggle
+ # SecureToken.
+ #
+ # * 'secure_token' is a boolean property that sets the desired state
+ # for SecureToken. SecureToken token is required for FileVault full
+ # disk encryption.
+ #
+ # * 'secure_token_password' is the plaintext password required to enable
+ # or disable secure_token for a user. If no salt is specified we assume
+ # the 'password' property corresponds to a plaintext password and will
+ # attempt to use it in place of secure_token_password if it not set.
+ class MacUser < Chef::Resource::User
+ unified_mode true
+
+ provides :mac_user
+ provides :user, platform: "mac_os_x", platform_version: ">= 10.14"
+
+ introduced "15.3"
+
+ property :iterations, Integer,
+ description: "The number of iterations for a password with a SALTED-SHA512-PBKDF2 shadow hash.",
+ default: 57803, desired_state: false
+
+ # Overload gid to set our default gid to 20, the macOS "staff" group.
+ # We also allow a string group name here which we'll attempt to resolve
+ # or create in the provider.
+ property :gid, [Integer, String], description: "The numeric group identifier.", default: 20, coerce: ->(gid) do
+ begin
+ Integer(gid) # Try and coerce a group id string into an integer
+ rescue
+ gid # assume we have a group name
+ end
+ end
+
+ # Overload the password so we can set a length requirements and update the
+ # description.
+ property :password, String, description: "The plain text user password", sensitive: true, coerce: ->(password) {
+ # It would be nice if this could be in callbacks but we need the context
+ # of the resource to get the salt property so we have to do it in coerce.
+ if salt && password !~ /^[[:xdigit:]]{256}$/
+ raise Chef::Exceptions::User, "Password must be a SALTED-SHA512-PBKDF2 shadow hash entropy when a shadow hash salt is given"
+ end
+
+ password
+ },
+ callbacks: {
+ "Password length must be >= 4" => ->(password) { password.size >= 4 },
+ }
+
+ # Overload home so we set our default.
+ property :home, String, description: "The user home directory", default: lazy { "/Users/#{name}" }
+
+ property :admin, [TrueClass, FalseClass], description: "Create the user as an admin", default: false
+
+ # Hide a user account in the macOS login window
+ property :hidden, [TrueClass, FalseClass, nil], description: "Hide account from loginwindow and system preferences", default: nil, introduced: "15.8"
+
+ # TCC on macOS >= 10.14 requires admin credentials of an Admin user that
+ # has SecureToken enabled in order to toggle SecureToken.
+ property :admin_username, String, description: "Admin username for superuser actions"
+ property :admin_password, String, description: "Admin password for superuser actions", sensitive: true
+
+ property :secure_token, [TrueClass, FalseClass], description: "Enable SecureToken for the user", default: false
+ # In order to enable SecureToken for a user we require the plaintext password.
+ property :secure_token_password, String, description: "The plaintext password for enabling SecureToken", sensitive: true, default: lazy {
+ # In some cases the user can pass the plaintext value to "password" instead of
+ # SALTED-SHA512-PBKDF2 entropy. In those cases we'll default to the
+ # same value.
+ (salt.nil? && password) ? password : nil
+ }
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/user/pw_user.rb b/lib/chef/resource/user/pw_user.rb
index 873be19d59..2f97500970 100644
--- a/lib/chef/resource/user/pw_user.rb
+++ b/lib/chef/resource/user/pw_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,13 +15,13 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class PwUser < Chef::Resource::User
- resource_name :pw_user
+ unified_mode true
provides :pw_user
provides :user, os: "freebsd"
diff --git a/lib/chef/resource/user/solaris_user.rb b/lib/chef/resource/user/solaris_user.rb
index bb897228b9..ab76cb7ae9 100644
--- a/lib/chef/resource/user/solaris_user.rb
+++ b/lib/chef/resource/user/solaris_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,13 +15,13 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class SolarisUser < Chef::Resource::User
- resource_name :solaris_user
+ unified_mode true
provides :solaris_user
provides :user, os: %w{omnios solaris2}
diff --git a/lib/chef/resource/user/windows_user.rb b/lib/chef/resource/user/windows_user.rb
index d1a249fb50..d738ba1636 100644
--- a/lib/chef/resource/user/windows_user.rb
+++ b/lib/chef/resource/user/windows_user.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,16 +15,25 @@
# limitations under the License.
#
-require "chef/resource/user"
+require_relative "../user"
class Chef
class Resource
class User
class WindowsUser < Chef::Resource::User
- resource_name :windows_user
+ unified_mode true
provides :windows_user
provides :user, os: "windows"
+
+ property :full_name, String,
+ description: "The full name of the user.",
+ introduced: "14.6"
+
+ # Override the property from the parent class to coerce to integer.
+ property :uid, [ String, Integer, NilClass ], # nil for backwards compat
+ description: "The numeric user identifier.",
+ coerce: proc { |n| n && Integer(n) rescue n }
end
end
end
diff --git a/lib/chef/resource/user_ulimit.rb b/lib/chef/resource/user_ulimit.rb
new file mode 100644
index 0000000000..d138eeabf3
--- /dev/null
+++ b/lib/chef/resource/user_ulimit.rb
@@ -0,0 +1,116 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2012, Brightcove, 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 "../resource"
+
+class Chef
+ class Resource
+ class UserUlimit < Chef::Resource
+ unified_mode true
+
+ provides :user_ulimit
+
+ description "Use the **user_ulimit** resource to create individual ulimit files that are installed into the `/etc/security/limits.d/` directory."
+ introduced "16.0"
+ examples <<~DOC
+ **Set filehandle limit for the tomcat user**:
+
+ ```ruby
+ user_ulimit 'tomcat' do
+ filehandle_limit 8192
+ end
+ ```
+
+ **Specify a username that differs from the name given to the resource block**:
+
+ ```ruby
+ user_ulimit 'Bump filehandle limits for tomcat user' do
+ username 'tomcat'
+ filehandle_limit 8192
+ end
+ ```
+
+ **Set filehandle limit for the tomcat user with a non-default filename**:
+
+ ```ruby
+ user_ulimit 'tomcat' do
+ filehandle_limit 8192
+ filename 'tomcat_filehandle_limits.conf'
+ end
+ ```
+ DOC
+
+ property :username, String, name_property: true
+ property :filehandle_limit, [String, Integer]
+ property :filehandle_soft_limit, [String, Integer]
+ property :filehandle_hard_limit, [String, Integer]
+ property :process_limit, [String, Integer]
+ property :process_soft_limit, [String, Integer]
+ property :process_hard_limit, [String, Integer]
+ property :memory_limit, [String, Integer]
+ property :core_limit, [String, Integer]
+ property :core_soft_limit, [String, Integer]
+ property :core_hard_limit, [String, Integer]
+ property :stack_limit, [String, Integer]
+ property :stack_soft_limit, [String, Integer]
+ property :stack_hard_limit, [String, Integer]
+ property :rtprio_limit, [String, Integer]
+ property :rtprio_soft_limit, [String, Integer]
+ property :rtprio_hard_limit, [String, Integer]
+ property :virt_limit, [String, Integer]
+ property :filename, String,
+ coerce: proc { |m| m.end_with?(".conf") ? m : m + ".conf" },
+ default: lazy { |r| r.username == "*" ? "00_all_limits.conf" : "#{r.username}_limits.conf" }
+
+ action :create do
+ template "/etc/security/limits.d/#{new_resource.filename}" do
+ source ::File.expand_path("support/ulimit.erb", __dir__)
+ local true
+ mode "0644"
+ variables(
+ ulimit_user: new_resource.username,
+ filehandle_limit: new_resource.filehandle_limit,
+ filehandle_soft_limit: new_resource.filehandle_soft_limit,
+ filehandle_hard_limit: new_resource.filehandle_hard_limit,
+ process_limit: new_resource.process_limit,
+ process_soft_limit: new_resource.process_soft_limit,
+ process_hard_limit: new_resource.process_hard_limit,
+ memory_limit: new_resource.memory_limit,
+ core_limit: new_resource.core_limit,
+ core_soft_limit: new_resource.core_soft_limit,
+ core_hard_limit: new_resource.core_hard_limit,
+ stack_limit: new_resource.stack_limit,
+ stack_soft_limit: new_resource.stack_soft_limit,
+ stack_hard_limit: new_resource.stack_hard_limit,
+ rtprio_limit: new_resource.rtprio_limit,
+ rtprio_soft_limit: new_resource.rtprio_soft_limit,
+ rtprio_hard_limit: new_resource.rtprio_hard_limit,
+ virt_limit: new_resource.virt_limit
+ )
+ end
+ end
+
+ action :delete do
+ file "/etc/security/limits.d/#{new_resource.filename}" do
+ action :delete
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/whyrun_safe_ruby_block.rb b/lib/chef/resource/whyrun_safe_ruby_block.rb
index db11ba34d2..6dde0539a7 100644
--- a/lib/chef/resource/whyrun_safe_ruby_block.rb
+++ b/lib/chef/resource/whyrun_safe_ruby_block.rb
@@ -19,6 +19,8 @@
class Chef
class Resource
class WhyrunSafeRubyBlock < Chef::Resource::RubyBlock
+ provides :whyrun_safe_ruby_block
+ unified_mode true
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..731ce9333e
--- /dev/null
+++ b/lib/chef/resource/windows_ad_join.rb
@@ -0,0 +1,242 @@
+#
+# 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_relative "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class WindowsAdJoin < Chef::Resource
+ provides :windows_ad_join
+
+ unified_mode true
+
+ description "Use the **windows_ad_join** resource to join a Windows Active Directory domain."
+ introduced "14.0"
+ examples <<~DOC
+ **Join a domain**
+
+ ```ruby
+ windows_ad_join 'ad.example.org' do
+ domain_user 'nick'
+ domain_password 'p@ssw0rd1'
+ end
+ ```
+
+ **Join a domain, as `win-workstation`**
+
+ ```ruby
+ windows_ad_join 'ad.example.org' do
+ domain_user 'nick'
+ domain_password 'p@ssw0rd1'
+ new_hostname 'win-workstation'
+ end
+ ```
+
+ **Leave the current domain and re-join the `local` workgroup**
+
+ ```ruby
+ windows_ad_join 'Leave domain' do
+ action :leave
+ workgroup 'local'
+ end
+ ```
+ DOC
+
+ property :domain_name, String,
+ description: "An optional property to set the FQDN of the Active Directory domain to join if it differs from the resource block's name.",
+ validation_message: "The 'domain_name' property must be a FQDN.",
+ regex: /.\../, # anything.anything
+ name_property: true
+
+ property :domain_user, String,
+ description: "The domain user that will be used to join the domain.",
+ required: true
+
+ property :domain_password, String,
+ description: "The password for the domain user. Note that this resource is set to hide sensitive information by default. ",
+ required: true
+
+ property :ou_path, String,
+ description: "The path to the Organizational Unit where the host will be placed."
+
+ property :reboot, Symbol,
+ equal_to: %i{immediate delayed never request_reboot reboot_now},
+ validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the #{ChefUtils::Dist::Infra::PRODUCT} run completes), and :never (Don't reboot)",
+ description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the #{ChefUtils::Dist::Infra::PRODUCT} run completes, or never. Note that a reboot is necessary for changes to take effect.",
+ default: :immediate
+
+ property :reboot_delay, Integer,
+ description: "The amount of time (in minutes) to delay a reboot request.",
+ default: 0,
+ introduced: "16.5"
+
+ property :new_hostname, String,
+ description: "Specifies a new hostname for the computer in the new domain.",
+ introduced: "14.5"
+
+ property :workgroup_name, String,
+ description: "Specifies the name of a workgroup to which the computer is added to when it is removed from the domain. The default value is WORKGROUP. This property is only applicable to the :leave action.",
+ introduced: "15.4"
+
+ # define this again so we can default it to true. Otherwise failures print the password
+ property :sensitive, [TrueClass, FalseClass],
+ default: true, desired_state: false
+
+ action :join do
+ description "Join the Active Directory domain."
+
+ unless on_desired_domain?
+ cmd = "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;"
+ cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{sanitize_usename}\",$pswd);"
+ cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential"
+ cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path
+ cmd << " -NewName \"#{new_resource.new_hostname}\"" if new_resource.new_hostname
+ cmd << " -Force"
+
+ converge_by("join Active Directory domain #{new_resource.domain_name}") do
+ ps_run = powershell_exec(cmd)
+ if ps_run.error?
+ if sensitive?
+ raise "Failed to join the domain #{new_resource.domain_name}: *suppressed sensitive resource output*"
+ else
+ raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.errors}"
+ end
+ end
+
+ unless new_resource.reboot == :never
+ reboot "Reboot to join domain #{new_resource.domain_name}" do
+ action clarify_reboot(new_resource.reboot)
+ delay_mins new_resource.reboot_delay
+ reason "Reboot to join domain #{new_resource.domain_name}"
+ end
+ end
+ end
+ end
+ end
+
+ action :leave do
+ description "Leave the Active Directory domain."
+
+ if joined_to_domain?
+ cmd = ""
+ cmd << "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;"
+ cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{sanitize_usename}\",$pswd);"
+ cmd << "Remove-Computer"
+ cmd << " -UnjoinDomainCredential $credential"
+ cmd << " -NewName \"#{new_resource.new_hostname}\"" if new_resource.new_hostname
+ cmd << " -WorkgroupName \"#{new_resource.workgroup_name}\"" if new_resource.workgroup_name
+ cmd << " -Force"
+
+ converge_by("leave Active Directory domain #{node_domain}") do
+ ps_run = powershell_exec(cmd)
+ if ps_run.error?
+ if sensitive?
+ raise "Failed to leave the domain #{node_domain}: *suppressed sensitive resource output*"
+ else
+ raise "Failed to leave the domain #{node_domain}: #{ps_run.errors}"
+ end
+ end
+
+ unless new_resource.reboot == :never
+ reboot "Reboot to leave domain #{new_resource.domain_name}" do
+ action clarify_reboot(new_resource.reboot)
+ delay_mins new_resource.reboot_delay
+ reason "Reboot to leave domain #{new_resource.domain_name}"
+ end
+ end
+ end
+ end
+ end
+
+ action_class do
+ #
+ # @return [String] The domain name the node is joined to. When the node
+ # is not joined to a domain this will return the name of the
+ # workgroup the node is a member of.
+ #
+ def node_domain
+ node_domain = powershell_exec!("(Get-WmiObject Win32_ComputerSystem).Domain")
+ raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.errors}}" if node_domain.error?
+
+ node_domain.result.downcase.strip
+ end
+
+ #
+ # @return [String] The workgroup the node is a member of. This will
+ # return an empty string if the system is not a member of a
+ # workgroup.
+ #
+ def node_workgroup
+ node_workgroup = powershell_exec!("(Get-WmiObject Win32_ComputerSystem).Workgroup")
+ raise "Failed to check if the system is currently a member of a workgroup" if node_workgroup.error?
+
+ node_workgroup.result
+ end
+
+ #
+ # @return [true, false] Whether or not the node is joined to ANY domain
+ #
+ def joined_to_domain?
+ node_workgroup.empty? && !node_domain.empty?
+ end
+
+ #
+ # @return [true, false] Whether or not the node is joined to the domain
+ # defined by the resource :domain_name property.
+ #
+ def on_desired_domain?
+ node_domain == new_resource.domain_name.downcase
+ end
+
+ #
+ # @return [String] the correct user and domain to use.
+ # if the domain_user property contains an @ symbol followed by any number of non white space characters
+ # then we assume it is a user from another domain than the one specified in the resource domain_name property.
+ # if this is the case we do not append the domain_name property to the domain_user property
+ # the domain_user and domain_name form the UPN (userPrincipalName)
+ # The specification for the UPN format is RFC 822
+ # links: https://docs.microsoft.com/en-us/windows/win32/ad/naming-properties#userprincipalname https://tools.ietf.org/html/rfc822
+ # regex: https://rubular.com/r/isAWojpTMKzlnp
+ def sanitize_usename
+ if /@/.match?(new_resource.domain_user)
+ new_resource.domain_user
+ else
+ "#{new_resource.domain_user}@#{new_resource.domain_name}"
+ end
+ end
+
+ # This resource historically took `:immediate` and `:delayed` as arguments to the reboot property but then
+ # tried to shove that straight to the `reboot` resource which objected strenuously
+ def clarify_reboot(reboot_action)
+ case reboot_action
+ when :immediate
+ :reboot_now
+ when :delayed
+ :request_reboot
+ else
+ reboot_action
+ end
+ end
+
+ def sensitive?
+ !!new_resource.sensitive
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_audit_policy.rb b/lib/chef/resource/windows_audit_policy.rb
new file mode 100644
index 0000000000..433e18e197
--- /dev/null
+++ b/lib/chef/resource/windows_audit_policy.rb
@@ -0,0 +1,232 @@
+#
+# Author:: Ross Moles (<rmoles@chef.io>)
+# Author:: Rachel Rice (<rrice@chef.io>)
+# Author:: Davin Taddeo (<davin@chef.io>)
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsAuditPolicy < Chef::Resource
+ WIN_AUDIT_SUBCATEGORIES = ["Account Lockout",
+ "Application Generated",
+ "Application Group Management",
+ "Audit Policy Change",
+ "Authentication Policy Change",
+ "Authorization Policy Change",
+ "Central Policy Staging",
+ "Certification Services",
+ "Computer Account Management",
+ "Credential Validation",
+ "DPAPI Activity",
+ "Detailed Directory Service Replication",
+ "Detailed File Share",
+ "Directory Service Access",
+ "Directory Service Changes",
+ "Directory Service Replication",
+ "Distribution Group Management",
+ "File Share",
+ "File System",
+ "Filtering Platform Connection",
+ "Filtering Platform Packet Drop",
+ "Filtering Platform Policy Change",
+ "Group Membership",
+ "Handle Manipulation",
+ "IPsec Driver",
+ "IPsec Extended Mode",
+ "IPsec Main Mode",
+ "IPsec Quick Mode",
+ "Kerberos Authentication Service",
+ "Kerberos Service Ticket Operations",
+ "Kernel Object",
+ "Logoff",
+ "Logon",
+ "MPSSVC Rule-Level Policy Change",
+ "Network Policy Server",
+ "Non Sensitive Privilege Use",
+ "Other Account Logon Events",
+ "Other Account Management Events",
+ "Other Logon/Logoff Events",
+ "Other Object Access Events",
+ "Other Policy Change Events",
+ "Other Privilege Use Events",
+ "Other System Events",
+ "Plug and Play Events",
+ "Process Creation",
+ "Process Termination",
+ "RPC Events",
+ "Registry",
+ "Removable Storage",
+ "SAM",
+ "Security Group Management",
+ "Security State Change",
+ "Security System Extension",
+ "Sensitive Privilege Use",
+ "Special Logon",
+ "System Integrity",
+ "Token Right Adjusted Events",
+ "User / Device Claims",
+ "User Account Management",
+ ].freeze
+
+ unified_mode true
+
+ provides :windows_audit_policy
+
+ description "Use the **windows_audit_policy** resource to configure system level and per-user Windows advanced audit policy settings."
+ introduced "16.2"
+
+ examples <<~DOC
+ **Set Logon and Logoff policy to "Success and Failure"**:
+
+ ```ruby
+ windows_audit_policy "Set Audit Policy for 'Logon and Logoff' actions to 'Success and Failure'" do
+ subcategory %w(Logon Logoff)
+ success true
+ failure true
+ action :set
+ end
+ ```
+
+ **Set Credential Validation policy to "Success"**:
+
+ ```ruby
+ windows_audit_policy "Set Audit Policy for 'Credential Validation' actions to 'Success'" do
+ subcategory 'Credential Validation'
+ success true
+ failure false
+ action :set
+ end
+ ```
+
+ **Enable CrashOnAuditFail option**:
+
+ ```ruby
+ windows_audit_policy 'Enable CrashOnAuditFail option' do
+ crash_on_audit_fail true
+ action :set
+ end
+ ```
+ DOC
+
+ property :subcategory, [String, Array],
+ coerce: proc { |p| Array(p) },
+ description: "The audit policy subcategory, specified by GUID or name. Applied system-wide if no user is specified.",
+ callbacks: { "Subcategories entered should be actual advanced audit policy subcategories" => proc { |n| (Array(n) - WIN_AUDIT_SUBCATEGORIES).empty? } }
+
+ property :success, [true, false],
+ description: "Specify success auditing. By setting this property to true the resource will enable success for the category or sub category. Success is the default and is applied if neither success nor failure are specified."
+
+ property :failure, [true, false],
+ description: "Specify failure auditing. By setting this property to true the resource will enable failure for the category or sub category. Success is the default and is applied if neither success nor failure are specified."
+
+ property :include_user, String,
+ description: "The audit policy specified by the category or subcategory is applied per-user if specified. When a user is specified, include user. Include and exclude cannot be used at the same time."
+
+ property :exclude_user, String,
+ description: "The audit policy specified by the category or subcategory is applied per-user if specified. When a user is specified, exclude user. Include and exclude cannot be used at the same time."
+
+ property :crash_on_audit_fail, [true, false],
+ description: "Setting this audit policy option to true will cause the system to crash if the auditing system is unable to log events."
+
+ property :full_privilege_auditing, [true, false],
+ description: "Setting this audit policy option to true will force the audit of all privilege changes except SeAuditPrivilege. Setting this property may cause the logs to fill up more quickly."
+
+ property :audit_base_objects, [true, false],
+ description: "Setting this audit policy option to true will force the system to assign a System Access Control List to named objects to enable auditing of base objects such as mutexes."
+
+ property :audit_base_directories, [true, false],
+ description: "Setting this audit policy option to true will force the system to assign a System Access Control List to named objects to enable auditing of container objects such as directories."
+
+ action :set do
+ unless new_resource.subcategory.nil?
+ new_resource.subcategory.each do |subcategory|
+ next if subcategory_configured?(subcategory, new_resource.success, new_resource.failure)
+
+ s_val = new_resource.success ? "enable" : "disable"
+ f_val = new_resource.failure ? "enable" : "disable"
+ converge_by "Update Audit Policy for \"#{subcategory}\" to Success:#{s_val} and Failure:#{f_val}" do
+ cmd = "auditpol /set "
+ cmd += "/user:\"#{new_resource.include_user}\" /include " if new_resource.include_user
+ cmd += "/user:\"#{new_resource.exclude_user}\" /exclude " if new_resource.exclude_user
+ cmd += "/subcategory:\"#{subcategory}\" /success:#{s_val} /failure:#{f_val}"
+ powershell_exec!(cmd)
+ end
+ end
+ end
+
+ if !new_resource.crash_on_audit_fail.nil? && option_configured?("CrashOnAuditFail", new_resource.crash_on_audit_fail)
+ val = new_resource.crash_on_audit_fail ? "Enable" : "Disable"
+ converge_by "Configure Audit: CrashOnAuditFail to #{val}" do
+ cmd = "auditpol /set /option:CrashOnAuditFail /value:#{val}"
+ powershell_exec!(cmd)
+ end
+ end
+
+ if !new_resource.full_privilege_auditing.nil? && option_configured?("FullPrivilegeAuditing", new_resource.full_privilege_auditing)
+ val = new_resource.full_privilege_auditing ? "Enable" : "Disable"
+ converge_by "Configure Audit: FullPrivilegeAuditing to #{val}" do
+ cmd = "auditpol /set /option:FullPrivilegeAuditing /value:#{val}"
+ powershell_exec!(cmd)
+ end
+ end
+
+ if !new_resource.audit_base_directories.nil? && option_configured?("AuditBaseDirectories", new_resource.audit_base_directories)
+ val = new_resource.audit_base_directories ? "Enable" : "Disable"
+ converge_by "Configure Audit: AuditBaseDirectories to #{val}" do
+ cmd = "auditpol /set /option:AuditBaseDirectories /value:#{val}"
+ powershell_exec!(cmd)
+ end
+ end
+
+ if !new_resource.audit_base_objects.nil? && option_configured?("AuditBaseObjects", new_resource.audit_base_objects)
+ val = new_resource.audit_base_objects ? "Enable" : "Disable"
+ converge_by "Configure Audit: AuditBaseObjects to #{val}" do
+ cmd = "auditpol /set /option:AuditBaseObjects /value:#{val}"
+ powershell_exec!(cmd)
+ end
+ end
+ end
+
+ action_class do
+ def subcategory_configured?(sub_cat, success_value, failure_value)
+ setting = if success_value && failure_value
+ "Success and Failure$"
+ elsif success_value && !failure_value
+ "Success$"
+ elsif !success_value && failure_value
+ "#{sub_cat}\\s+Failure$"
+ else
+ "No Auditing"
+ end
+ powershell_exec!(<<-CODE).result
+ $auditpol_config = auditpol /get /subcategory:"#{sub_cat}"
+ if ($auditpol_config | Select-String "#{setting}") { return $true } else { return $false }
+ CODE
+ end
+
+ def option_configured?(option_name, option_setting)
+ setting = option_setting ? "Enabled$" : "Disabled$"
+ powershell_exec!(<<-CODE).result
+ $auditpol_config = auditpol /get /option:#{option_name}
+ if ($auditpol_config | Select-String "#{setting}") { return $true } else { return $false }
+ CODE
+ 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..4885a02676
--- /dev/null
+++ b/lib/chef/resource/windows_auto_run.rb
@@ -0,0 +1,99 @@
+#
+# Author:: Paul Morton (<pmorton@biaprotect.com>)
+# Copyright:: 2011-2018, Business Intelligence Associates, Inc.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsAutorun < Chef::Resource
+ unified_mode true
+
+ provides(:windows_auto_run) { true }
+
+ description "Use the **windows_auto_run** resource to set applications to run at login."
+ introduced "14.0"
+ examples <<~DOC
+ **Run BGInfo at login**
+
+ ```ruby
+ windows_auto_run 'BGINFO' do
+ program 'C:/Sysinternals/bginfo.exe'
+ args '\'C:/Sysinternals/Config.bgi\' /NOLICPROMPT /TIMER:0'
+ action :create
+ end
+ ```
+ DOC
+
+ property :program_name, String,
+ description: "The name of the program to run at login if it differs from the resource block's 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 that will run at login."
+
+ property :args, String,
+ description: "Any arguments to be used with 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
+
+ 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"
+
+ 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/windows_certificate.rb b/lib/chef/resource/windows_certificate.rb
new file mode 100644
index 0000000000..5800fe0f45
--- /dev/null
+++ b/lib/chef/resource/windows_certificate.rb
@@ -0,0 +1,356 @@
+#
+# Author:: Richard Lavey (richard.lavey@calastone.com)
+#
+# Copyright:: 2015-2017, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../util/path_helper"
+require_relative "../resource"
+module Win32
+ autoload :Certstore, "win32-certstore" if Chef::Platform.windows?
+end
+autoload :OpenSSL, "openssl"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class WindowsCertificate < Chef::Resource
+ unified_mode true
+
+ provides :windows_certificate
+
+ description "Use the **windows_certificate** resource to install a certificate into the Windows certificate store from a file. The resource grants read-only access to the private key for designated accounts. Due to current limitations in WinRM, installing certificates remotely may not work if the operation requires a user profile. Operations on the local machine store should still work."
+ introduced "14.7"
+ examples <<~DOC
+ **Add PFX cert to local machine personal store and grant accounts read-only access to private key**
+
+ ```ruby
+ windows_certificate 'c:/test/mycert.pfx' do
+ pfx_password 'password'
+ private_key_acl ["acme\\fred", "pc\\jane"]
+ end
+ ```
+
+ **Add cert to trusted intermediate store**
+
+ ```ruby
+ windows_certificate 'c:/test/mycert.cer' do
+ store_name 'CA'
+ end
+ ```
+
+ **Remove all certificates matching the subject**
+
+ ```ruby
+ windows_certificate 'me.acme.com' do
+ action :delete
+ end
+ ```
+ DOC
+
+ property :source, String,
+ description: "The source file (for create and acl_add), thumbprint (for delete and acl_add) or subject (for delete) if it differs from the resource block's name.",
+ name_property: true
+
+ property :pfx_password, String,
+ description: "The password to access the source if it is a pfx file."
+
+ property :private_key_acl, Array,
+ description: "An array of 'domain\\account' entries to be granted read-only access to the certificate's private key. Not idempotent."
+
+ property :store_name, String,
+ description: "The certificate store to manipulate.",
+ default: "MY", equal_to: ["TRUSTEDPUBLISHER", "TrustedPublisher", "CLIENTAUTHISSUER", "REMOTE DESKTOP", "ROOT", "TRUSTEDDEVICES", "WEBHOSTING", "CA", "AUTHROOT", "TRUSTEDPEOPLE", "MY", "SMARTCARDROOT", "TRUST", "DISALLOWED"]
+
+ property :user_store, [TrueClass, FalseClass],
+ description: "Use the user store of the local machine store if set to false.",
+ default: false
+
+ property :cert_path, String,
+ description: "The path to the certificate."
+
+ # lazy used to set default value of sensitive to true if password is set
+ property :sensitive, [TrueClass, FalseClass],
+ description: "Ensure that sensitive resource data is not logged by the #{ChefUtils::Dist::Infra::CLIENT}.",
+ default: lazy { pfx_password ? true : false }, skip_docs: true
+
+ property :exportable, [TrueClass, FalseClass],
+ description: "Ensure that imported pfx certificate is exportable. Please provide 'true' if you want the certificate to be exportable.",
+ default: false,
+ introduced: "16.8"
+
+ action :create do
+ description "Creates or updates a certificate."
+
+ # Extension of the certificate
+ ext = ::File.extname(new_resource.source)
+
+ # PFX certificates contains private keys and we import them with some other approach
+ import_certificates(fetch_cert_object(ext), (ext == ".pfx"))
+ end
+
+ # acl_add is a modify-if-exists operation : not idempotent
+ action :acl_add do
+ description "Adds read-only entries to a certificate's private key ACL."
+
+ if ::File.exist?(new_resource.source)
+ hash = "$cert.GetCertHashString()"
+ code_script = cert_script(false)
+ guard_script = cert_script(false)
+ else
+ # make sure we have no spaces in the hash string
+ hash = "\"#{new_resource.source.gsub(/\s/, "")}\""
+ code_script = ""
+ guard_script = ""
+ end
+ code_script << acl_script(hash)
+ guard_script << cert_exists_script(hash)
+
+ powershell_script "setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}" do
+ convert_boolean_return true
+ code code_script
+ only_if guard_script
+ sensitive if new_resource.sensitive
+ end
+ end
+
+ action :delete do
+ description "Deletes a certificate."
+ cert_obj = fetch_cert
+ if cert_obj
+ converge_by("Deleting certificate #{new_resource.source} from Store #{new_resource.store_name}") do
+ delete_cert
+ end
+ else
+ Chef::Log.debug("Certificate not found")
+ end
+ end
+
+ action :fetch do
+ description "Fetches a certificate."
+
+ cert_obj = fetch_cert
+ if cert_obj
+ show_or_store_cert(cert_obj)
+ else
+ Chef::Log.debug("Certificate not found")
+ end
+ end
+
+ action :verify do
+ description ""
+
+ out = verify_cert
+ if !!out == out
+ out = out ? "Certificate is valid" : "Certificate not valid"
+ end
+ Chef::Log.info(out.to_s)
+ end
+
+ action_class do
+ def add_cert(cert_obj)
+ store = ::Win32::Certstore.open(new_resource.store_name)
+ store.add(cert_obj)
+ end
+
+ def add_pfx_cert
+ exportable = new_resource.exportable ? 1 : 0
+ store = ::Win32::Certstore.open(new_resource.store_name)
+ store.add_pfx(new_resource.source, new_resource.pfx_password, exportable)
+ end
+
+ def delete_cert
+ store = ::Win32::Certstore.open(new_resource.store_name)
+ store.delete(new_resource.source)
+ end
+
+ def fetch_cert
+ store = ::Win32::Certstore.open(new_resource.store_name)
+ store.get(new_resource.source)
+ end
+
+ # Checks whether a certificate with the given thumbprint
+ # is already present and valid in certificate store
+ # If the certificate is not present, verify_cert returns a String: "Certificate not found"
+ # But if it is present but expired, it returns a Boolean: false
+ # Otherwise, it returns a Boolean: true
+ def verify_cert(thumbprint = new_resource.source)
+ store = ::Win32::Certstore.open(new_resource.store_name)
+ store.valid?(thumbprint)
+ end
+
+ def show_or_store_cert(cert_obj)
+ if new_resource.cert_path
+ export_cert(cert_obj, new_resource.cert_path)
+ if ::File.size(new_resource.cert_path) > 0
+ Chef::Log.info("Certificate export in #{new_resource.cert_path}")
+ else
+ ::File.delete(new_resource.cert_path)
+ end
+ else
+ Chef::Log.info(cert_obj.display)
+ end
+ end
+
+ def export_cert(cert_obj, cert_path)
+ out_file = ::File.new(cert_path, "w+")
+ case ::File.extname(cert_path)
+ when ".pem"
+ out_file.puts(cert_obj.to_pem)
+ when ".der"
+ out_file.puts(cert_obj.to_der)
+ when ".cer"
+ cert_out = shell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CER").stdout
+ out_file.puts(cert_out)
+ when ".crt"
+ cert_out = shell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CRT").stdout
+ out_file.puts(cert_out)
+ when ".pfx"
+ cert_out = shell_out("openssl pkcs12 -export -nokeys -in #{cert_obj.to_pem} -outform PFX").stdout
+ out_file.puts(cert_out)
+ when ".p7b"
+ cert_out = shell_out("openssl pkcs7 -export -nokeys -in #{cert_obj.to_pem} -outform P7B").stdout
+ out_file.puts(cert_out)
+ else
+ Chef::Log.info("Supported certificate format .pem, .der, .cer, .crt, .pfx and .p7b")
+ end
+ out_file.close
+ end
+
+ def cert_location
+ @location ||= new_resource.user_store ? "CurrentUser" : "LocalMachine"
+ end
+
+ def cert_script(persist)
+ cert_script = "$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2"
+ file = Chef::Util::PathHelper.cleanpath(new_resource.source)
+ cert_script << " \"#{file}\""
+ if ::File.extname(file.downcase) == ".pfx"
+ cert_script << ", \"#{new_resource.pfx_password}\""
+ if persist && new_resource.user_store
+ cert_script << ", ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet)"
+ elsif persist
+ cert_script << ", ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset)"
+ end
+ end
+ cert_script << "\n"
+ end
+
+ def cert_exists_script(hash)
+ <<-EOH
+ $hash = #{hash}
+ Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
+ EOH
+ end
+
+ def within_store_script
+ inner_script = yield "$store"
+ <<-EOH
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{cert_location})
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ #{inner_script}
+ $store.Close()
+ EOH
+ end
+
+ def acl_script(hash)
+ return "" if new_resource.private_key_acl.nil? || new_resource.private_key_acl.empty?
+
+ # this PS came from http://blogs.technet.com/b/operationsguy/archive/2010/11/29/provide-access-to-private-keys-commandline-vs-powershell.aspx
+ # and from https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx
+ set_acl_script = <<-EOH
+ $hash = #{hash}
+ $storeCert = Get-ChildItem "cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
+ if ($storeCert -eq $null) { throw 'no key exists.' }
+ $keyname = $storeCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
+ if ($keyname -eq $null) { throw 'no private key exists.' }
+ if ($storeCert.PrivateKey.CspKeyContainerInfo.MachineKeyStore)
+ {
+ $fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\MachineKeys\\$keyname"
+ }
+ else
+ {
+ $currentUser = New-Object System.Security.Principal.NTAccount($Env:UserDomain, $Env:UserName)
+ $userSID = $currentUser.Translate([System.Security.Principal.SecurityIdentifier]).Value
+ $fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\$userSID\\$keyname"
+ }
+ EOH
+ new_resource.private_key_acl.each do |name|
+ set_acl_script << "$uname='#{name}'; icacls $fullpath /grant $uname`:RX\n"
+ end
+ set_acl_script
+ end
+
+ # Method returns an OpenSSL::X509::Certificate object. Might also return multiple certificates if present in certificate path
+ #
+ # Based on its extension, the certificate contents are used to initialize
+ # PKCS12 (PFX), PKCS7 (P7B) objects which contains OpenSSL::X509::Certificate.
+ #
+ # @note Other then PEM, all the certificates are usually in binary format, and hence
+ # their contents are loaded by using File.binread
+ #
+ # @param ext [String] Extension of the certificate
+ #
+ # @return [OpenSSL::X509::Certificate] Object containing certificate's attributes
+ #
+ # @raise [OpenSSL::PKCS12::PKCS12Error] When incorrect password is provided for PFX certificate
+ #
+ def fetch_cert_object(ext)
+ contents = ::File.binread(new_resource.source)
+
+ case ext
+ when ".pfx"
+ pfx = OpenSSL::PKCS12.new(contents, new_resource.pfx_password)
+ if pfx.ca_certs.nil?
+ pfx.certificate
+ else
+ [pfx.certificate] + pfx.ca_certs
+ end
+ when ".p7b"
+ OpenSSL::PKCS7.new(contents).certificates
+ else
+ OpenSSL::X509::Certificate.new(contents)
+ end
+ end
+
+ # Imports the certificate object into cert store
+ #
+ # @param cert_objs [OpenSSL::X509::Certificate] Object containing certificate's attributes
+ #
+ # @param is_pfx [Boolean] true if we want to import a PFX certificate
+ #
+ def import_certificates(cert_objs, is_pfx)
+ [cert_objs].flatten.each do |cert_obj|
+ thumbprint = OpenSSL::Digest.new("SHA1", cert_obj.to_der).to_s # Fetch its thumbprint
+ # Need to check if return value is Boolean:true
+ # If not then the given certificate should be added in certstore
+ if verify_cert(thumbprint) == true
+ Chef::Log.debug("Certificate is already present")
+ else
+ converge_by("Adding certificate #{new_resource.source} into Store #{new_resource.store_name}") do
+ if is_pfx
+ add_pfx_cert
+ else
+ add_cert(cert_obj)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dfs_folder.rb b/lib/chef/resource/windows_dfs_folder.rb
new file mode 100644
index 0000000000..31f6814bcf
--- /dev/null
+++ b/lib/chef/resource/windows_dfs_folder.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Jason Field
+#
+# Copyright:: 2018, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsDfsFolder < Chef::Resource
+ unified_mode true
+
+ provides :windows_dfs_folder
+
+ description "Use the **windows_dfs_folder** resource to creates a folder within DFS as many levels deep as required."
+ introduced "15.0"
+
+ property :folder_path, String,
+ description: "An optional property to set the path of the dfs folder if it differs from the resource block's name.",
+ name_property: true
+
+ property :namespace_name, String,
+ description: "The namespace this should be created within.",
+ required: true
+
+ property :target_path, String,
+ description: "The target that this path will connect you to."
+
+ property :description, String,
+ description: "Description for the share."
+
+ action :create do
+ description "Creates the folder in dfs namespace."
+
+ raise "target_path is required for install" unless property_is_set?(:target_path)
+ raise "description is required for install" unless property_is_set?(:description)
+
+ powershell_script "Create or Update DFS Folder" do
+ code <<-EOH
+
+ $needs_creating = (Get-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -ErrorAction SilentlyContinue) -eq $null
+ if (!($needs_creating))
+ {
+ Remove-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -Force
+ }
+ New-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -TargetPath '#{new_resource.target_path}' -Description '#{new_resource.description}'
+ EOH
+ not_if "return ((Get-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -ErrorAction SilentlyContinue).Description -eq '#{new_resource.description}' -and (Get-DfsnFolderTarget -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}').TargetPath -eq '#{new_resource.target_path}' )"
+ end
+ end
+
+ action :delete do
+ description "Deletes the folder in the dfs namespace."
+
+ powershell_script "Delete DFS Namespace" do
+ code <<-EOH
+ Remove-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' -Force
+ EOH
+ only_if "return ((Get-DfsnFolder -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}\\#{new_resource.folder_path}' ) -ne $null)"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dfs_namespace.rb b/lib/chef/resource/windows_dfs_namespace.rb
new file mode 100644
index 0000000000..ddd8a0ee26
--- /dev/null
+++ b/lib/chef/resource/windows_dfs_namespace.rb
@@ -0,0 +1,115 @@
+#
+# Author:: Jason Field
+#
+# Copyright:: 2018, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsDfsNamespace < Chef::Resource
+ unified_mode true
+
+ provides :windows_dfs_namespace
+
+ description "Use the **windows_dfs_namespace** resource to creates a share and DFS namespace on a Windows server."
+ introduced "15.0"
+
+ property :namespace_name, String,
+ description: "An optional property to set the dfs namespace if it differs from the resource block's name.",
+ name_property: true
+
+ property :description, String,
+ description: "Description of the share.",
+ required: true
+
+ property :full_users, Array,
+ description: "Determines which users should have full access to the share.",
+ default: ['BUILTIN\\administrators']
+
+ property :change_users, Array,
+ description: "Determines which users should have change access to the share.",
+ default: []
+
+ property :read_users, Array,
+ description: "Determines which users should have read access to the share.",
+ default: []
+
+ property :root, String,
+ description: "The root from which to create the DFS tree. Defaults to C:\\DFSRoots.",
+ default: 'C:\\DFSRoots'
+
+ action :create do
+ description "Creates the dfs namespace on the server."
+
+ directory file_path do
+ action :create
+ recursive true
+ end
+
+ windows_share new_resource.namespace_name do
+ action :create
+ path file_path
+ full_users new_resource.full_users
+ change_users new_resource.change_users
+ read_users new_resource.read_users
+ end
+
+ powershell_script "Create DFS Namespace" do
+ code <<-EOH
+ $needs_creating = (Get-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -ErrorAction SilentlyContinue) -eq $null
+ if ($needs_creating)
+ {
+ New-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -TargetPath '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -Type Standalone -Description '#{new_resource.description}'
+ }
+ else
+ {
+ Set-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -Description '#{new_resource.description}'
+ }
+ EOH
+ not_if "return (Get-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -ErrorAction SilentlyContinue).description -eq '#{new_resource.description}'"
+ end
+ end
+
+ action :delete do
+ description "Deletes a DFS Namespace including the directory on disk."
+
+ powershell_script "Delete DFS Namespace" do
+ code <<-EOH
+ Remove-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}' -Force
+ EOH
+ only_if "return ((Get-DfsnRoot -Path '\\\\#{ENV["COMPUTERNAME"]}\\#{new_resource.namespace_name}') -ne $null)"
+ end
+
+ windows_share new_resource.namespace_name do
+ action :delete
+ path file_path
+ end
+
+ directory file_path do
+ action :delete
+ recursive false # I will remove the top level but not any sub levels.
+ end
+ end
+
+ action_class do
+ def file_path
+ "#{new_resource.root}\\#{new_resource.namespace_name}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dfs_server.rb b/lib/chef/resource/windows_dfs_server.rb
new file mode 100644
index 0000000000..fc161f8189
--- /dev/null
+++ b/lib/chef/resource/windows_dfs_server.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Jason Field
+#
+# Copyright:: 2018, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsDfsServer < Chef::Resource
+ unified_mode true
+
+ provides :windows_dfs_server
+
+ description "Use the **windows_dfs_server** resource to set system-wide DFS settings."
+ introduced "15.0"
+
+ property :use_fqdn, [TrueClass, FalseClass],
+ description: "Indicates whether a DFS namespace server uses FQDNs in referrals. If this property is set to true, the server uses FQDNs in referrals. If this property is set to false then the server uses NetBIOS names.",
+ default: false
+
+ property :ldap_timeout_secs, Integer,
+ description: "",
+ default: 30
+
+ property :prefer_login_dc, [TrueClass, FalseClass],
+ description: "",
+ default: false
+
+ property :enable_site_costed_referrals, [TrueClass, FalseClass],
+ description: "",
+ default: false
+
+ property :sync_interval_secs, Integer,
+ description: "",
+ default: 3600
+
+ load_current_value do
+ ps_results = powershell_exec("Get-DfsnServerConfiguration -ComputerName '#{ENV["COMPUTERNAME"]}' | Select LdapTimeoutSec, PreferLogonDC, EnableSiteCostedReferrals, SyncIntervalSec, UseFqdn")
+
+ if ps_results.error?
+ raise "The dfs_server resource failed to fetch the current state via the Get-DfsnServerConfiguration PowerShell cmdlet. Is the DFS Windows feature installed?"
+ end
+
+ Chef::Log.debug("The Get-DfsnServerConfiguration results were #{ps_results.result}")
+ results = ps_results.result
+
+ use_fqdn results["UseFqdn"] || false
+ ldap_timeout_secs results["LdapTimeoutSec"]
+ prefer_login_dc results["PreferLogonDC"] || false
+ enable_site_costed_referrals results["EnableSiteCostedReferrals"] || false
+ sync_interval_secs results["SyncIntervalSec"]
+ end
+
+ action :configure do
+ description "Configure DFS settings."
+
+ converge_if_changed do
+ dfs_cmd = "Set-DfsnServerConfiguration -ComputerName '#{ENV["COMPUTERNAME"]}' -UseFqdn $#{new_resource.use_fqdn} -LdapTimeoutSec #{new_resource.ldap_timeout_secs} -SyncIntervalSec #{new_resource.sync_interval_secs}"
+ dfs_cmd << " -EnableSiteCostedReferrals $#{new_resource.enable_site_costed_referrals}" if new_resource.enable_site_costed_referrals != current_resource.enable_site_costed_referrals
+ dfs_cmd << " -PreferLogonDC $#{new_resource.prefer_login_dc}" if new_resource.prefer_login_dc != current_resource.prefer_login_dc
+ powershell_exec!(dfs_cmd)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dns_record.rb b/lib/chef/resource/windows_dns_record.rb
new file mode 100644
index 0000000000..329e1a3857
--- /dev/null
+++ b/lib/chef/resource/windows_dns_record.rb
@@ -0,0 +1,95 @@
+#
+# Author:: Jason Field
+#
+# Copyright:: 2018, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsDnsRecord < Chef::Resource
+ unified_mode true
+
+ provides :windows_dns_record
+
+ description "The windows_dns_record resource creates a DNS record for the given domain."
+ introduced "15.0"
+
+ property :record_name, String,
+ description: "An optional property to set the dns record name if it differs from the resource block's name.",
+ name_property: true
+
+ property :zone, String,
+ description: "The zone to create the record in.",
+ required: true
+
+ property :target, String,
+ description: "The target for the record.",
+ required: true
+
+ property :record_type, String,
+ description: "The type of record to create, can be either ARecord, CNAME or PTR.",
+ default: "ARecord", equal_to: %w{ARecord CNAME PTR}
+
+ property :dns_server, String,
+ description: "The name of the DNS server on which to create the record.",
+ default: "localhost",
+ introduced: "16.3"
+
+ action :create do
+ description "Creates and updates the DNS entry."
+
+ windows_feature "RSAT-DNS-Server" do
+ not_if new_resource.dns_server.casecmp?("localhost")
+ end
+
+ powershell_package "xDnsServer"
+
+ run_dsc_resource "Present"
+ end
+
+ action :delete do
+ description "Deletes a DNS entry."
+
+ windows_feature "RSAT-DNS-Server" do
+ not_if new_resource.dns_server.casecmp?("localhost")
+ end
+
+ powershell_package "xDnsServer"
+
+ run_dsc_resource "Absent"
+ end
+
+ action_class do
+ private
+
+ # @api private
+ def run_dsc_resource(ensure_prop)
+ dsc_resource "xDnsRecord #{new_resource.record_name}.#{new_resource.zone} #{ensure_prop}" do
+ module_name "xDnsServer"
+ resource :xDnsRecord
+ property :Ensure, ensure_prop
+ property :Name, new_resource.record_name
+ property :Zone, new_resource.zone
+ property :Type, new_resource.record_type
+ property :Target, new_resource.target
+ property :DnsServer, new_resource.dns_server
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_dns_zone.rb b/lib/chef/resource/windows_dns_zone.rb
new file mode 100644
index 0000000000..09555c880c
--- /dev/null
+++ b/lib/chef/resource/windows_dns_zone.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Jason Field
+#
+# Copyright:: 2018, Calastone Ltd.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsDnsZone < Chef::Resource
+ unified_mode true
+
+ provides :windows_dns_zone
+
+ description "The windows_dns_zone resource creates an Active Directory Integrated DNS Zone on the local server."
+ introduced "15.0"
+
+ property :zone_name, String,
+ description: "An optional property to set the dns zone name if it differs from the resource block's name.",
+ name_property: true
+
+ property :replication_scope, String,
+ description: "The replication scope for the zone, required if server_type set to 'Domain'.",
+ default: "Domain"
+
+ property :server_type, String,
+ description: "The type of DNS server, Domain or Standalone.",
+ default: "Domain", equal_to: %w{Domain Standalone}
+
+ action :create do
+ description "Creates and updates a DNS Zone."
+
+ powershell_package "xDnsServer"
+
+ run_dsc_resource "Present"
+ end
+
+ action :delete do
+ description "Deletes a DNS Zone."
+
+ powershell_package "xDnsServer"
+
+ run_dsc_resource "Absent"
+ end
+
+ action_class do
+ private
+
+ # @api private
+ def run_dsc_resource(ensure_prop)
+ if new_resource.server_type == "Domain"
+ dsc_resource "xDnsServerADZone #{new_resource.zone_name} #{ensure_prop}" do
+ module_name "xDnsServer"
+ resource :xDnsServerADZone
+ property :Ensure, ensure_prop
+ property :Name, new_resource.zone_name
+ property :ReplicationScope, new_resource.replication_scope
+ end
+ elsif new_resource.server_type == "Standalone"
+ dsc_resource "xDnsServerPrimaryZone #{new_resource.zone_name} #{ensure_prop}" do
+ module_name "xDnsServer"
+ resource :xDnsServerPrimaryZone
+ property :Ensure, ensure_prop
+ property :Name, new_resource.zone_name
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_env.rb b/lib/chef/resource/windows_env.rb
new file mode 100644
index 0000000000..ab65465ed6
--- /dev/null
+++ b/lib/chef/resource/windows_env.rb
@@ -0,0 +1,230 @@
+#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# 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_relative "../resource"
+require_relative "../mixin/windows_env_helper"
+
+class Chef
+ class Resource
+ class WindowsEnv < Chef::Resource
+ unified_mode true
+
+ provides :windows_env
+ provides :env # backwards compat with the pre-Chef 14 resource name
+
+ description "Use the **windows_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."
+ examples <<~DOC
+ **Set an environment variable**:
+
+ ```ruby
+ windows_env 'ComSpec' do
+ value 'C:\\Windows\\system32\\cmd.exe'
+ end
+ ```
+ DOC
+
+ default_action :create
+ allowed_actions :create, :delete, :modify
+
+ property :key_name, String,
+ description: "An optional property to set the name of the key that is to be created, deleted, or modified if it differs from the resource block's name.",
+ name_property: true
+
+ property :value, String,
+ description: "The value of the environmental variable to set.",
+ required: %i{create modify}
+
+ property :delim, [ String, nil, false ],
+ description: "The delimiter that is used to separate multiple values for a single key.",
+ desired_state: false
+
+ property :user, String, default: "<System>"
+
+ action_class do
+ include Chef::Mixin::WindowsEnvHelper
+
+ def whyrun_supported?
+ false
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsEnv.new(new_resource.name)
+ current_resource.key_name(new_resource.key_name)
+
+ if key_exists?
+ current_resource.value(env_value(new_resource.key_name))
+ else
+ logger.trace("#{new_resource} key does not exist")
+ end
+
+ current_resource
+ end
+
+ def key_exists?
+ @key_exists ||= !!env_value(new_resource.key_name)
+ end
+
+ 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?
+
+ # 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")
+ 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?
+ 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)
+ true # we removed the element and updated; do not delete the key
+ end
+ 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
+
+ action :create do
+ 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
+
+ action :delete do
+ 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
+
+ action :modify do
+ 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
+ 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..760a7fe3f1
--- /dev/null
+++ b/lib/chef/resource/windows_feature.rb
@@ -0,0 +1,149 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsFeature < Chef::Resource
+ unified_mode true
+
+ provides(:windows_feature) { true }
+
+ description "Use the **windows_feature** resource to add, remove or entirely 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"
+ examples <<~DOC
+ **Install the DHCP Server feature**:
+
+ ```ruby
+ windows_feature 'DHCPServer' do
+ action :install
+ end
+ ```
+
+ **Install the .Net 3.5.1 feature using repository files on DVD**:
+
+ ```ruby
+ windows_feature "NetFx3" do
+ action :install
+ source 'd:\\sources\\sxs'
+ end
+ ```
+
+ **Remove Telnet Server and Client features**:
+
+ ```ruby
+ windows_feature %w(TelnetServer TelnetClient) do
+ action :remove
+ end
+ ```
+
+ **Add the SMTP Server feature using the PowerShell provider**:
+
+ ```ruby
+ windows_feature 'smtp-server' do
+ action :install
+ all true
+ install_method :windows_feature_powershell
+ end
+ ```
+
+ **Install multiple features using one resource with the PowerShell provider**:
+
+ ```ruby
+ windows_feature %w(Web-Asp-Net45 Web-Net-Ext45) do
+ action :install
+ install_method :windows_feature_powershell
+ end
+ ```
+
+ **Install the Network Policy and Access Service feature, including the management tools**:
+
+ ```ruby
+ windows_feature 'NPAS' do
+ action :install
+ management_tools true
+ install_method :windows_feature_powershell
+ end
+ ```
+ DOC
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature(s) or role(s) to install if they differ from the resource block's name. The same feature may have different names depending on the underlying installation method being used (ie DHCPServer vs DHCP; DNS-Server-Full-Role vs DNS).",
+ name_property: true
+
+ property :source, String,
+ description: "Specify 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 for the roles, role services, or features (PowerShell-only).",
+ default: false
+
+ property :install_method, Symbol,
+ description: "The underlying installation method to use for feature installation. Specify `:windows_feature_dism` for DISM or `:windows_feature_powershell` for PowerShell.",
+ equal_to: %i{windows_feature_dism windows_feature_powershell windows_feature_servermanagercmd},
+ default: :windows_feature_dism
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for the feature installation.",
+ default: 600,
+ desired_state: false
+
+ 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
+ private
+
+ # 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 recipe to install either via dism or powershell (preferred)." if new_resource.install_method == :windows_feature_servermanagercmd
+
+ declare_resource(new_resource.install_method, 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 new_resource.install_method == :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..c9e2f355dc
--- /dev/null
+++ b/lib/chef/resource/windows_feature_dism.rb
@@ -0,0 +1,233 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+#
+# Copyright:: Copyright (c) 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_relative "../resource"
+require_relative "../platform/query_helpers"
+
+class Chef
+ class Resource
+ class WindowsFeatureDism < Chef::Resource
+ unified_mode true
+
+ provides(:windows_feature_dism) { true }
+
+ description "Use the **windows_feature_dism** resource to add, remove, or entirely delete Windows features and roles using DISM."
+ introduced "14.0"
+ examples <<~DOC
+ **Installing the TelnetClient service**:
+
+ ```ruby
+ windows_feature_dism "TelnetClient"
+ ```
+
+ **Installing two features by using an array**:
+
+ ```ruby
+ windows_feature_dism %w(TelnetClient TFTP)
+ ```
+ DOC
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature(s) or role(s) to install if they differ from the resource name.",
+ coerce: proc { |x| to_formatted_array(x) },
+ name_property: true
+
+ property :source, String,
+ description: "Specify a local repository for the feature install."
+
+ property :all, [TrueClass, FalseClass],
+ description: "Install all sub-features. When set to `true`, this is the equivalent of specifying the `/All` switch to `dism.exe`",
+ default: false
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for the feature installation.",
+ default: 600,
+ desired_state: false
+
+ # @return [Array] lowercase the array
+ def to_formatted_array(x)
+ x = x.split(/\s*,\s*/) if x.is_a?(String) # split multiple forms of a comma separated list
+ 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
+
+ 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
+ begin
+ shell_out!(install_command, returns: [0, 42, 127, 3010], timeout: new_resource.timeout)
+ rescue Mixlib::ShellOut::ShellCommandFailed => e
+ raise "Error 50 returned by DISM related to parent features, try setting the 'all' property to 'true' on the 'windows_feature_dism' resource." if required_parent_feature?(e.inspect)
+
+ raise e.message
+ end
+
+ 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"
+
+ 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
+ private
+
+ # @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"].dup
+
+ # removed features are also available for installation
+ available_for_install.concat(node["dism_features_cache"]["removed"])
+
+ # the intersection of the features to install & disabled/removed 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
+
+ #
+ # FIXME FIXME FIXME
+ # The node object should not be used for caching state like this and this is not a public API and may break.
+ # FIXME FIXME FIXME
+ #
+
+ # 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 isn't case sensitive so it's best to compare lowercase lists so the
+ # user input doesn't need to be case sensitive
+ feature_details.downcase!
+ node.override["dism_features_cache"][feature_type] << feature_details
+ end
+
+ def required_parent_feature?(error_message)
+ error_message.include?("Error: 50") && error_message.include?("required parent feature")
+ 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..735ed080ff
--- /dev/null
+++ b/lib/chef/resource/windows_feature_powershell.rb
@@ -0,0 +1,242 @@
+#
+# Author:: Greg Zapp (<greg.zapp@gmail.com>)
+#
+# Copyright:: Copyright (c) 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_relative "../json_compat"
+require_relative "../resource"
+require_relative "../platform/query_helpers"
+
+class Chef
+ class Resource
+ class WindowsFeaturePowershell < Chef::Resource
+ unified_mode true
+
+ provides(:windows_feature_powershell) { true }
+
+ description "Use the **windows_feature_powershell** resource to add, remove, or entirely delete Windows features and roles using PowerShell. This resource offers significant speed benefits over the windows_feature_dism resource, but requires installation of the Remote Server Administration Tools on non-server releases of Windows."
+ introduced "14.0"
+ examples <<~DOC
+ **Add the SMTP Server feature**:
+
+ ```ruby
+ windows_feature_powershell "smtp-server" do
+ action :install
+ all true
+ end
+ ```
+
+ **Install multiple features using one resource**:
+
+ ```ruby
+ windows_feature_powershell ['Web-Asp-Net45', 'Web-Net-Ext45'] do
+ action :install
+ end
+ ```
+
+ **Install the Network Policy and Access Service feature**:
+
+ ```ruby
+ windows_feature_powershell 'NPAS' do
+ action :install
+ management_tools true
+ end
+ ```
+ DOC
+
+ property :feature_name, [Array, String],
+ description: "The name of the feature(s) or role(s) to install if they differ from the resource block's name.",
+ coerce: proc { |x| to_formatted_array(x) },
+ name_property: true
+
+ property :source, String,
+ description: "Specify a local repository for the feature install."
+
+ property :all, [TrueClass, FalseClass],
+ description: "Install all subfeatures. When set to `true`, this is the equivalent of specifying the `-InstallAllSubFeatures` switch with `Add-WindowsFeature`.",
+ default: false
+
+ property :timeout, Integer,
+ description: "Specifies a timeout (in seconds) for the feature installation.",
+ default: 600,
+ desired_state: false
+
+ property :management_tools, [TrueClass, FalseClass],
+ description: "Install all applicable management tools for the roles, role services, or features.",
+ default: false
+
+ # Converts strings of features into an Array. Array objects are lowercased
+ # @return [Array] array of features
+ def to_formatted_array(x)
+ x = x.split(/\s*,\s*/) if x.is_a?(String) # split multiple forms of a comma separated list
+
+ # features aren't case sensitive so let's compare in lowercase
+ x.map(&:downcase)
+ end
+
+ action :install do
+ 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-WindowsFeature #{features_to_install.join(",")}"
+ install_command << " -IncludeAllSubFeature" if new_resource.all
+ install_command << " -Source \"#{new_resource.source}\"" if new_resource.source
+ install_command << " -IncludeManagementTools" if new_resource.management_tools
+
+ 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
+ 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!("Uninstall-WindowsFeature #{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
+ 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
+ # @return [Array] features the user has requested to install which need installation
+ def features_to_install
+ # the intersection of the features to install & disabled/removed features are what needs installing
+ @features_to_install ||= begin
+ features = node["powershell_features_cache"]["disabled"]
+ features |= node["powershell_features_cache"]["removed"] if new_resource.source
+ new_resource.feature_name & features
+ 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["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.")
+
+ #
+ # FIXME FIXME FIXME
+ # The node object should not be used for caching state like this and this is not a public API and may break.
+ # FIXME FIXME FIXME
+ #
+ 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 WindowsFeature
+ raw_list_of_features = powershell_out!("Get-WindowsFeature | Select-Object -Property Name,InstallState | ConvertTo-Json -Compress", timeout: new_resource.timeout).stdout
+
+ 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)
+ # add the lowercase feature name to the mash so we can compare it lowercase later
+ node.override["powershell_features_cache"][feature_type] << feature_details.downcase
+ 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
+ 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
+
+ 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"} removed from the host and cannot be installed." unless removed.empty?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_firewall_profile.rb b/lib/chef/resource/windows_firewall_profile.rb
new file mode 100644
index 0000000000..ada9729699
--- /dev/null
+++ b/lib/chef/resource/windows_firewall_profile.rb
@@ -0,0 +1,196 @@
+#
+# Author:: John McCrae (<jmccrae@chef.io>)
+# Author:: Davin Taddeo (<davin@chef.io>)
+# Copyright:: Copyright (c) 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.
+#
+
+class Chef
+ class Resource
+ class WindowsFirewallProfile < Chef::Resource
+ provides :windows_firewall_profile
+ description "Use the **windows_firewall_profile** resource to enable, disable, and configure the Windows firewall."
+ introduced "16.3"
+
+ examples <<~DOC
+ **Enable and Configure the Private Profile of the Windows Profile**:
+
+ ```ruby
+ windows_firewall_profile 'Private' do
+ default_inbound_action 'Block'
+ default_outbound_action 'Allow'
+ allow_inbound_rules true
+ display_notification false
+ action :enable
+ end
+ ```
+
+ **Enable and Configure the Public Profile of the Windows Firewall**:
+
+ ```ruby
+ windows_firewall_profile 'Public' do
+ default_inbound_action 'Block'
+ default_outbound_action 'Allow'
+ allow_inbound_rules false
+ display_notification false
+ action :enable
+ end
+ ```
+
+ **Disable the Domain Profile of the Windows Firewall**:
+
+ ```ruby
+ windows_firewall_profile 'Disable the Domain Profile of the Windows Firewall' do
+ profile 'Domain'
+ action :disable
+ end
+ ```
+ DOC
+
+ unified_mode true
+
+ property :profile, String,
+ name_property: true,
+ equal_to: %w{ Domain Public Private },
+ description: "Set the Windows Profile being configured"
+
+ property :default_inbound_action, [String, nil],
+ equal_to: %w{ Allow Block NotConfigured },
+ description: "Set the default policy for inbound network traffic"
+
+ property :default_outbound_action, [String, nil],
+ equal_to: %w{ Allow Block NotConfigured },
+ description: "Set the default policy for outbound network traffic"
+
+ property :allow_inbound_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to set inbound firewall rules"
+ property :allow_local_firewall_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Merges inbound firewall rules into the policy"
+ property :allow_local_ipsec_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to manage local connection security rules"
+ property :allow_user_apps, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow user applications to manage firewall"
+ property :allow_user_ports, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to manage firewall port rules"
+ property :allow_unicast_response, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow unicast responses to multicast and broadcast messages"
+ property :display_notification, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Display a notification when firewall blocks certain activity"
+
+ load_current_value do |desired|
+ ps_get_net_fw_profile = load_firewall_state(desired.profile)
+ output = powershell_exec(ps_get_net_fw_profile)
+ if output.result.empty?
+ current_value_does_not_exist!
+ else
+ state = output.result
+ end
+
+ default_inbound_action state["default_inbound_action"]
+ default_outbound_action state["default_outbound_action"]
+ allow_inbound_rules convert_to_ruby(state["allow_inbound_rules"])
+ allow_local_firewall_rules convert_to_ruby(state["allow_local_firewall_rules"])
+ allow_local_ipsec_rules convert_to_ruby(state["allow_local_ipsec_rules"])
+ allow_user_apps convert_to_ruby(state["allow_user_apps"])
+ allow_user_ports convert_to_ruby(state["allow_user_ports"])
+ allow_unicast_response convert_to_ruby(state["allow_unicast_response"])
+ display_notification convert_to_ruby(state["display_notification"])
+ end
+
+ def convert_to_ruby(obj)
+ if obj.to_s.downcase == "true"
+ true
+ elsif obj.to_s.downcase == "false"
+ false
+ elsif obj.to_s.downcase == "notconfigured"
+ "NotConfigured"
+ end
+ end
+
+ def convert_to_powershell(obj)
+ if obj.to_s.downcase == "true"
+ "True"
+ elsif obj.to_s.downcase == "false"
+ "False"
+ elsif obj.to_s.downcase == "notconfigured"
+ "NotConfigured"
+ end
+ end
+
+ action :enable do
+ converge_if_changed :default_inbound_action, :default_outbound_action, :allow_inbound_rules, :allow_local_firewall_rules,
+ :allow_local_ipsec_rules, :allow_user_apps, :allow_user_ports, :allow_unicast_response, :display_notification do
+ fw_cmd = firewall_command(new_resource.profile)
+ powershell_exec!(fw_cmd)
+ end
+ unless firewall_enabled?(new_resource.profile)
+ converge_by "Enable the #{new_resource.profile} Firewall Profile" do
+ cmd = "Set-NetFirewallProfile -Profile #{new_resource.profile} -Enabled \"True\""
+ powershell_exec!(cmd)
+ end
+ end
+ end
+
+ action :disable do
+ if firewall_enabled?(new_resource.profile)
+ converge_by "Disable the #{new_resource.profile} Firewall Profile" do
+ cmd = "Set-NetFirewallProfile -Profile #{new_resource.profile} -Enabled \"False\""
+ powershell_exec!(cmd)
+ end
+ end
+ end
+
+ action_class do
+ def firewall_command(fw_profile)
+ cmd = "Set-NetFirewallProfile -Profile \"#{fw_profile}\""
+ cmd << " -DefaultInboundAction \"#{new_resource.default_inbound_action}\"" unless new_resource.default_inbound_action.nil?
+ cmd << " -DefaultOutboundAction \"#{new_resource.default_outbound_action}\"" unless new_resource.default_outbound_action.nil?
+ cmd << " -AllowInboundRules \"#{convert_to_powershell(new_resource.allow_inbound_rules)}\"" unless new_resource.allow_inbound_rules.nil?
+ cmd << " -AllowLocalFirewallRules \"#{convert_to_powershell(new_resource.allow_local_firewall_rules)}\"" unless new_resource.allow_local_firewall_rules.nil?
+ cmd << " -AllowLocalIPsecRules \"#{convert_to_powershell(new_resource.allow_local_ipsec_rules)}\"" unless new_resource.allow_local_ipsec_rules.nil?
+ cmd << " -AllowUserApps \"#{convert_to_powershell(new_resource.allow_user_apps)}\"" unless new_resource.allow_user_apps.nil?
+ cmd << " -AllowUserPorts \"#{convert_to_powershell(new_resource.allow_user_ports)}\"" unless new_resource.allow_user_ports.nil?
+ cmd << " -AllowUnicastResponseToMulticast \"#{convert_to_powershell(new_resource.allow_unicast_response)}\"" unless new_resource.allow_unicast_response.nil?
+ cmd << " -NotifyOnListen \"#{convert_to_powershell(new_resource.display_notification)}\"" unless new_resource.display_notification.nil?
+ cmd
+ end
+
+ def firewall_enabled?(profile_name)
+ cmd = <<~CODE
+ $#{profile_name} = Get-NetFirewallProfile -Profile #{profile_name}
+ if ($#{profile_name}.Enabled) {
+ return $true
+ } else {return $false}
+ CODE
+ powershell_exec!(cmd).result
+ end
+ end
+
+ private
+
+ # build the command to load the current resource
+ # @return [String] current firewall state
+ def load_firewall_state(profile_name)
+ <<-EOH
+ Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
+ $#{profile_name} = Get-NetFirewallProfile -Profile #{profile_name}
+ ([PSCustomObject]@{
+ default_inbound_action = $#{profile_name}.DefaultInboundAction.ToString()
+ default_outbound_action = $#{profile_name}.DefaultOutboundAction.ToString()
+ allow_inbound_rules = $#{profile_name}.AllowInboundRules.ToString()
+ allow_local_firewall_rules = $#{profile_name}.AllowLocalFirewallRules.ToString()
+ allow_local_ipsec_rules = $#{profile_name}.AllowLocalIPsecRules.ToString()
+ allow_user_apps = $#{profile_name}.AllowUserApps.ToString()
+ allow_user_ports = $#{profile_name}.AllowUserPorts.ToString()
+ allow_unicast_response = $#{profile_name}.AllowUnicastResponseToMulticast.ToString()
+ display_notification = $#{profile_name}.NotifyOnListen.ToString()
+ })
+ EOH
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_firewall_rule.rb b/lib/chef/resource/windows_firewall_rule.rb
new file mode 100644
index 0000000000..a6f0614362
--- /dev/null
+++ b/lib/chef/resource/windows_firewall_rule.rb
@@ -0,0 +1,326 @@
+# Author:: Matt Clifton (spartacus003@hotmail.com)
+# Author:: Matt Stratton (matt.stratton@gmail.com)
+# Author:: Tor Magnus Rakvåg (tor.magnus@outlook.com)
+# Author:: Tim Smith (tsmith@chef.io)
+# Copyright:: 2013-2015 Matt Clifton
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2018, Intility AS
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 WindowsFirewallRule < Chef::Resource
+ unified_mode true
+
+ provides :windows_firewall_rule
+
+ description "Use the **windows_firewall_rule** resource to create, change or remove Windows firewall rules."
+ introduced "14.7"
+ examples <<~DOC
+ **Allowing port 80 access**:
+
+ ```ruby
+ windows_firewall_rule 'IIS' do
+ local_port '80'
+ protocol 'TCP'
+ firewall_action :allow
+ end
+ ```
+
+ **Allow protocol ICMPv6 with ICMP Type**:
+
+ ```ruby
+ windows_firewall_rule 'CoreNet-Rule' do
+ rule_name 'CoreNet-ICMP6-LR2-In'
+ display_name 'Core Networking - Multicast Listener Report v2 (ICMPv6-In)'
+ local_port 'RPC'
+ protocol 'ICMPv6'
+ icmp_type '8'
+ end
+ ```
+
+ **Blocking WinRM over HTTP on a particular IP**:
+
+ ```ruby
+ windows_firewall_rule 'Disable WinRM over HTTP' do
+ local_port '5985'
+ protocol 'TCP'
+ firewall_action :block
+ local_address '192.168.1.1'
+ end
+ ```
+
+ **Deleting an existing rule**
+
+ ```ruby
+ windows_firewall_rule 'Remove the SSH rule' do
+ rule_name 'ssh'
+ action :delete
+ end
+ ```
+ DOC
+
+ property :rule_name, String,
+ name_property: true,
+ description: "An optional property to set the name of the firewall rule to assign if it differs from the resource block's name."
+
+ property :description, String,
+ description: "The description to assign to the firewall rule."
+
+ property :displayname, String,
+ description: "The displayname to assign to the firewall rule.",
+ default: lazy { rule_name },
+ default_description: "The rule_name property value.",
+ introduced: "16.0"
+
+ property :group, String,
+ description: "Specifies that only matching firewall rules of the indicated group association are copied.",
+ introduced: "16.0"
+
+ property :local_address, String,
+ description: "The local address the firewall rule applies to."
+
+ property :local_port, [String, Integer, Array],
+ # split various formats of comma separated lists and provide a sorted array of strings to match PS output
+ coerce: proc { |d| d.is_a?(String) ? d.split(/\s*,\s*/).sort : Array(d).sort.map(&:to_s) },
+ description: "The local port the firewall rule applies to."
+
+ property :remote_address, String,
+ description: "The remote address the firewall rule applies to."
+
+ property :remote_port, [String, Integer, Array],
+ # split various formats of comma separated lists and provide a sorted array of strings to match PS output
+ coerce: proc { |d| d.is_a?(String) ? d.split(/\s*,\s*/).sort : Array(d).sort.map(&:to_s) },
+ description: "The remote port the firewall rule applies to."
+
+ property :direction, [Symbol, String],
+ default: :inbound, equal_to: %i{inbound outbound},
+ description: "The direction of the firewall rule. Direction means either inbound or outbound traffic.",
+ coerce: proc { |d| d.is_a?(String) ? d.downcase.to_sym : d }
+
+ property :protocol, String,
+ default: "TCP",
+ description: "The protocol the firewall rule applies to."
+
+ property :icmp_type, [String, Integer],
+ description: "Specifies the ICMP Type parameter for using a protocol starting with ICMP",
+ default: "Any",
+ introduced: "16.0"
+
+ property :firewall_action, [Symbol, String],
+ default: :allow, equal_to: %i{allow block notconfigured},
+ description: "The action of the firewall rule.",
+ coerce: proc { |f| f.is_a?(String) ? f.downcase.to_sym : f }
+
+ property :profile, [Symbol, String, Array],
+ default: :any,
+ description: "The profile the firewall rule applies to.",
+ coerce: proc { |p| Array(p).map(&:downcase).map(&:to_sym).sort },
+ callbacks: {
+ "contains values not in :public, :private, :domain, :any or :notapplicable" => lambda { |p|
+ p.all? { |e| %i{public private domain any notapplicable}.include?(e) }
+ },
+ }
+
+ property :program, String,
+ description: "The program the firewall rule applies to."
+
+ property :service, String,
+ description: "The service the firewall rule applies to."
+
+ property :interface_type, [Symbol, String],
+ default: :any, equal_to: %i{any wireless wired remoteaccess},
+ description: "The interface type the firewall rule applies to.",
+ coerce: proc { |i| i.is_a?(String) ? i.downcase.to_sym : i }
+
+ property :enabled, [TrueClass, FalseClass],
+ default: true,
+ description: "Whether or not to enable the firewall rule."
+
+ alias_method :localip, :local_address
+ alias_method :remoteip, :remote_address
+ alias_method :localport, :local_port
+ alias_method :remoteport, :remote_port
+ alias_method :interfacetype, :interface_type
+
+ load_current_value do
+ load_state_cmd = load_firewall_state(rule_name)
+ output = powershell_exec(load_state_cmd)
+ if output.result.empty?
+ current_value_does_not_exist!
+ else
+ state = output.result
+ end
+
+ # Need to reverse `$rule.Profile.ToString()` in powershell command
+ current_profiles = state["profile"].split(", ").map(&:to_sym)
+
+ description state["description"]
+ displayname state["displayname"]
+ group state["group"]
+ local_address state["local_address"]
+ local_port Array(state["local_port"]).sort
+ remote_address state["remote_address"]
+ remote_port Array(state["remote_port"]).sort
+ direction state["direction"]
+ protocol state["protocol"]
+ icmp_type state["icmp_type"]
+ firewall_action state["firewall_action"]
+ profile current_profiles
+ program state["program"]
+ service state["service"]
+ interface_type state["interface_type"]
+ enabled state["enabled"]
+ end
+
+ action :create do
+ description "Create a Windows firewall entry."
+ if current_resource
+ converge_if_changed :rule_name, :description, :displayname, :local_address, :local_port, :remote_address,
+ :remote_port, :direction, :protocol, :icmp_type, :firewall_action, :profile, :program, :service,
+ :interface_type, :enabled do
+ cmd = firewall_command("Set")
+ powershell_exec!(cmd)
+ end
+ converge_if_changed :group do
+ powershell_exec!("Remove-NetFirewallRule -Name '#{new_resource.rule_name}'")
+ cmd = firewall_command("New")
+ powershell_exec!(cmd)
+ end
+ else
+ converge_by("create firewall rule #{new_resource.rule_name}") do
+ cmd = firewall_command("New")
+ powershell_exec!(cmd)
+ end
+ end
+ end
+
+ action :delete do
+ description "Delete an existing Windows firewall entry."
+
+ if current_resource
+ converge_by("delete firewall rule #{new_resource.rule_name}") do
+ powershell_exec!("Remove-NetFirewallRule -Name '#{new_resource.rule_name}'")
+ end
+ else
+ Chef::Log.info("Firewall rule \"#{new_resource.rule_name}\" doesn't exist. Skipping.")
+ end
+ end
+
+ action_class do
+ # build the command to create a firewall rule based on new_resource values
+ # @return [String] firewall create command
+ def firewall_command(cmdlet_type)
+ cmd = "#{cmdlet_type}-NetFirewallRule -Name '#{new_resource.rule_name}'"
+ cmd << " -DisplayName '#{new_resource.displayname}'" if new_resource.displayname && cmdlet_type == "New"
+ cmd << " -NewDisplayName '#{new_resource.displayname}'" if new_resource.displayname && cmdlet_type == "Set"
+ cmd << " -Group '#{new_resource.group}'" if new_resource.group && cmdlet_type == "New"
+ cmd << " -Description '#{new_resource.description}'" if new_resource.description
+ cmd << " -LocalAddress '#{new_resource.local_address}'" if new_resource.local_address
+ cmd << " -LocalPort '#{new_resource.local_port.join("', '")}'" if new_resource.local_port
+ cmd << " -RemoteAddress '#{new_resource.remote_address}'" if new_resource.remote_address
+ cmd << " -RemotePort '#{new_resource.remote_port.join("', '")}'" if new_resource.remote_port
+ cmd << " -Direction '#{new_resource.direction}'" if new_resource.direction
+ cmd << " -Protocol '#{new_resource.protocol}'" if new_resource.protocol
+ cmd << " -IcmpType '#{new_resource.icmp_type}'"
+ cmd << " -Action '#{new_resource.firewall_action}'" if new_resource.firewall_action
+ cmd << " -Profile '#{new_resource.profile.join("', '")}'" if new_resource.profile
+ cmd << " -Program '#{new_resource.program}'" if new_resource.program
+ cmd << " -Service '#{new_resource.service}'" if new_resource.service
+ cmd << " -InterfaceType '#{new_resource.interface_type}'" if new_resource.interface_type
+ cmd << " -Enabled '#{new_resource.enabled}'"
+
+ cmd
+ end
+
+ def define_resource_requirements
+ requirements.assert(:create) do |a|
+ a.assertion do
+ if new_resource.icmp_type.is_a?(String)
+ !new_resource.icmp_type.empty?
+ elsif new_resource.icmp_type.is_a?(Integer)
+ !new_resource.icmp_type.nil?
+ end
+ end
+ a.failure_message("The :icmp_type property can not be empty in #{new_resource.rule_name}")
+ end
+
+ requirements.assert(:create) do |a|
+ a.assertion do
+ if new_resource.icmp_type.is_a?(Integer)
+ new_resource.protocol.start_with?("ICMP")
+ elsif new_resource.icmp_type.is_a?(String) && !new_resource.protocol.start_with?("ICMP")
+ new_resource.icmp_type == "Any"
+ else
+ true
+ end
+ end
+ a.failure_message("The :icmp_type property has a value of #{new_resource.icmp_type} set, but is not allowed for :protocol #{new_resource.protocol} in #{new_resource.rule_name}")
+ end
+
+ requirements.assert(:create) do |a|
+ a.assertion do
+ if new_resource.icmp_type.is_a?(Integer)
+ (0..255).cover?(new_resource.icmp_type)
+ elsif new_resource.icmp_type.is_a?(String) && !new_resource.icmp_type.include?(":") && new_resource.protocol.start_with?("ICMP")
+ (0..255).cover?(new_resource.icmp_type.to_i)
+ elsif new_resource.icmp_type.is_a?(String) && new_resource.icmp_type.include?(":") && new_resource.protocol.start_with?("ICMP")
+ new_resource.icmp_type.split(":").all? { |type| (0..255).cover?(type.to_i) }
+ else
+ true
+ end
+ end
+ a.failure_message("Can not set :icmp_type to #{new_resource.icmp_type} as one value is out of range (0 to 255) in #{new_resource.rule_name}")
+ end
+ end
+ end
+
+ private
+
+ # build the command to load the current resource
+ # @return [String] current firewall state
+ def load_firewall_state(rule_name)
+ <<-EOH
+ Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
+ $rule = Get-NetFirewallRule -Name '#{rule_name}'
+ $addressFilter = $rule | Get-NetFirewallAddressFilter
+ $portFilter = $rule | Get-NetFirewallPortFilter
+ $applicationFilter = $rule | Get-NetFirewallApplicationFilter
+ $serviceFilter = $rule | Get-NetFirewallServiceFilter
+ $interfaceTypeFilter = $rule | Get-NetFirewallInterfaceTypeFilter
+ ([PSCustomObject]@{
+ rule_name = $rule.Name
+ description = $rule.Description
+ displayname = $rule.DisplayName
+ group = $rule.Group
+ local_address = $addressFilter.LocalAddress
+ local_port = $portFilter.LocalPort
+ remote_address = $addressFilter.RemoteAddress
+ remote_port = $portFilter.RemotePort
+ direction = $rule.Direction.ToString()
+ protocol = $portFilter.Protocol
+ icmp_type = $portFilter.IcmpType
+ firewall_action = $rule.Action.ToString()
+ profile = $rule.Profile.ToString()
+ program = $applicationFilter.Program
+ service = $serviceFilter.Service
+ interface_type = $interfaceTypeFilter.InterfaceType.ToString()
+ enabled = [bool]::Parse($rule.Enabled.ToString())
+ })
+ EOH
+ 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..c9128aa4b0
--- /dev/null
+++ b/lib/chef/resource/windows_font.rb
@@ -0,0 +1,135 @@
+#
+# Copyright:: 2014-2018, Schuberg Philis BV.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsFont < Chef::Resource
+ require_relative "../util/path_helper"
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ **Install a font from a https source**:
+
+ ```ruby
+ windows_font 'Custom.otf' do
+ source 'https://example.com/Custom.otf'
+ end
+ ```
+ DOC
+
+ property :font_name, String,
+ description: "An optional property to set the name of the font to install if it differs from the resource block's name.",
+ name_property: true
+
+ property :source, String,
+ description: "A local filesystem path or URI that is used to source the font file.",
+ coerce: proc { |x| /^.:.*/.match?(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.match?(/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.match?(/mswin|mingw32|windows/)
+ fonts_dir = WIN32OLE.new("WScript.Shell").SpecialFolders("Fonts")
+ fonts_dir_local = Chef::Util::PathHelper.join(ENV["home"], "AppData/Local/Microsoft/Windows/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)) || ::File.exist?(Chef::Util::PathHelper.join(fonts_dir_local, 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" unless defined?(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..0072d70656 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +16,107 @@
# limitations under the License.
#
-require "chef/mixin/uris"
-require "chef/resource/package"
-require "chef/provider/package/windows"
-require "chef/win32/error" if RUBY_PLATFORM =~ /mswin|mingw|windows/
+require_relative "../mixin/uris"
+require_relative "package"
+require_relative "../provider/package/windows"
+require_relative "../win32/error" if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class WindowsPackage < Chef::Resource::Package
include Chef::Mixin::Uris
+ unified_mode true
- resource_name :windows_package
- provides :windows_package, os: "windows"
+ provides(:windows_package) { true }
provides :package, os: "windows"
+ description <<~DESC
+ Use the **windows_package** resource to manage packages on the Microsoft Windows platform.
+ The **windows_package** resource supports these installer formats:
+ * Microsoft Installer Package (MSI)
+ * Nullsoft Scriptable Install System (NSIS)
+ * Inno Setup (inno)
+ * Wise
+ * InstallShield
+ * Custom installers such as installing a non-.msi file that embeds an .msi-based installer
+
+ To enable idempotence of the `:install` action or to enable the `:remove` action with no source property specified,
+ `package_name` MUST be an exact match of the name used by the package installer. The names of installed packages
+ Windows knows about can be found in **Add/Remove programs**, in the output of `ohai packages`, or in the
+ `DisplayName` property in one of the following in the Windows registry:
+
+ * `HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall`
+ * `HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall`
+ * `HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall`
+
+ Note: If there are multiple versions of a package installed with the same display name, all of those packages will
+ be removed unless a version is provided in the **version** property or unless it can be discovered in the installer
+ file specified by the **source** property.
+ DESC
+
+ introduced "11.12"
+ examples <<~DOC
+ **Install a package**:
+
+ ```ruby
+ windows_package '7zip' do
+ action :install
+ source 'C:\\7z920.msi'
+ end
+ ```
+
+ **Specify a URL for the source attribute**:
+
+ ```ruby
+ windows_package '7zip' do
+ source 'http://www.7-zip.org/a/7z938-x64.msi'
+ end
+ ```
+
+ **Specify path and checksum**:
+
+ ```ruby
+ windows_package '7zip' do
+ source 'http://www.7-zip.org/a/7z938-x64.msi'
+ checksum '7c8e873991c82ad9cfc123415254ea6101e9a645e12977dcd518979e50fdedf3'
+ end
+ ```
+
+ **Modify remote_file resource attributes**:
+
+ The windows_package resource may specify a package at a remote location using the remote_file_attributes property. This uses the remote_file resource to download the contents at the specified URL and passes in a Hash that modifies the properties of the remote_file resource.
+
+ ```ruby
+ windows_package '7zip' do
+ source 'http://www.7-zip.org/a/7z938-x64.msi'
+ remote_file_attributes ({
+ :path => 'C:\\7zip.msi',
+ :checksum => '7c8e873991c82ad9cfc123415254ea6101e9a645e12977dcd518979e50fdedf3'
+ })
+ end
+ ```
+
+ **Download a nsis (Nullsoft) package resource**:
+
+ ```ruby
+ windows_package 'Mercurial 3.6.1 (64-bit)' do
+ source 'http://mercurial.selenic.com/release/windows/Mercurial-3.6.1-x64.exe'
+ checksum 'febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d'
+ end
+ ```
+
+ **Download a custom package**:
+
+ ```ruby
+ windows_package 'Microsoft Visual C++ 2005 Redistributable' do
+ source 'https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe'
+ installer_type :custom
+ options '/Q'
+ end
+ ```
+ DOC
+
allowed_actions :install, :remove
def initialize(name, run_context = nil)
@@ -37,19 +124,50 @@ class Chef
@source ||= source(@package_name) if @package_name.downcase.end_with?(".msi")
end
+ property :package_name, String,
+ description: "An optional property to set the package name if it differs from the resource block's name.",
+ identity: true
+
+ # we don't redefine the version property as a string here since we store the current value
+ # of version and that may be an array if multiple versions of a package are present on the system
+
+ # windows can't take array options yet
+ property :options, String,
+ description: "One (or more) additional options that are passed to the command."
+
# Unique to this resource
- property :installer_type, Symbol
- property :timeout, [ String, Integer ], default: 600
+ property :installer_type, Symbol,
+ equal_to: %i{custom inno installshield msi nsis wise},
+ description: "A symbol that specifies the type of package. Possible values: :custom (such as installing a non-.msi file that embeds an .msi-based installer), :inno (Inno Setup), :installshield (InstallShield), :msi (Microsoft Installer Package (MSI)), :nsis (Nullsoft Scriptable Install System (NSIS)), :wise (Wise)."
+
+ property :timeout, [ String, Integer ], default: 600,
+ default_description: "600 (seconds)",
+ description: "The amount of time (in seconds) to wait before timing out.",
+ desired_state: false
+
# In the past we accepted return code 127 for an unknown reason and 42 because of a bug
- property :returns, [ String, Integer, Array ], default: [ 0 ], desired_state: false
+ # we accept 3010 which means success, but a reboot is necessary
+ property :returns, [ String, Integer, Array ], default: [ 0, 3010 ],
+ desired_state: false,
+ description: "A comma-delimited list of return codes that indicate the success or failure of the package command that was run.",
+ default_description: "0 (success) and 3010 (success where a reboot is necessary)"
+
property :source, String,
- coerce: (proc do |s|
- unless s.nil?
- uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
- end
- end)
- property :checksum, String, desired_state: false
- property :remote_file_attributes, Hash, desired_state: false
+ coerce: (proc do |s|
+ unless s.nil?
+ uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
+ end
+ end),
+ default_description: "The resource block's name", # this property is basically a name_property but not really so we need to spell it out
+ description: "The path to a package in the local file system or the URL of a remote file that will be downloaded."
+
+ property :checksum, String,
+ desired_state: false, coerce: (proc { |c| c.downcase }),
+ description: "The SHA-256 checksum of the file. Use to prevent a file from being re-downloaded. When the local file matches the checksum, #{ChefUtils::Dist::Infra::PRODUCT} does not download it. Use when a URL is specified by the `source` property."
+
+ property :remote_file_attributes, Hash,
+ desired_state: false,
+ description: "If the source package to install is at a remote location, this property allows you to define a hash of properties which will be used by the underlying **remote_file** resource used to fetch the source."
end
end
end
diff --git a/lib/chef/resource/windows_pagefile.rb b/lib/chef/resource/windows_pagefile.rb
new file mode 100644
index 0000000000..4dfaae3be3
--- /dev/null
+++ b/lib/chef/resource/windows_pagefile.rb
@@ -0,0 +1,238 @@
+#
+# Copyright:: 2012-2018, Nordstrom, Inc.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsPagefile < Chef::Resource
+ unified_mode true
+
+ provides(:windows_pagefile) { true }
+
+ description "Use the **windows_pagefile** resource to configure pagefile settings on Windows."
+ introduced "14.0"
+ examples <<~DOC
+ **Set the system to manage pagefiles**:
+
+ ```ruby
+ windows_pagefile 'Enable automatic management of pagefiles' do
+ automatic_managed true
+ end
+ ```
+
+ **Delete a pagefile**:
+
+ ```ruby
+ windows_pagefile 'Delete the pagefile' do
+ path 'C:\pagefile.sys'
+ action :delete
+ end
+ ```
+
+ **Create a pagefile with an initial and maximum size**:
+
+ ```ruby
+ windows_pagefile 'create the pagefile' do
+ path 'C:\pagefile.sys'
+ initial_size 100
+ maximum_size 200
+ end
+ ```
+ DOC
+
+ property :path, String,
+ coerce: proc { |x| x.tr("/", '\\') },
+ description: "An optional property to set the pagefile name if it differs from the resource block's 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
+ private
+
+ # 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/.match?(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 running: 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..870ffdef3f
--- /dev/null
+++ b/lib/chef/resource/windows_path.rb
@@ -0,0 +1,92 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "../mixin/windows_env_helper" if ChefUtils.windows?
+require_relative "../mixin/wide_string"
+require_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsPath < Chef::Resource
+ unified_mode true
+
+ provides(:windows_path) { true }
+
+ description "Use the **windows_path** resource to manage the path environment variable on Microsoft Windows."
+ introduced "13.4"
+ examples <<~DOC
+ **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
+ ```
+ DOC
+
+ allowed_actions :add, :remove
+ default_action :add
+
+ property :path, String,
+ description: "An optional property to set the path value if it differs from the resource block's name.",
+ name_property: true
+
+ action_class do
+ include Chef::Mixin::WindowsEnvHelper if ChefUtils.windows?
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsPath.new(new_resource.name)
+ @current_resource.path(new_resource.path)
+ @current_resource
+ end
+ 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)
+ 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)
+ env "path" do
+ action :delete
+ delim ::File::PATH_SEPARATOR
+ value path.tr("/", '\\')
+ end
+ end
+ 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..dea15ba112
--- /dev/null
+++ b/lib/chef/resource/windows_printer.rb
@@ -0,0 +1,166 @@
+#
+# 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsPrinter < Chef::Resource
+ unified_mode true
+
+ autoload :Resolv, "resolv"
+
+ 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"
+ examples <<~DOC
+ **Create a printer**:
+
+ ```ruby
+ windows_printer 'HP LaserJet 5th Floor' do
+ driver_name 'HP LaserJet 4100 Series PCL6'
+ ipv4_address '10.4.64.38'
+ end
+ ```
+
+ **Delete a printer**:
+
+ Note: this doesn't delete the associated printer port. See windows_printer_port above for how to delete the port.
+
+ ```ruby
+ windows_printer 'HP LaserJet 5th Floor' do
+ action :delete
+ end
+ ```
+ DOC
+
+ property :device_id, String,
+ description: "An optional property to set the printer queue name if it differs from the resource block's name. Example: `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: "Determines whether or not this should be the system's default printer.",
+ default: false
+
+ property :driver_name, String,
+ description: "The exact name of printer driver installed on the system.",
+ required: [:create]
+
+ property :location, String,
+ description: "Printer location, such as `Fifth floor copy room`."
+
+ property :shared, [TrueClass, FalseClass],
+ description: "Determines whether or not the printer is shared.",
+ default: false
+
+ property :share_name, String,
+ description: "The name used to identify the shared printer."
+
+ property :ipv4_address, String,
+ description: "The IPv4 address of the printer, such as `10.4.64.23`",
+ callbacks: {
+ "The ipv4_address property must be in the IPv4 format of `WWW.XXX.YYY.ZZZ`" =>
+ proc { |v| v.match(Resolv::IPv4::Regex) },
+ }
+
+ PRINTERS_REG_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'.freeze unless defined?(PRINTERS_REG_KEY)
+
+ # @todo Set @current_resource printer properties from registry
+ load_current_value do |desired|
+ name desired.name
+ end
+
+ action :create do
+ description "Create a new printer and a printer port if one doesn't already exist."
+
+ if printer_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 printer_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
+ private
+
+ # does the printer exist
+ #
+ # @param [String] name the name of the printer
+ # @return [Boolean]
+ def printer_exists?
+ printer_reg_key = PRINTERS_REG_KEY + new_resource.name
+ logger.trace "Checking to see if this reg key exists: '#{printer_reg_key}'"
+ registry_key_exists?(printer_reg_key)
+ end
+
+ # creates the printer port and then the printer
+ def create_printer
+ # Create the printer port first
+ windows_printer_port new_resource.ipv4_address
+
+ port_name = "IP_#{new_resource.ipv4_address}"
+
+ declare_resource(:powershell_script, "Creating printer: #{new_resource.device_id}") 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.device_id}") do
+ code <<-EOH
+ $printer = Get-WMIObject -class Win32_Printer -EnableAllPrivileges -Filter "name = '#{new_resource.device_id}'"
+ $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..2a4eaa09b3
--- /dev/null
+++ b/lib/chef/resource/windows_printer_port.rb
@@ -0,0 +1,166 @@
+#
+# 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsPrinterPort < Chef::Resource
+ unified_mode true
+
+ autoload :Resolv, "resolv"
+
+ 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"
+ examples <<~DOC
+ **Delete a printer port**
+
+ ```ruby
+ windows_printer_port '10.4.64.37' do
+ action :delete
+ end
+ ```
+
+ **Delete a port with a custom port_name**
+
+ ```ruby
+ windows_printer_port '10.4.64.38' do
+ port_name 'My awesome port'
+ action :delete
+ end
+ ```
+
+ **Create a port with more options**
+
+ ```ruby
+ windows_printer_port '10.4.64.39' do
+ port_name 'My awesome port'
+ snmp_enabled true
+ port_protocol 2
+ end
+ ```
+ DOC
+
+ property :ipv4_address, String,
+ name_property: true,
+ description: "An optional property for the IPv4 address of the printer if it differs from the resource block's name.",
+ callbacks: {
+ "The ipv4_address property must be in the format of WWW.XXX.YYY.ZZZ!" =>
+ proc { |v| v.match(Resolv::IPv4::Regex) },
+ }
+
+ 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: "Determines if SNMP is 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]
+
+ PORTS_REG_KEY = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\\'.freeze unless defined?(PORTS_REG_KEY)
+
+ # @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}"
+ end
+
+ action :create do
+ description "Create the new printer port if it does not already exist."
+
+ if port_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 port_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
+ private
+
+ def port_exists?
+ name = new_resource.port_name || "IP_#{new_resource.ipv4_address}"
+ 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
+
+ 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_script.rb b/lib/chef/resource/windows_script.rb
index 7c39d9fba0..8c958c5cba 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,46 +16,33 @@
# limitations under the License.
#
-require "chef/platform/query_helpers"
-require "chef/resource/script"
-require "chef/mixin/windows_architecture_helper"
+require_relative "script"
+require_relative "../mixin/windows_architecture_helper"
class Chef
class Resource
class WindowsScript < Chef::Resource::Script
- # This is an abstract resource meant to be subclasses; thus no 'provides'
-
- set_guard_inherited_attributes(:architecture)
+ include Chef::Mixin::WindowsArchitectureHelper
- protected
+ unified_mode true
- def initialize(name, run_context, resource_name, interpreter_command)
- super(name, run_context)
- @interpreter = interpreter_command
- @resource_name = resource_name if resource_name
- @default_guard_interpreter = self.resource_name
- end
-
- include Chef::Mixin::WindowsArchitectureHelper
+ # This is an abstract resource meant to be subclasses; thus no 'provides'
- public
+ set_guard_inherited_attributes(:architecture)
def architecture(arg = nil)
- assert_architecture_compatible!(arg) if ! arg.nil?
+ assert_architecture_compatible!(arg) unless arg.nil?
result = set_or_return(
:architecture,
arg,
- :kind_of => Symbol
+ kind_of: Symbol
)
end
protected
def assert_architecture_compatible!(desired_architecture)
- if desired_architecture == :i386 && Chef::Platform.windows_nano_server?
- raise Chef::Exceptions::Win32ArchitectureIncorrect,
- "cannot execute script with requested architecture 'i386' on Windows Nano Server"
- elsif ! node_supports_windows_architecture?(node, desired_architecture)
+ unless node_supports_windows_architecture?(node, desired_architecture)
raise Chef::Exceptions::Win32ArchitectureIncorrect,
"cannot execute script with requested architecture '#{desired_architecture}' on a system with architecture '#{node_windows_architecture(node)}'"
end
diff --git a/lib/chef/resource/windows_security_policy.rb b/lib/chef/resource/windows_security_policy.rb
new file mode 100644
index 0000000000..78d56e2e46
--- /dev/null
+++ b/lib/chef/resource/windows_security_policy.rb
@@ -0,0 +1,165 @@
+#
+# Author:: Ashwini Nehate (<anehate@chef.io>)
+# Author:: Davin Taddeo (<davin@chef.io>)
+# Author:: Jeff Brimager (<jbrimager@chef.io>)
+# Copyright:: Copyright (c) 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_relative "../resource"
+require "tempfile" unless defined?(Tempfile)
+
+class Chef
+ class Resource
+ class WindowsSecurityPolicy < Chef::Resource
+ unified_mode true
+
+ provides :windows_security_policy
+
+ # The valid policy_names options found here
+ # https://github.com/ChrisAWalker/cSecurityOptions under 'AccountSettings'
+ policy_names = %w{LockoutDuration
+ MaximumPasswordAge
+ MinimumPasswordAge
+ MinimumPasswordLength
+ PasswordComplexity
+ PasswordHistorySize
+ LockoutBadCount
+ ResetLockoutCount
+ RequireLogonToChangePassword
+ ForceLogoffWhenHourExpire
+ NewAdministratorName
+ NewGuestName
+ ClearTextPassword
+ LSAAnonymousNameLookup
+ EnableAdminAccount
+ EnableGuestAccount
+ }
+ description "Use the **windows_security_policy** resource to set a security policy on the Microsoft Windows platform."
+ introduced "16.0"
+
+ examples <<~DOC
+ **Set Administrator Account to Enabled**:
+
+ ```ruby
+ windows_security_policy 'EnableAdminAccount' do
+ secvalue '1'
+ action :set
+ end
+ ```
+
+ **Rename Administrator Account**:
+
+ ```ruby
+ windows_security_policy 'NewAdministratorName' do
+ secvalue 'AwesomeChefGuy'
+ action :set
+ end
+ ```
+
+ **Set Guest Account to Disabled**:
+
+ ```ruby
+ windows_security_policy 'EnableGuestAccount' do
+ secvalue '0'
+ action :set
+ end
+ ```
+ DOC
+
+ property :secoption, String, name_property: true, required: true, equal_to: policy_names,
+ description: "The name of the policy to be set on windows platform to maintain its security."
+
+ property :secvalue, String, required: true,
+ description: "Policy value to be set for policy name."
+
+ load_current_value do |desired|
+ current_state = load_security_options
+
+ if desired.secoption == "ResetLockoutCount"
+ if desired.secvalue.to_i > 30
+ raise Chef::Exceptions::ValidationFailed, "The \"ResetLockoutCount\" value cannot be greater than 30 minutes"
+ end
+ end
+ if (desired.secoption == "ResetLockoutCount" || desired.secoption == "LockoutDuration") && current_state["LockoutBadCount"] == "0"
+ raise Chef::Exceptions::ValidationFailed, "#{desired.secoption} cannot be set unless the \"LockoutBadCount\" security policy has been set to a non-zero value"
+ end
+
+ secvalue current_state[desired.secoption.to_s]
+ end
+
+ action :set do
+ converge_if_changed :secvalue do
+ security_option = new_resource.secoption
+ security_value = new_resource.secvalue
+
+ file = Tempfile.new(["#{security_option}", ".inf"])
+ case security_option
+ when "LockoutBadCount"
+ cmd = "net accounts /LockoutThreshold:#{security_value}"
+ when "ResetLockoutCount"
+ cmd = "net accounts /LockoutWindow:#{security_value}"
+ when "LockoutDuration"
+ cmd = "net accounts /LockoutDuration:#{security_value}"
+ when "NewAdministratorName", "NewGuestName"
+ policy_line = "#{security_option} = \"#{security_value}\""
+ file.write("[Unicode]\r\nUnicode=yes\r\n[System Access]\r\n#{policy_line}\r\n[Version]\r\nsignature=\"$CHICAGO$\"\r\nRevision=1\r\n")
+ file.close
+ file_path = file.path.gsub("/", '\\')
+ cmd = "C:\\Windows\\System32\\secedit /configure /db C:\\windows\\security\\new.sdb /cfg #{file_path} /areas SECURITYPOLICY"
+ else
+ policy_line = "#{security_option} = #{security_value}"
+ file.write("[Unicode]\r\nUnicode=yes\r\n[System Access]\r\n#{policy_line}\r\n[Version]\r\nsignature=\"$CHICAGO$\"\r\nRevision=1\r\n")
+ file.close
+ file_path = file.path.gsub("/", '\\')
+ cmd = "C:\\Windows\\System32\\secedit /configure /db C:\\windows\\security\\new.sdb /cfg #{file_path} /areas SECURITYPOLICY"
+ end
+ shell_out!(cmd)
+ file.unlink
+ end
+ end
+
+ private
+
+ # Loads powershell to get current state on security options
+ def load_security_options
+ powershell_code = <<-CODE
+ C:\\Windows\\System32\\secedit /export /cfg $env:TEMP\\secopts_export.inf | Out-Null
+ # cspell:disable-next-line
+ $security_options_data = (Get-Content $env:TEMP\\secopts_export.inf | Select-String -Pattern "^[CEFLMNPR].* =.*$" | Out-String)
+ Remove-Item $env:TEMP\\secopts_export.inf -force
+ $security_options_hash = ($security_options_data -Replace '"'| ConvertFrom-StringData)
+ ([PSCustomObject]@{
+ RequireLogonToChangePassword = $security_options_hash.RequireLogonToChangePassword
+ PasswordComplexity = $security_options_hash.PasswordComplexity
+ LSAAnonymousNameLookup = $security_options_hash.LSAAnonymousNameLookup
+ EnableAdminAccount = $security_options_hash.EnableAdminAccount
+ PasswordHistorySize = $security_options_hash.PasswordHistorySize
+ MinimumPasswordLength = $security_options_hash.MinimumPasswordLength
+ ResetLockoutCount = $security_options_hash.ResetLockoutCount
+ MaximumPasswordAge = $security_options_hash.MaximumPasswordAge
+ ClearTextPassword = $security_options_hash.ClearTextPassword
+ NewAdministratorName = $security_options_hash.NewAdministratorName
+ LockoutDuration = $security_options_hash.LockoutDuration
+ EnableGuestAccount = $security_options_hash.EnableGuestAccount
+ ForceLogoffWhenHourExpire = $security_options_hash.ForceLogoffWhenHourExpire
+ MinimumPasswordAge = $security_options_hash.MinimumPasswordAge
+ NewGuestName = $security_options_hash.NewGuestName
+ LockoutBadCount = $security_options_hash.LockoutBadCount
+ })
+ CODE
+ powershell_exec(powershell_code).result
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_service.rb b/lib/chef/resource/windows_service.rb
index 405f7f6dbe..779c0e22ad 100644
--- a/lib/chef/resource/windows_service.rb
+++ b/lib/chef/resource/windows_service.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,56 +16,222 @@
# limitations under the License.
#
-require "chef/resource/service"
+require_relative "service"
+require_relative "../win32_service_constants"
class Chef
class Resource
class WindowsService < Chef::Resource::Service
+ include Chef::Win32ServiceConstants
+ unified_mode true
+
+ ALLOWED_START_TYPES = {
+ automatic: SERVICE_AUTO_START,
+ manual: SERVICE_DEMAND_START,
+ disabled: SERVICE_DISABLED,
+ }.freeze
# Until #1773 is resolved, you need to manually specify the windows_service resource
- # to use action :configure_startup and attribute startup_type
+ # to use action :configure_startup and properties 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 create, delete, or manage a service on the Microsoft Windows platform."
+ introduced "12.0"
+ examples <<~DOC
+ **Starting Services**
+
+ Start a service with a `manual` startup type:
+
+ ```ruby
+ windows_service 'BITS' do
+ action :configure_startup
+ startup_type :manual
+ end
+ ```
+
+ **Creating Services**
- identity_attr :service_name
+ Create a service named chef-client:
+
+ ```ruby
+ windows_service 'chef-client' do
+ action :create
+ binary_path_name "C:\\opscode\\chef\\bin"
+ end
+ ```
+
+ Create a service with `service_name` and `display_name`:
+
+ ```ruby
+ windows_service 'Setup chef-client as a service' do
+ action :create
+ display_name 'CHEF-CLIENT'
+ service_name 'chef-client'
+ binary_path_name "C:\\opscode\\chef\\bin"
+ end
+ ```
+
+ Create a service with the `manual` startup type:
+
+ ```ruby
+ windows_service 'chef-client' do
+ action :create
+ binary_path_name "C:\\opscode\\chef\\bin"
+ startup_type :manual
+ end
+ ```
+
+ Create a service with the `disabled` startup type:
+
+ ```ruby
+ windows_service 'chef-client' do
+ action :create
+ binary_path_name "C:\\opscode\\chef\\bin"
+ startup_type :disabled
+ end
+ ```
+
+ Create a service with the `automatic` startup type and delayed start enabled:
+
+ ```ruby
+ windows_service 'chef-client' do
+ action :create
+ binary_path_name "C:\\opscode\\chef\\bin"
+ startup_type :automatic
+ delayed_start true
+ end
+ ```
- state_attrs :enabled, :running
+ Create a service with a description:
- def initialize(name, run_context = nil)
- super
- @startup_type = :automatic
- @run_as_user = ""
- @run_as_password = ""
+ ```ruby
+ windows_service 'chef-client' do
+ action :create
+ binary_path_name "C:\\opscode\\chef\\bin"
+ startup_type :automatic
+ description "Chef client as service"
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 ]
- )
+ **Deleting Services**
+
+ Delete a service named chef-client:
+
+ ```ruby
+ windows_service 'chef-client' do
+ action :delete
end
+ ```
+
+ Delete a service with the `service_name` property:
- def run_as_user(arg = nil)
- set_or_return(
- :run_as_user,
- arg,
- :kind_of => [ String ]
- )
+ ```ruby
+ windows_service 'Delete chef client' do
+ action :delete
+ service_name 'chef-client'
end
+ ```
+
+ **Configuring Services**
+
+ Change an existing service from automatic to manual startup:
- def run_as_password(arg = nil)
- set_or_return(
- :run_as_password,
- arg,
- :kind_of => [ String ]
- )
+ ```ruby
+ windows_service 'chef-client' do
+ action :configure
+ binary_path_name "C:\\opscode\\chef\\bin"
+ startup_type :manual
end
+ ```
+ DOC
+
+ allowed_actions :configure_startup, :create, :delete, :configure
+
+ property :timeout, Integer,
+ description: "The amount of time (in seconds) to wait before timing out.",
+ default: 60,
+ desired_state: false
+
+ property :display_name, String, regex: /^.{1,256}$/,
+ description: "The display name to be used by user interface programs to identify the service. This string has a maximum length of 256 characters.",
+ validation_message: "The display_name can only be a maximum of 256 characters!",
+ introduced: "14.0"
+
+ # https://github.com/chef/win32-service/blob/ffi/lib/win32/windows/constants.rb#L19-L29
+ property :desired_access, Integer,
+ default: SERVICE_ALL_ACCESS,
+ introduced: "14.0"
+
+ # https://github.com/chef/win32-service/blob/ffi/lib/win32/windows/constants.rb#L31-L41
+ property :service_type, Integer, default: SERVICE_WIN32_OWN_PROCESS,
+ introduced: "14.0"
+
+ # Valid options:
+ # - :automatic
+ # - :manual
+ # - :disabled
+ # Reference: https://github.com/chef/win32-service/blob/ffi/lib/win32/windows/constants.rb#L49-L54
+ property :startup_type, [Symbol],
+ equal_to: %i{automatic manual disabled},
+ default: :automatic,
+ description: "Use to specify the startup type of the service.",
+ 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
+ }
+
+ # 1 == delayed start is enabled
+ # 0 == NO delayed start
+ property :delayed_start, [TrueClass, FalseClass],
+ introduced: "14.0",
+ description: "Set the startup type to delayed start. This only applies if `startup_type` is `:automatic`",
+ default: false, coerce: proc { |x|
+ if x.is_a?(Integer)
+ x == 0 ? false : true
+ else
+ x
+ end
+ }
+
+ # https://github.com/chef/win32-service/blob/ffi/lib/win32/windows/constants.rb#L43-L47
+ property :error_control, Integer,
+ default: SERVICE_ERROR_NORMAL,
+ introduced: "14.0"
+
+ property :binary_path_name, String,
+ introduced: "14.0",
+ description: "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"
+
+ property :load_order_group, String,
+ introduced: "14.0",
+ description: "The name of the service's load ordering group(s)."
+
+ property :dependencies, [String, Array],
+ description: "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.",
+ introduced: "14.0"
+
+ property :description, String,
+ description: "Description of the service.",
+ introduced: "14.0"
+
+ property :run_as_user, String,
+ description: "The user under which a Microsoft Windows service runs.",
+ default: "localsystem",
+ coerce: proc { |x| x.downcase }
+
+ property :run_as_password, String,
+ description: "The password for the user specified by `run_as_user`.",
+ default: ""
end
end
end
diff --git a/lib/chef/resource/windows_share.rb b/lib/chef/resource/windows_share.rb
new file mode 100644
index 0000000000..fe1e976747
--- /dev/null
+++ b/lib/chef/resource/windows_share.rb
@@ -0,0 +1,345 @@
+#
+# Author:: Sölvi Páll Ásgeirsson (<solvip@gmail.com>)
+# Author:: Richard Lavey (richard.lavey@calastone.com)
+# Author:: Tim Smith (tsmith@chef.io)
+#
+# Copyright:: 2014-2017, Sölvi Páll Ásgeirsson.
+# Copyright:: Copyright (c) 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_relative "../resource"
+require_relative "../util/path_helper"
+
+class Chef
+ class Resource
+ class WindowsShare < Chef::Resource
+ unified_mode true
+
+ provides :windows_share
+
+ description "Use the **windows_share** resource to create, modify and remove Windows shares."
+ introduced "14.7"
+ examples <<~DOC
+ **Create a share**:
+
+ ```ruby
+ windows_share 'foo' do
+ action :create
+ path 'C:\\foo'
+ full_users ['DOMAIN_A\\some_user', 'DOMAIN_B\\some_other_user']
+ read_users ['DOMAIN_C\\Domain users']
+ end
+ ```
+
+ **Delete a share**:
+
+ ```ruby
+ windows_share 'foo' do
+ action :delete
+ end
+ ```
+ DOC
+
+ # Specifies a name for the SMB share. The name may be composed of any valid file name characters, but must be less than 80 characters long. The names pipe and mailslot are reserved for use by the computer.
+ property :share_name, String,
+ description: "An optional property to set the share name if it differs from the resource block's name.",
+ name_property: true
+
+ # Specifies the path of the location of the folder to share. The path must be fully qualified. Relative paths or paths that contain wildcard characters are not permitted.
+ property :path, String,
+ description: "The path of the folder to share. Required when creating. If the share already exists on a different path then it is deleted and re-created.",
+ coerce: proc { |p| p.tr("/", "\\") || p }
+
+ # Specifies an optional description of the SMB share. A description of the share is displayed by running the Get-SmbShare cmdlet. The description may not contain more than 256 characters.
+ property :description, String,
+ description: "The description to be applied to the share.",
+ default: ""
+
+ # Specifies which accounts are granted full permission to access the share. Use a comma-separated list to specify multiple accounts. An account may not be specified more than once in the FullAccess, ChangeAccess, or ReadAccess parameter lists, but may be specified once in the FullAccess, ChangeAccess, or ReadAccess parameter list and once in the NoAccess parameter list.
+ property :full_users, Array,
+ description: "The users that should have 'Full control' permissions on the share in domain\\username format.",
+ default: lazy { [] }, coerce: proc { |u| u.sort }
+
+ # Specifies which users are granted modify permission to access the share
+ property :change_users, Array,
+ description: "The users that should have 'modify' permission on the share in domain\\username format.",
+ default: lazy { [] }, coerce: proc { |u| u.sort }
+
+ # Specifies which users are granted read permission to access the share. Multiple users can be specified by supplying a comma-separated list.
+ property :read_users, Array,
+ description: "The users that should have 'read' permission on the share in domain\\username format.",
+ default: lazy { [] }, coerce: proc { |u| u.sort }
+
+ # Specifies the lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer. By default, new SMB shares are persistent, and non-temporary.
+ property :temporary, [TrueClass, FalseClass],
+ description: "The lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer.",
+ default: false
+
+ # Specifies the scope name of the share.
+ property :scope_name, String,
+ description: "The scope name of the share.",
+ default: "*"
+
+ # Specifies the continuous availability time-out for the share.
+ property :ca_timeout, Integer,
+ description: "The continuous availability time-out for the share.",
+ default: 0
+
+ # Indicates that the share is continuously available.
+ property :continuously_available, [TrueClass, FalseClass],
+ description: "Indicates that the share is continuously available.",
+ default: false
+
+ # Specifies the caching mode of the offline files for the SMB share.
+ # property :caching_mode, String, equal_to: %w(None Manual Documents Programs BranchCache)
+
+ # Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited.
+ property :concurrent_user_limit, Integer,
+ description: "The maximum number of concurrently connected users the share can accommodate.",
+ default: 0
+
+ # Indicates that the share is encrypted.
+ property :encrypt_data, [TrueClass, FalseClass],
+ description: "Indicates that the share is encrypted.",
+ default: false
+
+ # Specifies which files and folders in the SMB share are visible to users. AccessBased: SMB does not the display the files and folders for a share to a user unless that user has rights to access the files and folders. By default, access-based enumeration is disabled for new SMB shares. Unrestricted: SMB displays files and folders to a user even when the user does not have permission to access the items.
+ # property :folder_enumeration_mode, String, equal_to: %(AccessBased Unrestricted)
+
+ load_current_value do |desired|
+ # this command selects individual objects because EncryptData & CachingMode have underlying
+ # types that get converted to their Integer values by ConvertTo-Json & we need to make sure
+ # those get written out as strings
+ share_state_cmd = "Get-SmbShare -Name '#{desired.share_name}' | Select-Object Name,Path, Description, Temporary, CATimeout, ContinuouslyAvailable, ConcurrentUserLimit, EncryptData"
+
+ Chef::Log.debug("Running '#{share_state_cmd}' to determine share state'")
+ ps_results = powershell_exec(share_state_cmd)
+
+ # detect a failure without raising and then set current_resource to nil
+ if ps_results.error?
+ Chef::Log.debug("Error fetching share state: #{ps_results.errors}")
+ current_value_does_not_exist!
+ end
+
+ Chef::Log.debug("The Get-SmbShare results were #{ps_results.result}")
+ results = ps_results.result
+
+ path results["Path"]
+ description results["Description"]
+ temporary results["Temporary"]
+ ca_timeout results["CATimeout"]
+ continuously_available results["ContinuouslyAvailable"]
+ # caching_mode results['CachingMode']
+ concurrent_user_limit results["ConcurrentUserLimit"]
+ encrypt_data results["EncryptData"]
+ # folder_enumeration_mode results['FolderEnumerationMode']
+
+ perm_state_cmd = %{Get-SmbShareAccess -Name "#{desired.share_name}" | Select-Object AccountName,AccessControlType,AccessRight}
+
+ Chef::Log.debug("Running '#{perm_state_cmd}' to determine share permissions state'")
+ ps_perm_results = powershell_exec(perm_state_cmd)
+
+ # we raise here instead of warning like above because we'd only get here if the above Get-SmbShare
+ # command was successful and that continuing would leave us with 1/2 known state
+ raise "Could not determine #{desired.share_name} share permissions by running '#{perm_state_cmd}'" if ps_perm_results.error?
+
+ Chef::Log.debug("The Get-SmbShareAccess results were #{ps_perm_results.result}")
+
+ f_users, c_users, r_users = parse_permissions(ps_perm_results.result)
+
+ full_users f_users
+ change_users c_users
+ read_users r_users
+ end
+
+ # given the string output of Get-SmbShareAccess parse out
+ # arrays of full access users, change users, and read only users
+ def parse_permissions(json_results)
+ json_results = [json_results] unless json_results.is_a?(Array) # single result is not an array
+
+ f_users = []
+ c_users = []
+ r_users = []
+
+ json_results.each do |perm|
+ next unless perm["AccessControlType"] == 0 # allow
+
+ case perm["AccessRight"]
+ when 0 then f_users << stripped_account(perm["AccountName"]) # 0 full control
+ when 1 then c_users << stripped_account(perm["AccountName"]) # 1 == change
+ when 2 then r_users << stripped_account(perm["AccountName"]) # 2 == read
+ end
+ end
+ [f_users, c_users, r_users]
+ end
+
+ # local names are returned from Get-SmbShareAccess in the full format MACHINE\\NAME
+ # but users of this resource would simply say NAME so we need to strip the values for comparison
+ def stripped_account(name)
+ name.slice!("#{node["hostname"]}\\")
+ name
+ end
+
+ action :create do
+ description "Create and modify Windows shares."
+
+ # we do this here instead of requiring the property because :delete doesn't need path set
+ raise "No path property set" unless new_resource.path
+
+ converge_if_changed do
+ # you can't actually change the path so you have to delete the old share first
+ if different_path?
+ Chef::Log.debug("The path has changed so we will delete and recreate share")
+ delete_share
+ create_share
+ elsif current_resource.nil?
+ # powershell cmdlet for create is different than updates
+ Chef::Log.debug("The current resource is nil so we will create a new share")
+ create_share
+ else
+ Chef::Log.debug("The current resource was not nil so we will update an existing share")
+ update_share
+ end
+
+ # creating the share does not set permissions so we need to update
+ update_permissions
+ end
+ end
+
+ action :delete do
+ description "Delete an existing Windows share."
+
+ if current_resource.nil?
+ Chef::Log.debug("#{new_resource.share_name} does not exist - nothing to do")
+ else
+ converge_by("delete #{new_resource.share_name}") do
+ delete_share
+ end
+ end
+ end
+
+ action_class do
+ private
+
+ def different_path?
+ return false if current_resource.nil? # going from nil to something isn't different for our concerns
+ return false if current_resource.path == Chef::Util::PathHelper.cleanpath(new_resource.path)
+
+ true
+ end
+
+ def delete_share
+ delete_command = "Remove-SmbShare -Name '#{new_resource.share_name}' -Force"
+
+ Chef::Log.debug("Running '#{delete_command}' to remove the share")
+ powershell_exec!(delete_command)
+ end
+
+ def update_share
+ update_command = "Set-SmbShare -Name '#{new_resource.share_name}' -Description '#{new_resource.description}' -ConcurrentUserLimit #{new_resource.concurrent_user_limit} -CATimeout #{new_resource.ca_timeout} -EncryptData:#{bool_string(new_resource.encrypt_data)} -ContinuouslyAvailable:#{bool_string(new_resource.continuously_available)} -Force"
+ update_command << " -ScopeName #{new_resource.scope_name}" unless new_resource.scope_name == "*" # passing * causes the command to fail
+ update_command << " -Temporary:#{bool_string(new_resource.temporary)}" if new_resource.temporary # only set true
+
+ Chef::Log.debug("Running '#{update_command}' to update the share")
+ powershell_exec!(update_command)
+ end
+
+ def create_share
+ raise "#{new_resource.path} is missing or not a directory. Shares cannot be created if the path doesn't first exist." unless ::File.directory? new_resource.path
+
+ share_cmd = "New-SmbShare -Name '#{new_resource.share_name}' -Path '#{Chef::Util::PathHelper.cleanpath(new_resource.path)}' -Description '#{new_resource.description}' -ConcurrentUserLimit #{new_resource.concurrent_user_limit} -CATimeout #{new_resource.ca_timeout} -EncryptData:#{bool_string(new_resource.encrypt_data)} -ContinuouslyAvailable:#{bool_string(new_resource.continuously_available)}"
+ share_cmd << " -ScopeName #{new_resource.scope_name}" unless new_resource.scope_name == "*" # passing * causes the command to fail
+ share_cmd << " -Temporary:#{bool_string(new_resource.temporary)}" if new_resource.temporary # only set true
+
+ Chef::Log.debug("Running '#{share_cmd}' to create the share")
+ powershell_exec!(share_cmd)
+
+ # New-SmbShare adds the "Everyone" user with read access no matter what so we need to remove it
+ # before we add our permissions
+ revoke_user_permissions(["Everyone"])
+ end
+
+ # determine what users in the current state don't exist in the desired state
+ # users/groups will have their permissions updated with the same command that
+ # sets it, but removes must be performed with Revoke-SmbShareAccess
+ def users_to_revoke
+ @users_to_revoke ||= begin
+ # if the resource doesn't exist then nothing needs to be revoked
+ if current_resource.nil?
+ []
+ else # if it exists then calculate the current to new resource diffs
+ (current_resource.full_users + current_resource.change_users + current_resource.read_users) - (new_resource.full_users + new_resource.change_users + new_resource.read_users)
+ end
+ end
+ end
+
+ # update existing permissions on a share
+ def update_permissions
+ # revoke any users that had something, but now has nothing
+ revoke_user_permissions(users_to_revoke) unless users_to_revoke.empty?
+
+ # set permissions for each of the permission types
+ %w{full read change}.each do |perm_type|
+ # set permissions for a brand new share OR
+ # update permissions if the current state and desired state differ
+ next unless permissions_need_update?(perm_type)
+
+ grant_command = "Grant-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{new_resource.send("#{perm_type}_users").join('","')}\" -Force -AccessRight #{perm_type}"
+
+ Chef::Log.debug("Running '#{grant_command}' to update the share permissions")
+ powershell_exec!(grant_command)
+ end
+ end
+
+ # determine if permissions need to be updated.
+ # Brand new share with no permissions defined: no
+ # Brand new share with permissions defined: yes
+ # Existing share with differing permissions: yes
+ #
+ # @param [String] type the permissions type (Full, Read, or Change)
+ def permissions_need_update?(type)
+ property_name = "#{type}_users"
+
+ # brand new share, but nothing to set
+ return false if current_resource.nil? && new_resource.send(property_name).empty?
+
+ # brand new share with new permissions to set
+ return true if current_resource.nil? && !new_resource.send(property_name).empty?
+
+ # there's a difference between the current and desired state
+ return true unless (new_resource.send(property_name) - current_resource.send(property_name)).empty?
+
+ # anything else
+ false
+ end
+
+ # revoke user permissions from a share
+ # @param [Array] users
+ def revoke_user_permissions(users)
+ revoke_command = "Revoke-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{users.join('","')}\" -Force"
+ Chef::Log.debug("Running '#{revoke_command}' to revoke share permissions")
+ powershell_exec!(revoke_command)
+ end
+
+ # convert True/False into "$True" & "$False"
+ def bool_string(bool)
+ # bool ? 1 : 0
+ bool ? "$true" : "$false"
+ end
+ end
+
+ 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..f2264445ba
--- /dev/null
+++ b/lib/chef/resource/windows_shortcut.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Doug MacEachern <dougm@vmware.com>
+# Copyright:: 2010-2018, VMware, Inc.
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsShortcut < Chef::Resource
+ unified_mode true
+
+ provides(:windows_shortcut) { true }
+
+ description "Use the **windows_shortcut** resource to create shortcut files on Windows."
+ introduced "14.0"
+ examples <<~DOC
+ **Create a shortcut with a description**:
+
+ ```ruby
+ windows_shortcut 'C:\\shortcut_dir.lnk' do
+ target 'C:\\original_dir'
+ description 'Make a shortcut to C:\\original_dir'
+ end
+ ```
+ DOC
+
+ property :shortcut_name, String,
+ description: "An optional property to set the shortcut name if it differs from the resource block's name.",
+ name_property: true
+
+ property :target, String,
+ description: "The destination that 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. Accepts the format of `path, index`, where index is the icon file to use. See Microsoft's [documentation](https://msdn.microsoft.com/en-us/library/3s9bx7at.aspx) for details"
+
+ load_current_value do |desired|
+ require "win32ole" if RUBY_PLATFORM.match?(/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..29bade29ce
--- /dev/null
+++ b/lib/chef/resource/windows_task.rb
@@ -0,0 +1,1070 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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-utils" unless defined?(ChefUtils::CANARY)
+require_relative "../resource"
+require_relative "../win32/security" if ChefUtils.windows_ruby?
+autoload :ISO8601, "iso8601" if ChefUtils.windows_ruby?
+require_relative "../util/path_helper"
+require "win32/taskscheduler" if ChefUtils.windows_ruby?
+
+class Chef
+ class Resource
+ class WindowsTask < Chef::Resource
+ unified_mode true
+
+ provides(:windows_task) { true }
+
+ description "Use the **windows_task** resource to create, delete or run a Windows scheduled task."
+ introduced "13.0"
+ examples <<~DOC
+ **Create a scheduled task to run every 15 minutes as the Administrator user**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ user 'Administrator'
+ password 'password'
+ command 'chef-client'
+ run_level :highest
+ frequency :minute
+ frequency_modifier 15
+ end
+ ```
+
+ **Create a scheduled task to run every 2 days**:
+
+ ``` ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :daily
+ frequency_modifier 2
+ end
+ ```
+
+ **Create a scheduled task to run on specific days of the week**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :weekly
+ day 'Mon, Thu'
+ end
+ ```
+
+ **Create a scheduled task to run only once**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :once
+ start_time '16:10'
+ end
+ ```
+
+ **Create a scheduled task to run on current day every 3 weeks and delay upto 1 min**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :weekly
+ frequency_modifier 3
+ random_delay '60'
+ end
+ ```
+
+ **Create a scheduled task to run weekly starting on Dec 28th 2018**:
+
+ ```ruby
+ windows_task 'chef-client 8' do
+ command 'chef-client'
+ run_level :highest
+ frequency :weekly
+ start_day '12/28/2018'
+ end
+ ```
+
+ **Create a scheduled task to run every Monday, Friday every 2 weeks**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :weekly
+ frequency_modifier 2
+ day 'Mon, Fri'
+ end
+ ```
+
+ **Create a scheduled task to run when computer is idle with idle duration 20 min**:
+
+ ```ruby
+ windows_task 'chef-client' do
+ command 'chef-client'
+ run_level :highest
+ frequency :on_idle
+ idle_time 20
+ end
+ ```
+
+ **Delete a task named "old task"**:
+ ```ruby
+ windows_task 'old task' do
+ action :delete
+ end
+ ```
+
+ **Enable a task named "chef-client"**:
+ ```ruby
+ windows_task 'chef-client' do
+ action :enable
+ end
+ ```
+
+ **Disable a task named "ProgramDataUpdater" with TaskPath "\\Microsoft\\Windows\\Application Experience\\ProgramDataUpdater"**
+ ```ruby
+ windows_task '\\Microsoft\\Windows\\Application Experience\\ProgramDataUpdater' do
+ action :disable
+ end
+ ```
+ DOC
+
+ allowed_actions :create, :delete, :run, :end, :enable, :disable, :change
+ default_action :create
+
+ property :task_name, String, regex: [%r{\A[^/\:\*\?\<\>\|]+\z}],
+ description: "An optional property to set the task name if it differs from the resource block's name. Example: `Task Name` or `/Task Name`",
+ name_property: true
+
+ property :command, String,
+ description: "The command to be executed by the windows scheduled task."
+
+ property :cwd, String,
+ description: "The directory the task will be run from."
+
+ property :user, String,
+ description: "The user to run the task as.",
+ default: lazy { Chef::ReservedNames::Win32::Security::SID.LocalSystem.account_simple_name if ChefUtils.windows_ruby? },
+ default_description: "The localized SYSTEM user for the node."
+
+ property :password, String,
+ description: "The user's password. The user property must be set if using this property."
+
+ property :run_level, Symbol, equal_to: %i{highest limited},
+ description: "Run with `:limited` or `:highest` privileges.",
+ default: :limited
+
+ property :force, [TrueClass, FalseClass],
+ description: "When used with create, will update the task.",
+ default: false
+
+ property :interactive_enabled, [TrueClass, FalseClass],
+ description: "Allow task to run interactively or non-interactively. Requires user and password to also be set.",
+ default: false
+
+ property :frequency_modifier, [Integer, String],
+ default: 1
+
+ property :frequency, Symbol, equal_to: %i{minute hourly daily weekly monthly once on_logon onstart on_idle none},
+ description: "The frequency with which to run the task."
+
+ property :start_day, String,
+ description: "Specifies the first date on which the task runs in **MM/DD/YYYY** format.",
+ default_description: "The current date."
+
+ property :start_time, String,
+ description: "Specifies the start time to run the task, in **HH:mm** format."
+
+ property :day, [String, Integer],
+ description: "The day(s) on which the task runs."
+
+ property :months, String,
+ description: "The Months of the year on which the task runs, such as: `JAN, FEB` or `*`. Multiple months should be comma delimited. e.g. `Jan, Feb, Mar, Dec`."
+
+ property :idle_time, Integer,
+ description: "For `:on_idle` frequency, the time (in minutes) without user activity that must pass to trigger the task, from `1` - `999`."
+
+ property :random_delay, [String, Integer],
+ description: "Delays the task up to a given time (in seconds)."
+
+ property :execution_time_limit, [String, Integer],
+ description: "The maximum time the task will run. This field accepts either seconds or an ISO8601 duration value.",
+ default: "PT72H",
+ default_description: "PT72H (72 hours in ISO8601 duration format)"
+
+ property :minutes_duration, [String, Integer],
+ description: ""
+
+ property :minutes_interval, [String, Integer],
+ description: ""
+
+ property :priority, Integer,
+ description: "Use to set Priority Levels range from 0 to 10.",
+ default: 7, callbacks: { "should be in range of 0 to 10" => proc { |v| v >= 0 && v <= 10 } }
+
+ property :disallow_start_if_on_batteries, [TrueClass, FalseClass],
+ introduced: "14.4", default: false,
+ description: "Disallow start of the task if the system is running on battery power."
+
+ property :stop_if_going_on_batteries, [TrueClass, FalseClass],
+ introduced: "14.4", default: false,
+ description: "Scheduled task option when system is switching on battery."
+
+ property :description, String,
+ introduced: "14.7",
+ description: "The task description."
+
+ property :start_when_available, [TrueClass, FalseClass],
+ introduced: "14.15", default: false,
+ description: "To start the task at any time after its scheduled time has passed."
+
+ attr_accessor :exists, :task, :command_arguments
+
+ VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze
+ 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 *}.freeze
+ VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}.freeze
+
+ 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_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 update 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? || !(%i{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, third 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 separated by comma."
+ end
+
+ # frequency_modifier 2-12 is used to set every (n) months, so using :months property with frequency_modifier is not valid since they both used to set months.
+ # Not checking value 1 here for frequency_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_modifier 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 %i{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 %r{^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$}.match?(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]$/.match?(start_time)
+ else
+ raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once
+ end
+ end
+
+ # System users will not require a password
+ # Other users will require a password if the task is non-interactive.
+ #
+ # @param [String] user
+ # @param [String] password
+ #
+ def validate_user_and_password(user, password)
+ if non_system_user?(user)
+ if password.nil? && !interactive_enabled
+ raise ArgumentError, "Please provide a password or check if this task needs to be interactive! Valid passwordless users are: '#{Chef::ReservedNames::Win32::Security::SID::SYSTEM_USER.join("', '")}'"
+ end
+ else
+ unless password.nil?
+ raise ArgumentError, "Password is not required for system users."
+ end
+ end
+ end
+
+ # Password is not required for system user and required for non-system user.
+ def password_required?(user)
+ @password_required ||= (!user.nil? && !Chef::ReservedNames::Win32::Security::SID.system_user?(user))
+ end
+
+ alias non_system_user? password_required?
+
+ def validate_create_frequency_modifier(frequency, frequency_modifier)
+ if (%i{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 %i{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).to_s
+ # returns 'PT65707200S'
+ def sec_to_dur(seconds)
+ ISO8601::Duration.new(seconds.to_i).to_s
+ end
+
+ def sec_to_min(seconds)
+ seconds.to_i / 60
+ end
+
+ action_class do
+ if ChefUtils.windows_ruby?
+ include ::Win32
+
+ MONTHS = {
+ JAN: ::Win32::TaskScheduler::JANUARY,
+ FEB: ::Win32::TaskScheduler::FEBRUARY,
+ MAR: ::Win32::TaskScheduler::MARCH,
+ APR: ::Win32::TaskScheduler::APRIL,
+ MAY: ::Win32::TaskScheduler::MAY,
+ JUN: ::Win32::TaskScheduler::JUNE,
+ JUL: ::Win32::TaskScheduler::JULY,
+ AUG: ::Win32::TaskScheduler::AUGUST,
+ SEP: ::Win32::TaskScheduler::SEPTEMBER,
+ OCT: ::Win32::TaskScheduler::OCTOBER,
+ NOV: ::Win32::TaskScheduler::NOVEMBER,
+ DEC: ::Win32::TaskScheduler::DECEMBER,
+ }.freeze
+
+ DAYS_OF_WEEK = { MON: ::Win32::TaskScheduler::MONDAY,
+ TUE: ::Win32::TaskScheduler::TUESDAY,
+ WED: ::Win32::TaskScheduler::WEDNESDAY,
+ THU: ::Win32::TaskScheduler::THURSDAY,
+ FRI: ::Win32::TaskScheduler::FRIDAY,
+ SAT: ::Win32::TaskScheduler::SATURDAY,
+ SUN: ::Win32::TaskScheduler::SUNDAY }.freeze
+
+ WEEKS_OF_MONTH = {
+ FIRST: ::Win32::TaskScheduler::FIRST_WEEK,
+ SECOND: ::Win32::TaskScheduler::SECOND_WEEK,
+ THIRD: ::Win32::TaskScheduler::THIRD_WEEK,
+ FOURTH: ::Win32::TaskScheduler::FOURTH_WEEK,
+ }.freeze
+
+ DAYS_OF_MONTH = {
+ 1 => ::Win32::TaskScheduler::TASK_FIRST,
+ 2 => ::Win32::TaskScheduler::TASK_SECOND,
+ 3 => ::Win32::TaskScheduler::TASK_THIRD,
+ 4 => ::Win32::TaskScheduler::TASK_FOURTH,
+ 5 => ::Win32::TaskScheduler::TASK_FIFTH,
+ 6 => ::Win32::TaskScheduler::TASK_SIXTH,
+ 7 => ::Win32::TaskScheduler::TASK_SEVENTH,
+ 8 => ::Win32::TaskScheduler::TASK_EIGHTH,
+ # cspell:disable-next-line
+ 9 => ::Win32::TaskScheduler::TASK_NINETH,
+ 10 => ::Win32::TaskScheduler::TASK_TENTH,
+ 11 => ::Win32::TaskScheduler::TASK_ELEVENTH,
+ 12 => ::Win32::TaskScheduler::TASK_TWELFTH,
+ 13 => ::Win32::TaskScheduler::TASK_THIRTEENTH,
+ 14 => ::Win32::TaskScheduler::TASK_FOURTEENTH,
+ 15 => ::Win32::TaskScheduler::TASK_FIFTEENTH,
+ 16 => ::Win32::TaskScheduler::TASK_SIXTEENTH,
+ 17 => ::Win32::TaskScheduler::TASK_SEVENTEENTH,
+ 18 => ::Win32::TaskScheduler::TASK_EIGHTEENTH,
+ 19 => ::Win32::TaskScheduler::TASK_NINETEENTH,
+ 20 => ::Win32::TaskScheduler::TASK_TWENTIETH,
+ 21 => ::Win32::TaskScheduler::TASK_TWENTY_FIRST,
+ 22 => ::Win32::TaskScheduler::TASK_TWENTY_SECOND,
+ 23 => ::Win32::TaskScheduler::TASK_TWENTY_THIRD,
+ 24 => ::Win32::TaskScheduler::TASK_TWENTY_FOURTH,
+ 25 => ::Win32::TaskScheduler::TASK_TWENTY_FIFTH,
+ 26 => ::Win32::TaskScheduler::TASK_TWENTY_SIXTH,
+ 27 => ::Win32::TaskScheduler::TASK_TWENTY_SEVENTH,
+ 28 => ::Win32::TaskScheduler::TASK_TWENTY_EIGHTH,
+ 29 => ::Win32::TaskScheduler::TASK_TWENTY_NINTH,
+ # cspell:disable-next-line
+ 30 => ::Win32::TaskScheduler::TASK_THIRTYETH,
+ 31 => ::Win32::TaskScheduler::TASK_THIRTY_FIRST,
+ }.freeze
+
+ PRIORITY = { "critical" => 0, "highest" => 1, "above_normal_2" => 2 , "above_normal_3" => 3, "normal_4" => 4,
+ "normal_5" => 5, "normal_6" => 6, "below_normal_7" => 7, "below_normal_8" => 8, "lowest" => 9, "idle" => 10 }.freeze
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsTask.new(new_resource.name)
+ task = ::Win32::TaskScheduler.new(new_resource.task_name, nil, "\\", false)
+ @current_resource.exists = task.exists?(new_resource.task_name)
+ if @current_resource.exists
+ 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)
+ end
+ @current_resource
+ end
+
+ # separated command arguments from :command property
+ def set_command_and_arguments
+ cmd, *args = Chef::Util::PathHelper.split_args(new_resource.command)
+ new_resource.command = cmd
+ new_resource.command_arguments = args.join(" ")
+ end
+
+ 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, new_resource.interactive_enabled)
+ task.application_name = new_resource.command if new_resource.command
+ task.parameters = new_resource.command_arguments if new_resource.command_arguments
+ 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.description = new_resource.description unless new_resource.description.nil?
+ 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 == ::Win32::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 == ::Win32::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 getting 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.author != new_resource.user ||
+ task.application_name != new_resource.command ||
+ description_needs_update?(task) ||
+ task.parameters != new_resource.command_arguments.to_s ||
+ task.principals[:run_level] != run_level ||
+ task.settings[:disallow_start_if_on_batteries] != new_resource.disallow_start_if_on_batteries ||
+ task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries ||
+ task.settings[:start_when_available] != new_resource.start_when_available)
+ 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.author.to_s.casecmp(new_resource.user.to_s) != 0 ||
+ task.application_name != new_resource.command ||
+ description_needs_update?(task) ||
+ task.parameters != new_resource.command_arguments.to_s ||
+ task.working_directory != new_resource.cwd.to_s ||
+ task.principals[:logon_type] != logon_type ||
+ task.principals[:run_level] != run_level ||
+ PRIORITY[task.priority] != new_resource.priority ||
+ task.settings[:disallow_start_if_on_batteries] != new_resource.disallow_start_if_on_batteries ||
+ task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries ||
+ task.settings[:start_when_available] != new_resource.start_when_available
+ if trigger_type == ::Win32::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 == ::Win32::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
+ ::Win32::TaskScheduler::ONCE
+ when :daily
+ ::Win32::TaskScheduler::DAILY
+ when :weekly
+ ::Win32::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) ? ::Win32::TaskScheduler::MONTHLYDATE : ::Win32::TaskScheduler::MONTHLYDOW
+ when :on_idle
+ ::Win32::TaskScheduler::ON_IDLE
+ when :onstart
+ ::Win32::TaskScheduler::AT_SYSTEMSTART
+ when :on_logon
+ ::Win32::TaskScheduler::AT_LOGON
+ else
+ raise ArgumentError, "Please set frequency"
+ end
+ end
+
+ def type
+ case trigger_type
+ when ::Win32::TaskScheduler::ONCE
+ { once: nil }
+ when ::Win32::TaskScheduler::DAILY
+ { days_interval: new_resource.frequency_modifier.to_i }
+ when ::Win32::TaskScheduler::WEEKLY
+ { weeks_interval: new_resource.frequency_modifier.to_i, days_of_week: days_of_week.to_i }
+ when ::Win32::TaskScheduler::MONTHLYDATE
+ { months: months_of_year.to_i, days: days_of_month.to_i }
+ when ::Win32::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 ::Win32::TaskScheduler::ON_IDLE
+ # TODO: handle option for this trigger
+ when ::Win32::TaskScheduler::AT_LOGON
+ # TODO: handle option for this trigger
+ when ::Win32::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.to_s.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.to_s.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
+ ::Win32::TaskScheduler::TASK_RUNLEVEL_HIGHEST
+ when :limited
+ ::Win32::TaskScheduler::TASK_RUNLEVEL_LUA
+ end
+ end
+
+ # TODO: while creating the configuration settings win32-taskscheduler it accepts execution time limit values in ISO8601 format
+ 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[:priority] = new_resource.priority
+ settings[:disallow_start_if_on_batteries] = new_resource.disallow_start_if_on_batteries
+ settings[:stop_if_going_on_batteries] = new_resource.stop_if_going_on_batteries
+ settings[:start_when_available] = new_resource.start_when_available
+ settings
+ end
+
+ def principal_settings
+ settings = {}
+ settings[:run_level] = run_level
+ settings[:logon_type] = logon_type
+ settings
+ end
+
+ def description_needs_update?(task)
+ task.description != new_resource.description unless new_resource.description.nil?
+ 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 comparison.
+ user_id = new_resource.user.to_s
+ password = new_resource.password.to_s
+ if Chef::ReservedNames::Win32::Security::SID.service_account_user?(user_id)
+ ::Win32::TaskScheduler::TASK_LOGON_SERVICE_ACCOUNT
+ elsif Chef::ReservedNames::Win32::Security::SID.group_user?(user_id)
+ ::Win32::TaskScheduler::TASK_LOGON_GROUP
+ elsif !user_id.empty? && !password.empty?
+ if new_resource.interactive_enabled
+ ::Win32::TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN
+ else
+ ::Win32::TaskScheduler::TASK_LOGON_PASSWORD
+ end
+ else
+ ::Win32::TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN
+ end
+ end
+
+ # This method checks if task and command properties exist since those two are mandatory properties 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
+
+ action :create do
+ set_command_and_arguments if new_resource.command
+
+ 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 = ::Win32::TaskScheduler.new
+ if new_resource.frequency == :none
+ task.new_work_item(new_resource.task_name, {}, { user: new_resource.user, password: new_resource.password, interactive: new_resource.interactive_enabled })
+ task.activate(new_resource.task_name)
+ else
+ task.new_work_item(new_resource.task_name, trigger, { user: new_resource.user, password: new_resource.password, interactive: new_resource.interactive_enabled })
+ end
+ task.application_name = new_resource.command
+ task.parameters = new_resource.command_arguments if new_resource.command_arguments
+ 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, new_resource.interactive_enabled)
+ task.creator = new_resource.user
+ task.description = new_resource.description unless new_resource.description.nil?
+ task.activate(new_resource.task_name)
+ end
+ end
+ end
+
+ action :run do
+ 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
+
+ action :delete do
+ if current_resource.exists
+ logger.trace "#{new_resource} task exists"
+ converge_by("delete scheduled task #{new_resource}") do
+ ts = ::Win32::TaskScheduler.new
+ ts.delete(current_resource.task_name)
+ end
+ else
+ logger.warn "#{new_resource} task does not exist - nothing to do"
+ end
+ end
+
+ action :end do
+ 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
+
+ action :enable do
+ 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
+
+ action :disable do
+ 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 which disables 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
+
+ action_class do
+ alias_method :action_change, :action_create
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_uac.rb b/lib/chef/resource/windows_uac.rb
new file mode 100644
index 0000000000..db5d5fd173
--- /dev/null
+++ b/lib/chef/resource/windows_uac.rb
@@ -0,0 +1,114 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsUac < Chef::Resource
+ unified_mode true
+
+ provides :windows_uac
+
+ description 'The *windows_uac* resource configures UAC on Windows hosts by setting registry keys at `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System`'
+ introduced "15.0"
+ examples <<~DOC
+ **Disable UAC prompts for the admin**:
+
+ ``` ruby
+ windows_uac 'Disable UAC prompts for the admin' do
+ enable_uac true
+ prompt_on_secure_desktop false
+ consent_behavior_admins :no_prompt
+ end
+ ```
+
+ **Disable UAC entirely**:
+
+ ``` ruby
+ windows_uac 'Disable UAC entirely' do
+ enable_uac false
+ end
+ ```
+ DOC
+
+ # https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/user-account-control-group-policy-and-registry-key-settings#user-account-control-virtualize-file-and-registry-write-failures-to-per-user-locations
+ property :enable_uac, [TrueClass, FalseClass],
+ description: 'Enable or disable UAC Admin Approval Mode. If this is changed a system restart is required. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA.',
+ default: true # EnableLUA
+
+ property :require_signed_binaries, [TrueClass, FalseClass],
+ description: 'Only elevate executables that are signed and validated. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA\ValidateAdminCodeSignatures.',
+ default: false
+
+ property :prompt_on_secure_desktop, [TrueClass, FalseClass],
+ description: 'Switch to the secure desktop when prompting for elevation. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA\PromptOnSecureDesktop.',
+ default: true
+
+ property :detect_installers, [TrueClass, FalseClass],
+ description: 'Detect application installations and prompt for elevation. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA\EnableInstallerDetection.'
+
+ property :consent_behavior_admins, Symbol,
+ description: 'Behavior of the elevation prompt for administrators in Admin Approval Mode. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA\ConsentPromptBehaviorAdmin.',
+ equal_to: %i{no_prompt secure_prompt_for_creds secure_prompt_for_consent prompt_for_creds prompt_for_consent prompt_for_consent_non_windows_binaries},
+ default: :prompt_for_consent_non_windows_binaries
+
+ property :consent_behavior_users, Symbol,
+ description: 'Behavior of the elevation prompt for standard users. Sets HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA\ConsentPromptBehaviorUser.',
+ equal_to: %i{auto_deny secure_prompt_for_creds prompt_for_creds},
+ default: :prompt_for_creds
+
+ action :configure do
+ description 'Configures UAC by setting registry keys at \'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\''
+
+ registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' do
+ values [{ name: "EnableLUA", type: :dword, data: bool_to_reg(new_resource.enable_uac) },
+ { name: "ValidateAdminCodeSignatures", type: :dword, data: bool_to_reg(new_resource.require_signed_binaries) },
+ { name: "PromptOnSecureDesktop", type: :dword, data: bool_to_reg(new_resource.prompt_on_secure_desktop) },
+ { name: "ConsentPromptBehaviorAdmin", type: :dword, data: consent_behavior_admins_symbol_to_reg(new_resource.consent_behavior_admins) },
+ { name: "ConsentPromptBehaviorUser", type: :dword, data: consent_behavior_users_symbol_to_reg(new_resource.consent_behavior_users) },
+ { name: "EnableInstallerDetection", type: :dword, data: bool_to_reg(new_resource.detect_installers) },
+ ]
+ action :create
+ end
+ end
+
+ action_class do
+ # converts a Ruby true/false to a 1 or 0
+ #
+ # @return [Integer] 1:true, 0: false
+ def bool_to_reg(bool)
+ bool ? 1 : 0
+ end
+
+ # converts the symbols we use in the consent_behavior_admins property into numbers 0-5 based on their array index
+ #
+ # @return [Integer]
+ def consent_behavior_admins_symbol_to_reg(sym)
+ %i{no_prompt secure_prompt_for_creds secure_prompt_for_consent prompt_for_creds prompt_for_consent prompt_for_consent_non_windows_binaries}.index(sym)
+ end
+
+ # converts the symbols we use in the consent_behavior_users property into numbers 0-2 based on their array index
+ #
+ # @return [Integer]
+ def consent_behavior_users_symbol_to_reg(sym)
+ %i{auto_deny secure_prompt_for_creds prompt_for_creds}.index(sym)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_user_privilege.rb b/lib/chef/resource/windows_user_privilege.rb
new file mode 100644
index 0000000000..971338303d
--- /dev/null
+++ b/lib/chef/resource/windows_user_privilege.rb
@@ -0,0 +1,223 @@
+#
+# Author:: Jared Kauppila (<jared@kauppi.la>)
+# Author:: Vasundhara Jagdale(<vasundhara.jagdale@chef.io>)
+# Copyright:: Copyright (c) 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_relative "../resource"
+
+class Chef
+ class Resource
+ class WindowsUserPrivilege < Chef::Resource
+ unified_mode true
+
+ provides :windows_user_privilege
+ description "The windows_user_privilege resource allows to add and set principal (User/Group) to the specified privilege.\n Ref: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment"
+
+ introduced "16.0"
+
+ examples <<~DOC
+ **Set the SeNetworkLogonRight Privilege for the Builtin Administrators Group and Authenticated Users**:
+
+ ```ruby
+ windows_user_privilege 'Network Logon Rights' do
+ privilege 'SeNetworkLogonRight'
+ users ['BUILTIN\\Administrators', 'NT AUTHORITY\\Authenticated Users']
+ action :set
+ end
+ ```
+
+ **Add the SeDenyRemoteInteractiveLogonRight Privilege to the Builtin Guests and Local Accounts User Groups**:
+
+ ```ruby
+ windows_user_privilege 'Remote interactive logon' do
+ privilege 'SeDenyRemoteInteractiveLogonRight'
+ users ['Builtin\\Guests', 'NT AUTHORITY\\Local Account']
+ action :add
+ end
+ ```
+
+ **Provide only the Builtin Guests and Administrator Groups with the SeCreatePageFile Privilege**:
+
+ ```ruby
+ windows_user_privilege 'Create Pagefile' do
+ privilege 'SeCreatePagefilePrivilege'
+ users ['BUILTIN\\Guests', 'BUILTIN\\Administrators']
+ action :set
+ end
+ ```
+
+ **Remove the SeCreatePageFile Privilege from the Builtin Guests Group**:
+
+ ```ruby
+ windows_user_privilege 'Create Pagefile' do
+ privilege 'SeCreatePagefilePrivilege'
+ users ['BUILTIN\\Guests']
+ action :remove
+ end
+ ```
+
+ **Clear all users from the SeDenyNetworkLogonRight Privilege**:
+
+ ```ruby
+ windows_user_privilege 'Allow any user the Network Logon right' do
+ privilege 'SeDenyNetworkLogonRight'
+ action :clear
+ end
+ ```
+ DOC
+
+ PRIVILEGE_OPTS = %w{ SeAssignPrimaryTokenPrivilege
+ SeAuditPrivilege
+ SeBackupPrivilege
+ SeBatchLogonRight
+ SeChangeNotifyPrivilege
+ SeCreateGlobalPrivilege
+ SeCreatePagefilePrivilege
+ SeCreatePermanentPrivilege
+ SeCreateSymbolicLinkPrivilege
+ SeCreateTokenPrivilege
+ SeDebugPrivilege
+ SeDenyBatchLogonRight
+ SeDenyInteractiveLogonRight
+ SeDenyNetworkLogonRight
+ SeDenyRemoteInteractiveLogonRight
+ SeDenyServiceLogonRight
+ SeEnableDelegationPrivilege
+ SeImpersonatePrivilege
+ SeIncreaseBasePriorityPrivilege
+ SeIncreaseQuotaPrivilege
+ SeIncreaseWorkingSetPrivilege
+ SeInteractiveLogonRight
+ SeLoadDriverPrivilege
+ SeLockMemoryPrivilege
+ SeMachineAccountPrivilege
+ SeManageVolumePrivilege
+ SeNetworkLogonRight
+ SeProfileSingleProcessPrivilege
+ SeRelabelPrivilege
+ SeRemoteInteractiveLogonRight
+ SeRemoteShutdownPrivilege
+ SeRestorePrivilege
+ SeSecurityPrivilege
+ SeServiceLogonRight
+ SeShutdownPrivilege
+ SeSyncAgentPrivilege
+ SeSystemEnvironmentPrivilege
+ SeSystemProfilePrivilege
+ SeSystemtimePrivilege
+ SeTakeOwnershipPrivilege
+ SeTcbPrivilege
+ SeTimeZonePrivilege
+ SeTrustedCredManAccessPrivilege
+ SeUndockPrivilege
+ }.freeze
+
+ property :principal, String,
+ description: "An optional property to add the user to the given privilege. Use only with add and remove action.",
+ name_property: true
+
+ property :users, [Array, String],
+ description: "An optional property to set the privilege for given users. Use only with set action.",
+ coerce: proc { |v| Array(v) }
+
+ property :privilege, [Array, String],
+ description: "One or more privileges to set for users.",
+ required: true,
+ coerce: proc { |v| Array(v) },
+ callbacks: {
+ "Privilege property restricted to the following values: #{PRIVILEGE_OPTS}" => lambda { |n| (n - PRIVILEGE_OPTS).empty? },
+ }
+
+ load_current_value do |new_resource|
+ if new_resource.principal && (new_resource.action.include?(:add) || new_resource.action.include?(:remove))
+ privilege Chef::ReservedNames::Win32::Security.get_account_right(new_resource.principal)
+ end
+ end
+
+ action :add do
+ ([*new_resource.privilege] - [*current_resource.privilege]).each do |user_right|
+ converge_by("adding user '#{new_resource.principal}' privilege #{user_right}") do
+ Chef::ReservedNames::Win32::Security.add_account_right(new_resource.principal, user_right)
+ end
+ end
+ end
+
+ action :set do
+ if new_resource.users.nil? || new_resource.users.empty?
+ raise Chef::Exceptions::ValidationFailed, "Users are required property with set action."
+ end
+
+ users = []
+
+ # Getting users with its domain for comparison
+ new_resource.users.each do |user|
+ user = Chef::ReservedNames::Win32::Security.lookup_account_name(user)
+ users << user[1].account_name if user
+ end
+
+ new_resource.privilege.each do |privilege|
+ accounts = Chef::ReservedNames::Win32::Security.get_account_with_user_rights(privilege)
+
+ # comparing the existing accounts for privilege with users
+ unless users == accounts
+ # Removing only accounts which is not matching with users in new_resource
+ (accounts - users).each do |account|
+ converge_by("removing user '#{account}' from privilege #{privilege}") do
+ Chef::ReservedNames::Win32::Security.remove_account_right(account, privilege)
+ end
+ end
+
+ # Adding only users which is not already exist
+ (users - accounts).each do |user|
+ converge_by("adding user '#{user}' to privilege #{privilege}") do
+ Chef::ReservedNames::Win32::Security.add_account_right(user, privilege)
+ end
+ end
+ end
+ end
+ end
+
+ action :clear do
+ new_resource.privilege.each do |privilege|
+ accounts = Chef::ReservedNames::Win32::Security.get_account_with_user_rights(privilege)
+
+ # comparing the existing accounts for privilege with users
+ # Removing only accounts which is not matching with users in new_resource
+ accounts.each do |account|
+ converge_by("removing user '#{account}' from privilege #{privilege}") do
+ Chef::ReservedNames::Win32::Security.remove_account_right(account, privilege)
+ end
+ end
+ end
+ end
+
+ action :remove do
+ curr_res_privilege = current_resource.privilege
+ missing_res_privileges = (new_resource.privilege - curr_res_privilege)
+
+ if missing_res_privileges
+ Chef::Log.info("User \'#{new_resource.principal}\' for Privilege: #{missing_res_privileges.join(", ")} not found. Nothing to remove.")
+ end
+
+ (new_resource.privilege - missing_res_privileges).each do |user_right|
+ converge_by("removing user #{new_resource.principal} from privilege #{user_right}") do
+ Chef::ReservedNames::Win32::Security.remove_account_right(new_resource.principal, user_right)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_workgroup.rb b/lib/chef/resource/windows_workgroup.rb
new file mode 100644
index 0000000000..3c49f7cb3e
--- /dev/null
+++ b/lib/chef/resource/windows_workgroup.rb
@@ -0,0 +1,130 @@
+#
+# Author:: Derek Groh (<derekgroh@github.io>)
+# Copyright:: 2018, Derek Groh
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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 "../resource"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+class Chef
+ class Resource
+ class WindowsWorkgroup < Chef::Resource
+ unified_mode true
+
+ provides :windows_workgroup
+
+ description "Use the **windows_workgroup** resource to join or change the workgroup of a Windows host."
+ introduced "14.5"
+ examples <<~DOC
+ **Join a workgroup**:
+
+ ``` ruby
+ windows_workgroup 'myworkgroup'
+ ```
+
+ **Join a workgroup using a specific user**:
+
+ ``` ruby
+ windows_workgroup 'myworkgroup' do
+ user 'Administrator'
+ password 'passw0rd'
+ end
+ ```
+ DOC
+
+ property :workgroup_name, String,
+ description: "An optional property to set the workgroup name if it differs from the resource block's name.",
+ validation_message: "The 'workgroup_name' property must not contain spaces.",
+ regex: /^\S*$/, # no spaces
+ name_property: true
+
+ property :user, String,
+ description: "The local administrator user to use to change the workgroup. Required if using the `password` property.",
+ desired_state: false
+
+ property :password, String,
+ description: "The password for the local administrator user. Required if using the `user` property.",
+ sensitive: true,
+ desired_state: false
+
+ property :reboot, Symbol,
+ equal_to: %i{never request_reboot reboot_now},
+ validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the #{ChefUtils::Dist::Infra::PRODUCT} run completes), and :never (Don't reboot)",
+ description: "Controls the system reboot behavior post workgroup joining. Reboot immediately, after the #{ChefUtils::Dist::Infra::PRODUCT} run completes, or never. Note that a reboot is necessary for changes to take effect.",
+ coerce: proc { |x| clarify_reboot(x) },
+ default: :immediate, desired_state: false
+
+ # This resource historically took `:immediate` and `:delayed` as arguments to the reboot property but then
+ # tried to shove that straight to the `reboot` resource which objected strenuously. We need to convert these
+ # legacy actions into actual reboot actions
+ #
+ # @return [Symbol] chef reboot resource action
+ def clarify_reboot(reboot_action)
+ case reboot_action
+ when :immediate
+ :reboot_now
+ when :delayed
+ :request_reboot
+ else
+ reboot_action
+ end
+ end
+
+ # define this again so we can default it to true. Otherwise failures print the password
+ # FIXME: this should now be unnecessary with the password property itself marked sensitive?
+ property :sensitive, [TrueClass, FalseClass],
+ default: true, desired_state: false
+
+ action :join do
+ description "Update the workgroup."
+
+ unless workgroup_member?
+ converge_by("join workstation workgroup #{new_resource.workgroup_name}") do
+ ps_run = powershell_exec(join_command)
+ raise "Failed to join the workgroup #{new_resource.workgroup_name}: #{ps_run.errors}}" if ps_run.error?
+
+ unless new_resource.reboot == :never
+ reboot "Reboot to join workgroup #{new_resource.workgroup_name}" do
+ action new_resource.reboot
+ reason "Reboot to join workgroup #{new_resource.workgroup_name}"
+ end
+ end
+ end
+ end
+ end
+
+ action_class do
+ # return [String] the appropriate PS command to joint the workgroup
+ def join_command
+ cmd = ""
+ cmd << "$pswd = ConvertTo-SecureString \'#{new_resource.password}\' -AsPlainText -Force;" if new_resource.password
+ cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.user}\",$pswd);" if new_resource.password
+ cmd << "Add-Computer -WorkgroupName #{new_resource.workgroup_name}"
+ cmd << " -Credential $credential" if new_resource.password
+ cmd << " -Force"
+ cmd
+ end
+
+ # @return [Boolean] is the node a member of the workgroup specified in the resource
+ def workgroup_member?
+ node_workgroup = powershell_exec!("(Get-WmiObject -Class Win32_ComputerSystem).Workgroup")
+ raise "Failed to determine if system already a member of workgroup #{new_resource.workgroup_name}" if node_workgroup.error?
+
+ String(node_workgroup.result).downcase.strip == new_resource.workgroup_name.downcase
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb
index 9d69897f5f..f7c4517c6d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,33 +16,148 @@
# limitations under the License.
#
-require "chef/resource/package"
-require "chef/provider/package/yum"
+require_relative "package"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
class Chef
class Resource
class YumPackage < Chef::Resource::Package
- resource_name :yum_package
- provides :package, os: "linux", platform_family: %w{rhel fedora}
-
- # Install a specific arch
- property :arch, [ String, Array ]
- # the {} on the proc here is because rspec chokes if it's do...end
- property :flush_cache,
- Hash,
- default: { before: false, after: false },
- coerce: proc { |v|
- if v.is_a?(Array)
- v.each_with_object({}) { |arg, obj| obj[arg] = true }
- elsif v.any?
- v
- else
- { before: v, after: v }
- end
- }
- property :allow_downgrade, [ true, false ], default: false
- property :yum_binary, String
+ unified_mode true
+ provides :yum_package
+ provides :package, platform_family: "fedora_derived"
+
+ 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."
+ examples <<~DOC
+ **Install an exact version**:
+
+ ``` ruby
+ yum_package 'netpbm = 10.35.58-8.el8'
+ ```
+
+ **Install a minimum version**:
+
+ ``` ruby
+ yum_package 'netpbm >= 10.35.58-8.el8'
+ ```
+
+ **Install a minimum version using the default action**:
+
+ ``` ruby
+ yum_package 'netpbm'
+ ```
+
+ **Install a version without worrying about the exact release**:
+
+ ``` ruby
+ yum_package 'netpbm-10.35*'
+ ```
+
+
+ **To install a package**:
+
+ ``` ruby
+ yum_package 'netpbm' do
+ action :install
+ end
+ ```
+
+ **To install a partial minimum version**:
+
+ ``` ruby
+ yum_package 'netpbm >= 10'
+ ```
+
+ **To install a specific architecture**:
+
+ ``` ruby
+ yum_package 'netpbm' do
+ arch 'i386'
+ end
+ ```
+
+ or:
+
+ ``` ruby
+ yum_package 'netpbm.x86_64'
+ ```
+
+ **To install a specific version-release**
+
+ ``` ruby
+ yum_package 'netpbm' do
+ version '10.35.58-8.el8'
+ end
+ ```
+
+ **Handle cookbook_file and yum_package resources in the same recipe**:
+
+ When a **cookbook_file** resource and a **yum_package** resource are
+ both called from within the same recipe, use the `flush_cache` attribute
+ to dump the in-memory Yum cache, and then use the repository immediately
+ to ensure that the correct package is installed:
+
+ ``` ruby
+ cookbook_file '/etc/yum.repos.d/custom.repo' do
+ source 'custom'
+ mode '0755'
+ end
+
+ yum_package 'pkg-that-is-only-in-custom-repo' do
+ action :install
+ flush_cache [ :before ]
+ end
+ ```
+ DOC
+
+ # 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 modifications
+ # of variables eliminate entire classes of bugs).
+ # </lecture>
+ property :package_name, [ String, Array ],
+ description: "One of the following: the name of a package, the name of a package and its architecture, the name of a dependency.",
+ identity: true, coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+
+ property :version, [ String, Array ],
+ description: "The version of a package to be installed or upgraded. This property is ignored when using the `:upgrade` action.",
+ coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+
+ property :arch, [ String, Array ],
+ description: "The architecture of the package to be installed or upgraded. This value can also be passed as part of the package name.",
+ coerce: proc { |x| x.is_a?(Array) ? x.to_a : x }
+
+ property :flush_cache, Hash,
+ description: "Flush the in-memory cache before or after a Yum operation that installs, upgrades, or removes a package. Accepts a Hash in the form: { :before => true/false, :after => true/false } or an Array in the form [ :before, :after ].\nYum automatically synchronizes remote metadata to a local cache. The #{ChefUtils::Dist::Infra::CLIENT} creates a copy of the local cache, and then stores it in-memory during the #{ChefUtils::Dist::Infra::CLIENT} run. The in-memory cache allows packages to be installed during the #{ChefUtils::Dist::Infra::CLIENT} run without the need to continue synchronizing the remote metadata to the local cache while the #{ChefUtils::Dist::Infra::CLIENT} run is in-progress.",
+ 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
+ }
+
+ property :allow_downgrade, [ TrueClass, FalseClass ],
+ description: "Allow downgrading a package to satisfy requested version requirements.",
+ default: true,
+ desired_state: false
+
+ property :yum_binary, String,
+ description: "The path to the yum binary."
end
end
end
diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb
index 3633f4421b..b5ad2688eb 100644
--- a/lib/chef/resource/yum_repository.rb
+++ b/lib/chef/resource/yum_repository.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,64 +16,195 @@
# limitations under the License.
#
-require "chef/resource"
+require_relative "../resource"
class Chef
class Resource
class YumRepository < Chef::Resource
- resource_name :yum_repository
- provides :yum_repository
-
- # http://linux.die.net/man/5/yum.conf
- property :baseurl, String, regex: /.*/
- property :cost, String, regex: /^\d+$/
- property :clean_headers, [TrueClass, FalseClass], default: false # deprecated
- property :clean_metadata, [TrueClass, FalseClass], default: true
- property :description, String, regex: /.*/, default: "Yum Repository"
- property :enabled, [TrueClass, FalseClass], default: true
- property :enablegroups, [TrueClass, FalseClass]
- property :exclude, String, regex: /.*/
- property :failovermethod, String, equal_to: %w{priority roundrobin}
- property :fastestmirror_enabled, [TrueClass, FalseClass]
- property :gpgcheck, [TrueClass, FalseClass]
- property :gpgkey, [String, Array], regex: /.*/
- property :http_caching, String, equal_to: %w{packages all none}
- property :include_config, String, regex: /.*/
- property :includepkgs, String, regex: /.*/
- 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 :mirrorexpire, String, regex: /.*/
- property :mirrorlist, String, regex: /.*/
- property :mirror_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
- property :mirrorlist_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
- property :mode, default: "0644"
- property :priority, String, regex: /^(\d?[0-9]|[0-9][0-9])$/
- property :proxy, String, regex: /.*/
- property :proxy_username, String, regex: /.*/
- property :proxy_password, String, regex: /.*/
- property :username, String, regex: /.*/
- property :password, String, regex: /.*/
- property :repo_gpgcheck, [TrueClass, FalseClass]
- property :report_instanceid, [TrueClass, FalseClass]
- property :repositoryid, String, regex: /.*/, name_attribute: true
- property :sensitive, [TrueClass, FalseClass], default: false
- property :skip_if_unavailable, [TrueClass, FalseClass]
- property :source, String, regex: /.*/
- property :sslcacert, String, regex: /.*/
- property :sslclientcert, String, regex: /.*/
- property :sslclientkey, String, regex: /.*/
- property :sslverify, [TrueClass, FalseClass]
- property :timeout, String, regex: /^\d+$/
- property :options, Hash
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ **Add an internal company repository**:
+
+ ```ruby
+ yum_repository 'OurCo' do
+ description 'OurCo yum repository'
+ mirrorlist 'http://artifacts.ourco.org/mirrorlist?repo=ourco-8&arch=$basearch'
+ gpgkey 'http://artifacts.ourco.org/pub/yum/RPM-GPG-KEY-OURCO-8'
+ action :create
+ end
+ ```
+
+ **Delete a repository**:
+
+ ```ruby
+ yum_repository 'CentOS-Media' do
+ action :delete
+ end
+ ```
+ DOC
+
+ # http://linux.die.net/man/5/yum.conf as well as
+ # http://dnf.readthedocs.io/en/latest/conf_ref.html
+ property :reposdir, String,
+ description: "The directory where the Yum repository files should be stored",
+ default: "/etc/yum.repos.d/",
+ introduced: "16.9"
+
+ property :baseurl, [String, Array],
+ description: "URL to the directory where the Yum repository's `repodata` directory lives. Can be an `http://`, `https://` or a `ftp://` URLs. You can specify multiple URLs in one `baseurl` statement."
+
+ property :clean_headers, [TrueClass, FalseClass],
+ description: "Specifies whether you want to purge the package data files that are downloaded from a Yum repository and held in a cache directory.",
+ deprecated: true,
+ default: false
+
+ property :clean_metadata, [TrueClass, FalseClass],
+ description: "Specifies whether you want to purge all of the packages downloaded from a Yum repository and held in a cache directory.",
+ default: true
+
+ property :cost, String, regex: /^\d+$/,
+ description: "Relative cost of accessing this repository. Useful for weighing one repo's packages as greater/less than any other.",
+ validation_message: "The cost property must be a numeric value!"
+
+ property :description, String,
+ description: "Descriptive name for the repository channel and maps to the 'name' parameter in a repository .conf.",
+ default: "Yum Repository"
+
+ property :enabled, [TrueClass, FalseClass],
+ description: "Specifies whether or not Yum should use this repository.",
+ default: true
+
+ property :enablegroups, [TrueClass, FalseClass],
+ description: "Specifies whether Yum will allow the use of package groups for this repository."
+
+ property :exclude, String,
+ description: "List of packages to exclude from updates or installs. This should be a space separated list. Shell globs using wildcards (eg. * and ?) are allowed."
+
+ property :failovermethod, String,
+ description: "Method to determine how to switch to a new server if the current one fails, which can either be `roundrobin` or `priority`. `roundrobin` randomly selects a URL out of the list of URLs to start with and proceeds through each of them as it encounters a failure contacting the host. `priority` starts from the first `baseurl` listed and reads through them sequentially.",
+ equal_to: %w{priority roundrobin}
+
+ property :fastestmirror_enabled, [TrueClass, FalseClass],
+ description: "Specifies whether to use the fastest mirror from a repository configuration when more than one mirror is listed in that configuration."
+
+ property :gpgcheck, [TrueClass, FalseClass],
+ description: "Specifies whether or not Yum should perform a GPG signature check on the packages received from a repository.",
+ default: true
+
+ property :gpgkey, [String, Array],
+ description: "URL pointing to the ASCII-armored GPG key file for the repository. This is used if Yum needs a public key to verify a package and the required key hasn't been imported into the RPM database. If this option is set, Yum will automatically import the key from the specified URL. Multiple URLs may be specified in the same manner as the baseurl option. If a GPG key is required to install a package from a repository, all keys specified for that repository will be installed.\nMultiple URLs may be specified in the same manner as the baseurl option. If a GPG key is required to install a package from a repository, all keys specified for that repository will be installed."
+
+ property :http_caching, String, equal_to: %w{packages all none},
+ description: "Determines how upstream HTTP caches are instructed to handle any HTTP downloads that Yum does. This option can take the following values:\n - `all` means all HTTP downloads should be cached\n - `packages` means only RPM package downloads should be cached, but not repository metadata downloads\n - `none` means no HTTP downloads should be cached.\n\nThe default value of `all` is recommended unless you are experiencing caching related issues."
+
+ property :include_config, String,
+ description: "An external configuration file using the format `url://to/some/location`."
+
+ property :includepkgs, String,
+ description: "Inverse of exclude property. This is a list of packages you want to use from a repository. If this option lists only one package then that is all Yum will ever see from the repository."
+
+ property :keepalive, [TrueClass, FalseClass],
+ description: "Determines whether or not HTTP/1.1 `keep-alive` should be used with this repository."
+
+ property :make_cache, [TrueClass, FalseClass],
+ description: "Determines whether package files downloaded by Yum stay in cache directories. By using cached data, you can carry out certain operations without a network connection.",
+ default: true
+
+ property :max_retries, [String, Integer],
+ description: "Number of times any attempt to retrieve a file should retry before returning an error. Setting this to `0` makes Yum try forever."
+
+ property :metadata_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/, /never/],
+ description: "Time (in seconds) after which the metadata will expire. If the current metadata downloaded is less than the value specified, then Yum will not update the metadata against the repository. If you find that Yum is not downloading information on updates as often as you would like lower the value of this option. You can also change from the default of using seconds to using days, hours or minutes by appending a `d`, `h` or `m` respectively. The default is six hours to compliment yum-updates running once per hour. It is also possible to use the word `never`, meaning that the metadata will never expire. Note: When using a metalink file, the metalink must always be newer than the metadata for the repository due to the validation, so this timeout also applies to the metalink file.",
+ validation_message: "The metadata_expire property must be a numeric value for time in seconds, the string 'never', or a numeric value appended with with `d`, `h`, or `m`!"
+
+ property :metalink, String,
+ description: "Specifies a URL to a metalink file for the repomd.xml, a list of mirrors for the entire repository are generated by converting the mirrors for the repomd.xml file to a baseurl."
+
+ property :mirror_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/],
+ description: "Time (in seconds) after which the mirrorlist locally cached will expire. If the current mirrorlist is less than this many seconds old then Yum will not download another copy of the mirrorlist, it has the same extra format as metadata_expire. If you find that Yum is not downloading the mirrorlists as often as you would like lower the value of this option. You can also change from the default of using seconds to using days, hours or minutes by appending a `d`, `h` or `m` respectively.",
+ validation_message: "The mirror_expire property must be a numeric value for time in seconds, the string 'never', or a numeric value appended with with `d`, `h`, or `m`!"
+
+ property :mirrorlist_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/],
+ description: "Specifies the time (in seconds) after which the mirrorlist locally cached will expire. If the current mirrorlist is less than the value specified, then Yum will not download another copy of the mirrorlist. You can also change from the default of using seconds to using days, hours or minutes by appending a `d`, `h` or `m` respectively.",
+ validation_message: "The mirrorlist_expire property must be a numeric value for time in seconds, the string 'never', or a numeric value appended with with `d`, `h`, or `m`!"
+
+ property :mirrorlist, String,
+ description: "URL to a file containing a list of baseurls. This can be used instead of or with the baseurl option. Substitution variables, described below, can be used with this option."
+
+ property :mode, [String, Integer],
+ description: "Permissions mode of .repo file on disk. This is useful for scenarios where secrets are in the repo file. If this value is set to `600`, normal users will not be able to use Yum search, Yum info, etc.",
+ default: "0644"
+
+ property :options, Hash,
+ description: "Specifies the repository options."
+
+ property :password, String,
+ description: "Password to use with the username for basic authentication."
+
+ property :priority, String, regex: /^(\d?[1-9]|[0-9][0-9])$/,
+ description: "Assigns a priority to a repository where the priority value is between `1` and `99` inclusive. Priorities are used to enforce ordered protection of repositories. Packages from repositories with a lower priority (higher numerical value) will never be used to upgrade packages that were installed from a repository with a higher priority (lower numerical value). The repositories with the lowest numerical priority number have the highest priority.",
+ validation_message: "The priority property must be a numeric value from 1-99!"
+
+ property :proxy_password, String,
+ description: "Password for this proxy."
+
+ property :proxy_username, String,
+ description: "Username to use for proxy."
+
+ property :proxy, String,
+ description: "URL to the proxy server that Yum should use."
+
+ property :repo_gpgcheck, [TrueClass, FalseClass],
+ description: "Determines whether or not Yum should perform a GPG signature check on the repodata from this repository."
+
+ property :report_instanceid, [TrueClass, FalseClass],
+ description: "Determines whether to report the instance ID when using Amazon Linux AMIs and repositories."
+
+ property :repositoryid, String, regex: [%r{^[^/]+$}],
+ description: "An optional property to set the repository name if it differs from the resource block's name.",
+ validation_message: "repositoryid property cannot contain a forward slash '/'",
+ name_property: true
+
+ property :skip_if_unavailable, [TrueClass, FalseClass],
+ description: "Allow yum to continue if this repository cannot be contacted for any reason."
+
+ property :source, String,
+ description: "Use a custom template source instead of the default one."
+
+ property :sslcacert, String,
+ description: "Path to the directory containing the databases of the certificate authorities Yum should use to verify SSL certificates."
+
+ property :sslclientcert, String,
+ description: "Path to the SSL client certificate Yum should use to connect to repos/remote sites."
+
+ property :sslclientkey, String,
+ description: "Path to the SSL client key Yum should use to connect to repos/remote sites."
+
+ property :sslverify, [TrueClass, FalseClass],
+ description: "Determines whether Yum will verify SSL certificates/hosts."
+
+ property :throttle, [String, Integer],
+ description: "Enable bandwidth throttling for downloads."
+
+ property :timeout, String, regex: /^\d+$/,
+ description: "Number of seconds to wait for a connection before timing out. Defaults to 30 seconds. This may be too short of a time for extremely overloaded sites.",
+ validation_message: "The timeout property must be a numeric value!"
+
+ property :username, String,
+ description: "Username to use for basic authentication to a repository."
default_action :create
- allowed_actions :create, :remove, :make_cache, :add, :delete
+ allowed_actions :create, :remove, :makecache, :add, :delete
# provide compatibility with the yum cookbook < 3.0 properties
alias_method :url, :baseurl
alias_method :keyurl, :gpgkey
+ alias_method :mirrorexpire, :mirror_expire
end
end
end
diff --git a/lib/chef/resource/zypper_package.rb b/lib/chef/resource/zypper_package.rb
index f9e3eef49e..5901090abd 100644
--- a/lib/chef/resource/zypper_package.rb
+++ b/lib/chef/resource/zypper_package.rb
@@ -16,13 +16,58 @@
# limitations under the License.
#
-require "chef/resource/package"
+require_relative "package"
class Chef
class Resource
class ZypperPackage < Chef::Resource::Package
- resource_name :zypper_package
+ unified_mode true
+
+ provides :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."
+ examples <<~DOC
+ **Install a package using package manager:**
+
+ ``` ruby
+ zypper_package 'name of package' do
+ action :install
+ end
+ ```
+
+ **Install a package using local file:**
+
+ ``` ruby
+ zypper_package 'jwhois' do
+ action :install
+ source '/path/to/jwhois.rpm'
+ end
+ ```
+
+ **Install without using recommend packages as a dependency:**
+
+ ``` ruby
+ package 'apache2' do
+ options '--no-recommends'
+ end
+ ```
+ DOC
+
+ property :gpg_check, [ TrueClass, FalseClass ],
+ description: "Verify the package's GPG signature. Can also be controlled site-wide using the `zypper_check_gpg` config option.",
+ default: lazy { Chef::Config[:zypper_check_gpg] }, default_description: "true"
+
+ property :allow_downgrade, [ TrueClass, FalseClass ],
+ description: "Allow downgrading a package to satisfy requested version requirements.",
+ default: true,
+ desired_state: false,
+ introduced: "13.6"
+
+ property :global_options, [ String, Array ],
+ description: "One (or more) additional command options that are passed to the command. For example, common zypper directives, such as `--no-recommends`. See the [zypper man page](https://en.opensuse.org/SDB:Zypper_manual_(plain)) for the full list.",
+ coerce: proc { |x| x.is_a?(String) ? x.shellsplit : x },
+ introduced: "14.6"
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..05856cc9bc
--- /dev/null
+++ b/lib/chef/resource/zypper_repository.rb
@@ -0,0 +1,116 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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 "../resource"
+
+class Chef
+ class Resource
+ class ZypperRepository < Chef::Resource
+ unified_mode true
+
+ 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"
+ examples <<~DOC
+ **Add the Apache repo on openSUSE Leap 15**:
+
+ ``` ruby
+ zypper_repository 'apache' do
+ baseurl 'http://download.opensuse.org/repositories/Apache'
+ path '/openSUSE_Leap_15.0'
+ type 'rpm-md'
+ priority '100'
+ end
+ ```
+ DOC
+
+ property :repo_name, String,
+ regex: [%r{^[^/]+$}],
+ description: "An optional property to set the repository name if it differs from the resource block's name.",
+ validation_message: "repo_name property cannot contain a forward slash `/`",
+ name_property: true
+
+ property :description, String,
+ description: "The description of the repository that will be shown by the `zypper repos` command."
+
+ property :type, String,
+ description: "Specifies the repository type.",
+ default: "NONE"
+
+ property :enabled, [TrueClass, FalseClass],
+ description: "Determines whether or not the repository should be enabled.",
+ default: true
+
+ property :autorefresh, [TrueClass, FalseClass],
+ description: "Determines whether or not the repository should be refreshed automatically.",
+ default: true
+
+ property :gpgcheck, [TrueClass, FalseClass],
+ description: "Determines whether or not to perform a GPG signature check on the repository.",
+ default: true
+
+ property :gpgkey, String,
+ description: "The location of the repository key to be imported."
+
+ property :baseurl, String,
+ description: "The base URL for the Zypper repository, such as `http://download.opensuse.org`."
+
+ property :mirrorlist, String,
+ description: "The URL of the mirror list that will be used."
+
+ property :path, String,
+ description: "The relative path from the repository's base URL."
+
+ property :priority, Integer,
+ description: "Determines the priority of the Zypper repository.",
+ default: 99
+
+ property :keeppackages, [TrueClass, FalseClass],
+ description: "Determines whether or not packages should be saved.",
+ default: false
+
+ property :mode, [String, Integer],
+ description: "The file mode of the repository file.",
+ default: "0644"
+
+ property :refresh_cache, [TrueClass, FalseClass],
+ description: "Determines whether or not the package cache should be refreshed.",
+ default: true
+
+ property :source, String,
+ description: "The name of the template for the repository file. Only necessary if you're not using the built in template."
+
+ property :cookbook, String,
+ description: "The cookbook to source the repository template file from. Only necessary if you're not using the built in template.",
+ desired_state: false
+
+ property :gpgautoimportkeys, [TrueClass, FalseClass],
+ description: "Automatically import the specified key when setting up the repository.",
+ 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 57c57dd8c3..9f2cd657f9 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,23 +43,14 @@ 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?"
end
+
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,18 +70,17 @@ 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
+ # Force to compile_time execution if the flag is set
+ if resource.compile_time
+ Array(resource.action).each do |action|
+ resource.run_action(action)
+ end
+ resource.action :nothing
+ end
+
resource
end
@@ -103,54 +93,9 @@ class Chef
@resource_class ||= Chef::Resource.resource_for_node(type, run_context.node)
end
- def is_trivial_resource?(resource)
- trivial_resource = resource_class.new(name, run_context)
- # force un-lazy the name property on the created trivial resource
- name_property = resource_class.properties.find { |sym, p| p.name_property? }
- trivial_resource.send(name_property[0]) unless name_property.nil?
- identicalish_resources?(trivial_resource, 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
- message = "Cloning resource attributes for #{resource} from prior resource (CHEF-3694)"
- message << "\nPrevious #{prior_resource}: #{prior_resource.source_line}" if prior_resource.source_line
- message << "\nCurrent #{resource}: #{resource.source_line}" if resource.source_line
- Chef.log_deprecation(message)
- 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
-require "chef/exceptions"
-require "chef/resource"
-require "chef/log"
+require_relative "exceptions"
+require_relative "resource"
+require_relative "log"
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index 8eaa2961c4..588fe12f0a 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,11 @@
# limitations under the License.
#
-require "chef/resource_collection/resource_set"
-require "chef/resource_collection/resource_list"
-require "chef/resource_collection/resource_collection_serialization"
-require "chef/log"
-require "forwardable"
+require_relative "resource_collection/resource_set"
+require_relative "resource_collection/resource_list"
+require_relative "resource_collection/resource_collection_serialization"
+require_relative "log"
+require "forwardable" unless defined?(Forwardable)
##
# ResourceCollection currently handles two tasks:
@@ -32,6 +32,8 @@ class Chef
include ResourceCollectionSerialization
extend Forwardable
+ attr_accessor :unified_mode
+
attr_reader :resource_set, :resource_list
attr_accessor :run_context
@@ -41,11 +43,12 @@ class Chef
@run_context = run_context
@resource_set = ResourceSet.new
@resource_list = ResourceList.new
+ @unified_mode = false
end
# @param resource [Chef::Resource] The resource to insert
# @param resource_type [String,Symbol] If known, the resource type used in the recipe, Eg `package`, `execute`
- # @param instance_name [String] If known, the recource name as used in the recipe, IE `vim` in `package 'vim'`
+ # @param instance_name [String] If known, the resource name as used in the recipe, IE `vim` in `package 'vim'`
# This method is meant to be the 1 insert method necessary in the future. It should support all known use cases
# for writing into the ResourceCollection.
def insert(resource, opts = {})
@@ -57,11 +60,15 @@ class Chef
else
resource_set.insert_as(resource)
end
+ if unified_mode
+ run_context.runner.run_all_actions(resource)
+ end
end
def delete(key)
- resource_list.delete(key)
- resource_set.delete(key)
+ res = resource_set.delete(key)
+ resource_list.delete(res.to_s)
+ res
end
# @deprecated
@@ -86,9 +93,9 @@ class Chef
# Read-only methods are simple to delegate - doing that below
resource_list_methods = Enumerable.instance_methods +
- [:iterator, :all_resources, :[], :each, :execute_each_resource, :each_index, :empty?] -
+ %i{iterator all_resources [] each execute_each_resource each_index empty?} -
[:find] # find overridden below
- resource_set_methods = [:resources, :keys, :validate_lookup_spec!]
+ resource_set_methods = %i{resources keys validate_lookup_spec!}
def_delegators :resource_list, *resource_list_methods
def_delegators :resource_set, *resource_set_methods
@@ -117,12 +124,23 @@ 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)
rc.resource_collection.resource_set.lookup(key)
rescue Chef::Exceptions::ResourceNotFound
raise if rc.parent_run_context.nil?
+
lookup_recursive(rc.parent_run_context, key)
end
@@ -130,6 +148,7 @@ class Chef
rc.resource_collection.resource_set.find(*args)
rescue Chef::Exceptions::ResourceNotFound
raise if rc.parent_run_context.nil?
+
find_recursive(rc.parent_run_context, *args)
end
end
diff --git a/lib/chef/resource_collection/resource_collection_serialization.rb b/lib/chef/resource_collection/resource_collection_serialization.rb
index 0e76296a4a..524f2a6c54 100644
--- a/lib/chef/resource_collection/resource_collection_serialization.rb
+++ b/lib/chef/resource_collection/resource_collection_serialization.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
+require_relative "../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)
+ def to_h
+ instance_vars = {}
+ instance_variables.each do |iv|
+ instance_vars[iv] = instance_variable_get(iv)
end
{
"json_class" => self.class.name,
@@ -30,6 +33,8 @@ class Chef
}
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
Chef::JSONCompat.to_json(to_hash, *a)
end
@@ -39,19 +44,24 @@ 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)
- unless arg.kind_of?(Chef::Resource)
+ unless arg.is_a?(Chef::Resource)
raise ArgumentError, "Cannot insert a #{arg.class} into a resource collection: must be a subclass of Chef::Resource"
end
+
true
end
end
diff --git a/lib/chef/resource_collection/resource_list.rb b/lib/chef/resource_collection/resource_list.rb
index 9fe012d4c3..a290ecad62 100644
--- a/lib/chef/resource_collection/resource_list.rb
+++ b/lib/chef/resource_collection/resource_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/resource_collection/stepable_iterator"
-require "chef/resource_collection/resource_collection_serialization"
-require "forwardable"
+require_relative "../resource"
+require_relative "stepable_iterator"
+require_relative "resource_collection_serialization"
+require "forwardable" unless defined?(Forwardable)
# This class keeps the list of all known Resources in the order they are to be executed in. It also keeps a pointer
# to the most recently executed resource so we can add resources-to-execute after this point.
@@ -36,11 +36,11 @@ class Chef
private :resources
# Delegate direct access methods to the @resources array
# 4 extra methods here are not included in the Enumerable's instance methods
- direct_access_methods = Enumerable.instance_methods + [ :[], :each, :each_index, :empty? ]
+ direct_access_methods = Enumerable.instance_methods + %i{[] each each_index empty?}
def_delegators :resources, *(direct_access_methods)
def initialize
- @resources = Array.new
+ @resources = []
@insert_after_idx = nil
end
@@ -69,11 +69,13 @@ class Chef
def delete(key)
raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+
key = key.to_s
ret = @resources.reject! { |r| r.to_s == key }
if ret.nil?
raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
end
+
ret
end
@@ -95,6 +97,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..26521010bd 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/resource"
-require "chef/resource_collection/resource_collection_serialization"
+require_relative "../resource"
+require_relative "resource_collection_serialization"
class Chef
class ResourceCollection
@@ -26,14 +26,17 @@ class Chef
# Matches a multiple resource lookup specification,
# e.g., "service[nginx,unicorn]"
- MULTIPLE_RESOURCE_MATCH = /^(.+)\[(.+?),(.+)\]$/
+ MULTIPLE_RESOURCE_MATCH = /^(.+)\[(.+?),(.+)\]$/.freeze
# Matches a single resource lookup specification,
# e.g., "service[nginx]"
- SINGLE_RESOURCE_MATCH = /^(.+)\[(.+)\]$/
+ SINGLE_RESOURCE_MATCH = /^(.+)\[(.*)\]$/.freeze
+
+ # Matches e.g. "apt_update" with no name
+ NAMELESS_RESOURCE_MATCH = /^([^\[\]\s]+)$/.freeze
def initialize
- @resources_by_key = Hash.new
+ @resources_by_key = {}
end
def keys
@@ -50,22 +53,26 @@ class Chef
def lookup(key)
raise ArgumentError, "Must pass a Chef::Resource or String to lookup" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+
key = key.to_s
res = @resources_by_key[key]
unless res
raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
end
+
res
end
def delete(key)
raise ArgumentError, "Must pass a Chef::Resource or String to delete" unless key.is_a?(String) || key.is_a?(Chef::Resource)
+
key = key.to_s
res = @resources_by_key.delete(key)
if res == @resources_by_key.default
raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{key} (did you define it first?)"
end
+
res
end
@@ -82,7 +89,7 @@ class Chef
# Raises an ArgumentError if you feed it bad lookup information
# Raises a Runtime Error if it can't find the resources you are looking for.
def find(*args)
- results = Array.new
+ results = []
args.each do |arg|
case arg
when Hash
@@ -116,22 +123,26 @@ class Chef
# * Chef::Exceptions::InvalidResourceSpecification for all invalid input.
def validate_lookup_spec!(query_object)
case query_object
- when Chef::Resource
- true
- when SINGLE_RESOURCE_MATCH, MULTIPLE_RESOURCE_MATCH
- true
- when Hash
+ when Chef::Resource, SINGLE_RESOURCE_MATCH, MULTIPLE_RESOURCE_MATCH, NAMELESS_RESOURCE_MATCH, Hash
true
when String
raise Chef::Exceptions::InvalidResourceSpecification,
- "The string `#{query_object}' is not valid for resource collection lookup. Correct syntax is `resource_type[resource_name]'"
+ "The string `#{query_object}' is not valid for resource collection lookup. Correct syntax is `resource_type[resource_name]'"
else
raise Chef::Exceptions::InvalidResourceSpecification,
- "The object `#{query_object.inspect}' is not valid for resource collection lookup. " +
- "Use a String like `resource_type[resource_name]' or a Chef::Resource object"
+ "The object `#{query_object.inspect}' is not valid for resource collection lookup. " +
+ "Use a String like `resource_type[resource_name]' or a Chef::Resource object"
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)
@@ -139,34 +150,50 @@ class Chef
end
def find_resource_by_hash(arg)
- results = Array.new
+ results = []
arg.each do |resource_type, name_list|
- instance_names = name_list.kind_of?(Array) ? name_list : [ name_list ]
+ instance_names = name_list.is_a?(Array) ? name_list : [ name_list ]
instance_names.each do |instance_name|
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 = []
+ 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
+
+ if arg =~ NAMELESS_RESOURCE_MATCH
+ resource_type = $1
+ name = ""
+ return [ lookup(create_key(resource_type, name)) ]
end
- return results
+
+ 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 958ffa28cb..24a8f676d5 100644
--- a/lib/chef/resource_collection/stepable_iterator.rb
+++ b/lib/chef/resource_collection/stepable_iterator.rb
@@ -20,8 +20,7 @@ class Chef
class StepableIterator
def self.for_collection(new_collection)
- instance = new(new_collection)
- instance
+ new(new_collection)
end
attr_accessor :collection
@@ -82,6 +81,7 @@ class Chef
def step
return nil if @position == size
+
call_iterator_block
@position += 1
end
diff --git a/lib/chef/resource_definition.rb b/lib/chef/resource_definition.rb
index aa114af46c..e97ff52b6a 100644
--- a/lib/chef/resource_definition.rb
+++ b/lib/chef/resource_definition.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/mixin/from_file"
-require "chef/mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mixin/params_validate"
class Chef
class ResourceDefinition
@@ -29,20 +29,22 @@ class Chef
def initialize(node = nil)
@name = nil
- @params = Hash.new
+ @params = {}
@recipe = nil
@node = node
end
def define(resource_name, prototype_params = nil, &block)
- unless resource_name.kind_of?(Symbol)
+ unless resource_name.is_a?(Symbol)
raise ArgumentError, "You must use a symbol when defining a new resource!"
end
+
@name = resource_name
if prototype_params
- unless prototype_params.kind_of?(Hash)
+ unless prototype_params.is_a?(Hash)
raise ArgumentError, "You must pass a hash as the prototype parameters for a definition."
end
+
@params = prototype_params
end
if Kernel.block_given?
@@ -62,7 +64,7 @@ class Chef
end
def to_s
- "#{name}"
+ (name).to_s
end
end
end
diff --git a/lib/chef/resource_definition_list.rb b/lib/chef/resource_definition_list.rb
index 22751249e4..709d543668 100644
--- a/lib/chef/resource_definition_list.rb
+++ b/lib/chef/resource_definition_list.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/mixin/from_file"
-require "chef/resource_definition"
+require_relative "mixin/from_file"
+require_relative "resource_definition"
class Chef
class ResourceDefinitionList
@@ -26,7 +26,7 @@ class Chef
attr_accessor :defines
def initialize
- @defines = Hash.new
+ @defines = {}
end
def define(resource_name, prototype_params = nil, &block)
diff --git a/lib/chef/resource_inspector.rb b/lib/chef/resource_inspector.rb
new file mode 100644
index 0000000000..6d320f4202
--- /dev/null
+++ b/lib/chef/resource_inspector.rb
@@ -0,0 +1,118 @@
+# Copyright:: Copyright (c) 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 "cookbook_loader"
+require_relative "cookbook/file_vendor"
+require_relative "cookbook/file_system_file_vendor"
+require_relative "resource/lwrp_base"
+require_relative "run_context"
+require_relative "node"
+require_relative "resources"
+require_relative "json_compat"
+
+class Chef
+ module ResourceInspector
+ def self.get_default(default)
+ if default.is_a?(Chef::DelayedEvaluator)
+ # ideally we'd get the block we pass to `lazy`, but the best we can do
+ # is to get the source location, which then results in reparsing the source
+ # code for the resource ourselves and just no
+ "lazy default"
+ else
+ default.is_a?(Symbol) ? default.inspect : default # inspect properly returns symbols
+ end
+ end
+
+ def self.extract_resource(resource, complete = false)
+ data = {}
+ data[:description] = resource.description
+ # data[:deprecated] = resource.deprecated || false
+ data[:default_action] = resource.default_action
+ data[:actions] = resource.allowed_actions
+ data[:examples] = resource.examples
+ data[:introduced] = resource.introduced
+ data[:preview] = resource.preview_resource
+
+ properties = unless complete
+ resource.properties.reject { |_, k| k.options[:declared_in] == Chef::Resource || k.options[:skip_docs] }
+ else
+ resource.properties.reject { |_, k| k.options[:skip_docs] }
+ 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,
+ required: opts[:required] || false,
+ default: opts[:default_description] || get_default(opts[:default]),
+ name_property: opts[:name_property] || false,
+ equal_to: sort_equal_to(opts[:equal_to]) }
+ end
+ data
+ end
+
+ def self.sort_equal_to(equal_to)
+ Array(equal_to).sort.map(&:inspect)
+ rescue ArgumentError
+ Array(equal_to).map(&:inspect)
+ 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)
+ cookbook = loader.load_cookbook(name)
+ resources = cookbook.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 arguments [Array, String] One of more paths to a cookbook or a resource file to inspect
+ # @param complete [TrueClass, FalseClass] Whether to show properties defined in the base Resource class
+ # @return [String] JSON formatting of all resources
+ 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
+ Array(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)
+ acc[r.resource_name] = extract_resource(r, complete)
+ end
+ end
+ end
+
+ Chef::JSONCompat.to_json_pretty(output)
+ end
+
+ def self.start
+ puts inspect(ARGV, complete: true)
+ end
+
+ end
+end
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 8422870e2a..4051ac2f49 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -1,9 +1,9 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Prajakta Purohit (prajakta@chef.io>)
-# Auther:: Tyler Cloke (<tyler@opscode.com>)
+# Author:: Tyler Cloke (<tyler@opscode.com>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,95 +19,44 @@
# limitations under the License.
#
-require "uri"
-require "securerandom"
-require "chef/event_dispatch/base"
+require_relative "event_dispatch/base"
class Chef
class ResourceReporter < EventDispatch::Base
+ def for_json(action_record)
+ new_resource = action_record.new_resource
+ current_resource = action_record.current_resource
- 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
- report.new_resource = new_resource
- report.action = action
- report.current_resource = current_resource
- report
- end
-
- def self.new_for_exception(new_resource, action)
- report = new
- report.new_resource = new_resource
- report.action = action
- report
- end
-
- # Future: Some resources store state information that does not convert nicely
- # to json. We can't call a resource's state method here, since there are conflicts
- # with some LWRPs, so we can't override a resource's state method to return
- # json-friendly state data.
- #
- # The registry key resource returns json-friendly state data through its state
- # attribute, and uses a read-only variable for fetching true state data. If
- # we have conflicts with other resources reporting json incompatible state, we
- # may want to extend the state_attrs API with the ability to rename POST'd
- # attrs.
- def for_json
- as_hash = {}
- as_hash["type"] = new_resource.resource_name.to_sym
- as_hash["name"] = new_resource.name.to_s
- as_hash["id"] = new_resource.identity.to_s
- as_hash["after"] = new_resource.state_for_resource_reporter
- as_hash["before"] = current_resource ? current_resource.state_for_resource_reporter : {}
- as_hash["duration"] = (elapsed_time * 1000).to_i.to_s
- as_hash["delta"] = new_resource.diff if new_resource.respond_to?("diff")
- as_hash["delta"] = "" if as_hash["delta"].nil?
-
- # TODO: rename as "action"
- as_hash["result"] = action.to_s
- if success?
- else
- #as_hash["result"] = "failed"
- end
- if new_resource.cookbook_name
- as_hash["cookbook_name"] = new_resource.cookbook_name
- as_hash["cookbook_version"] = new_resource.cookbook_version.version
- end
+ as_hash = {}
+ as_hash["type"] = new_resource.resource_name.to_sym
+ as_hash["name"] = new_resource.name.to_s
+ as_hash["id"] = new_resource.identity.to_s
+ as_hash["after"] = new_resource.state_for_resource_reporter
+ as_hash["before"] = current_resource ? current_resource.state_for_resource_reporter : {}
+ as_hash["duration"] = ( action_record.elapsed_time * 1000 ).to_i.to_s
+ as_hash["delta"] = new_resource.diff if new_resource.respond_to?("diff")
+ as_hash["delta"] = "" if as_hash["delta"].nil?
- as_hash
+ # TODO: rename as "action"
+ as_hash["result"] = action_record.action.to_s
+ if new_resource.cookbook_name
+ as_hash["cookbook_name"] = new_resource.cookbook_name
+ as_hash["cookbook_version"] = new_resource.cookbook_version.version
end
- def finish
- self.elapsed_time = new_resource.elapsed_time
- end
-
- def success?
- !self.exception
- end
- end # End class ResouceReport
+ as_hash
+ end
- attr_reader :updated_resources
attr_reader :status
attr_reader :exception
- attr_reader :run_id
attr_reader :error_descriptions
+ attr_reader :action_collection
+ attr_reader :rest_client
- PROTOCOL_VERSION = "0.1.0"
+ PROTOCOL_VERSION = "0.1.0".freeze
def initialize(rest_client)
- if Chef::Config[:enable_reporting] && !Chef::Config[:why_run]
- @reporting_enabled = true
- else
- @reporting_enabled = false
- end
- @updated_resources = []
- @total_res_count = 0
- @pending_update = nil
+ @pending_update = nil
@status = "success"
@exception = nil
@rest_client = rest_client
@@ -121,8 +70,8 @@ class Chef
if reporting_enabled?
begin
resource_history_url = "reports/nodes/#{node_name}/runs"
- server_response = @rest_client.post(resource_history_url, { :action => :start, :run_id => run_id,
- :start_time => start_time.to_s }, headers)
+ server_response = rest_client.post(resource_history_url, { action: :start, run_id: run_id,
+ start_time: start_time.to_s }, headers)
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
handle_error_starting_run(e, resource_history_url)
end
@@ -154,55 +103,17 @@ 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
- @reporting_enabled = false
+ @runs_endpoint_failed = true
end
def run_id
@run_status.run_id
end
- def resource_current_state_loaded(new_resource, action, current_resource)
- unless nested_resource?(new_resource)
- @pending_update = ResourceReport.new_with_current_state(new_resource, action, current_resource)
- end
- end
-
- def resource_up_to_date(new_resource, action)
- @total_res_count += 1
- @pending_update = nil unless nested_resource?(new_resource)
- end
-
- def resource_skipped(resource, action, conditional)
- @total_res_count += 1
- @pending_update = nil unless nested_resource?(resource)
- end
-
- def resource_updated(new_resource, action)
- @total_res_count += 1
- end
-
- def resource_failed(new_resource, action, exception)
- @total_res_count += 1
- unless nested_resource?(new_resource)
- @pending_update ||= ResourceReport.new_for_exception(new_resource, action)
- @pending_update.exception = exception
- end
- description = Formatters::ErrorMapper.resource_failed(new_resource, action, exception)
- @error_descriptions = description.for_json
- end
-
- def resource_completed(new_resource)
- if @pending_update && !nested_resource?(new_resource)
- @pending_update.finish
- @updated_resources << @pending_update
- @pending_update = nil
- end
- end
-
def run_completed(node)
@status = "success"
post_reporting_data
@@ -222,17 +133,22 @@ class Chef
@expanded_run_list = run_list_expansion
end
+ def action_collection_registration(action_collection)
+ @action_collection = action_collection
+ action_collection.register(self) if reporting_enabled?
+ end
+
def post_reporting_data
if reporting_enabled?
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)
+ rest_client.raw_request(:POST, resource_history_url, headers({ "Content-Encoding" => "gzip" }), compressed_data)
rescue StandardError => e
if e.respond_to? :response
Chef::FileCache.store("failed-reporting-data.json", Chef::JSONCompat.to_json_pretty(run_data), 0640)
@@ -242,7 +158,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
@@ -263,15 +179,24 @@ class Chef
@run_status.end_time
end
+ # get only the top level resources and strip out the subcollections
+ def updated_resources
+ @updated_resources ||= action_collection&.filtered_collection(max_nesting: 0, up_to_date: false, skipped: false, unprocessed: false) || {}
+ end
+
+ def total_res_count
+ updated_resources.count
+ end
+
def prepare_run_data
run_data = {}
run_data["action"] = "end"
- run_data["resources"] = updated_resources.map do |resource_record|
- resource_record.for_json
+ run_data["resources"] = updated_resources.map do |action_record|
+ for_json(action_record)
end
run_data["status"] = @status
run_data["run_list"] = Chef::JSONCompat.to_json(@run_status.node.run_list)
- run_data["total_res_count"] = @total_res_count.to_s
+ run_data["total_res_count"] = total_res_count.to_s
run_data["data"] = {}
run_data["start_time"] = start_time.to_s
run_data["end_time"] = end_time.to_s
@@ -303,18 +228,10 @@ class Chef
@error_descriptions = description.for_json
end
- def reporting_enabled?
- @reporting_enabled
- end
-
private
- # 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)
- @pending_update && @pending_update.new_resource != new_resource
+ def reporting_enabled?
+ Chef::Config[:enable_reporting] && !Chef::Config[:why_run] && !@runs_endpoint_failed
end
def encode_gzip(data)
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 769272d637..df32b18164 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/platform/resource_priority_map"
-require "chef/mixin/convert_to_class_name"
+require_relative "exceptions"
+require_relative "platform/resource_priority_map"
+require_relative "mixin/convert_to_class_name"
class Chef
class ResourceResolver
@@ -29,8 +29,8 @@ class Chef
# @param node [Chef::Node] The node against which to resolve. `nil` causes
# platform filters to be ignored.
#
- def self.resolve(resource_name, node: nil, canonical: nil)
- new(node, resource_name, canonical: canonical).resolve
+ def self.resolve(resource_name, node: nil)
+ new(node, resource_name).resolve
end
#
@@ -40,11 +40,9 @@ class Chef
# @param resource_name [Symbol] The resource DSL name (e.g. `:file`).
# @param node [Chef::Node] The node against which to resolve. `nil` causes
# platform filters to be ignored.
- # @param canonical [Boolean] `true` or `false` to match canonical or
- # non-canonical values only. `nil` to ignore canonicality.
#
- def self.list(resource_name, node: nil, canonical: nil)
- new(node, resource_name, canonical: canonical).list
+ def self.list(resource_name, node: nil)
+ new(node, resource_name).list
end
include Chef::Mixin::ConvertToClassName
@@ -54,14 +52,7 @@ 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
#
# Create a resolver.
@@ -69,27 +60,24 @@ class Chef
# @param node [Chef::Node] The node against which to resolve. `nil` causes
# platform filters to be ignored.
# @param resource_name [Symbol] The resource DSL name (e.g. `:file`).
- # @param canonical [Boolean] `true` or `false` to match canonical or
- # non-canonical values only. `nil` to ignore canonicality. Default: `nil`
#
# @api private use Chef::ResourceResolver.resolve or .list instead.
- def initialize(node, resource_name, canonical: nil)
+ def initialize(node, resource_name)
@node = node
@resource_name = resource_name.to_sym
- @canonical = canonical
end
# @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 +85,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
@@ -140,7 +128,7 @@ class Chef
# @api private
def potential_handlers
- handler_map.list(node, resource_name, canonical: canonical).uniq
+ handler_map.list(node, resource_name).uniq
end
def enabled_handlers
@@ -151,7 +139,7 @@ class Chef
@prioritized_handlers ||= begin
enabled_handlers = self.enabled_handlers
- prioritized = priority_map.list(node, resource_name, canonical: canonical).flatten(1)
+ prioritized = priority_map.list(node, resource_name).flatten(1)
prioritized &= enabled_handlers # Filter the priority map by the actual enabled handlers
prioritized |= enabled_handlers # Bring back any handlers that aren't in the priority map, at the *end* (ordered set)
prioritized
@@ -161,25 +149,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 2afd47a8f4..843d5610b8 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,82 +16,158 @@
# limitations under the License.
#
-require "chef/resource/apt_package"
-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/cookbook_file"
-require "chef/resource/chef_gem"
-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/dpkg_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"
-require "chef/resource/ips_package"
-require "chef/resource/gem_package"
-require "chef/resource/git"
-require "chef/resource/group"
-require "chef/resource/http_request"
-require "chef/resource/homebrew_package"
-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/mdadm"
-require "chef/resource/mount"
-require "chef/resource/ohai"
-require "chef/resource/openbsd_package"
-require "chef/resource/package"
-require "chef/resource/pacman_package"
-require "chef/resource/paludis_package"
-require "chef/resource/perl"
-require "chef/resource/portage_package"
-require "chef/resource/powershell_script"
-require "chef/resource/osx_profile"
-require "chef/resource/python"
-require "chef/resource/reboot"
-require "chef/resource/registry_key"
-require "chef/resource/remote_directory"
-require "chef/resource/remote_file"
-require "chef/resource/rpm_package"
-require "chef/resource/solaris_package"
-require "chef/resource/route"
-require "chef/resource/ruby"
-require "chef/resource/ruby_block"
-require "chef/resource/scm"
-require "chef/resource/script"
-require "chef/resource/service"
-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_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_relative "resource/alternatives"
+require_relative "resource/apt_package"
+require_relative "resource/apt_preference"
+require_relative "resource/apt_repository"
+require_relative "resource/apt_update"
+require_relative "resource/archive_file"
+require_relative "resource/bash"
+require_relative "resource/batch"
+require_relative "resource/breakpoint"
+require_relative "resource/build_essential"
+require_relative "resource/cookbook_file"
+require_relative "resource/chef_client_config"
+require_relative "resource/chef_client_cron"
+require_relative "resource/chef_client_launchd"
+require_relative "resource/chef_client_scheduled_task"
+require_relative "resource/chef_client_systemd_timer"
+require_relative "resource/chef_client_trusted_certificate"
+require_relative "resource/chef_gem"
+require_relative "resource/chef_handler"
+require_relative "resource/chef_sleep"
+require_relative "resource/chef_vault_secret"
+require_relative "resource/chocolatey_config"
+require_relative "resource/chocolatey_feature"
+require_relative "resource/chocolatey_package"
+require_relative "resource/chocolatey_source"
+require_relative "resource/cron/cron"
+require_relative "resource/cron_access"
+require_relative "resource/cron/cron_d"
+require_relative "resource/csh"
+require_relative "resource/directory"
+require_relative "resource/dmg_package"
+require_relative "resource/dpkg_package"
+require_relative "resource/dnf_package"
+require_relative "resource/dsc_script"
+require_relative "resource/dsc_resource"
+require_relative "resource/execute"
+require_relative "resource/file"
+require_relative "resource/freebsd_package"
+require_relative "resource/ips_package"
+require_relative "resource/gem_package"
+require_relative "resource/scm/git"
+require_relative "resource/group"
+require_relative "resource/http_request"
+require_relative "resource/hostname"
+require_relative "resource/homebrew_cask"
+require_relative "resource/homebrew_package"
+require_relative "resource/homebrew_tap"
+require_relative "resource/homebrew_update"
+require_relative "resource/ifconfig"
+require_relative "resource/kernel_module"
+require_relative "resource/ksh"
+require_relative "resource/launchd"
+require_relative "resource/link"
+require_relative "resource/locale"
+require_relative "resource/log"
+require_relative "resource/macports_package"
+require_relative "resource/macos_userdefaults"
+require_relative "resource/mdadm"
+require_relative "resource/mount"
+require_relative "resource/notify_group"
+require_relative "resource/ohai"
+require_relative "resource/ohai_hint"
+require_relative "resource/openbsd_package"
+require_relative "resource/openssl_dhparam"
+require_relative "resource/openssl_ec_private_key"
+require_relative "resource/openssl_ec_public_key"
+require_relative "resource/openssl_rsa_private_key"
+require_relative "resource/openssl_rsa_public_key"
+require_relative "resource/openssl_x509_certificate"
+require_relative "resource/openssl_x509_crl"
+require_relative "resource/openssl_x509_request"
+require_relative "resource/package"
+require_relative "resource/pacman_package"
+require_relative "resource/paludis_package"
+require_relative "resource/perl"
+require_relative "resource/plist"
+require_relative "resource/portage_package"
+require_relative "resource/powershell_package_source"
+require_relative "resource/powershell_script"
+require_relative "resource/osx_profile"
+require_relative "resource/python"
+require_relative "resource/reboot"
+require_relative "resource/registry_key"
+require_relative "resource/remote_directory"
+require_relative "resource/remote_file"
+require_relative "resource/rhsm_errata_level"
+require_relative "resource/rhsm_errata"
+require_relative "resource/rhsm_register"
+require_relative "resource/rhsm_repo"
+require_relative "resource/rhsm_subscription"
+require_relative "resource/rpm_package"
+require_relative "resource/snap_package"
+require_relative "resource/solaris_package"
+require_relative "resource/route"
+require_relative "resource/ruby"
+require_relative "resource/ruby_block"
+require_relative "resource/script"
+require_relative "resource/service"
+require_relative "resource/sudo"
+require_relative "resource/sysctl"
+require_relative "resource/swap_file"
+require_relative "resource/systemd_unit"
+require_relative "resource/ssh_known_hosts_entry"
+require_relative "resource/windows_service"
+require_relative "resource/scm/subversion"
+require_relative "resource/smartos_package"
+require_relative "resource/template"
+require_relative "resource/user"
+require_relative "resource/user/aix_user"
+require_relative "resource/user/dscl_user"
+require_relative "resource/user/linux_user"
+require_relative "resource/user/mac_user"
+require_relative "resource/user/pw_user"
+require_relative "resource/user/solaris_user"
+require_relative "resource/user/windows_user"
+require_relative "resource/user_ulimit"
+require_relative "resource/whyrun_safe_ruby_block"
+require_relative "resource/windows_env"
+require_relative "resource/windows_package"
+require_relative "resource/yum_package"
+require_relative "resource/yum_repository"
+require_relative "resource/lwrp_base"
+require_relative "resource/bff_package"
+require_relative "resource/zypper_package"
+require_relative "resource/zypper_repository"
+require_relative "resource/cab_package"
+require_relative "resource/powershell_package"
+require_relative "resource/msu_package"
+require_relative "resource/windows_ad_join"
+require_relative "resource/windows_audit_policy"
+require_relative "resource/windows_auto_run"
+require_relative "resource/windows_certificate"
+require_relative "resource/windows_dfs_folder"
+require_relative "resource/windows_dfs_namespace"
+require_relative "resource/windows_dfs_server"
+require_relative "resource/windows_dns_record"
+require_relative "resource/windows_dns_zone"
+require_relative "resource/windows_feature"
+require_relative "resource/windows_feature_dism"
+require_relative "resource/windows_feature_powershell"
+require_relative "resource/windows_firewall_profile"
+require_relative "resource/windows_firewall_rule"
+require_relative "resource/windows_font"
+require_relative "resource/windows_pagefile"
+require_relative "resource/windows_path"
+require_relative "resource/windows_printer"
+require_relative "resource/windows_printer_port"
+require_relative "resource/windows_share"
+require_relative "resource/windows_shortcut"
+require_relative "resource/windows_task"
+require_relative "resource/windows_uac"
+require_relative "resource/windows_workgroup"
+require_relative "resource/timezone"
+require_relative "resource/windows_user_privilege"
+require_relative "resource/windows_security_policy" \ No newline at end of file
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..d43e331da4 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,14 +18,14 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/run_list"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/server_api"
-require "chef/search/query"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "run_list"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "server_api"
+require_relative "search/query"
class Chef
class Role
@@ -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 = ""
@@ -57,7 +55,7 @@ class Chef
set_or_return(
:name,
arg,
- :regex => /^[\-[:alnum:]_]+$/
+ regex: /^[\-[:alnum:]_]+$/
)
end
@@ -65,7 +63,7 @@ class Chef
set_or_return(
:description,
arg,
- :kind_of => String
+ kind_of: String
)
end
@@ -88,12 +86,12 @@ class Chef
end
def active_run_list_for(environment)
- @env_run_lists.has_key?(environment) ? environment : "_default"
+ @env_run_lists.key?(environment) ? environment : "_default"
end
# Per environment run lists
def env_run_lists(env_run_lists = nil)
- if !env_run_lists.nil?
+ unless env_run_lists.nil?
unless env_run_lists.key?("_default")
msg = "_default key is required in env_run_lists.\n"
msg << "(env_run_lists: #{env_run_lists.inspect})"
@@ -108,7 +106,7 @@ class Chef
alias :env_run_list :env_run_lists
def env_run_lists_add(env_run_lists = nil)
- if !env_run_lists.nil?
+ unless env_run_lists.nil?
env_run_lists.each { |k, v| @env_run_lists[k] = Chef::RunList.new(*Array(v)) }
end
@env_run_lists
@@ -120,7 +118,7 @@ class Chef
set_or_return(
:default_attributes,
arg,
- :kind_of => Hash
+ kind_of: Hash
)
end
@@ -128,14 +126,14 @@ class Chef
set_or_return(
:override_attributes,
arg,
- :kind_of => Hash
+ kind_of: Hash
)
end
- def to_hash
+ def to_h
env_run_lists_without_default = @env_run_lists.dup
env_run_lists_without_default.delete("_default")
- result = {
+ {
"name" => @name,
"description" => @description,
"json_class" => self.class.name,
@@ -143,20 +141,21 @@ class Chef
"override_attributes" => @override_attributes,
"chef_type" => "role",
- #Render to_json correctly for run_list items (both run_list and evn_run_lists)
- #so malformed json does not result
- "run_list" => run_list.run_list.map { |item| item.to_s },
+ # Render to_json correctly for run_list items (both run_list and evn_run_lists)
+ # so malformed json does not result
+ "run_list" => run_list.run_list.map(&:to_s),
"env_run_lists" => env_run_lists_without_default.inject({}) do |accumulator, (k, v)|
- accumulator[k] = v.map { |x| x.to_s }
+ accumulator[k] = v.map(&:to_s)
accumulator
end,
}
- result
end
+ alias_method :to_hash, :to_h
+
# Serialize this object as a hash
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def update_from!(o)
@@ -168,12 +167,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"])
@@ -183,7 +176,7 @@ class Chef
# _default run_list is in 'run_list' for newer clients, and
# 'recipes' for older clients.
- env_run_list_hash = { "_default" => (o.has_key?("run_list") ? o["run_list"] : o["recipes"]) }
+ env_run_list_hash = { "_default" => (o.key?("run_list") ? o["run_list"] : o["recipes"]) }
# Clients before 0.10 do not include env_run_lists, so only
# merge if it's there.
@@ -198,7 +191,7 @@ class Chef
# Get the list of all roles from the API.
def self.list(inflate = false)
if inflate
- response = Hash.new
+ response = {}
Chef::Search::Query.new.search(:role) do |n|
response[n.name] = n unless n.nil?
end
@@ -230,8 +223,9 @@ class Chef
def save
begin
chef_server_rest.put("roles/#{@name}", self)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
raise e unless e.response.code == "404"
+
chef_server_rest.post("roles", self)
end
self
@@ -254,18 +248,19 @@ class Chef
paths = Array(Chef::Config[:role_path])
paths.each do |path|
roles_files = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(path), "**", "**"))
- js_files = roles_files.select { |file| file.match(/\/#{name}\.json$/) }
- rb_files = roles_files.select { |file| file.match(/\/#{name}\.rb$/) }
+ js_files = roles_files.select { |file| file.match(%r{/#{name}\.json$}) }
+ rb_files = roles_files.select { |file| file.match(%r{/#{name}\.rb$}) }
if js_files.count > 1 || rb_files.count > 1
raise Chef::Exceptions::DuplicateRole, "Multiple roles of same type found named #{name}"
end
+
js_path, rb_path = js_files.first, rb_files.first
- if js_path && File.exists?(js_path)
+ if js_path && File.exist?(js_path)
# from_json returns object.class => json_class in the JSON.
hsh = Chef::JSONCompat.parse(IO.read(js_path))
return from_hash(hsh)
- elsif rb_path && File.exists?(rb_path)
+ elsif rb_path && File.exist?(rb_path)
role = Chef::Role.new
role.name(name)
role.from_file(rb_path)
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 5d29f766c9..75c18f2fcf 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,25 +17,43 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/resource_collection"
-require "chef/cookbook_version"
-require "chef/node"
-require "chef/role"
-require "chef/log"
-require "chef/recipe"
-require "chef/run_context/cookbook_compiler"
-require "chef/event_dispatch/events_output_stream"
-require "forwardable"
+require_relative "resource_collection"
+require_relative "cookbook_version"
+require_relative "node"
+require_relative "role"
+require_relative "log"
+require_relative "recipe"
+require_relative "run_context/cookbook_compiler"
+require_relative "event_dispatch/events_output_stream"
+require_relative "train_transport"
+require_relative "exceptions"
+require "forwardable" unless defined?(Forwardable)
+autoload :Set, "set"
class Chef
- # == Chef::RunContext
# Value object that loads and tracks the context of a Chef run
class RunContext
+ extend Forwardable
+
#
# Global state
#
+ # Common rest object for using to talk to the Chef Server, this strictly 'validates' utf8
+ # and will throw. (will be nil on solo-legacy runs)
+ #
+ # @return [Chef::ServerAPI]
+ #
+ attr_accessor :rest
+
+ # Common rest object for using to talk to the Chef Server, this has utf8 sanitization turned
+ # on and will replace invalid utf8 with valid characters. (will be nil on solo-legacy runs)
+ #
+ # @return [Chef::ServerAPI]
+ #
+ attr_accessor :rest_clean
+
#
# The node for this run
#
@@ -58,14 +76,12 @@ class Chef
#
attr_reader :definitions
- #
# Event dispatcher for this run.
#
# @return [Chef::EventDispatch::Dispatcher]
#
- attr_reader :events
+ attr_accessor :events
- #
# Hash of factoids for a reboot request.
#
# @return [Hash]
@@ -76,7 +92,6 @@ class Chef
# Scoped state
#
- #
# The parent run context.
#
# @return [Chef::RunContext] The parent run context, or `nil` if this is the
@@ -84,7 +99,16 @@ 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).
#
@@ -94,16 +118,20 @@ class Chef
#
attr_reader :resource_collection
+ # Handle to the global action_collection of executed actions for reporting / data_collector /etc
#
- # The list of control groups to execute during the audit phase
+ # @return [Chef::ActionCollection
+ #
+ attr_accessor :action_collection
+
+ # Pointer back to the Chef::Runner that created this
#
- attr_reader :audits
+ attr_accessor :runner
#
# Notification handling
#
- #
# A Hash containing the before notifications triggered by resources
# during the converge phase of the chef run.
#
@@ -112,7 +140,6 @@ class Chef
#
attr_reader :before_notification_collection
- #
# A Hash containing the immediate notifications triggered by resources
# during the converge phase of the chef run.
#
@@ -121,7 +148,6 @@ class Chef
#
attr_reader :immediate_notification_collection
- #
# A Hash containing the delayed (end of run) notifications triggered by
# resources during the converge phase of the chef run.
#
@@ -130,7 +156,6 @@ class Chef
#
attr_reader :delayed_notification_collection
- #
# An Array containing the delayed (end of run) notifications triggered by
# resources during the converge phase of the chef run.
#
@@ -138,6 +163,22 @@ class Chef
#
attr_reader :delayed_actions
+ # A Set keyed by the string name, of all the resources that are updated. We do not
+ # track actions or individual resource objects, since this matches the behavior of
+ # the notification collections which are keyed by Strings.
+ #
+ attr_reader :updated_resources
+
+ # @return [Boolean] If the resource_collection is in unified_mode (no separate converge phase)
+ #
+ def_delegator :resource_collection, :unified_mode
+
+ # 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,24 +188,30 @@ class Chef
# @param events [EventDispatch::Dispatcher] The event dispatcher for this
# run.
#
- def initialize(node, cookbook_collection, events)
- @node = node
- @cookbook_collection = cookbook_collection
+ def initialize(node = nil, cookbook_collection = nil, events = nil, logger = nil)
@events = events
-
- node.run_context = self
- node.set_cookbook_attribute
-
- @definitions = Hash.new
+ @logger = logger || Chef::Log.with_child
+ self.node = node if node
+ self.cookbook_collection = cookbook_collection if cookbook_collection
+ @definitions = {}
@loaded_recipes_hash = {}
@loaded_attributes_hash = {}
@reboot_info = {}
@cookbook_compiler = nil
- @delayed_actions = []
initialize_child_state
end
+ def node=(node)
+ @node = node
+ node.run_context = self
+ end
+
+ def cookbook_collection=(cookbook_collection)
+ @cookbook_collection = cookbook_collection
+ node.set_cookbook_attribute
+ end
+
#
# Triggers the compile phase of the chef run.
#
@@ -180,30 +227,34 @@ class Chef
# Initialize state that applies to both Chef::RunContext and Chef::ChildRunContext
#
def initialize_child_state
- @audits = {}
@resource_collection = Chef::ResourceCollection.new(self)
@before_notification_collection = Hash.new { |h, k| h[k] = [] }
@immediate_notification_collection = Hash.new { |h, k| h[k] = [] }
@delayed_notification_collection = Hash.new { |h, k| h[k] = [] }
@delayed_actions = []
+ @updated_resources = Set.new
end
#
# 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)
# Note for the future, notification.notifying_resource may be an instance
# of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
# with a string value.
+ if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key)
+ raise Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource.new(notification)
+ end
+
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)
# Note for the future, notification.notifying_resource may be an instance
@@ -215,53 +266,68 @@ class Chef
#
# 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)
# Note for the future, notification.notifying_resource may be an instance
# of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
# with a string value.
+ if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key)
+ add_delayed_action(notification)
+ end
delayed_notification_collection[notification.notifying_resource.declared_key] << notification
end
- #
- # Adds a delayed action to the +delayed_actions+.
+ # Adds a delayed action to the delayed_actions collection
#
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
end
end
- #
# Get the list of before notifications sent by the given resource.
#
# @return [Array[Notification]]
#
def before_notifications(resource)
- return before_notification_collection[resource.declared_key]
+ key = resource.is_a?(String) ? resource : resource.declared_key
+ before_notification_collection[key]
end
- #
# Get the list of immediate notifications sent by the given resource.
#
# @return [Array[Notification]]
#
def immediate_notifications(resource)
- return immediate_notification_collection[resource.declared_key]
+ key = resource.is_a?(String) ? resource : resource.declared_key
+ immediate_notification_collection[key]
end
+ # Get the list of immediate notifications pending to the given resource
#
+ # @return [Array[Notification]]
+ #
+ def reverse_immediate_notifications(resource)
+ immediate_notification_collection.map do |k, v|
+ v.select do |n|
+ (n.resource.is_a?(String) && n.resource == resource.declared_key) ||
+ n.resource == resource
+ end
+ end.flatten
+ end
+
# Get the list of delayed (end of run) notifications sent by the given
# resource.
#
# @return [Array[Notification]]
#
def delayed_notifications(resource)
- return delayed_notification_collection[resource.declared_key]
+ key = resource.is_a?(String) ? resource : resource.declared_key
+ delayed_notification_collection[key]
end
#
@@ -278,7 +344,7 @@ class Chef
# @see DSL::IncludeRecipe#include_recipe
#
def include_recipe(*recipe_names, current_cookbook: nil)
- result_recipes = Array.new
+ result_recipes = []
recipe_names.flatten.each do |recipe_name|
if result = load_recipe(recipe_name, current_cookbook: current_cookbook)
result_recipes << result
@@ -294,31 +360,31 @@ 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)
-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,
-first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're
-including it from in that cookbook's metadata.
-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,
+ first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're
+ including it from in that cookbook's metadata.
+ 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)
@@ -338,11 +404,11 @@ ERROR_MESSAGE
# @raise [Chef::Exceptions::RecipeNotFound] If the file does not exist.
#
def load_recipe_file(recipe_file)
- if !File.exist?(recipe_file)
+ unless File.exist?(recipe_file)
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
@@ -410,7 +476,7 @@ ERROR_MESSAGE
# @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
#
def loaded_fully_qualified_recipe?(cookbook, recipe)
- loaded_recipes_hash.has_key?("#{cookbook}::#{recipe}")
+ loaded_recipes_hash.key?("#{cookbook}::#{recipe}")
end
#
@@ -445,7 +511,7 @@ ERROR_MESSAGE
# @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
#
def loaded_fully_qualified_attribute?(cookbook, attribute_file)
- loaded_attributes_hash.has_key?("#{cookbook}::#{attribute_file}")
+ loaded_attributes_hash.key?("#{cookbook}::#{attribute_file}")
end
#
@@ -536,19 +602,42 @@ 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
+ # Remote transport from Train
+ #
+ # @return [Train::Plugins::Transport] The child class for our train transport.
+ #
+ def transport
+ @transport ||= Chef::TrainTransport.new(logger).build_transport
+ end
+
+ # Remote connection object from Train
+ #
+ # @return [Train::Plugins::Transport::BaseConnection]
+ #
+ def transport_connection
+ @transport_connection ||= transport&.connection
+ end
+
#
# Create a child RunContext.
#
@@ -565,27 +654,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.
#
@@ -594,12 +662,16 @@ ERROR_MESSAGE
class ChildRunContext < RunContext
extend Forwardable
def_delegators :parent_run_context, *%w{
+ action_collection
+ action_collection=
cancel_reboot
config
cookbook_collection
+ cookbook_collection=
cookbook_compiler
definitions
events
+ events=
has_cookbook_file_in_cookbook?
has_template_in_cookbook?
load
@@ -612,13 +684,21 @@ ERROR_MESSAGE
loaded_recipe?
loaded_recipes
loaded_recipes_hash
+ logger
node
+ node=
open_stream
reboot_info
reboot_info=
reboot_requested?
request_reboot
resolve_attribute
+ rest
+ rest=
+ rest_clean
+ rest_clean=
+ transport
+ transport_connection
unreachable_cookbook?
}
@@ -632,10 +712,10 @@ ERROR_MESSAGE
end
CHILD_STATE = %w{
- audits
- audits=
- create_child
add_delayed_action
+ before_notification_collection
+ before_notifications
+ create_child
delayed_actions
delayed_notification_collection
delayed_notification_collection=
@@ -643,24 +723,28 @@ ERROR_MESSAGE
immediate_notification_collection
immediate_notification_collection=
immediate_notifications
- before_notification_collection
- before_notifications
include_recipe
initialize_child_state
load_recipe
load_recipe_file
notifies_before
- notifies_immediately
notifies_delayed
+ notifies_immediately
parent_run_context
resource_collection
resource_collection=
- }.map { |x| x.to_sym }
+ reverse_immediate_notifications
+ root_run_context
+ runner
+ runner=
+ unified_mode
+ updated_resources
+ }.map(&:to_sym)
# Verify that we didn't miss any methods
unless @__skip_method_checking # hook specifically for compat_resource
missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
- if !missing_methods.empty?
+ unless 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
diff --git a/lib/chef/run_context/cookbook_compiler.rb b/lib/chef/run_context/cookbook_compiler.rb
index b2a8d236a3..27461fea9a 100644
--- a/lib/chef/run_context/cookbook_compiler.rb
+++ b/lib/chef/run_context/cookbook_compiler.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,12 @@
# limitations under the License.
#
-require "set"
-require "chef/log"
-require "chef/recipe"
-require "chef/resource/lwrp_base"
-require "chef/provider/lwrp_base"
-require "chef/resource_definition_list"
+autoload :Set, "set"
+require_relative "../log"
+require_relative "../recipe"
+require_relative "../resource/lwrp_base"
+require_relative "../provider/lwrp_base"
+require_relative "../resource_definition_list"
class Chef
class RunContext
@@ -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
@@ -96,16 +100,45 @@ class Chef
def compile_libraries
@events.library_load_start(count_files_by_segment(:libraries))
cookbook_order.each do |cookbook|
- load_libraries_from_cookbook(cookbook)
+ eager_load_libraries = cookbook_collection[cookbook].metadata.eager_load_libraries
+ if eager_load_libraries == true # actually true, not truthy
+ load_libraries_from_cookbook(cookbook)
+ else
+ $LOAD_PATH.unshift File.expand_path("libraries", cookbook_collection[cookbook].root_dir)
+ if eager_load_libraries # we have a String or Array<String> and not false
+ load_libraries_from_cookbook(cookbook, eager_load_libraries)
+ end
+ end
end
@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
@@ -136,17 +169,17 @@ class Chef
def compile_recipes
@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
- @events.recipe_file_load_failed(path, e, recipe)
- raise
- end
+
+ 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
+ @events.recipe_file_load_failed(path, e, recipe)
+ raise
+
end
@events.recipe_load_complete
end
@@ -166,18 +199,29 @@ 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] == "root_files/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
list_of_attr_files.each do |filename|
+ next unless File.extname(filename) == ".rb"
+
load_attribute_file(cookbook_name.to_s, filename)
end
end
def load_attribute_file(cookbook_name, filename)
- Chef::Log.debug("Node #{node.name} loading cookbook #{cookbook_name}'s attribute file #{filename}")
+ logger.trace("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
@@ -185,31 +229,36 @@ class Chef
raise
end
- def load_libraries_from_cookbook(cookbook_name)
- 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}")
- Kernel.load(filename)
- @events.library_file_loaded(filename)
- rescue Exception => e
- @events.library_file_load_failed(filename, e)
- raise
- end
+ def load_libraries_from_cookbook(cookbook_name, globs = "**/*.rb")
+ each_file_in_cookbook_by_segment(cookbook_name, :libraries, globs) do |filename|
+
+ logger.trace("Loading cookbook #{cookbook_name}'s library file: #{filename}")
+ Kernel.require(filename)
+ @events.library_file_loaded(filename)
+ rescue Exception => e
+ @events.library_file_load_failed(filename, e)
+ raise
+
end
end
def load_lwrps_from_cookbook(cookbook_name)
files_in_cookbook_by_segment(cookbook_name, :providers).each do |filename|
+ next unless File.extname(filename) == ".rb"
+ next if File.basename(filename).match?(/^_/)
+
load_lwrp_provider(cookbook_name, filename)
end
files_in_cookbook_by_segment(cookbook_name, :resources).each do |filename|
+ next unless File.extname(filename) == ".rb"
+ next if File.basename(filename).match?(/^_/)
+
load_lwrp_resource(cookbook_name, filename)
end
end
def load_lwrp_provider(cookbook_name, filename)
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}")
+ logger.trace("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
@@ -218,7 +267,7 @@ class Chef
end
def load_lwrp_resource(cookbook_name, filename)
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
+ logger.trace("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
@@ -226,14 +275,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"
+
+ logger.trace "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|
+ next unless File.extname(filename) == ".rb"
+
begin
- Chef::Log.debug("Loading cookbook #{cookbook_name}'s definitions from #{filename}")
+ logger.trace("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)
@@ -259,23 +323,39 @@ 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).count { |record| record[:name] == root_alias } : 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
+
+ # Iterates through all files in given cookbook segment, yielding the full path to the file
+ # if it matches one of the given globs. Returns matching files in lexical sort order. Supports
+ # extended globbing. The segment should not be included in the glob.
+ #
+ def each_file_in_cookbook_by_segment(cookbook, segment, globs)
+ cookbook_collection[cookbook].files_for(segment).sort_by { |record| record[:path] }.each do |record|
+ Array(globs).each do |glob|
+ target = record[:path].delete_prefix("#{segment}/")
+ if File.fnmatch(glob, target, File::FNM_PATHNAME | File::FNM_EXTGLOB | File::FNM_DOTMATCH)
+ yield record[:full_path]
+ break
+ end
+ end
+ end
end
# Yields the name, as a symbol, of each cookbook depended on by
# +cookbook_name+ in lexical sort order.
def each_cookbook_dep(cookbook_name, &block)
cookbook = cookbook_collection[cookbook_name]
- cookbook.metadata.dependencies.keys.sort.map { |x| x.to_sym }.each(&block)
+ cookbook.metadata.dependencies.keys.sort.map(&:to_sym).each(&block)
end
# Given a +recipe_name+, finds the file associated with the recipe.
diff --git a/lib/chef/run_list.rb b/lib/chef/run_list.rb
index 3ac5fab07b..2779dd69aa 100644
--- a/lib/chef/run_list.rb
+++ b/lib/chef/run_list.rb
@@ -4,7 +4,7 @@
# Author:: Tim Hinderliter (<tim@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,10 +19,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/run_list/run_list_item"
-require "chef/run_list/run_list_expansion"
-require "chef/run_list/versioned_recipe_list"
-require "chef/mixin/params_validate"
+require_relative "run_list/run_list_item"
+require_relative "run_list/run_list_expansion"
+require_relative "run_list/versioned_recipe_list"
+require_relative "mixin/params_validate"
class Chef
class RunList
@@ -70,10 +70,11 @@ class Chef
alias :add :<<
def ==(other)
- if other.kind_of?(Chef::RunList)
+ if other.is_a?(Chef::RunList)
other.run_list_items == @run_list_items
else
return false unless other.respond_to?(:size) && (other.size == @run_list_items.size)
+
other_run_list_items = other.dup
other_run_list_items.map! { |item| coerce_to_run_list_item(item) }
@@ -86,7 +87,7 @@ class Chef
end
def for_json
- to_a.map { |item| item.to_s }
+ to_a.map(&:to_s)
end
def to_json(*a)
@@ -122,7 +123,7 @@ class Chef
def reset!(*args)
@run_list_items.clear
args.flatten.each do |item|
- if item.kind_of?(Chef::RunList)
+ if item.is_a?(Chef::RunList)
item.each { |r| self << r }
else
self << item
@@ -152,7 +153,7 @@ class Chef
end
def coerce_to_run_list_item(item)
- item.kind_of?(RunListItem) ? item : parse_entry(item)
+ item.is_a?(RunListItem) ? item : parse_entry(item)
end
def expansion_for_data_source(environment, data_source, opts = {})
diff --git a/lib/chef/run_list/run_list_expansion.rb b/lib/chef/run_list/run_list_expansion.rb
index b895b53523..413d0a3db8 100644
--- a/lib/chef/run_list/run_list_expansion.rb
+++ b/lib/chef/run_list/run_list_expansion.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/mash"
+require_relative "../mash"
-require "chef/mixin/deep_merge"
+require_relative "../mixin/deep_merge"
-require "chef/role"
-require "chef/server_api"
-require "chef/json_compat"
+require_relative "../role"
+require_relative "../server_api"
+require_relative "../json_compat"
class Chef
class RunList
@@ -64,7 +64,7 @@ class Chef
def initialize(environment, run_list_items, source = nil)
@environment = environment
- @missing_roles_with_including_role = Array.new
+ @missing_roles_with_including_role = []
@run_list_items = run_list_items.dup
@source = source
@@ -102,6 +102,7 @@ class Chef
# nil if the role does not exist
def inflate_role(role_name, included_by)
return false if applied_role?(role_name) # Prevent infinite loops
+
applied_role(role_name)
fetch_role(role_name, included_by)
end
@@ -112,7 +113,7 @@ class Chef
end
def applied_role?(role_name)
- @applied_roles.has_key?(role_name)
+ @applied_roles.key?(role_name)
end
# Returns an array of role names that were expanded; this
@@ -140,18 +141,20 @@ class Chef
end
def errors
- @missing_roles_with_including_role.map { |item| item.first }
+ @missing_roles_with_including_role.map(&:first)
end
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
- def to_hash
- seen_items = { :recipe => {}, :role => {} }
- { :id => @environment, :run_list => convert_run_list_trace("top level", seen_items) }
+ def to_h
+ seen_items = { recipe: {}, role: {} }
+ { id: @environment, run_list: convert_run_list_trace("top level", seen_items) }
end
+ alias_method :to_hash, :to_h
+
private
# these methods modifies internal state based on arguments, so hide it.
@@ -185,12 +188,12 @@ class Chef
seen_items[item.type][item.name] = true
case item.type
when :recipe
- { :type => "recipe", :name => item.name, :version => item.version, :skipped => !!skipped }
+ { type: "recipe", name: item.name, version: item.version, skipped: !!skipped }
when :role
error = @role_errors[item.name]
missing = @all_missing_roles[item.name]
- { :type => :role, :name => item.name, :children => (missing || error || skipped) ? [] : convert_run_list_trace(item.to_s, seen_items),
- :missing => missing, :error => error, :skipped => skipped }
+ { type: :role, name: item.name, children: (missing || error || skipped) ? [] : convert_run_list_trace(item.to_s, seen_items),
+ missing: missing, error: error, skipped: skipped }
end
end
end
@@ -217,7 +220,7 @@ class Chef
def fetch_role(name, included_by)
Chef::Role.from_hash(rest.get("roles/#{name}"))
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.message == '404 "Not Found"'
role_not_found(name, included_by)
else
diff --git a/lib/chef/run_list/run_list_item.rb b/lib/chef/run_list/run_list_item.rb
index 0abfb106ff..f169afc90d 100644
--- a/lib/chef/run_list/run_list_item.rb
+++ b/lib/chef/run_list/run_list_item.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,10 +18,10 @@
class Chef
class RunList
class RunListItem
- QUALIFIED_RECIPE = %r{^recipe\[([^\]@]+)(@([0-9]+(\.[0-9]+){1,2}))?\]$}
- QUALIFIED_ROLE = %r{^role\[([^\]]+)\]$}
- VERSIONED_UNQUALIFIED_RECIPE = %r{^([^@]+)(@([0-9]+(\.[0-9]+){1,2}))$}
- FALSE_FRIEND = %r{[\[\]]}
+ QUALIFIED_RECIPE = /^recipe\[([^\]@]+)(@([0-9]+(\.[0-9]+){1,2}))?\]$/.freeze
+ QUALIFIED_ROLE = /^role\[([^\]]+)\]$/.freeze
+ VERSIONED_UNQUALIFIED_RECIPE = /^([^@]+)(@([0-9]+(\.[0-9]+){1,2}))$/.freeze
+ FALSE_FRIEND = /[\[\]]/.freeze
attr_reader :name, :type, :version
@@ -32,7 +32,7 @@ class Chef
assert_hash_is_valid_run_list_item!(item)
@type = (item["type"] || item[:type]).to_sym
@name = item["name"] || item[:name]
- if item.has_key?("version") || item.has_key?(:version)
+ if item.key?("version") || item.key?(:version)
@version = item["version"] || item[:version]
end
when String
@@ -80,8 +80,8 @@ class Chef
end
def ==(other)
- if other.kind_of?(String)
- self.to_s == other.to_s
+ if other.is_a?(String)
+ 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..f9a0a3b332 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +15,8 @@
# WITHOUT WARRANTIES 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"
-require "chef/version_constraint"
+require_relative "../version_class"
+require_relative "../version_constraint"
# Why does this class exist?
# Why did we not just modify RunList/RunListItem?
@@ -26,36 +26,36 @@ class Chef
def initialize
super
- @versions = Hash.new
+ @versions = {}
end
def add_recipe(name, version = nil)
- if version && @versions.has_key?(name)
+ if version && @versions.key?(name)
unless Chef::Version.new(@versions[name]) == Chef::Version.new(version)
raise Chef::Exceptions::CookbookVersionConflict, "Run list requires #{name} at versions #{@versions[name]} and #{version}"
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 }
+ { name: recipe_name, version_constraint: constraint }
end
end
# 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/run_lock.rb b/lib/chef/run_lock.rb
index 08d58fd164..1f83b7ea5a 100644
--- a/lib/chef/run_lock.rb
+++ b/lib/chef/run_lock.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/mixin/create_path"
+require_relative "mixin/create_path"
require "fcntl"
-if Chef::Platform.windows?
- require "chef/win32/mutex"
+if ChefUtils.windows?
+ require_relative "win32/mutex"
end
-require "chef/config"
-require "chef/exceptions"
-require "timeout"
+require_relative "config"
+require_relative "exceptions"
+require "timeout" unless defined?(Timeout)
+require "chef-utils" unless defined?(ChefUtils::CANARY)
class Chef
@@ -95,8 +96,8 @@ class Chef
# Waits until acquiring the system-wide lock.
#
def wait
- Chef::Log.warn("Chef client #{runpid} is running, will wait for it to finish and then run.")
- if Chef::Platform.windows?
+ Chef::Log.warn("#{ChefUtils::Dist::Infra::PRODUCT} #{runpid} is running, will wait for it to finish and then run.")
+ if ChefUtils.windows?
mutex.wait
else
runlock.flock(File::LOCK_EX)
@@ -115,7 +116,7 @@ class Chef
# Release the system-wide lock.
def release
if runlock
- if Chef::Platform.windows?
+ if ChefUtils.windows?
mutex.release
else
runlock.flock(File::LOCK_UN)
@@ -137,7 +138,7 @@ class Chef
# @api private solely for race condition tests
def acquire_lock
- if Chef::Platform.windows?
+ if ChefUtils.windows?
acquire_win32_mutex
else
# If we support FD_CLOEXEC, then use it.
@@ -172,7 +173,7 @@ class Chef
# Mutex name is case-sensitive contrary to other things in
# windows. "\" is the only invalid character.
def acquire_win32_mutex
- @mutex = Chef::ReservedNames::Win32::Mutex.new("Global\\#{runlock_file.gsub(/[\\]/, "/").downcase}")
+ @mutex = Chef::ReservedNames::Win32::Mutex.new("Global\\#{runlock_file.tr('\\', "/").downcase}")
mutex.test
end
diff --git a/lib/chef/run_status.rb b/lib/chef/run_status.rb
index c3b7945bc9..6e7c83ff48 100644
--- a/lib/chef/run_status.rb
+++ b/lib/chef/run_status.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,17 +25,13 @@ class Chef::RunStatus
attr_reader :events
- attr_reader :run_context
-
- attr_writer :run_context
+ attr_accessor :run_context
attr_reader :start_time
attr_reader :end_time
- attr_reader :exception
-
- attr_writer :exception
+ attr_accessor :exception
attr_accessor :run_id
@@ -53,6 +49,7 @@ class Chef::RunStatus
# sets +end_time+ to the current time
def stop_clock
+ @start_time ||= Time.now # if we failed so early we didn't get a start time
@end_time = Time.now
end
@@ -74,7 +71,7 @@ class Chef::RunStatus
# The list of all resources in the current run context's +resource_collection+
# that are marked as updated
def updated_resources
- @run_context && @run_context.resource_collection.select { |r| r.updated }
+ @run_context && @run_context.resource_collection.select(&:updated)
end
# The backtrace from +exception+, if any
@@ -102,20 +99,22 @@ class Chef::RunStatus
# * :updated_resources
# * :exception
# * :backtrace
- def to_hash
+ def to_h
# use a flat hash here so we can't errors from intermediate values being nil
- { :node => node,
- :success => success?,
- :start_time => start_time,
- :end_time => end_time,
- :elapsed_time => elapsed_time,
- :all_resources => all_resources,
- :updated_resources => updated_resources,
- :exception => formatted_exception,
- :backtrace => backtrace,
- :run_id => run_id }
+ { node: node,
+ success: success?,
+ start_time: start_time,
+ end_time: end_time,
+ elapsed_time: elapsed_time,
+ all_resources: all_resources,
+ updated_resources: updated_resources,
+ exception: formatted_exception,
+ backtrace: backtrace,
+ run_id: run_id }
end
+ alias_method :to_hash, :to_h
+
# Returns a string of the format "ExceptionClass: message" or +nil+ if no
# +exception+ is set.
def formatted_exception
diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb
index cd5615bcec..4405843a9b 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,10 +18,10 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/mixin/params_validate"
-require "chef/node"
-require "chef/resource_collection"
+require_relative "exceptions"
+require_relative "mixin/params_validate"
+require_relative "node"
+require_relative "resource_collection"
class Chef
# == Chef::Runner
@@ -34,6 +34,7 @@ class Chef
def initialize(run_context)
@run_context = run_context
+ @run_context.runner = self
end
def delayed_actions
@@ -44,6 +45,10 @@ class Chef
@run_context.events
end
+ def updated_resources
+ @run_context.updated_resources
+ end
+
# Determine the appropriate provider for the given resource, then
# execute it.
def run_action(resource, action, notification_type = nil, notifying_resource = nil)
@@ -65,36 +70,71 @@ class Chef
end
end
- # Actually run the action for realsies
+ # Actually run the action for releases
resource.run_action(action, notification_type, notifying_resource)
# Execute any immediate and queue up any delayed notifications
# associated with the resource, but only if it was updated *this time*
# we ran an action on it.
if resource.updated_by_last_action?
+ updated_resources.add(resource.declared_key) # track updated resources for unified_mode
run_context.immediate_notifications(resource).each do |notification|
- Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)")
- run_action(notification.resource, notification.action, :immediate, resource)
+ if notification.resource.is_a?(String) && run_context.unified_mode
+ Chef::Log.debug("immediate notification from #{resource} to #{notification.resource} is delayed until declaration due to unified_mode")
+ else
+ Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)")
+ run_action(notification.resource, notification.action, :immediate, resource)
+ end
end
run_context.delayed_notifications(resource).each do |notification|
- # send the notification to the run_context of the receiving resource
- notification.resource.run_context.add_delayed_action(notification)
+ if notification.resource.is_a?(String)
+ # for string resources that have not be declared yet in unified mode we only support notifying the current run_context
+ run_context.add_delayed_action(notification)
+ else
+ # send the notification to the run_context of the receiving resource
+ notification.resource.run_context.add_delayed_action(notification)
+ end
+ end
+ end
+ end
+
+ # Runs all of the actions on a given resource. This fires notifications and marks
+ # the resource as having been executed by the runner.
+ #
+ # @param resource [Chef::Resource] the resource to run
+ #
+ def run_all_actions(resource)
+ Array(resource.action).each { |action| run_action(resource, action) }
+ if run_context.unified_mode
+ run_context.reverse_immediate_notifications(resource).each do |n|
+ if updated_resources.include?(n.notifying_resource.declared_key)
+ n.resolve_resource_reference(run_context.resource_collection)
+ Chef::Log.info("#{resource} sent #{n.action} action to #{n.resource} (immediate at declaration time)")
+ run_action(n.resource, n.action, :immediate, n.notifying_resource)
+ end
end
end
+ ensure
+ resource.executed_by_runner = true
end
- # Iterates over the +resource_collection+ in the +run_context+ calling
- # +run_action+ for each resource in turn.
+ # Iterates over the resource_collection in the run_context calling
+ # run_action for each resource in turn.
+ #
def converge
# Resolve all lazy/forward references in notifications
- run_context.resource_collection.each do |resource|
- resource.resolve_notification_references
- end
+ run_context.resource_collection.each(&:resolve_notification_references)
# Execute each resource.
run_context.resource_collection.execute_each_resource do |resource|
- Array(resource.action).each { |action| run_action(resource, action) }
+ unless run_context.resource_collection.unified_mode
+ run_all_actions(resource)
+ end
+ end
+
+ if run_context.resource_collection.unified_mode
+ run_context.resource_collection.each { |r| r.resolve_notification_references(true) }
end
rescue Exception => e
@@ -113,7 +153,7 @@ class Chef
collected_failures.client_run_failure(error) unless error.nil?
delayed_actions.each do |notification|
result = run_delayed_notification(notification)
- if result.kind_of?(Exception)
+ if result.is_a?(Exception)
collected_failures.notification_failure(result)
end
end
@@ -123,7 +163,8 @@ class Chef
def run_delayed_notification(notification)
Chef::Log.info( "#{notification.notifying_resource} sending #{notification.action}"\
" action to #{notification.resource} (delayed)")
- # Struct of resource/action to call
+ # notifications may have lazy strings in them to resolve
+ notification.resolve_resource_reference(run_context.resource_collection)
run_action(notification.resource, notification.action, :delayed)
true
rescue Exception => e
diff --git a/lib/chef/scan_access_control.rb b/lib/chef/scan_access_control.rb
index f55a106e6d..d81166c28a 100644
--- a/lib/chef/scan_access_control.rb
+++ b/lib/chef/scan_access_control.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,7 +33,7 @@ class Chef
# Not yet sure if this is the optimal way to solve the problem. But it's
# progress towards the end goal.
#
- # TODO: figure out if all this works with OS X's negative uids
+ # TODO: figure out if all this works with macOS' negative uids
# TODO: windows
class ScanAccessControl
@@ -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 bea8205935..c278ea9a68 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,19 @@
# limitations under the License.
#
-require "chef/config"
-require "chef/exceptions"
-require "chef/server_api"
+require_relative "../config"
+require_relative "../exceptions"
+require_relative "../server_api"
-require "uri"
-require "addressable/uri"
+autoload :URI, "uri"
+module Addressable
+ autoload :URI, "addressable/uri"
+end
class Chef
class Search
class Query
- attr_accessor :rest
attr_reader :config
def initialize(url = nil, config: Chef::Config)
@@ -39,28 +40,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
@@ -85,6 +64,19 @@ WARNDEP
validate_type(type)
args_h = hashify_args(*args)
+ if args_h[:fuzz]
+ if type.to_sym == :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
@@ -101,7 +93,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)
@@ -114,8 +106,16 @@ WARNDEP
private
+ def fuzzify_node_query(query)
+ if !/:/.match?(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)
+ unless t.is_a?(String) || t.is_a?(Symbol)
msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." +
"Usage: search(:node, QUERY[, OPTIONAL_ARGS])" +
" `knife search environment QUERY (options)`"
@@ -124,33 +124,30 @@ WARNDEP
end
def hashify_args(*args)
- return Hash.new if args.empty?
+ return {} if args.empty?
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]
+ args_h = {}
+ args_h[:start] = args[0] if args[0]
+ args_h[:rows] = args[1]
+ args_h[:filter_result] = args[2]
args_h
end
- QUERY_PARAM_VALUE = Addressable::URI::CharacterClasses::QUERY + "\\&\\;"
-
def escape_value(s)
- s && Addressable::URI.encode_component(s.to_s, QUERY_PARAM_VALUE)
+ query_param_value = Addressable::URI::CharacterClasses::QUERY + "\\&\\;"
+ s && Addressable::URI.encode_component(s.to_s, query_param_value)
end
- def create_query_string(type, query, rows, start, sort)
+ def create_query_string(type, query, rows, start)
qstr = "search/#{type}?q=#{escape_value(query)}"
- qstr += "&sort=#{escape_value(sort)}" if sort
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..7f59c33b93 100644
--- a/lib/chef/server_api.rb
+++ b/lib/chef/server_api.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,21 +16,25 @@
# limitations under the License.
#
-require "chef/http"
-require "chef/http/authenticator"
-require "chef/http/cookie_manager"
-require "chef/http/decompressor"
-require "chef/http/json_input"
-require "chef/http/json_output"
-require "chef/http/remote_request_id"
-require "chef/http/validate_content_length"
+require_relative "http"
+require_relative "http/authenticator"
+require_relative "http/cookie_manager"
+require_relative "http/decompressor"
+require_relative "http/json_input"
+require_relative "http/json_output"
+require_relative "http/remote_request_id"
+require_relative "http/validate_content_length"
+require_relative "http/api_versions"
class Chef
class ServerAPI < Chef::HTTP
def initialize(url = Chef::Config[:chef_server_url], options = {})
+ # # If making a change here, also update Chef::Knife::Raw::RawInputServerAPI.
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[:ssh_agent_signing] ||= Chef::Config[:ssh_agent_signing]
options[:signing_key_filename] = nil if chef_zero_uri?(url)
options[:inflate_json_class] = false
super(url, options)
@@ -42,6 +46,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
@@ -66,13 +71,9 @@ class Chef
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
end
end
-require "chef/config"
+require_relative "config"
diff --git a/lib/chef/server_api_versions.rb b/lib/chef/server_api_versions.rb
new file mode 100644
index 0000000000..30e2802ef0
--- /dev/null
+++ b/lib/chef/server_api_versions.rb
@@ -0,0 +1,63 @@
+#--
+# Copyright:: Copyright (c) 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" unless defined?(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 negotiated?
+ !@versions.nil? || unversioned?
+ end
+
+ def reset!
+ @versions = nil
+ @unversioned = false
+ end
+ end
+end
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index 26683cc25d..a425129fa8 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -15,21 +15,28 @@
# limitations under the License.
#
-require "singleton"
-require "pp"
-require "etc"
-require "mixlib/cli"
-
-require "chef"
-require "chef/version"
-require "chef/client"
-require "chef/config"
-require "chef/config_fetcher"
-
-require "chef/shell/shell_session"
-require "chef/shell/ext"
-require "chef/json_compat"
-require "chef/util/path_helper"
+module Mixlib
+ module Authentication
+ autoload :Log, "mixlib/authentication"
+ end
+end
+require "singleton" unless defined?(Singleton)
+require "pp" unless defined?(PP)
+require "etc" unless defined?(Etc)
+require "mixlib/cli" unless defined?(Mixlib::CLI)
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+
+require_relative "../chef"
+require_relative "version"
+require_relative "client"
+require_relative "config"
+require_relative "config_fetcher"
+
+require_relative "shell/shell_session"
+require_relative "workstation_config_loader"
+require_relative "shell/ext"
+require_relative "json_compat"
+require_relative "util/path_helper"
# = Shell
# Shell is Chef in an IRB session. Shell can interact with a Chef server via the
@@ -40,7 +47,6 @@ module Shell
LEADERS[Chef::Node] = ":attributes"
class << self
- attr_accessor :client_type
attr_accessor :options
attr_accessor :env
attr_writer :editor
@@ -60,8 +66,19 @@ module Shell
# to get access to the main object before irb starts.
::IRB.setup(nil)
+ irb_conf[:USE_COLORIZE] = options.config[:use_colorize]
+ irb_conf[:USE_SINGLELINE] = options.config[:use_singleline]
+ irb_conf[:USE_MULTILINE] = options.config[:use_multiline]
+ pp irb_conf[:USE_MULTILINE]
+
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 +91,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
@@ -91,7 +115,7 @@ module Shell
end
# Set the irb_conf object to something other than IRB.conf
- # usful for testing.
+ # useful for testing.
def self.irb_conf=(conf_hash)
@irb_conf = conf_hash
end
@@ -107,12 +131,14 @@ module Shell
irb_conf[:IRB_RC] = lambda do |conf|
m = conf.main
- conf.prompt_c = "chef#{leader(m)} > "
+ conf.prompt_c = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} > "
conf.return_format = " => %s \n"
- conf.prompt_i = "chef#{leader(m)} (#{Chef::VERSION})> "
- conf.prompt_n = "chef#{leader(m)} ?> "
- conf.prompt_s = "chef#{leader(m)}%l> "
+ conf.prompt_i = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} (#{Chef::VERSION})> "
+ conf.prompt_n = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)} ?> "
+ conf.prompt_s = "#{ChefUtils::Dist::Infra::EXEC}#{leader(m)}%l> "
conf.use_tracer = false
+ conf.instance_variable_set(:@use_multiline, false)
+ conf.instance_variable_set(:@use_singleline, false)
end
end
@@ -124,6 +150,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
@@ -144,11 +171,10 @@ module Shell
puts "run `help' for help, `exit' or ^D to quit."
puts
- puts "Ohai2u#{greeting}!"
end
def self.greeting
- " #{Etc.getlogin}@#{Shell.session.node["fqdn"]}"
+ "#{Etc.getlogin}@#{Shell.session.node["fqdn"]}"
rescue NameError, ArgumentError
""
end
@@ -167,8 +193,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
@@ -190,85 +217,109 @@ module Shell
@footer
end
- banner("chef-shell #{Chef::VERSION}\n\nUsage: chef-shell [NAMED_CONF] (OPTIONS)")
-
- footer(<<-FOOTER)
-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.
-FOOTER
+ banner("#{ChefUtils::Dist::Infra::SHELL} #{Chef::VERSION}\n\nUsage: #{ChefUtils::Dist::Infra::SHELL} [NAMED_CONF] (OPTIONS)")
+
+ footer(<<~FOOTER)
+ When no CONFIG is specified, #{ChefUtils::Dist::Infra::SHELL} attempts to load a default configuration file:
+ * If a NAMED_CONF is given, #{ChefUtils::Dist::Infra::SHELL} will load ~/#{ChefUtils::Dist::Infra::USER_CONF_DIR}/NAMED_CONF/#{ChefUtils::Dist::Infra::SHELL_CONF}
+ * If no NAMED_CONF is given #{ChefUtils::Dist::Infra::SHELL} will load ~/#{ChefUtils::Dist::Infra::USER_CONF_DIR}/#{ChefUtils::Dist::Infra::SHELL_CONF} if it exists
+ * If no #{ChefUtils::Dist::Infra::SHELL_CONF} can be found, #{ChefUtils::Dist::Infra::SHELL} falls back to load:
+ #{ChefConfig::Config.etc_chef_dir}/client.rb if -z option is given.
+ #{ChefConfig::Config.etc_chef_dir}/solo.rb if --solo-legacy-mode option is given.
+ #{ChefUtils::Dist::Infra::USER_CONF_DIR}/config.rb if -s option is given.
+ #{ChefUtils::Dist::Infra::USER_CONF_DIR}/knife.rb if -s option is given.
+ FOOTER
+
+ option :use_multiline,
+ long: "--[no-]multiline",
+ default: true,
+ description: "[Do not] use multiline editor module"
+
+ option :use_singleline,
+ long: "--[no-]singleline",
+ default: true,
+ description: "[Do not] use singleline editor module"
+
+ option :use_colorize,
+ long: "--[no-]colorize",
+ default: true,
+ description: "[Do not] use colorization"
option :config_file,
- :short => "-c CONFIG",
- :long => "--config CONFIG",
- :description => "The configuration file to use"
+ short: "-c CONFIG",
+ long: "--config CONFIG",
+ description: "The configuration file to use"
option :help,
- :short => "-h",
- :long => "--help",
- :description => "Show this message",
- :on => :tail,
- :boolean => true,
- :proc => proc { print_help }
+ short: "-h",
+ long: "--help",
+ description: "Show this message",
+ on: :tail,
+ boolean: true,
+ proc: proc { print_help }
option :log_level,
- :short => "-l LOG_LEVEL",
- :long => "--log-level LOG_LEVEL",
- :description => "Set the logging level",
- :proc => proc { |level| Chef::Config.log_level = level.to_sym; Shell.setup_logger }
+ short: "-l LOG_LEVEL",
+ long: "--log-level LOG_LEVEL",
+ description: "Set the logging level",
+ proc: proc { |level| Chef::Config.log_level = level.to_sym; Shell.setup_logger }
option :standalone,
- :short => "-a",
- :long => "--standalone",
- :description => "standalone session",
- :default => true,
- :boolean => true
-
- option :shell_solo,
- :short => "-s",
- :long => "--solo",
- :description => "chef-solo session",
- :boolean => true,
- :proc => proc { Chef::Config[:solo] = true }
+ short: "-a",
+ long: "--standalone",
+ description: "Standalone session",
+ default: true,
+ boolean: true
+
+ option :solo_shell,
+ short: "-s",
+ long: "--solo",
+ description: "#{ChefUtils::Dist::Solo::PRODUCT} session",
+ boolean: true,
+ proc: proc { Chef::Config[:solo] = true }
option :client,
- :short => "-z",
- :long => "--client",
- :description => "chef-client session",
- :boolean => true
+ short: "-z",
+ long: "--client",
+ description: "#{ChefUtils::Dist::Infra::PRODUCT} session",
+ boolean: true
+
+ option :solo_legacy_shell,
+ long: "--solo-legacy-mode",
+ description: "#{ChefUtils::Dist::Solo::PRODUCT} legacy session",
+ boolean: true,
+ proc: proc { Chef::Config[:solo_legacy_mode] = true }
option :json_attribs,
- :short => "-j JSON_ATTRIBS",
- :long => "--json-attributes JSON_ATTRIBS",
- :description => "Load attributes from a JSON file or URL",
- :proc => nil
+ short: "-j JSON_ATTRIBS",
+ long: "--json-attributes JSON_ATTRIBS",
+ description: "Load attributes from a JSON file or URL",
+ proc: nil
option :chef_server_url,
- :short => "-S CHEFSERVERURL",
- :long => "--server CHEFSERVERURL",
- :description => "The chef server URL",
- :proc => nil
+ short: "-S CHEFSERVERURL",
+ long: "--server CHEFSERVERURL",
+ description: "The #{ChefUtils::Dist::Server::PRODUCT} URL",
+ proc: nil
option :version,
- :short => "-v",
- :long => "--version",
- :description => "Show chef version",
- :boolean => true,
- :proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
- :exit => 0
+ short: "-v",
+ long: "--version",
+ description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version",
+ boolean: true,
+ proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
+ exit: 0
option :override_runlist,
- :short => "-o RunlistItem,RunlistItem...",
- :long => "--override-runlist RunlistItem,RunlistItem...",
- :description => "Replace current run list with specified items",
- :proc => lambda { |items| items.split(",").map { |item| Chef::RunList::RunListItem.new(item) } }
+ short: "-o RunlistItem,RunlistItem...",
+ long: "--override-runlist RunlistItem,RunlistItem...",
+ description: "Replace current run list with specified items",
+ proc: lambda { |items| items.split(",").map { |item| Chef::RunList::RunListItem.new(item) } }
option :skip_cookbook_sync,
- :long => "--[no-]skip-cookbook-sync",
- :description => "Use cached cookbooks without overwriting local differences from the server",
- :boolean => false
+ long: "--[no-]skip-cookbook-sync",
+ description: "Use cached cookbooks without overwriting local differences from the server",
+ boolean: false
def self.print_help
instance = new
@@ -281,7 +332,7 @@ FOOTER
end
def self.setup!
- self.new.parse_opts
+ new.parse_opts
end
def parse_opts
@@ -293,7 +344,7 @@ FOOTER
config[:config_file] = config_file_for_shell_mode(environment)
config_msg = config[:config_file] || "none (standalone session)"
puts "loading configuration: #{config_msg}"
- Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
+ Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exist?(config[:config_file]) && File.readable?(config[:config_file])
Chef::Config.merge!(config)
end
@@ -305,18 +356,20 @@ FOOTER
config[:config_file]
elsif environment
Shell.env = environment
- config_file_to_try = ::File.join(dot_chef_dir, environment, "chef_shell.rb")
+ config_file_to_try = ::File.join(dot_chef_dir, environment, ChefUtils::Dist::Infra::SHELL_CONF)
unless ::File.exist?(config_file_to_try)
- puts "could not find chef-shell config for environment #{environment} at #{config_file_to_try}"
+ puts "could not find #{ChefUtils::Dist::Infra::SHELL} config for environment #{environment} at #{config_file_to_try}"
exit 1
end
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]
- Chef::Config.platform_specific_path("/etc/chef/solo.rb")
+ elsif dot_chef_dir && ::File.exist?(File.join(dot_chef_dir, ChefUtils::Dist::Infra::SHELL_CONF))
+ File.join(dot_chef_dir, ChefUtils::Dist::Infra::SHELL_CONF)
+ elsif config[:solo_legacy_shell]
+ Chef::Config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/solo.rb")
elsif config[:client]
- Chef::Config.platform_specific_path("/etc/chef/client.rb")
+ Chef::Config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/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 0c10309521..b884658e98 100644
--- a/lib/chef/shell/ext.rb
+++ b/lib/chef/shell/ext.rb
@@ -16,15 +16,16 @@
# limitations under the License.
#
-require "tempfile"
-require "chef/recipe"
-require "fileutils"
-require "chef/dsl/platform_introspection"
-require "chef/version"
-require "chef/shell/shell_session"
-require "chef/shell/model_wrapper"
-require "chef/server_api"
-require "chef/json_compat"
+require "tempfile" unless defined?(Tempfile)
+require_relative "../recipe"
+require "fileutils" unless defined?(FileUtils)
+require_relative "../dsl/platform_introspection"
+require_relative "../version"
+require_relative "shell_session"
+require_relative "model_wrapper"
+require_relative "../server_api"
+require_relative "../json_compat"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
module Shell
module Extensions
@@ -36,7 +37,7 @@ module Shell
module ObjectCoreExtensions
def ensure_session_select_defined
- # irb breaks if you prematurely define IRB::JobMangager
+ # irb breaks if you prematurely define IRB::JobManager
# 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) # rubocop:disable Lint/NestedMethodDefinition
@@ -46,8 +47,8 @@ module Shell
unless jobs.respond_to?(:session_select)
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) }
+ session = if target_context.is_a?(Class)
+ select_session_by_context { |main| main.is_a?(target_context) }
else
select_session_by_context { |main| main.equal?(target_context) }
end
@@ -61,19 +62,19 @@ module Shell
if subsession = jobs.select_shell_session(context_obj)
jobs.switch(subsession)
else
- irb(context_obj)
+ irb(context_obj) # rubocop: disable Lint/Debugger
end
end
def help_banner
banner = []
banner << ""
- banner << "chef-shell Help"
+ banner << "#{ChefUtils::Dist::Infra::SHELL} Help"
banner << "".ljust(80, "=")
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 +85,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 +160,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
@@ -190,12 +191,12 @@ module Shell
extend Shell::Extensions::ObjectCoreExtensions
desc "prints this help message"
- explain(<<-E)
-## SUMMARY ##
- When called with no argument, +help+ prints a table of all
- chef-shell commands. When called with an argument COMMAND, +help+
- prints a detailed explanation of the command if available, or the
- description if no explanation is available.
+ explain(<<~E)
+ ## SUMMARY ##
+ When called with no argument, +help+ prints a table of all
+ #{ChefUtils::Dist::Infra::SHELL} commands. When called with an argument COMMAND, +help+
+ prints a detailed explanation of the command if available, or the
+ description if no explanation is available.
E
def help(commmand = nil)
if commmand
@@ -203,16 +204,14 @@ module Shell
else
puts help_banner
end
- :ucanhaz_halp
+ :help
end
alias :halp :help
- desc "prints information about chef"
+ desc "prints information about #{ChefUtils::Dist::Infra::PRODUCT}"
def version
- puts "This is the chef-shell.\n" +
- " Chef Version: #{::Chef::VERSION}\n" +
- " http://www.chef.io/\n" +
- " http://docs.chef.io/"
+ puts "Welcome to the #{ChefUtils::Dist::Infra::SHELL} #{::Chef::VERSION}\n" +
+ "For usage see https://docs.chef.io/chef_shell/"
:ucanhaz_automation
end
alias :shell :version
@@ -229,7 +228,7 @@ module Shell
:attributes
end
- desc "run chef using the current recipe"
+ desc "run #{ChefUtils::Dist::Infra::PRODUCT} using the current recipe"
def run_chef
Chef::Log.level = :debug
session = Shell.session
@@ -238,11 +237,11 @@ module Shell
runrun
end
- desc "returns an object to control a paused chef run"
- subcommands :resume => "resume the chef run",
- :step => "run only the next resource",
- :skip_back => "move back in the run list",
- :skip_forward => "move forward in the run list"
+ desc "returns an object to control a paused #{ChefUtils::Dist::Infra::PRODUCT} run"
+ subcommands resume: "resume the #{ChefUtils::Dist::Infra::PRODUCT} run",
+ step: "run only the next resource",
+ skip_back: "move back in the run list",
+ skip_forward: "move forward in the run list"
def chef_run
Shell.session.resource_collection.iterator
end
@@ -302,18 +301,18 @@ module Shell
RESTApiExtensions = Proc.new do
desc "edit an object in your EDITOR"
- explain(<<-E)
-## SUMMARY ##
- +edit(object)+ allows you to edit any object that can be converted to JSON.
- When finished editing, this method will return the edited object:
-
- new_node = edit(existing_node)
-
-## EDITOR SELECTION ##
- chef-shell looks for an editor using the following logic
- 1. Looks for an EDITOR set by Shell.editor = "EDITOR"
- 2. Looks for an EDITOR configured in your chef-shell config file
- 3. Uses the value of the EDITOR environment variable
+ explain(<<~E)
+ ## SUMMARY ##
+ +edit(object)+ allows you to edit any object that can be converted to JSON.
+ When finished editing, this method will return the edited object:
+
+ new_node = edit(existing_node)
+
+ ## EDITOR SELECTION ##
+ #{ChefUtils::Dist::Infra::SHELL} looks for an editor using the following logic
+ 1. Looks for an EDITOR set by Shell.editor = "EDITOR"
+ 2. Looks for an EDITOR configured in your #{ChefUtils::Dist::Infra::SHELL} config file
+ 3. Uses the value of the EDITOR environment variable
E
def edit(object)
unless Shell.editor
@@ -321,7 +320,7 @@ module Shell
return :failburger
end
- filename = "chef-shell-edit-#{object.class.name}-"
+ filename = "#{ChefUtils::Dist::Infra::SHELL}-edit-#{object.class.name}-"
if object.respond_to?(:name)
filename += object.name
elsif object.respond_to?(:id)
@@ -340,196 +339,196 @@ module Shell
end
desc "Find and edit API clients"
- explain(<<-E)
-## SUMMARY ##
- +clients+ allows you to query you chef server for information about your api
- clients.
+ explain(<<~E)
+ ## SUMMARY ##
+ +clients+ allows you to query you chef server for information about your api
+ clients.
-## LIST ALL CLIENTS ##
- To see all clients on the system, use
+ ## LIST ALL CLIENTS ##
+ To see all clients on the system, use
- clients.all #=> [<Chef::ApiClient...>, ...]
+ clients.all #=> [<Chef::ApiClient...>, ...]
- If the output from all is too verbose, or you're only interested in a specific
- value from each of the objects, you can give a code block to +all+:
+ If the output from all is too verbose, or you're only interested in a specific
+ value from each of the objects, you can give a code block to +all+:
- clients.all { |client| client.name } #=> [CLIENT1_NAME, CLIENT2_NAME, ...]
+ clients.all { |client| client.name } #=> [CLIENT1_NAME, CLIENT2_NAME, ...]
-## SHOW ONE CLIENT ##
- To see a specific client, use
+ ## SHOW ONE CLIENT ##
+ To see a specific client, use
- clients.show(CLIENT_NAME)
+ clients.show(CLIENT_NAME)
-## SEARCH FOR CLIENTS ##
- You can also search for clients using +find+ or +search+. You can use the
- familiar string search syntax:
+ ## SEARCH FOR CLIENTS ##
+ You can also search for clients using +find+ or +search+. You can use the
+ familiar string search syntax:
- clients.search("KEY:VALUE")
+ clients.search("KEY:VALUE")
- Just as the +all+ subcommand, the +search+ subcommand can use a code block to
- filter or transform the information returned from the search:
+ Just as the +all+ subcommand, the +search+ subcommand can use a code block to
+ filter or transform the information returned from the search:
- clients.search("KEY:VALUE") { |c| c.name }
+ clients.search("KEY:VALUE") { |c| c.name }
- You can also use a Hash based syntax, multiple search conditions will be
- joined with AND.
+ You can also use a Hash based syntax, multiple search conditions will be
+ joined with AND.
- clients.find :KEY => :VALUE, :KEY2 => :VALUE2, ...
+ clients.find :KEY => :VALUE, :KEY2 => :VALUE2, ...
-## BULK-EDIT CLIENTS ##
- **BE CAREFUL, THIS IS DESTRUCTIVE**
- You can bulk edit API Clients using the +transform+ subcommand, which requires
- a code block. Each client will be saved after the code block is run. If the
- code block returns +nil+ or +false+, that client will be skipped:
+ ## BULK-EDIT CLIENTS ##
+ **BE CAREFUL, THIS IS DESTRUCTIVE**
+ You can bulk edit API Clients using the +transform+ subcommand, which requires
+ a code block. Each client will be saved after the code block is run. If the
+ code block returns +nil+ or +false+, that client will be skipped:
- clients.transform("*:*") do |client|
- if client.name =~ /borat/i
- client.admin(false)
- true
- else
- nil
- end
- end
+ clients.transform("*:*") do |client|
+ if client.name =~ /borat/i
+ client.admin(false)
+ true
+ else
+ nil
+ end
+ end
- This will strip the admin privileges from any client named after borat.
+ This will strip the admin privileges from any client named after borat.
E
- subcommands :all => "list all api clients",
- :show => "load an api client by name",
- :search => "search for API clients",
- :transform => "edit all api clients via a code block and save them"
+ subcommands all: "list all api clients",
+ show: "load an api client by name",
+ search: "search for API clients",
+ transform: "edit all api clients via a code block and save them"
def clients
@clients ||= Shell::ModelWrapper.new(Chef::ApiClient, :client)
end
desc "Find and edit cookbooks"
- subcommands :all => "list all cookbooks",
- :show => "load a cookbook by name",
- :transform => "edit all cookbooks via a code block and save them"
+ subcommands all: "list all cookbooks",
+ show: "load a cookbook by name",
+ transform: "edit all cookbooks via a code block and save them"
def cookbooks
@cookbooks ||= Shell::ModelWrapper.new(Chef::CookbookVersion)
end
desc "Find and edit nodes via the API"
- explain(<<-E)
-## SUMMARY ##
- +nodes+ Allows you to query your chef server for information about your nodes.
+ explain(<<~E)
+ ## SUMMARY ##
+ +nodes+ Allows you to query your chef server for information about your nodes.
-## LIST ALL NODES ##
- You can list all nodes using +all+ or +list+
+ ## LIST ALL NODES ##
+ You can list all nodes using +all+ or +list+
- nodes.all #=> [<Chef::Node...>, <Chef::Node...>, ...]
+ nodes.all #=> [<Chef::Node...>, <Chef::Node...>, ...]
- To limit the information returned for each node, pass a code block to the +all+
- subcommand:
+ To limit the information returned for each node, pass a code block to the +all+
+ subcommand:
- nodes.all { |node| node.name } #=> [NODE1_NAME, NODE2_NAME, ...]
+ nodes.all { |node| node.name } #=> [NODE1_NAME, NODE2_NAME, ...]
-## SHOW ONE NODE ##
- You can show the data for a single node using the +show+ subcommand:
+ ## SHOW ONE NODE ##
+ You can show the data for a single node using the +show+ subcommand:
- nodes.show("NODE_NAME") => <Chef::Node @name="NODE_NAME" ...>
+ nodes.show("NODE_NAME") => <Chef::Node @name="NODE_NAME" ...>
-## SEARCH FOR NODES ##
- You can search for nodes using the +search+ or +find+ subcommands:
+ ## SEARCH FOR NODES ##
+ You can search for nodes using the +search+ or +find+ subcommands:
- nodes.find(:name => "app*") #=> [<Chef::Node @name="app1.example.com" ...>, ...]
+ nodes.find(:name => "app*") #=> [<Chef::Node @name="app1.example.com" ...>, ...]
- Similarly to +all+, you can pass a code block to limit or transform the
- information returned:
+ Similarly to +all+, you can pass a code block to limit or transform the
+ information returned:
- nodes.find(:name => "app#") { |node| node.ec2 }
+ nodes.find(:name => "app#") { |node| node.ec2 }
-## BULK EDIT NODES ##
- **BE CAREFUL, THIS OPERATION IS DESTRUCTIVE**
+ ## BULK EDIT NODES ##
+ **BE CAREFUL, THIS OPERATION IS DESTRUCTIVE**
- Bulk edit nodes by passing a code block to the +transform+ or +bulk_edit+
- subcommand. The block will be applied to each matching node, and then the node
- will be saved. If the block returns +nil+ or +false+, that node will be
- skipped.
+ Bulk edit nodes by passing a code block to the +transform+ or +bulk_edit+
+ subcommand. The block will be applied to each matching node, and then the node
+ will be saved. If the block returns +nil+ or +false+, that node will be
+ skipped.
- nodes.transform do |node|
- if node.fqdn =~ /.*\\.preprod\\.example\\.com/
- node.set[:environment] = "preprod"
- end
- end
+ nodes.transform do |node|
+ if node.fqdn =~ /.*\\.preprod\\.example\\.com/
+ node.set[:environment] = "preprod"
+ end
+ end
- This will assign the attribute to every node with a FQDN matching the regex.
+ This will assign the attribute to every node with a FQDN matching the regex.
E
- subcommands :all => "list all nodes",
- :show => "load a node by name",
- :search => "search for nodes",
- :transform => "edit all nodes via a code block and save them"
+ subcommands all: "list all nodes",
+ show: "load a node by name",
+ search: "search for nodes",
+ transform: "edit all nodes via a code block and save them"
def nodes
@nodes ||= Shell::ModelWrapper.new(Chef::Node)
end
desc "Find and edit roles via the API"
- explain(<<-E)
-## SUMMARY ##
- +roles+ allows you to query and edit roles on your Chef server.
-
-## SUBCOMMANDS ##
- * all (list)
- * show (load)
- * search (find)
- * transform (bulk_edit)
-
-## SEE ALSO ##
- See the help for +nodes+ for more information about the subcommands.
+ explain(<<~E)
+ ## SUMMARY ##
+ +roles+ allows you to query and edit roles on your Chef server.
+
+ ## SUBCOMMANDS ##
+ * all (list)
+ * show (load)
+ * search (find)
+ * transform (bulk_edit)
+
+ ## SEE ALSO ##
+ See the help for +nodes+ for more information about the subcommands.
E
- subcommands :all => "list all roles",
- :show => "load a role by name",
- :search => "search for roles",
- :transform => "edit all roles via a code block and save them"
+ subcommands all: "list all roles",
+ show: "load a role by name",
+ search: "search for roles",
+ transform: "edit all roles via a code block and save them"
def roles
@roles ||= Shell::ModelWrapper.new(Chef::Role)
end
desc "Find and edit +databag_name+ via the api"
- explain(<<-E)
-## SUMMARY ##
- +databags(DATABAG_NAME)+ allows you to query and edit data bag items on your
- Chef server. Unlike other commands for working with data on the server,
- +databags+ requires the databag name as an argument, for example:
- databags(:users).all
-
-## SUBCOMMANDS ##
- * all (list)
- * show (load)
- * search (find)
- * transform (bulk_edit)
-
-## SEE ALSO ##
- See the help for +nodes+ for more information about the subcommands.
+ explain(<<~E)
+ ## SUMMARY ##
+ +databags(DATABAG_NAME)+ allows you to query and edit data bag items on your
+ Chef server. Unlike other commands for working with data on the server,
+ +databags+ requires the databag name as an argument, for example:
+ databags(:users).all
+
+ ## SUBCOMMANDS ##
+ * all (list)
+ * show (load)
+ * search (find)
+ * transform (bulk_edit)
+
+ ## SEE ALSO ##
+ See the help for +nodes+ for more information about the subcommands.
E
- subcommands :all => "list all items in the data bag",
- :show => "load a data bag item by id",
- :search => "search for items in the data bag",
- :transform => "edit all items via a code block and save them"
+ subcommands all: "list all items in the data bag",
+ show: "load a data bag item by id",
+ search: "search for items in the data bag",
+ transform: "edit all items via a code block and save them"
def databags(databag_name)
@named_databags_wrappers ||= {}
@named_databags_wrappers[databag_name] ||= Shell::NamedDataBagWrapper.new(databag_name)
end
desc "Find and edit environments via the API"
- explain(<<-E)
-## SUMMARY ##
- +environments+ allows you to query and edit environments on your Chef server.
-
-## SUBCOMMANDS ##
- * all (list)
- * show (load)
- * search (find)
- * transform (bulk_edit)
-
-## SEE ALSO ##
- See the help for +nodes+ for more information about the subcommands.
+ explain(<<~E)
+ ## SUMMARY ##
+ +environments+ allows you to query and edit environments on your Chef server.
+
+ ## SUBCOMMANDS ##
+ * all (list)
+ * show (load)
+ * search (find)
+ * transform (bulk_edit)
+
+ ## SEE ALSO ##
+ See the help for +nodes+ for more information about the subcommands.
E
- subcommands :all => "list all environments",
- :show => "load an environment by name",
- :search => "search for environments",
- :transform => "edit all environments via a code block and save them"
+ subcommands all: "list all environments",
+ show: "load an environment by name",
+ search: "search for environments",
+ transform: "edit all environments via a code block and save them"
def environments
@environments ||= Shell::ModelWrapper.new(Chef::Environment)
end
diff --git a/lib/chef/shell/model_wrapper.rb b/lib/chef/shell/model_wrapper.rb
index 8c3e456a9b..78bbdb8ef6 100644
--- a/lib/chef/shell/model_wrapper.rb
+++ b/lib/chef/shell/model_wrapper.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/mixin/convert_to_class_name"
-require "chef/mixin/language"
+require_relative "../mixin/convert_to_class_name"
+require_relative "../dsl/data_query"
module Shell
class ModelWrapper
@@ -33,6 +33,7 @@ module Shell
def search(query)
return all if query.to_s == "all"
+
results = []
Chef::Search::Query.new.search(@model_symbol, format_query(query)) do |obj|
if block_given?
@@ -81,7 +82,7 @@ module Shell
# the user wanted instead of the URI=>object stuff
def list_objects
objects = @model_class.method(:list).arity == 0 ? @model_class.list : @model_class.list(true)
- objects.map { |obj| Array(obj).find { |o| o.kind_of?(@model_class) } }
+ objects.map { |obj| Array(obj).find { |o| o.is_a?(@model_class) } }
end
def format_query(query)
diff --git a/lib/chef/shell/shell_session.rb b/lib/chef/shell/shell_session.rb
index a458286157..a17d8bbc84 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,16 +18,17 @@
# limitations under the License.
#
-require "chef/recipe"
-require "chef/run_context"
-require "chef/config"
-require "chef/client"
-require "chef/cookbook/cookbook_collection"
-require "chef/cookbook_loader"
-require "chef/run_list/run_list_expansion"
-require "chef/formatters/base"
-require "chef/formatters/doc"
-require "chef/formatters/minimal"
+require_relative "../recipe"
+require_relative "../run_context"
+require_relative "../config"
+require_relative "../client"
+require_relative "../cookbook/cookbook_collection"
+require_relative "../cookbook_loader"
+require_relative "../run_list/run_list_expansion"
+require_relative "../formatters/base"
+require_relative "../formatters/doc"
+require_relative "../formatters/minimal"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
module Shell
class ShellSession
@@ -38,8 +39,9 @@ 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
formatter = Chef::Formatters.new(Chef::Config.formatter, STDOUT, STDERR)
@@ -73,6 +75,8 @@ module Shell
run_context.resource_collection
end
+ attr_writer :run_context
+
def run_context
@run_context ||= rebuild_context
end
@@ -86,7 +90,7 @@ module Shell
end
def save_node
- raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run chef-shell in client mode?"
+ raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run #{ChefUtils::Dist::Infra::SHELL} in client mode?"
end
def rebuild_context
@@ -127,7 +131,7 @@ module Shell
def shorten_node_inspect
def @node.inspect # rubocop:disable Lint/NestedMethodDefinition
- "<Chef::Node:0x#{self.object_id.to_s(16)} @name=\"#{self.name}\">"
+ "<Chef::Node:0x#{object_id.to_s(16)} @name=\"#{name}\">"
end
end
@@ -151,7 +155,7 @@ module Shell
def rebuild_node
Chef::Config[:solo_legacy_mode] = true
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.load_node
@client.build_node
@@ -159,9 +163,9 @@ module Shell
end
- class SoloSession < ShellSession
+ class SoloLegacySession < ShellSession
- session_type :solo
+ session_type :solo_legacy_mode
def definitions
@run_context.definitions
@@ -183,7 +187,7 @@ module Shell
def rebuild_node
# Tell the client we're chef solo so it won't try to contact the server
Chef::Config[:solo_legacy_mode] = true
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.load_node
@client.build_node
@@ -191,10 +195,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
@@ -214,7 +222,7 @@ module Shell
def rebuild_node
# Make sure the client knows this is not chef solo
Chef::Config[:solo_legacy_mode] = false
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
@client.run_ohai
@client.register
@client.load_node
@@ -223,6 +231,12 @@ module Shell
end
+ class SoloSession < ClientSession
+
+ session_type :solo
+
+ end
+
class DoppelGangerClient < Chef::Client
attr_reader :node_name
@@ -241,20 +255,20 @@ 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)
@run_list_expansion = @node.expand!("server")
@expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings
Chef::Log.info("Run List is [#{@node.run_list}]")
- Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]")
+ Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(", ")}]")
@node
end
def register
- @rest = Chef::ServerAPI.new(Chef::Config[:chef_server_url], :client_name => Chef::Config[:node_name],
- :signing_key_filename => Chef::Config[:client_key])
+ @rest = Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: Chef::Config[:node_name],
+ signing_key_filename: Chef::Config[:client_key])
end
end
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/train_transport.rb b/lib/chef/train_transport.rb
new file mode 100644
index 0000000000..4fe1fcadec
--- /dev/null
+++ b/lib/chef/train_transport.rb
@@ -0,0 +1,29 @@
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 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/mixin/train_transport" unless defined?(ChefConfig::Mixin::TrainTransport)
+
+class Chef
+ class TrainTransport
+ include ChefConfig::Mixin::TrainTransport
+
+ def config
+ require "chef/config" unless defined?(Chef::Config)
+ Chef::Config
+ end
+ end
+end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index a6fc21646d..e578cc2131 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,24 +15,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/search/query"
-require "chef/server_api"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "search/query"
+require_relative "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
@@ -49,35 +49,35 @@ class Chef
end
def chef_rest_v0
- @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" })
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0" })
end
def name(arg = nil)
set_or_return(:name, arg,
- :regex => /^[a-z0-9\-_]+$/)
+ regex: /^[a-z0-9\-_]+$/)
end
def admin(arg = nil)
set_or_return(:admin,
- arg, :kind_of => [TrueClass, FalseClass])
+ arg, kind_of: [TrueClass, FalseClass])
end
def public_key(arg = nil)
set_or_return(:public_key,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def private_key(arg = nil)
set_or_return(:private_key,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def password(arg = nil)
set_or_return(:password,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
- def to_hash
+ def to_h
result = {
"name" => @name,
"public_key" => @public_key,
@@ -88,8 +88,10 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def destroy
@@ -97,34 +99,32 @@ 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_h.merge(new_user))
end
def update(new_key = false)
- payload = { :name => name, :admin => admin }
+ payload = { name: name, admin: admin }
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_h.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::HTTPClientException => e
+ if e.response.code == "409"
+ update(new_key)
+ else
+ raise e
end
end
def reregister
- reregistered_self = chef_rest_v0.put("users/#{name}", { :name => name, :admin => admin, :private_key => true })
+ reregistered_self = chef_rest_v0.put("users/#{name}", { name: name, admin: admin, private_key: true })
private_key(reregistered_self["private_key"])
self
end
@@ -154,13 +154,8 @@ 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")
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0" }).get("users")
users = if response.is_a?(Array)
transform_ohc_list_response(response) # OHC/OPC
else
@@ -177,7 +172,7 @@ class Chef
end
def self.load(name)
- response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }).get("users/#{name}")
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { api_version: "0" }).get("users/#{name}")
Chef::User.from_hash(response)
end
@@ -186,7 +181,7 @@ class Chef
# into the form
# { "USERNAME" => "URI" }
def self.transform_ohc_list_response(response)
- new_response = Hash.new
+ new_response = {}
response.each do |u|
name = u["user"]["username"]
new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb
index db44ced9d4..945f0197df 100644
--- a/lib/chef/user_v1.rb
+++ b/lib/chef/user_v1.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,15 +15,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/config"
-require "chef/mixin/params_validate"
-require "chef/mixin/from_file"
-require "chef/mash"
-require "chef/json_compat"
-require "chef/search/query"
-require "chef/mixin/api_version_request_handling"
-require "chef/exceptions"
-require "chef/server_api"
+require_relative "config"
+require_relative "mixin/params_validate"
+require_relative "mixin/from_file"
+require_relative "mash"
+require_relative "json_compat"
+require_relative "search/query"
+require_relative "mixin/api_version_request_handling"
+require_relative "exceptions"
+require_relative "server_api"
# OSC 11 BACKWARDS COMPATIBILITY NOTE (remove after OSC 11 support ends)
#
@@ -39,7 +39,7 @@ class Chef
include Chef::Mixin::ParamsValidate
include Chef::Mixin::ApiVersionRequestHandling
- SUPPORTED_API_VERSIONS = [0, 1]
+ SUPPORTED_API_VERSIONS = [0, 1].freeze
def initialize
@username = nil
@@ -55,64 +55,64 @@ class Chef
end
def chef_root_rest_v0
- @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { :api_version => "0" })
+ @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { api_version: "0" })
end
def chef_root_rest_v1
- @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { :api_version => "1" })
+ @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { api_version: "1" })
end
def username(arg = nil)
set_or_return(:username, arg,
- :regex => /^[a-z0-9\-_]+$/)
+ regex: /^[a-z0-9\-_]+$/)
end
def display_name(arg = nil)
set_or_return(:display_name,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def first_name(arg = nil)
set_or_return(:first_name,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def middle_name(arg = nil)
set_or_return(:middle_name,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def last_name(arg = nil)
set_or_return(:last_name,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def email(arg = nil)
set_or_return(:email,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def create_key(arg = nil)
set_or_return(:create_key, arg,
- :kind_of => [TrueClass, FalseClass])
+ kind_of: [TrueClass, FalseClass])
end
def public_key(arg = nil)
set_or_return(:public_key,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def private_key(arg = nil)
set_or_return(:private_key,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
def password(arg = nil)
set_or_return(:password,
- arg, :kind_of => String)
+ arg, kind_of: String)
end
- def to_hash
+ def to_h
result = {
"username" => @username,
}
@@ -128,8 +128,10 @@ class Chef
result
end
+ alias_method :to_hash, :to_h
+
def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
+ Chef::JSONCompat.to_json(to_h, *a)
end
def destroy
@@ -141,17 +143,18 @@ class Chef
# try v1, fail back to v0 if v1 not supported
begin
payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password,
+ username: @username,
+ display_name: @display_name,
+ first_name: @first_name,
+ last_name: @last_name,
+ email: @email,
+ password: @password,
}
payload[:public_key] = @public_key unless @public_key.nil?
payload[:create_key] = @create_key unless @create_key.nil?
payload[:middle_name] = @middle_name unless @middle_name.nil?
raise Chef::Exceptions::InvalidUserAttribute, "You cannot set both public_key and create_key for create." if !@create_key.nil? && !@public_key.nil?
+
new_user = chef_root_rest_v1.post("users", payload)
# get the private_key out of the chef_key hash if it exists
@@ -162,17 +165,18 @@ class Chef
new_user["public_key"] = new_user["chef_key"]["public_key"]
new_user.delete("chef_key")
end
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# rescue API V0 if 406 and the server supports V0
supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
raise e unless supported_versions && supported_versions.include?(0)
+
payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password,
+ username: @username,
+ display_name: @display_name,
+ first_name: @first_name,
+ last_name: @last_name,
+ email: @email,
+ password: @password,
}
payload[:middle_name] = @middle_name unless @middle_name.nil?
payload[:public_key] = @public_key unless @public_key.nil?
@@ -180,12 +184,12 @@ 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_h.merge(new_user))
end
def update(new_key = false)
begin
- payload = { :username => username }
+ payload = { username: username }
payload[:display_name] = display_name unless display_name.nil?
payload[:first_name] = first_name unless first_name.nil?
payload[:middle_name] = middle_name unless middle_name.nil?
@@ -198,7 +202,7 @@ class Chef
payload[:private_key] = new_key if new_key
updated_user = chef_root_rest_v1.put("users/#{username}", payload)
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
if e.response.code == "400"
# if a 400 is returned but the error message matches the error related to private / public key fields, try V0
# else, raise the 400
@@ -213,29 +217,27 @@ 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_h.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::HTTPClientException => 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_h.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
- rescue Net::HTTPServerException => e
+ rescue Net::HTTPClientException => e
# if there was a 406 related to versioning, give error explaining that
# only API version 0 is supported for reregister command
if e.response.code == "406" && e.response["x-ops-server-api-version"]
@@ -276,11 +278,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)
@@ -316,7 +313,7 @@ class Chef
# into the form
# { "USERNAME" => "URI" }
def self.transform_list_response(response)
- new_response = Hash.new
+ new_response = {}
response.each do |u|
name = u["user"]["username"]
new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
diff --git a/lib/chef/util/backup.rb b/lib/chef/util/backup.rb
index 8bf2b3f25b..e739488fb9 100644
--- a/lib/chef/util/backup.rb
+++ b/lib/chef/util/backup.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/util/path_helper"
+require_relative "path_helper"
class Chef
class Util
@@ -36,7 +36,7 @@ class Chef
slice_number = @new_resource.backup
backup_files = sorted_backup_files
if backup_files.length >= @new_resource.backup
- remainder = backup_files.slice(slice_number..-1)
+ remainder = backup_files.slice(slice_number..)
remainder.each do |backup_to_delete|
delete_backup(backup_to_delete)
end
@@ -52,7 +52,7 @@ class Chef
nanoseconds = sprintf("%6f", time.to_f).split(".")[1]
savetime = time.strftime("%Y%m%d%H%M%S.#{nanoseconds}")
backup_filename = "#{path}.chef-#{savetime}"
- backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows
+ backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") # strip drive letter on Windows
end
end
@@ -69,7 +69,7 @@ class Chef
def do_backup
FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path]
- FileUtils.cp(path, backup_path, :preserve => true)
+ FileUtils.cp(path, backup_path, preserve: true)
Chef::Log.info("#{@new_resource} backed up to #{backup_path}")
end
@@ -87,7 +87,7 @@ class Chef
end
def sorted_backup_files
- unsorted_backup_files.sort { |a, b| b <=> a }
+ unsorted_backup_files.sort.reverse # faster than sort { |a, b| b <=> a }
end
end
end
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index bb1b4e2b95..0774dea813 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -1,5 +1,5 @@
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,15 +40,11 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE
# SOFTWARE.
-require "diff/lcs"
-require "diff/lcs/hunk"
-
class Chef
class Util
class Diff
# @todo: to_a, to_s, to_json, inspect defs, accessors for @diff and @error
# @todo: move coercion to UTF-8 into to_json
- # @todo: replace shellout to diff -u with diff-lcs gem
def for_output
# formatted output to a terminal uses arrays of strings and returns error strings
@@ -58,13 +54,14 @@ class Chef
def for_reporting
# caller needs to ensure that new files aren't posted to resource reporting
return nil if @diff.nil?
+
@diff.join("\\n")
end
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")
+ unless File.exist?(file)
+ Chef::Log.trace("File #{file} does not exist to diff against, using empty tempfile")
tempfile = Tempfile.new("chef-diff")
file = tempfile.path
end
@@ -86,11 +83,14 @@ class Chef
# produces a unified-output-format diff with 3 lines of context
# ChefFS uses udiff() directly
def udiff(old_file, new_file)
+ require "diff/lcs"
+ require "diff/lcs/hunk"
+
diff_str = ""
file_length_difference = 0
- old_data = IO.readlines(old_file).map { |e| e.chomp }
- new_data = IO.readlines(new_file).map { |e| e.chomp }
+ old_data = IO.readlines(old_file).map(&:chomp)
+ new_data = IO.readlines(new_file).map(&:chomp)
diff_data = ::Diff::LCS.diff(old_data, new_data)
return diff_str if old_data.empty? && new_data.empty?
@@ -106,18 +106,19 @@ class Chef
# join them. otherwise, print out the old one.
old_hunk = hunk = nil
diff_data.each do |piece|
- begin
- hunk = ::Diff::LCS::Hunk.new(old_data, new_data, piece, 3, file_length_difference)
- file_length_difference = hunk.file_length_difference
- next unless old_hunk
- next if hunk.merge(old_hunk)
- diff_str << old_hunk.diff(:unified) << "\n"
- ensure
- old_hunk = hunk
- end
+
+ hunk = ::Diff::LCS::Hunk.new(old_data, new_data, piece, 3, file_length_difference)
+ file_length_difference = hunk.file_length_difference
+ next unless old_hunk
+ next if hunk.merge(old_hunk)
+
+ diff_str << old_hunk.diff(:unified) << "\n"
+ ensure
+ old_hunk = hunk
+
end
diff_str << old_hunk.diff(:unified) << "\n"
- return diff_str
+ diff_str
end
private
@@ -134,12 +135,12 @@ class Chef
return "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)"
end
- # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs
+ # macOS(BSD?) diff will *sometimes* happily spit out nasty binary diffs
return "(current file is binary, diff output suppressed)" if is_binary?(old_file)
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
@@ -150,14 +151,14 @@ class Chef
if !diff_str.empty? && diff_str != "No differences encountered\n"
if diff_str.length > diff_output_threshold
- return "(long diff of over #{diff_output_threshold} characters, diff output suppressed)"
+ "(long diff of over #{diff_output_threshold} characters, diff output suppressed)"
else
diff_str = encode_diff_for_json(diff_str)
@diff = diff_str.split("\n")
- return "(diff available)"
+ "(diff available)"
end
else
- return "(no diff)"
+ "(no diff)"
end
end
@@ -169,14 +170,15 @@ class Chef
begin
return buff !~ /\A[\s[:print:]]*\z/m
rescue ArgumentError => e
- return true if e.message =~ /invalid byte sequence/
+ return true if /invalid byte sequence/.match?(e.message)
+
raise
end
end
end
def encode_diff_for_json(diff_str)
- diff_str.encode!("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?")
+ diff_str.encode!("UTF-8", invalid: :replace, undef: :replace, replace: "?")
end
end
diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb
index 8b492d483a..7785dc3990 100644
--- a/lib/chef/util/dsc/configuration_generator.rb
+++ b/lib/chef/util/dsc/configuration_generator.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,38 +16,37 @@
# limitations under the License.
#
-require "chef/util/powershell/cmdlet"
+require_relative "../../mixin/powershell_exec"
class Chef::Util::DSC
class ConfigurationGenerator
+ include Chef::Mixin::PowershellExec
+
def initialize(node, config_directory)
@node = node
@config_directory = config_directory
end
- def configuration_document_from_script_code(code, configuration_flags, imports, shellout_flags)
- Chef::Log.debug("DSC: DSC code:\n '#{code}'")
+ def configuration_document_from_script_code(code, configuration_flags, imports)
+ 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)
+ configuration_document_from_script_path(generated_script_path, "chef_dsc", configuration_flags)
ensure
::FileUtils.rm(generated_script_path)
end
end
- def configuration_document_from_script_path(script_path, configuration_name, configuration_flags, shellout_flags)
+ def configuration_document_from_script_path(script_path, configuration_name, configuration_flags)
validate_configuration_name!(configuration_name)
- document_generation_cmdlet = Chef::Util::Powershell::Cmdlet.new(
- @node,
- configuration_document_generation_code(script_path, configuration_name))
-
- merged_configuration_flags = get_merged_configuration_flags!(configuration_flags, configuration_name)
+ config_generation_code = configuration_document_generation_code(script_path, configuration_name)
+ switches_string = command_switches_string(get_merged_configuration_flags!(configuration_flags, configuration_name))
- document_generation_cmdlet.run!(merged_configuration_flags, shellout_flags)
+ powershell_exec!("#{config_generation_code} #{switches_string}")
configuration_document_location = find_configuration_document(configuration_name)
- if ! configuration_document_location
+ unless configuration_document_location
raise "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
end
@@ -58,6 +57,50 @@ class Chef::Util::DSC
protected
+ def validate_switch_name!(switch_parameter_name)
+ unless switch_parameter_name.match?(/\A[A-Za-z]+[_a-zA-Z0-9]*\Z/)
+ raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
+ end
+ end
+
+ def escape_parameter_value(parameter_value)
+ parameter_value.gsub(/(`|'|"|#)/, '`\1')
+ end
+
+ def escape_string_parameter_value(parameter_value)
+ "'#{escape_parameter_value(parameter_value)}'"
+ end
+
+ def command_switches_string(switches)
+ command_switches = switches.map do |switch_name, switch_value|
+ if switch_name.class != Symbol
+ raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name}'. The switch must be specified as a Symbol'"
+ end
+
+ validate_switch_name!(switch_name)
+
+ switch_argument = ""
+ switch_present = true
+
+ case switch_value
+ when Numeric, Float
+ switch_argument = switch_value.to_s
+ when FalseClass
+ switch_present = false
+ when TrueClass
+ # nothing
+ when String
+ switch_argument = escape_string_parameter_value(switch_value)
+ else
+ raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`"
+ end
+
+ switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(" ").strip : ""
+ end
+
+ command_switches.join(" ")
+ end
+
# From PowerShell error help for the Configuration language element:
# Standard names may only contain letters (a-z, A-Z), numbers (0-9), and underscore (_).
# The name may not be null or empty, and should start with a letter.
@@ -68,12 +111,13 @@ class Chef::Util::DSC
end
def get_merged_configuration_flags!(configuration_flags, configuration_name)
- merged_configuration_flags = { :outputpath => configuration_document_directory(configuration_name) }
+ merged_configuration_flags = { outputpath: configuration_document_directory(configuration_name) }
if configuration_flags
configuration_flags.map do |switch, value|
if merged_configuration_flags.key?(switch.to_s.downcase.to_sym)
raise ArgumentError, "The `flags` attribute for the dsc_script resource contained a command line switch :#{switch} that is disallowed."
end
+
merged_configuration_flags[switch.to_s.downcase.to_sym] = value
end
end
@@ -81,16 +125,16 @@ class Chef::Util::DSC
end
def configuration_code(code, configuration_name, imports)
- <<-EOF
-$ProgressPreference = 'SilentlyContinue';
-Configuration '#{configuration_name}'
-{
- #{generate_import_resource_statements(imports).join(" \n")}
- node 'localhost'
- {
- #{code}
- }
-}
+ <<~EOF
+ $ProgressPreference = 'SilentlyContinue';
+ Configuration '#{configuration_name}'
+ {
+ #{generate_import_resource_statements(imports).join(" \n")}
+ node 'localhost'
+ {
+ #{code}
+ }
+ }
EOF
end
@@ -100,7 +144,7 @@ Configuration '#{configuration_name}'
if resources.length == 0 || resources.include?("*")
"Import-DscResource -ModuleName #{resource_module}"
else
- "Import-DscResource -ModuleName #{resource_module} -Name #{resources.join(',')}"
+ "Import-DscResource -ModuleName #{resource_module} -Name #{resources.join(",")}"
end
end
else
@@ -131,9 +175,7 @@ Configuration '#{configuration_name}'
end
def get_configuration_document(document_path)
- ::File.open(document_path, "rb") do |file|
- file.read
- end
+ ::File.open(document_path, "rb", &:read)
end
end
end
diff --git a/lib/chef/util/dsc/lcm_output_parser.rb b/lib/chef/util/dsc/lcm_output_parser.rb
index bdcedff7f8..d05ea3ba68 100644
--- a/lib/chef/util/dsc/lcm_output_parser.rb
+++ b/lib/chef/util/dsc/lcm_output_parser.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/log"
-require "chef/util/dsc/resource_info"
-require "chef/exceptions"
+require_relative "../../log"
+require_relative "resource_info"
+require_relative "../../exceptions"
class Chef
class Util
@@ -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,12 +53,64 @@ class Chef
# )
# ]
#
- def self.parse(lcm_output)
- lcm_output ||= ""
- current_resource = Hash.new
+ # 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)
+ lcm_output = String(lcm_output).split("\n")
+ test_dsc_configuration ? test_dsc_parser(lcm_output) : what_if_parser(lcm_output)
+ end
+
+ def self.test_dsc_parser(lcm_output)
+ current_resource = {}
+
+ resources = []
+ lcm_output.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", "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)
+ current_resource = {}
resources = []
- lcm_output.lines.each do |line|
+ lcm_output.each do |line|
op_action, op_type, info = parse_line(line)
case op_action
@@ -73,9 +125,9 @@ class Chef
if current_resource[:name]
resources.push(current_resource)
end
- current_resource = { :name => info }
+ 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
@@ -105,9 +157,9 @@ class Chef
def self.parse_line(line)
if match = line.match(/^.*?:.*?:\s*LCM:\s*\[(.*?)\](.*)/)
- # If the line looks like
- # What If: [machinename]: LCM: [op_action op_type] message
- # extract op_action, op_type, and message
+ # If the line looks like
+ # What If: [machinename]: LCM: [op_action op_type] message
+ # extract op_action, op_type, and message
operation, info = match.captures
op_action, op_type = operation.strip.split(" ").map { |m| m.downcase.to_sym }
else
@@ -119,7 +171,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..c0f9c72da8 100644
--- a/lib/chef/util/dsc/local_configuration_manager.rb
+++ b/lib/chef/util/dsc/local_configuration_manager.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,25 +16,27 @@
# limitations under the License.
#
-require "chef/util/powershell/cmdlet"
-require "chef/util/dsc/lcm_output_parser"
+require_relative "../../mixin/powershell_exec"
+require_relative "lcm_output_parser"
class Chef::Util::DSC
class LocalConfigurationManager
+ include Chef::Mixin::PowershellExec
+
def initialize(node, configuration_path)
@node = node
@configuration_path = configuration_path
clear_execution_time
end
- 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?
- configuration_update_required?(status.return_value)
+ def test_configuration(configuration_document)
+ status = run_configuration_cmdlet(configuration_document, false)
+ log_dsc_exception(status.errors.join("\n")) if status.error?
+ configuration_update_required?(status.result)
end
- def set_configuration(configuration_document, shellout_flags)
- run_configuration_cmdlet(configuration_document, true, shellout_flags)
+ def set_configuration(configuration_document)
+ run_configuration_cmdlet(configuration_document, true)
end
def last_operation_execution_time_seconds
@@ -45,64 +47,75 @@ 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 }" : ""
+ def run_configuration_cmdlet(configuration_document, apply_configuration)
+ 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}")
+ cmd = lcm_command(apply_configuration)
+ Chef::Log.trace("DSC: Calling DSC Local Config Manager with:\n#{cmd}")
+
+ status = powershell_exec(cmd)
if apply_configuration
- status = cmdlet.run!({}, shellout_flags)
- else
- status = cmdlet.run({}, shellout_flags)
+ status.error!
end
ensure
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 | Out-String"
+ 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)
- # LCM returns an error if any of the resources do not support the opptional What-If
+ 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 optional 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(/\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.include?("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..49ca46832a 100644
--- a/lib/chef/util/dsc/resource_store.rb
+++ b/lib/chef/util/dsc/resource_store.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "chef/util/powershell/cmdlet"
-require "chef/util/powershell/cmdlet_result"
-require "chef/exceptions"
+require_relative "../../mixin/powershell_exec"
+require_relative "../../exceptions"
class Chef
class Util
class DSC
class ResourceStore
+ include Chef::Mixin::PowershellExec
def self.instance
@@instance ||= ResourceStore.new.tap do |store|
@@ -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
@@ -83,19 +83,13 @@ class Chef
# Returns a list of dsc resources
def query_resources
- cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource",
- :object)
- result = cmdlet.run
- result.return_value
+ powershell_exec("get-dscresource").result
end
# Returns a list of dsc resources matching the provided name
def query_resource(resource_name)
- cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource #{resource_name}",
- :object)
- result = cmdlet.run
- ret_val = result.return_value
- if ret_val.nil?
+ ret_val = powershell_exec("get-dscresource #{resource_name}").result
+ if ret_val.empty?
[]
elsif ret_val.is_a? Array
ret_val
diff --git a/lib/chef/util/editor.rb b/lib/chef/util/editor.rb
index fa4f0ec12e..4b6e08fe74 100644
--- a/lib/chef/util/editor.rb
+++ b/lib/chef/util/editor.rb
@@ -1,6 +1,6 @@
#
# Author:: Chris Bandy (<bandy.chris@gmail.com>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/util/file_edit.rb b/lib/chef/util/file_edit.rb
index 5aa33fd169..0cd1f4dce1 100644
--- a/lib/chef/util/file_edit.rb
+++ b/lib/chef/util/file_edit.rb
@@ -1,6 +1,6 @@
#
# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/util/editor"
-require "fileutils"
+require_relative "editor"
+require "fileutils" unless defined?(FileUtils)
class Chef
class Util
@@ -30,6 +30,7 @@ class Chef
def initialize(filepath)
raise ArgumentError, "File '#{filepath}' does not exist" unless File.exist?(filepath)
+
@editor = Editor.new(File.open(filepath, &:readlines))
@original_pathname = filepath
@file_edited = false
@@ -40,38 +41,38 @@ class Chef
@file_edited
end
- #search the file line by line and match each line with the given regex
- #if matched, replace the whole line with newline.
+ # search the file line by line and match each line with the given regex
+ # if matched, replace the whole line with newline.
def search_file_replace_line(regex, newline)
@changes = (editor.replace_lines(regex, newline) > 0) || @changes
end
- #search the file line by line and match each line with the given regex
- #if matched, replace the match (all occurrences) with the replace parameter
+ # search the file line by line and match each line with the given regex
+ # if matched, replace the match (all occurrences) with the replace parameter
def search_file_replace(regex, replace)
@changes = (editor.replace(regex, replace) > 0) || @changes
end
- #search the file line by line and match each line with the given regex
- #if matched, delete the line
+ # search the file line by line and match each line with the given regex
+ # if matched, delete the line
def search_file_delete_line(regex)
@changes = (editor.remove_lines(regex) > 0) || @changes
end
- #search the file line by line and match each line with the given regex
- #if matched, delete the match (all occurrences) from the line
+ # search the file line by line and match each line with the given regex
+ # if matched, delete the match (all occurrences) from the line
def search_file_delete(regex)
search_file_replace(regex, "")
end
- #search the file line by line and match each line with the given regex
- #if matched, insert newline after each matching line
+ # search the file line by line and match each line with the given regex
+ # if matched, insert newline after each matching line
def insert_line_after_match(regex, newline)
@changes = (editor.append_line_after(regex, newline) > 0) || @changes
end
- #search the file line by line and match each line with the given regex
- #if not matched, insert newline at the end of the file
+ # search the file line by line and match each line with the given regex
+ # if not matched, insert newline at the end of the file
def insert_line_if_no_match(regex, newline)
@changes = (editor.append_line_if_missing(regex, newline) > 0) || @changes
end
@@ -80,11 +81,11 @@ class Chef
!!@changes
end
- #Make a copy of old_file and write new file out (only if file changed)
+ # Make a copy of old_file and write new file out (only if file changed)
def write_file
if @changes
backup_pathname = original_pathname + ".old"
- FileUtils.cp(original_pathname, backup_pathname, :preserve => true)
+ FileUtils.cp(original_pathname, backup_pathname, preserve: true)
File.open(original_pathname, "w") do |newfile|
editor.lines.each do |line|
newfile.puts(line)
diff --git a/lib/chef/util/path_helper.rb b/lib/chef/util/path_helper.rb
index 6389458b6a..0eb3f59b49 100644
--- a/lib/chef/util/path_helper.rb
+++ b/lib/chef/util/path_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb
deleted file mode 100644
index e300266b1e..0000000000
--- a/lib/chef/util/powershell/cmdlet.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright 2014-2016, 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 "mixlib/shellout"
-require "chef/mixin/windows_architecture_helper"
-require "chef/util/powershell/cmdlet_result"
-
-class Chef
- class Util
- class Powershell
- class Cmdlet
- def initialize(node, cmdlet, output_format = nil, output_format_options = {})
- @output_format = output_format
- @node = node
-
- case output_format
- when nil
- @json_format = false
- when :json
- @json_format = true
- when :text
- @json_format = false
- when :object
- @json_format = true
- else
- raise ArgumentError, "Invalid output format #{output_format} specified"
- end
-
- @cmdlet = cmdlet
- @output_format_options = output_format_options
- end
-
- attr_reader :output_format
-
- def run(switches = {}, execution_options = {}, *arguments)
- streams = { :json => CmdletStream.new("json"),
- :verbose => CmdletStream.new("verbose"),
- }
-
- arguments_string = arguments.join(" ")
-
- switches_string = command_switches_string(switches)
-
- json_depth = 5
-
- if @json_format && @output_format_options.has_key?(:depth)
- json_depth = @output_format_options[:depth]
- end
-
- 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 "\
- "($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} "\
- "#{arguments_string} #{redirections}"\
- "#{json_command}\";if ( ! $? ) { exit 1 }"
-
- augmented_options = { :returns => [0], :live_stream => false }.merge(execution_options)
- command = Mixlib::ShellOut.new(command_string, augmented_options)
-
- status = nil
-
- with_os_architecture(@node) do
- status = command.run_command
- end
-
- CmdletResult.new(status, streams, @output_format)
- end
-
- def run!(switches = {}, execution_options = {}, *arguments)
- result = run(switches, execution_options, arguments)
-
- if ! result.succeeded?
- raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{result.stderr}"
- end
-
- result
- end
-
- protected
-
- include Chef::Mixin::WindowsArchitectureHelper
-
- def validate_switch_name!(switch_parameter_name)
- if !!(switch_parameter_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
- raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
- end
- end
-
- def escape_parameter_value(parameter_value)
- parameter_value.gsub(/(`|'|"|#)/, '`\1')
- end
-
- def escape_string_parameter_value(parameter_value)
- "'#{escape_parameter_value(parameter_value)}'"
- end
-
- def command_switches_string(switches)
- command_switches = switches.map do |switch_name, switch_value|
- if switch_name.class != Symbol
- raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name}'. The switch must be specified as a Symbol'"
- end
-
- validate_switch_name!(switch_name)
-
- switch_argument = ""
- switch_present = true
-
- case switch_value
- when Numeric
- switch_argument = switch_value.to_s
- when Float
- switch_argument = switch_value.to_s
- when FalseClass
- switch_present = false
- when TrueClass
- when String
- switch_argument = escape_string_parameter_value(switch_value)
- else
- raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`"
- end
-
- switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(" ").strip : ""
- end
-
- command_switches.join(" ")
- end
-
- class CmdletStream
- def initialize(name)
- @filename = Dir::Tmpname.create(name) {}
- ObjectSpace.define_finalizer(self, self.class.destroy(@filename))
- end
-
- def path
- @filename
- end
-
- def read
- if File.exist? @filename
- File.open(@filename, "rb:bom|UTF-16LE") do |f|
- f.read.encode("UTF-8")
- end
- end
- end
-
- def self.destroy(name)
- proc { File.delete(name) if File.exists? name }
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/util/powershell/cmdlet_result.rb b/lib/chef/util/powershell/cmdlet_result.rb
deleted file mode 100644
index 82aef4da40..0000000000
--- a/lib/chef/util/powershell/cmdlet_result.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright 2014-2016, 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/json_compat"
-
-class Chef
- class Util
- class Powershell
- class CmdletResult
- attr_reader :output_format
-
- def initialize(status, streams, output_format)
- @status = status
- @output_format = output_format
- @streams = streams
- end
-
- def stdout
- @status.stdout
- end
-
- def stderr
- @status.stderr
- end
-
- def stream(name)
- @streams[name].read
- end
-
- def return_value
- if output_format == :object
- Chef::JSONCompat.parse(stream(:json))
- elsif output_format == :json
- stream(:json)
- else
- @status.stdout
- end
- end
-
- def succeeded?
- @succeeded = @status.status.exitstatus == 0
- end
- end
- end
- end
-end
diff --git a/lib/chef/util/powershell/ps_credential.rb b/lib/chef/util/powershell/ps_credential.rb
index 32810b98a6..0404f5a1ac 100644
--- a/lib/chef/util/powershell/ps_credential.rb
+++ b/lib/chef/util/powershell/ps_credential.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,26 +16,30 @@
# limitations under the License.
#
-require "chef/win32/crypto" if Chef::Platform.windows?
+require_relative "../../win32/crypto" if ChefUtils.windows?
-class Chef::Util::Powershell
- class PSCredential
- def initialize(username, password)
- @username = username
- @password = password
- end
+class Chef
+ class Util
+ class Powershell
+ class PSCredential
+ def initialize(username, password)
+ @username = username
+ @password = password
+ end
- def to_psobject
- "New-Object System.Management.Automation.PSCredential('#{@username}',('#{encrypt(@password)}' | ConvertTo-SecureString))"
- end
+ def to_psobject
+ "New-Object System.Management.Automation.PSCredential('#{@username}',('#{encrypt(@password)}' | ConvertTo-SecureString))"
+ end
- alias to_s to_psobject
- alias to_text to_psobject
+ alias to_s to_psobject
+ alias to_text to_psobject
- private
+ private
- def encrypt(str)
- Chef::ReservedNames::Win32::Crypto.encrypt(str)
+ def encrypt(str)
+ Chef::ReservedNames::Win32::Crypto.encrypt(str)
+ end
+ end
end
end
end
diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb
index edca589034..8016262b6f 100644
--- a/lib/chef/util/selinux.rb
+++ b/lib/chef/util/selinux.rb
@@ -3,7 +3,7 @@
# Author:: Kevin Keane
# Author:: Lamont Granquist (<lamont@chef.io>)
#
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2013-2016, North County Tech Center, LLC
#
# License:: Apache License, Version 2.0
@@ -20,8 +20,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/mixin/shell_out"
-require "chef/mixin/which"
+require_relative "../mixin/shell_out"
+require_relative "../mixin/which"
class Chef
class Util
@@ -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!(restorecon_path, restorecon_flags)
else
Chef::Log.warn "Can not find 'restorecon' on the system. Skipping selinux security context restore."
end
@@ -71,19 +72,19 @@ class Chef
def check_selinux_enabled?
if selinuxenabled_path
- cmd = shell_out!(selinuxenabled_path, :returns => [0, 1])
+ cmd = shell_out!(selinuxenabled_path, returns: [0, 1])
case cmd.exitstatus
when 1
- return false
+ false
when 0
- return true
+ true
else
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/threaded_job_queue.rb b/lib/chef/util/threaded_job_queue.rb
index eaffd9ea70..bce25e9225 100644
--- a/lib/chef/util/threaded_job_queue.rb
+++ b/lib/chef/util/threaded_job_queue.rb
@@ -1,4 +1,4 @@
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "thread"
-
class Chef
class Util
# A simple threaded job queue
@@ -54,7 +52,7 @@ class Chef
end
end
workers.each { |worker| self << Thread.method(:exit) }
- workers.each { |worker| worker.join }
+ workers.each(&:join)
end
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..b29f24565c
--- /dev/null
+++ b/lib/chef/util/windows/logon_session.rb
@@ -0,0 +1,129 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+#
+# Copyright:: Copyright (c) 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_relative "../../win32/api/security" if ChefUtils.windows?
+require_relative "../../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)
+
+ unless 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!
+
+ unless 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)
+
+ unless 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
+
+ unless 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!
+ unless 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..a762faa5bd 100644
--- a/lib/chef/util/windows/net_group.rb
+++ b/lib/chef/util/windows/net_group.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/util/windows"
-require "chef/win32/net"
+require_relative "../windows"
+require_relative "../../win32/net"
-#wrapper around a subset of the NetGroup* APIs.
+# wrapper around a subset of the NetGroup* APIs.
class Chef::Util::Windows::NetGroup
private
@@ -35,50 +35,44 @@ 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_group_set_info(comment)
+ Chef::ReservedNames::Win32::NetUser.net_local_group_set_info(nil, groupname, comment)
+ 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..9e6735429b 100644
--- a/lib/chef/util/windows/net_use.rb
+++ b/lib/chef/util/windows/net_use.rb
@@ -16,12 +16,12 @@
# limitations under the License.
#
-#the Win32 Volume APIs do not support mapping network drives. not supported by WMI either.
-#see also: WNetAddConnection2 and WNetAddConnection3
-#see also cmd.exe: net use /?
+# the Win32 Volume APIs do not support mapping network drives. not supported by WMI either.
+# see also: WNetAddConnection2 and WNetAddConnection3
+# see also cmd.exe: net use /?
-require "chef/util/windows"
-require "chef/win32/net"
+require_relative "../windows"
+require_relative "../../win32/net"
class Chef::Util::Windows::NetUse < Chef::Util::Windows
def initialize(localname)
@@ -38,7 +38,7 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows
def add(args)
if args.class == String
remote = args
- args = Hash.new
+ args = {}
args[:remote] = remote
end
args[:local] ||= use_name
@@ -59,24 +59,20 @@ 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
- get_info()[:remote]
+ get_info[:remote]
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..b6767c41c5 100644
--- a/lib/chef/util/windows/net_user.rb
+++ b/lib/chef/util/windows/net_user.rb
@@ -16,19 +16,20 @@
# limitations under the License.
#
-require "chef/util/windows"
-require "chef/exceptions"
-require "chef/win32/net"
-require "chef/win32/security"
+require_relative "../windows"
+require_relative "../../exceptions"
+require_relative "../../win32/net"
+require_relative "../../win32/security"
-#wrapper around a subset of the NetUser* APIs.
-#nothing Chef specific, but not complete enough to be its own gem, so util for now.
+# wrapper around a subset of the NetUser* APIs.
+# nothing Chef specific, but not complete enough to be its own gem, so util for now.
class Chef::Util::Windows::NetUser < Chef::Util::Windows
private
NetUser = Chef::ReservedNames::Win32::NetUser
Security = Chef::ReservedNames::Win32::Security
+ Win32APIError = Chef::ReservedNames::Win32::API::Error
USER_INFO_3_TRANSFORM = {
name: :usri3_name,
@@ -60,7 +61,7 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
profile: :usri3_profile,
home_dir_drive: :usri3_home_dir_drive,
password_expired: :usri3_password_expired,
- }
+ }.freeze
def transform_usri3(args)
args.inject({}) do |memo, (k, v)|
@@ -78,11 +79,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
@@ -93,15 +92,21 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
LOGON32_PROVIDER_DEFAULT = Security::LOGON32_PROVIDER_DEFAULT
LOGON32_LOGON_NETWORK = Security::LOGON32_LOGON_NETWORK
- #XXX for an extra painful alternative, see: http://support.microsoft.com/kb/180548
+ # 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
+ token = Security.logon_user(@username, nil, passwd,
+ LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT)
+ true
+ rescue Chef::Exceptions::Win32APIError => e
+ Chef::Log.trace(e)
+ # we're only interested in the incorrect password failures
+ if /System Error Code: 1326/.match?(e.to_s)
return false
end
+
+ # all other exceptions will assume we cannot logon for a different reason
+ Chef::Log.trace("Unable to login with the specified credentials. Assuming the credentials are valid.")
+ true
end
def get_info
@@ -116,14 +121,14 @@ 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
def user_modify
user = get_info
- user[:last_logon] = user[:units_per_week] = 0 #ignored as per USER_INFO_3 doc
- user[:logon_hours] = nil #PBYTE field; \0 == no changes
+ user[:last_logon] = user[:units_per_week] = 0 # ignored as per USER_INFO_3 doc
+ user[:logon_hours] = nil # PBYTE field; \0 == no changes
yield(user)
set_info(user)
end
@@ -137,19 +142,17 @@ 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
user_modify do |user|
user[:flags] |= NetUser::UF_ACCOUNTDISABLE
- #This does not set the password to nil. It (for some reason) means to ignore updating the field.
- #See similar behavior for the logon_hours field documented at
- #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
+ # This does not set the password to nil. It (for some reason) means to ignore updating the field.
+ # See similar behavior for the logon_hours field documented at
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
user[:password] = nil
end
end
@@ -157,14 +160,14 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
def enable_account
user_modify do |user|
user[:flags] &= ~NetUser::UF_ACCOUNTDISABLE
- #This does not set the password to nil. It (for some reason) means to ignore updating the field.
- #See similar behavior for the logon_hours field documented at
- #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
+ # This does not set the password to nil. It (for some reason) means to ignore updating the field.
+ # See similar behavior for the logon_hours field documented at
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
user[:password] = nil
end
end
def check_enabled
- (get_info()[:flags] & NetUser::UF_ACCOUNTDISABLE) != 0
+ (get_info[:flags] & NetUser::UF_ACCOUNTDISABLE) != 0
end
end
diff --git a/lib/chef/util/windows/volume.rb b/lib/chef/util/windows/volume.rb
index a18644cece..e197604c34 100644
--- a/lib/chef/util/windows/volume.rb
+++ b/lib/chef/util/windows/volume.rb
@@ -16,44 +16,34 @@
# limitations under the License.
#
-#simple wrapper around Volume APIs. might be possible with WMI, but possibly more complex.
+# simple wrapper around Volume APIs. might be possible with WMI, but possibly more complex.
-require "chef/win32/api/file"
-require "chef/util/windows"
+require_relative "../../win32/api/file"
+require_relative "../windows"
class Chef::Util::Windows::Volume < Chef::Util::Windows
attr_reader :mount_point
def initialize(name)
- name += "\\" unless name =~ /\\$/ #trailing slash required
+ name += "\\" unless /\\$/.match?(name) # trailing slash required
@mount_point = name
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 947cd9c0b2..d8b7a74674 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -1,4 +1,4 @@
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,15 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# NOTE: This file is generated by running `rake version` in the top level of
# this repo. Do not edit this manually. Edit the VERSION file and run the rake
# task instead.
-#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+require_relative "version_string"
class Chef
- CHEF_ROOT = File.expand_path("../..", __FILE__)
- VERSION = "12.14.75"
+ CHEF_ROOT = File.expand_path("..", __dir__)
+ VERSION = Chef::VersionString.new("17.0.32")
end
#
diff --git a/lib/chef/version/platform.rb b/lib/chef/version/platform.rb
index 07b1a17b11..83e2a4570a 100644
--- a/lib/chef/version/platform.rb
+++ b/lib/chef/version/platform.rb
@@ -14,8 +14,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require "chef/version_class"
+require_relative "../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 f26368902d..4f3102281f 100644
--- a/lib/chef/version_class.rb
+++ b/lib/chef/version_class.rb
@@ -1,6 +1,6 @@
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,8 +33,8 @@ class Chef
end
def <=>(other)
- [:major, :minor, :patch].each do |method|
- version = self.send(method)
+ %i{major minor patch}.each do |method|
+ version = send(method)
begin
ans = (version <=> other.send(method))
rescue NoMethodError # if the other thing isn't a version object, return nil
diff --git a/lib/chef/version_constraint.rb b/lib/chef/version_constraint.rb
index f10325f946..0abbbb49b5 100644
--- a/lib/chef/version_constraint.rb
+++ b/lib/chef/version_constraint.rb
@@ -1,6 +1,6 @@
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +14,14 @@
# WITHOUT WARRANTIES 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"
+require_relative "version_class"
class Chef
class VersionConstraint
- DEFAULT_CONSTRAINT = ">= 0.0.0"
- STANDARD_OPS = %w{< > <= >=}
- OPS = %w{< > = <= >= ~>}
- PATTERN = /^(#{OPS.join('|')}) *([0-9].*)$/
+ DEFAULT_CONSTRAINT = ">= 0.0.0".freeze
+ STANDARD_OPS = %w{< > <= >=}.freeze
+ OPS = %w{< > = <= >= ~>}.freeze
+ PATTERN = /^(#{OPS.join('|')}) *([0-9].*)$/.freeze
VERSION_CLASS = Chef::Version
attr_reader :op, :version
@@ -90,7 +90,7 @@ class Chef
parse(constraint_spec.first)
else
msg = "only one version constraint operation is supported, but you gave #{constraint_spec.size} "
- msg << "['#{constraint_spec.join(', ')}']"
+ msg << "['#{constraint_spec.join(", ")}']"
raise Chef::Exceptions::InvalidVersionConstraint, msg
end
end
diff --git a/lib/chef/version_constraint/platform.rb b/lib/chef/version_constraint/platform.rb
index 29f4678bb5..0c04d61c8f 100644
--- a/lib/chef/version_constraint/platform.rb
+++ b/lib/chef/version_constraint/platform.rb
@@ -13,9 +13,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/version_constraint"
-require "chef/version/platform"
+require_relative "../version_constraint"
+require_relative "../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..8da5df570a
--- /dev/null
+++ b/lib/chef/version_string.rb
@@ -0,0 +1,20 @@
+# 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 "chef-utils/version_string"
+
+class Chef
+ VersionString = ChefUtils::VersionString
+end
diff --git a/lib/chef/whitelist.rb b/lib/chef/whitelist.rb
deleted file mode 100644
index 58d0bd70c6..0000000000
--- a/lib/chef/whitelist.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-
-require "chef/exceptions"
-
-class Chef
- class Whitelist
-
- # filter takes two arguments - the data you want to filter, and a whitelisted array
- # of keys you want included. 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.
- #
- # Whitelist.filter(
- # { "filesystem" => {
- # "/dev/disk" => {
- # "size" => "10mb"
- # },
- # "map - autohome" => {
- # "size" => "10mb"
- # }
- # },
- # "network" => {
- # "interfaces" => {
- # "eth0" => {...},
- # "eth1" => {...}
- # }
- # }
- # },
- # ["network/interfaces/eth0", ["filesystem", "/dev/disk"]])
- # will capture the eth0 and /dev/disk subtrees.
- def self.filter(data, whitelist = nil)
- return data if whitelist.nil?
-
- new_data = {}
- whitelist.each do |item|
- add_data(data, new_data, item)
- end
- new_data
- end
-
- # Walk the data has according to the keys provided by the whitelisted item
- # and add the data to the whitelisting result.
- def self.add_data(data, new_data, item)
- parts = to_array(item)
-
- all_data = data
- filtered_data = new_data
- parts[0..-2].each do |part|
- unless all_data[part]
- Chef::Log.warn("Could not find whitelist attribute #{item}.")
- return nil
- end
-
- filtered_data[part] ||= {}
- filtered_data = filtered_data[part]
- all_data = all_data[part]
- end
-
- # Note: You can't do all_data[parts[-1]] here because the value
- # may be false-y
- unless all_data.key?(parts[-1])
- Chef::Log.warn("Could not find whitelist attribute #{item}.")
- return nil
- end
-
- filtered_data[parts[-1]] = all_data[parts[-1]]
- new_data
- end
-
- private_class_method :add_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/win32/api.rb b/lib/chef/win32/api.rb
index 64db9d2b63..957823220d 100644
--- a/lib/chef/win32/api.rb
+++ b/lib/chef/win32/api.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,9 @@
# limitations under the License.
#
-require "ffi"
-require "chef/reserved_names"
-require "chef/exceptions"
+require "ffi" unless defined?(FFI)
+require_relative "../reserved_names"
+require_relative "../exceptions"
class Chef
module ReservedNames::Win32
@@ -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
@@ -53,7 +51,7 @@ class Chef
host.typedef :bool, :BOOL
host.typedef :bool, :BOOLEAN
host.typedef :uchar, :BYTE # Byte (8 bits). Declared as unsigned char
- #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
+ # CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
host.typedef :char, :CHAR # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
host.typedef :uint32, :COLORREF # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info.
host.typedef :uint32, :DWORD # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
@@ -80,7 +78,7 @@ class Chef
host.typedef :ulong, :HDESK # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx
host.typedef :ulong, :HDROP # (L) Handle to an internal drop structure.
host.typedef :ulong, :HDWP # (L) Handle to a deferred window position structure.
- host.typedef :ulong, :HENHMETAFILE #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
+ host.typedef :ulong, :HENHMETAFILE # (L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
host.typedef :uint, :HFILE # (I) Special file handle to a file opened by OpenFile, not CreateFile.
# WinDef.h: #host.typedef int HFILE;
host.typedef :ulong, :HFONT # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx
@@ -96,7 +94,7 @@ class Chef
host.typedef :ulong, :HMENU # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx
host.typedef :ulong, :HMETAFILE # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
host.typedef :ulong, :HMODULE # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows.
- host.typedef :ulong, :HMONITOR # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) host.typedef HANDLE HMONITOR;
+ host.typedef :ulong, :HMONITOR # (L) Handle to a display monitor. WinDef.h: if(WINVER >= 0x0500) host.typedef HANDLE HMONITOR;
host.typedef :ulong, :HPALETTE # (L) Handle to a palette.
host.typedef :ulong, :HPEN # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx
host.typedef :long, :HRESULT # Return code used by COM interfaces. For more info, Structure of the COM Error Codes.
@@ -109,7 +107,7 @@ class Chef
host.typedef :int, :INT # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal.
host.typedef :int, :INT_PTR # Signed integer type for pointer precision. Use when casting a pointer to an integer
# to perform pointer arithmetic. BaseTsd.h:
- #if defined(_WIN64) host.typedef __int64 INT_PTR; #else host.typedef int INT_PTR;
+ # if defined(_WIN64) host.typedef __int64 INT_PTR; #else host.typedef int INT_PTR;
host.typedef :int32, :INT32 # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
host.typedef :int64, :INT64 # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
host.typedef :ushort, :LANGID # Language identifier. For more information, see Locales. WinNT.h: #host.typedef WORD LANGID;
@@ -117,14 +115,14 @@ class Chef
host.typedef :uint32, :LCID # Locale identifier. For more information, see Locales.
host.typedef :uint32, :LCTYPE # Locale information type. For a list, see Locale Information Constants.
host.typedef :uint32, :LGRPID # Language group identifier. For a list, see EnumLanguageGroupLocales.
- host.typedef :pointer, :LMSTR # Pointer to null termiated string of unicode characters
+ host.typedef :pointer, :LMSTR # Pointer to null terminated string of unicode characters
host.typedef :long, :LONG # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
host.typedef :int32, :LONG32 # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
host.typedef :int64, :LONG64 # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
host.typedef :int64, :LONGLONG # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
host.typedef :long, :LONG_PTR # Signed long type for pointer precision. Use when casting a pointer to a long to
# perform pointer arithmetic. BaseTsd.h:
- #if defined(_WIN64) host.typedef __int64 LONG_PTR; #else host.typedef long LONG_PTR;
+ # if defined(_WIN64) host.typedef __int64 LONG_PTR; #else host.typedef long LONG_PTR;
host.typedef :long, :LPARAM # Message parameter. WinDef.h as follows: #host.typedef LONG_PTR LPARAM;
host.typedef :pointer, :LPBOOL # Pointer to a BOOL. WinDef.h as follows: #host.typedef BOOL far *LPBOOL;
host.typedef :pointer, :LPBYTE # Pointer to a BYTE. WinDef.h as follows: #host.typedef BYTE far *LPBYTE;
@@ -162,7 +160,7 @@ class Chef
host.typedef :pointer, :PDWORD32 # Pointer to a DWORD32.
host.typedef :pointer, :PDWORD64 # Pointer to a DWORD64.
host.typedef :pointer, :PFLOAT # Pointer to a FLOAT.
- host.typedef :pointer, :PGENERICMAPPING #Pointer to GENERIC_MAPPING
+ host.typedef :pointer, :PGENERICMAPPING # Pointer to GENERIC_MAPPING
host.typedef :pointer, :PHALF_PTR # Pointer to a HALF_PTR.
host.typedef :pointer, :PHANDLE # Pointer to a HANDLE.
host.typedef :pointer, :PHKEY # Pointer to an HKEY.
@@ -237,7 +235,7 @@ class Chef
host.typedef :ulong_long, :USN # Update sequence number (USN).
host.typedef :ushort, :WCHAR # 16-bit Unicode character. For more information, see Character Sets Used By Fonts.
# In WinNT.h: host.typedef wchar_t WCHAR;
- #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
+ # WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
host.typedef :ushort, :WORD # 16-bit unsigned integer. The range is 0 through 65535 decimal.
host.typedef :uint, :WPARAM # Message parameter. WinDef.h as follows: host.typedef UINT_PTR WPARAM;
end
diff --git a/lib/chef/win32/api/command_line_helper.rb b/lib/chef/win32/api/command_line_helper.rb
new file mode 100644
index 0000000000..6ddc74ae0f
--- /dev/null
+++ b/lib/chef/win32/api/command_line_helper.rb
@@ -0,0 +1,89 @@
+#
+# Author:: Kapil Chouhan <kapil.chouhan@msystechnologies.com>
+# Copyright:: Copyright 2013-2020, 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 "../api"
+
+class Chef
+ module ReservedNames::Win32
+ module API
+ module CommandLineHelper
+ # extend Chef::ReservedNames::Win32
+ extend Chef::ReservedNames::Win32::API
+
+ ###############################################
+ # Win32 API Bindings
+ ###############################################
+
+ ffi_lib "Shell32"
+
+=begin
+LPWSTR * CommandLineToArgvW(
+ LPCWSTR lpCmdLine,
+ int *pNumArgs
+);
+=end
+
+ safe_attach_function :command_line_to_argv_w, :CommandLineToArgvW, %i{pointer pointer}, :pointer
+
+ ffi_lib "Kernel32"
+
+=begin
+LPSTR GetCommandLineA();
+=end
+
+ safe_attach_function :get_command_line, :GetCommandLineA, [], :pointer
+
+=begin
+HLOCAL LocalFree(
+ _Frees_ptr_opt_ HLOCAL hMem
+);
+=end
+
+ safe_attach_function :local_free, :LocalFree, [:pointer], :pointer
+
+ ###############################################
+ # Helpers
+ ###############################################
+
+ # It takes the supplied string and splits it into an array.
+ def command_line_to_argv_w_helper(args)
+ arguments_list = []
+ argv = args.to_wstring
+ result = get_command_line
+ argc = FFI::MemoryPointer.new(:int)
+
+ # Parses a Unicode command line string
+ # It is return an array of pointers to the command line arguments.
+ # Along with a count of such arguments
+ result = command_line_to_argv_w(argv, argc)
+ str_ptr = result.read_pointer
+ offset = 0
+ number_of_agrs = argc.read_int
+ number_of_agrs.times do
+ new_str_pointer = str_ptr.+(offset)
+ argument = new_str_pointer.read_wstring
+ arguments_list << argument
+ offset = offset + argument.length * 2 + 2
+ end
+ local_free(result)
+ arguments_list
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/win32/api/crypto.rb b/lib/chef/win32/api/crypto.rb
index 0abb908622..219ec163be 100644
--- a/lib/chef/win32/api/crypto.rb
+++ b/lib/chef/win32/api/crypto.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -35,8 +35,8 @@ class Chef
CRYPTPROTECT_AUDIT = 0x10
class CRYPT_INTEGER_BLOB < FFI::Struct
- layout :cbData, :DWORD, # Count, in bytes, of data
- :pbData, :pointer # Pointer to data buffer
+ layout :cbData, :DWORD, # Count, in bytes, of data
+ :pbData, :pointer # Pointer to data buffer
def initialize(str = nil)
super(nil)
if str
@@ -47,15 +47,15 @@ class Chef
end
- safe_attach_function :CryptProtectData, [
- :PDATA_BLOB,
- :LPCWSTR,
- :PDATA_BLOB,
- :pointer,
- :PCRYPTPROTECT_PROMPTSTRUCT,
- :DWORD,
- :PDATA_BLOB,
- ], :BOOL
+ safe_attach_function :CryptProtectData, %i{
+ PDATA_BLOB
+ LPCWSTR
+ PDATA_BLOB
+ pointer
+ PCRYPTPROTECT_PROMPTSTRUCT
+ DWORD
+ PDATA_BLOB
+ }, :BOOL
end
end
diff --git a/lib/chef/win32/api/error.rb b/lib/chef/win32/api/error.rb
index 12ccdb5ee9..0e1b943724 100644
--- a/lib/chef/win32/api/error.rb
+++ b/lib/chef/win32/api/error.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -64,7 +64,7 @@ class Chef
ERROR_SHARING_VIOLATION = 32
ERROR_LOCK_VIOLATION = 33
ERROR_WRONG_DISK = 34
- ERROR_FCB_UNAVAILABLE = 35 # gets returned for some unsuccessful DeviceIoControl calls
+ ERROR_FCB_UNAVAILABLE = 35 # gets returned for some unsuccessful DeviceIoControl calls
ERROR_SHARING_BUFFER_EXCEEDED = 36
ERROR_HANDLE_EOF = 38
ERROR_HANDLE_DISK_FULL = 39
@@ -90,6 +90,7 @@ class Chef
ERROR_TOO_MANY_NAMES = 68
ERROR_TOO_MANY_SESS = 69
ERROR_SHARING_PAUSED = 70
+ # cspell:disable-next-line
ERROR_REQ_NOT_ACCEP = 71
ERROR_REDIR_PAUSED = 72
@@ -194,12 +195,12 @@ class Chef
ERROR_INVALID_EXE_SIGNATURE = 191
ERROR_EXE_MARKED_INVALID = 192
ERROR_BAD_EXE_FORMAT = 193
- ERROR_ITERATED_DATA_EXCEEDS_64k = 194 # rubocop:disable Style/ConstantName
+ 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 # rubocop:disable Style/ConstantName
+ 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
@@ -211,9 +212,9 @@ class Chef
ERROR_META_EXPANSION_TOO_LONG = 208 # if "*a" > 8.3
ERROR_INVALID_SIGNAL_NUMBER = 209
ERROR_THREAD_1_INACTIVE = 210
- ERROR_INFO_NOT_AVAIL = 211 #@@ PTM 5550
+ ERROR_INFO_NOT_AVAIL = 211 # @@ PTM 5550
ERROR_LOCKED = 212
- ERROR_BAD_DYNALINK = 213 #@@ PTM 5760
+ ERROR_BAD_DYNALINK = 213 # @@ PTM 5760
ERROR_TOO_MANY_MODULES = 214
ERROR_NESTING_NOT_ALLOWED = 215
ERROR_EXE_MACHINE_TYPE_MISMATCH = 216
@@ -876,6 +877,7 @@ class Chef
# Flags for LoadLibraryEx
+ # cspell:disable-next-line
DONT_RESOLVE_DLL_REFERENCES = 0x00000001
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010
LOAD_LIBRARY_AS_DATAFILE = 0x00000002
@@ -905,8 +907,8 @@ DWORD WINAPI FormatMessage(
__in_opt va_list *Arguments
);
=end
- safe_attach_function :FormatMessageA, [:DWORD, :HANDLE, :DWORD, :DWORD, :LPTSTR, :DWORD, :varargs], :DWORD
- safe_attach_function :FormatMessageW, [:DWORD, :HANDLE, :DWORD, :DWORD, :LPWSTR, :DWORD, :varargs], :DWORD
+ safe_attach_function :FormatMessageA, %i{DWORD HANDLE DWORD DWORD LPTSTR DWORD varargs}, :DWORD
+ safe_attach_function :FormatMessageW, %i{DWORD HANDLE DWORD DWORD LPWSTR DWORD varargs}, :DWORD
=begin
DWORD WINAPI GetLastError(void);
@@ -918,7 +920,7 @@ void WINAPI SetLastError(
);
=end
safe_attach_function :SetLastError, [:DWORD], :void
- safe_attach_function :SetLastErrorEx, [:DWORD, :DWORD], :void
+ safe_attach_function :SetLastErrorEx, %i{DWORD DWORD}, :void
=begin
UINT WINAPI GetErrorMode(void);s
=end
@@ -938,7 +940,7 @@ HMODULE WINAPI LoadLibraryEx(
_In_ DWORD dwFlags
);
=end
- safe_attach_function :LoadLibraryExW, [:LPCTSTR, :HANDLE, :DWORD], :HANDLE
+ safe_attach_function :LoadLibraryExW, %i{LPCTSTR HANDLE DWORD}, :HANDLE
=begin
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152(v=vs.85).aspx
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index 7489c94fd9..c18bc08e8b 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Mark Mzyk (<mmzyk@ospcode.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,10 @@
# limitations under the License.
#
-require "chef/win32/api"
-require "chef/win32/api/security"
-require "chef/win32/api/system"
-require "chef/win32/unicode"
+require_relative "../api"
+require_relative "security"
+require_relative "system"
+require_relative "../unicode"
class Chef
module ReservedNames::Win32
@@ -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
@@ -188,7 +189,7 @@ class Chef
# see https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
class Translation < FFI::Struct
layout :w_lang, :WORD,
- :w_code_page, :WORD
+ :w_code_page, :WORD
end
=begin
@@ -199,7 +200,7 @@ typedef struct _FILETIME {
=end
class FILETIME < FFI::Struct
layout :dw_low_date_time, :DWORD,
- :dw_high_date_time, :DWORD
+ :dw_high_date_time, :DWORD
end
=begin
@@ -211,8 +212,8 @@ typedef struct _SECURITY_ATTRIBUTES {
=end
class SECURITY_ATTRIBUTES < FFI::Struct
layout :n_length, :DWORD,
- :lp_security_descriptor, :LPVOID,
- :b_inherit_handle, :DWORD
+ :lp_security_descriptor, :LPVOID,
+ :b_inherit_handle, :DWORD
end
=begin
@@ -231,15 +232,15 @@ typedef struct _WIN32_FIND_DATA {
=end
class WIN32_FIND_DATA < FFI::Struct
layout :dw_file_attributes, :DWORD,
- :ft_creation_time, FILETIME,
- :ft_last_access_time, FILETIME,
- :ft_last_write_time, FILETIME,
- :n_file_size_high, :DWORD,
- :n_file_size_low, :DWORD,
- :dw_reserved_0, :DWORD,
- :dw_reserved_1, :DWORD,
- :c_file_name, [:BYTE, MAX_PATH * 2],
- :c_alternate_file_name, [:BYTE, 14]
+ :ft_creation_time, FILETIME,
+ :ft_last_access_time, FILETIME,
+ :ft_last_write_time, FILETIME,
+ :n_file_size_high, :DWORD,
+ :n_file_size_low, :DWORD,
+ :dw_reserved_0, :DWORD,
+ :dw_reserved_1, :DWORD,
+ :c_file_name, [:BYTE, MAX_PATH * 2],
+ :c_alternate_file_name, [:BYTE, 14]
end
=begin
@@ -258,15 +259,15 @@ typedef struct _BY_HANDLE_FILE_INFORMATION {
=end
class BY_HANDLE_FILE_INFORMATION < FFI::Struct
layout :dw_file_attributes, :DWORD,
- :ft_creation_time, FILETIME,
- :ft_last_access_time, FILETIME,
- :ft_last_write_time, FILETIME,
- :dw_volume_serial_number, :DWORD,
- :n_file_size_high, :DWORD,
- :n_file_size_low, :DWORD,
- :n_number_of_links, :DWORD,
- :n_file_index_high, :DWORD,
- :n_file_index_low, :DWORD
+ :ft_creation_time, FILETIME,
+ :ft_last_access_time, FILETIME,
+ :ft_last_write_time, FILETIME,
+ :dw_volume_serial_number, :DWORD,
+ :n_file_size_high, :DWORD,
+ :n_file_size_low, :DWORD,
+ :n_number_of_links, :DWORD,
+ :n_file_index_high, :DWORD,
+ :n_file_index_low, :DWORD
end
=begin
@@ -315,6 +316,7 @@ typedef struct _REPARSE_DATA_BUFFER {
string_pointer.read_wstring(self[:PrintNameLength] / 2)
end
end
+
class REPARSE_DATA_BUFFER_MOUNT_POINT < FFI::Struct
layout :SubstituteNameOffset, :ushort,
:SubstituteNameLength, :ushort,
@@ -332,14 +334,17 @@ typedef struct _REPARSE_DATA_BUFFER {
string_pointer.read_wstring(self[:PrintNameLength] / 2)
end
end
+
class REPARSE_DATA_BUFFER_GENERIC < FFI::Struct
layout :DataBuffer, :uchar
end
+
class REPARSE_DATA_BUFFER_UNION < FFI::Union
layout :SymbolicLinkReparseBuffer, REPARSE_DATA_BUFFER_SYMBOLIC_LINK,
:MountPointReparseBuffer, REPARSE_DATA_BUFFER_MOUNT_POINT,
:GenericReparseBuffer, REPARSE_DATA_BUFFER_GENERIC
end
+
class REPARSE_DATA_BUFFER < FFI::Struct
layout :ReparseTag, :uint32,
:ReparseDataLength, :ushort,
@@ -368,7 +373,7 @@ HANDLE WINAPI CreateFile(
__in_opt HANDLE hTemplateFile
);
=end
- safe_attach_function :CreateFileW, [:LPCTSTR, :DWORD, :DWORD, :LPSECURITY_ATTRIBUTES, :DWORD, :DWORD, :pointer], :HANDLE
+ safe_attach_function :CreateFileW, %i{LPCTSTR DWORD DWORD LPSECURITY_ATTRIBUTES DWORD DWORD pointer}, :HANDLE
=begin
BOOL WINAPI FindClose(
@@ -392,7 +397,7 @@ DWORD WINAPI GetFinalPathNameByHandle(
__in DWORD dwFlags
);
=end
- safe_attach_function :GetFinalPathNameByHandleW, [:HANDLE, :LPTSTR, :DWORD, :DWORD], :DWORD
+ safe_attach_function :GetFinalPathNameByHandleW, %i{HANDLE LPTSTR DWORD DWORD}, :DWORD
=begin
BOOL WINAPI GetFileInformationByHandle(
@@ -400,7 +405,7 @@ BOOL WINAPI GetFileInformationByHandle(
__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
=end
- safe_attach_function :GetFileInformationByHandle, [:HANDLE, :LPBY_HANDLE_FILE_INFORMATION], :BOOL
+ safe_attach_function :GetFileInformationByHandle, %i{HANDLE LPBY_HANDLE_FILE_INFORMATION}, :BOOL
=begin
HANDLE WINAPI FindFirstFile(
@@ -408,7 +413,7 @@ HANDLE WINAPI FindFirstFile(
__out LPWIN32_FIND_DATA lpFindFileData
);
=end
- safe_attach_function :FindFirstFileW, [:LPCTSTR, :LPWIN32_FIND_DATA], :HANDLE
+ safe_attach_function :FindFirstFileW, %i{LPCTSTR LPWIN32_FIND_DATA}, :HANDLE
=begin
BOOL WINAPI CreateHardLink(
@@ -417,7 +422,7 @@ BOOL WINAPI CreateHardLink(
__reserved LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
=end
- safe_attach_function :CreateHardLinkW, [:LPCTSTR, :LPCTSTR, :LPSECURITY_ATTRIBUTES], :BOOLEAN
+ safe_attach_function :CreateHardLinkW, %i{LPCTSTR LPCTSTR LPSECURITY_ATTRIBUTES}, :BOOLEAN
=begin
BOOLEAN WINAPI CreateSymbolicLink(
@@ -426,7 +431,7 @@ BOOLEAN WINAPI CreateSymbolicLink(
__in DWORD dwFlags
);
=end
- safe_attach_function :CreateSymbolicLinkW, [:LPTSTR, :LPTSTR, :DWORD], :BOOLEAN
+ safe_attach_function :CreateSymbolicLinkW, %i{LPTSTR LPTSTR DWORD}, :BOOLEAN
=begin
DWORD WINAPI GetLongPathName(
@@ -435,7 +440,7 @@ DWORD WINAPI GetLongPathName(
__in DWORD cchBuffer
);
=end
- safe_attach_function :GetLongPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD
+ safe_attach_function :GetLongPathNameW, %i{LPCTSTR LPTSTR DWORD}, :DWORD
=begin
DWORD WINAPI GetShortPathName(
@@ -444,7 +449,7 @@ DWORD WINAPI GetShortPathName(
__in DWORD cchBuffer
);
=end
- safe_attach_function :GetShortPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD
+ safe_attach_function :GetShortPathNameW, %i{LPCTSTR LPTSTR DWORD}, :DWORD
=begin
BOOL WINAPI DeviceIoControl(
@@ -458,25 +463,25 @@ BOOL WINAPI DeviceIoControl(
__inout_opt LPOVERLAPPED lpOverlapped
);
=end
- safe_attach_function :DeviceIoControl, [:HANDLE, :DWORD, :LPVOID, :DWORD, :LPVOID, :DWORD, :LPDWORD, :pointer], :BOOL
+ safe_attach_function :DeviceIoControl, %i{HANDLE DWORD LPVOID DWORD LPVOID DWORD LPDWORD pointer}, :BOOL
-#BOOL WINAPI DeleteVolumeMountPoint(
- #_In_ LPCTSTR lpszVolumeMountPoint
-#);
+ # BOOL WINAPI DeleteVolumeMountPoint(
+ # _In_ LPCTSTR lpszVolumeMountPoint
+ # );
safe_attach_function :DeleteVolumeMountPointW, [:LPCTSTR], :BOOL
-#BOOL WINAPI SetVolumeMountPoint(
- #_In_ LPCTSTR lpszVolumeMountPoint,
- #_In_ LPCTSTR lpszVolumeName
-#);
- safe_attach_function :SetVolumeMountPointW, [:LPCTSTR, :LPCTSTR], :BOOL
+ # BOOL WINAPI SetVolumeMountPoint(
+ # _In_ LPCTSTR lpszVolumeMountPoint,
+ # _In_ LPCTSTR lpszVolumeName
+ # );
+ safe_attach_function :SetVolumeMountPointW, %i{LPCTSTR LPCTSTR}, :BOOL
-#BOOL WINAPI GetVolumeNameForVolumeMountPoint(
- #_In_ LPCTSTR lpszVolumeMountPoint,
- #_Out_ LPTSTR lpszVolumeName,
- #_In_ DWORD cchBufferLength
-#);
- safe_attach_function :GetVolumeNameForVolumeMountPointW, [:LPCTSTR, :LPTSTR, :DWORD], :BOOL
+ # BOOL WINAPI GetVolumeNameForVolumeMountPoint(
+ # _In_ LPCTSTR lpszVolumeMountPoint,
+ # _Out_ LPTSTR lpszVolumeName,
+ # _In_ DWORD cchBufferLength
+ # );
+ safe_attach_function :GetVolumeNameForVolumeMountPointW, %i{LPCTSTR LPTSTR DWORD}, :BOOL
=begin
BOOL WINAPI GetFileVersionInfo(
@@ -486,7 +491,7 @@ BOOL WINAPI GetFileVersionInfo(
_Out_ LPVOID lpData
);
=end
- safe_attach_function :GetFileVersionInfoW, [:LPCTSTR, :DWORD, :DWORD, :LPVOID], :BOOL
+ safe_attach_function :GetFileVersionInfoW, %i{LPCTSTR DWORD DWORD LPVOID}, :BOOL
=begin
DWORD WINAPI GetFileVersionInfoSize(
@@ -494,7 +499,7 @@ DWORD WINAPI GetFileVersionInfoSize(
_Out_opt_ LPDWORD lpdwHandle
);
=end
- safe_attach_function :GetFileVersionInfoSizeW, [:LPCTSTR, :LPDWORD], :DWORD
+ safe_attach_function :GetFileVersionInfoSizeW, %i{LPCTSTR LPDWORD}, :DWORD
=begin
BOOL WINAPI VerQueryValue(
@@ -504,7 +509,7 @@ BOOL WINAPI VerQueryValue(
_Out_ PUINT puLen
);
=end
- safe_attach_function :VerQueryValueW, [:LPCVOID, :LPCTSTR, :LPVOID, :PUINT], :BOOL
+ safe_attach_function :VerQueryValueW, %i{LPCVOID LPCTSTR LPVOID PUINT}, :BOOL
###############################################
# Helpers
@@ -512,7 +517,7 @@ BOOL WINAPI VerQueryValue(
# takes the given path pre-pends "\\?\" and
# UTF-16LE encodes it. Used to prepare paths
- # to be passed to the *W vesion of WinAPI File
+ # to be passed to the *W version of WinAPI File
# functions.
# This function is used by the "Link" resources where we need
# preserve relative paths because symbolic links can actually
@@ -537,24 +542,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
+ # Workaround for CHEF-4419:
+ # Make sure paths starting with "/" has a drive letter
+ # assigned from the current working directory.
+ # 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
end
# retrieves a file handle and passes it
@@ -562,34 +565,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..c9539d5c2d 100644
--- a/lib/chef/win32/api/installer.rb
+++ b/lib/chef/win32/api/installer.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/exceptions"
-require "chef/win32/api"
-require "chef/win32/error"
-require "pathname"
+require_relative "../../exceptions"
+require_relative "../api"
+require_relative "../error"
+require "pathname" unless defined?(Pathname)
class Chef
module ReservedNames::Win32
@@ -44,7 +44,7 @@ UINT MsiOpenPackage(
_Out_ MSIHANDLE *hProduct
);
=end
- safe_attach_function :msi_open_package, :MsiOpenPackageExA, [ :string, :int, :pointer ], :int
+ safe_attach_function :msi_open_package, :MsiOpenPackageExA, %i{string int pointer}, :int
=begin
UINT MsiGetProductProperty(
@@ -54,7 +54,7 @@ UINT MsiGetProductProperty(
_Inout_ DWORD *pcchValueBuf
);
=end
- safe_attach_function :msi_get_product_property, :MsiGetProductPropertyA, [ :pointer, :pointer, :pointer, :pointer ], :int
+ safe_attach_function :msi_get_product_property, :MsiGetProductPropertyA, %i{pointer pointer pointer pointer}, :int
=begin
UINT MsiGetProductInfo(
@@ -64,7 +64,7 @@ UINT MsiGetProductInfo(
_Inout_ DWORD *pcchValueBuf
);
=end
- safe_attach_function :msi_get_product_info, :MsiGetProductInfoA, [ :pointer, :pointer, :pointer, :pointer ], :int
+ safe_attach_function :msi_get_product_info, :MsiGetProductInfoA, %i{pointer pointer pointer pointer}, :int
=begin
UINT MsiCloseHandle(
@@ -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/memory.rb b/lib/chef/win32/api/memory.rb
index a00ac5fec8..aed27663fc 100644
--- a/lib/chef/win32/api/memory.rb
+++ b/lib/chef/win32/api/memory.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -56,7 +56,7 @@ HLOCAL WINAPI LocalAlloc(
__in SIZE_T uBytes
);
=end
- safe_attach_function :LocalAlloc, [ :UINT, :SIZE_T ], :pointer
+ safe_attach_function :LocalAlloc, %i{UINT SIZE_T}, :pointer
=begin
UINT WINAPI LocalFlags(
@@ -79,7 +79,7 @@ HLOCAL WINAPI LocalReAlloc(
__in UINT uFlags
);
=end
- safe_attach_function :LocalReAlloc, [ :pointer, :SIZE_T, :UINT ], :pointer
+ safe_attach_function :LocalReAlloc, %i{pointer SIZE_T UINT}, :pointer
=begin
UINT WINAPI LocalSize(
@@ -95,9 +95,9 @@ UINT WINAPI LocalSize(
ffi_lib FFI::Library::LIBC
safe_attach_function :malloc, [:size_t], :pointer
safe_attach_function :calloc, [:size_t], :pointer
- safe_attach_function :realloc, [:pointer, :size_t], :pointer
+ safe_attach_function :realloc, %i{pointer size_t}, :pointer
safe_attach_function :free, [:pointer], :void
- safe_attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
+ safe_attach_function :memcpy, %i{pointer pointer size_t}, :pointer
end
end
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb
index abf0dd83ec..374c2e231e 100644
--- a/lib/chef/win32/api/net.rb
+++ b/lib/chef/win32/api/net.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/win32/api"
-require "chef/win32/unicode"
+require_relative "../api"
+require_relative "../unicode"
class Chef
module ReservedNames::Win32
@@ -39,13 +39,14 @@ class Chef
UF_ACCOUNTDISABLE = 0x000002
UF_PASSWD_CANT_CHANGE = 0x000040
UF_NORMAL_ACCOUNT = 0x000200
+ # cspell:disable-next-line
UF_DONT_EXPIRE_PASSWD = 0x010000
USE_NOFORCE = 0
USE_FORCE = 1
- USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
+ USE_LOTS_OF_FORCE = 2 # every windows API should support this flag
- NERR_Success = 0 # rubocop:disable Style/ConstantName
+ NERR_Success = 0 # rubocop:disable Naming/ConstantName
ERROR_MORE_DATA = 234
ffi_lib "netapi32"
@@ -144,6 +145,11 @@ class Chef
layout :lgrpi0_name, :LPWSTR
end
+ class LOCALGROUP_INFO_1 < FFI::Struct
+ layout :lgrpi1_name, :LPWSTR,
+ :lgrpi1_comment, :LPWSTR
+ end
+
class USE_INFO_2 < FFI::Struct
include StructHelpers
@@ -158,36 +164,47 @@ class Chef
:ui2_domainname, :LMSTR
end
- #NET_API_STATUS NetLocalGroupAdd(
- #_In_ LPCWSTR servername,
- #_In_ DWORD level,
- #_In_ LPBYTE buf,
- #_Out_ LPDWORD parm_err
- #);
- safe_attach_function :NetLocalGroupAdd, [
- :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
- ], :DWORD
-
- #NET_API_STATUS NetLocalGroupDel(
- #_In_ LPCWSTR servername,
- #_In_ LPCWSTR groupname
- #);
- safe_attach_function :NetLocalGroupDel, [:LPCWSTR, :LPCWSTR], :DWORD
-
- #NET_API_STATUS NetLocalGroupGetMembers(
- #_In_ LPCWSTR servername,
- #_In_ LPCWSTR localgroupname,
- #_In_ DWORD level,
- #_Out_ LPBYTE *bufptr,
- #_In_ DWORD prefmaxlen,
- #_Out_ LPDWORD entriesread,
- #_Out_ LPDWORD totalentries,
- #_Inout_ PDWORD_PTR resumehandle
- #);
- safe_attach_function :NetLocalGroupGetMembers, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD,
- :LPDWORD, :LPDWORD, :PDWORD_PTR
- ], :DWORD
+ # NET_API_STATUS NetLocalGroupAdd(
+ # _In_ LPCWSTR servername,
+ # _In_ DWORD level,
+ # _In_ LPBYTE buf,
+ # _Out_ LPDWORD parm_err
+ # );
+ safe_attach_function :NetLocalGroupAdd, %i{
+ LPCWSTR DWORD LPBYTE LPDWORD
+ }, :DWORD
+
+ # NET_API_STATUS NetLocalGroupSetInfo(
+ # _In_ LPCWSTR servername,
+ # _In_ LPCWSTR groupname,
+ # _In_ DWORD level,
+ # _In_ LPBYTE buf,
+ # _Out_ LPDWORD parm_err
+ # );
+ safe_attach_function :NetLocalGroupSetInfo, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE LPDWORD
+ }, :DWORD
+
+ # NET_API_STATUS NetLocalGroupDel(
+ # _In_ LPCWSTR servername,
+ # _In_ LPCWSTR groupname
+ # );
+ safe_attach_function :NetLocalGroupDel, %i{LPCWSTR LPCWSTR}, :DWORD
+
+ # NET_API_STATUS NetLocalGroupGetMembers(
+ # _In_ LPCWSTR servername,
+ # _In_ LPCWSTR localgroupname,
+ # _In_ DWORD level,
+ # _Out_ LPBYTE *bufptr,
+ # _In_ DWORD prefmaxlen,
+ # _Out_ LPDWORD entriesread,
+ # _Out_ LPDWORD totalentries,
+ # _Inout_ PDWORD_PTR resumehandle
+ # );
+ safe_attach_function :NetLocalGroupGetMembers, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE DWORD
+ LPDWORD LPDWORD PDWORD_PTR
+ }, :DWORD
# NET_API_STATUS NetUserEnum(
# _In_ LPCWSTR servername,
@@ -199,113 +216,113 @@ class Chef
# _Out_ LPDWORD totalentries,
# _Inout_ LPDWORD resume_handle
# );
- safe_attach_function :NetUserEnum, [
- :LPCWSTR, :DWORD, :DWORD, :LPBYTE,
- :DWORD, :LPDWORD, :LPDWORD, :LPDWORD
- ], :DWORD
+ safe_attach_function :NetUserEnum, %i{
+ LPCWSTR DWORD DWORD LPBYTE
+ DWORD LPDWORD LPDWORD LPDWORD
+ }, :DWORD
# NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
# );
safe_attach_function :NetApiBufferFree, [:LPVOID], :DWORD
- #NET_API_STATUS NetUserAdd(
- #_In_ LMSTR servername,
- #_In_ DWORD level,
- #_In_ LPBYTE buf,
- #_Out_ LPDWORD parm_err
- #);
- safe_attach_function :NetUserAdd, [
- :LMSTR, :DWORD, :LPBYTE, :LPDWORD
- ], :DWORD
-
- #NET_API_STATUS NetLocalGroupAddMembers(
+ # NET_API_STATUS NetUserAdd(
+ # _In_ LMSTR servername,
+ # _In_ DWORD level,
+ # _In_ LPBYTE buf,
+ # _Out_ LPDWORD parm_err
+ # );
+ safe_attach_function :NetUserAdd, %i{
+ LMSTR DWORD LPBYTE LPDWORD
+ }, :DWORD
+
+ # NET_API_STATUS NetLocalGroupAddMembers(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR groupname,
# _In_ DWORD level,
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
- #);
- safe_attach_function :NetLocalGroupAddMembers, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
- ], :DWORD
+ # );
+ safe_attach_function :NetLocalGroupAddMembers, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE DWORD
+ }, :DWORD
- #NET_API_STATUS NetLocalGroupSetMembers(
+ # NET_API_STATUS NetLocalGroupSetMembers(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR groupname,
# _In_ DWORD level,
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
- #);
- safe_attach_function :NetLocalGroupSetMembers, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
- ], :DWORD
+ # );
+ safe_attach_function :NetLocalGroupSetMembers, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE DWORD
+ }, :DWORD
- #NET_API_STATUS NetLocalGroupDelMembers(
+ # NET_API_STATUS NetLocalGroupDelMembers(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR groupname,
# _In_ DWORD level,
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
- #);
- safe_attach_function :NetLocalGroupDelMembers, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
- ], :DWORD
+ # );
+ safe_attach_function :NetLocalGroupDelMembers, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE DWORD
+ }, :DWORD
- #NET_API_STATUS NetUserGetInfo(
+ # NET_API_STATUS NetUserGetInfo(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR username,
# _In_ DWORD level,
# _Out_ LPBYTE *bufptr
- #);
- safe_attach_function :NetUserGetInfo, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE
- ], :DWORD
+ # );
+ safe_attach_function :NetUserGetInfo, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE
+ }, :DWORD
- #NET_API_STATUS NetApiBufferFree(
+ # NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
- #);
+ # );
safe_attach_function :NetApiBufferFree, [:LPVOID], :DWORD
- #NET_API_STATUS NetUserSetInfo(
+ # NET_API_STATUS NetUserSetInfo(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR username,
# _In_ DWORD level,
# _In_ LPBYTE buf,
# _Out_ LPDWORD parm_err
- #);
- safe_attach_function :NetUserSetInfo, [
- :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
- ], :DWORD
+ # );
+ safe_attach_function :NetUserSetInfo, %i{
+ LPCWSTR LPCWSTR DWORD LPBYTE LPDWORD
+ }, :DWORD
- #NET_API_STATUS NetUserDel(
+ # NET_API_STATUS NetUserDel(
# _In_ LPCWSTR servername,
# _In_ LPCWSTR username
- #);
- safe_attach_function :NetUserDel, [:LPCWSTR, :LPCWSTR], :DWORD
-
- #NET_API_STATUS NetUseDel(
- #_In_ LMSTR UncServerName,
- #_In_ LMSTR UseName,
- #_In_ DWORD ForceCond
- #);
- safe_attach_function :NetUseDel, [:LMSTR, :LMSTR, :DWORD], :DWORD
-
- #NET_API_STATUS NetUseGetInfo(
- #_In_ LMSTR UncServerName,
- #_In_ LMSTR UseName,
- #_In_ DWORD Level,
- #_Out_ LPBYTE *BufPtr
- #);
- safe_attach_function :NetUseGetInfo, [:LMSTR, :LMSTR, :DWORD, :pointer], :DWORD
-
- #NET_API_STATUS NetUseAdd(
- #_In_ LMSTR UncServerName,
- #_In_ DWORD Level,
- #_In_ LPBYTE Buf,
- #_Out_ LPDWORD ParmError
- #);
- safe_attach_function :NetUseAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
+ # );
+ safe_attach_function :NetUserDel, %i{LPCWSTR LPCWSTR}, :DWORD
+
+ # NET_API_STATUS NetUseDel(
+ # _In_ LMSTR UncServerName,
+ # _In_ LMSTR UseName,
+ # _In_ DWORD ForceCond
+ # );
+ safe_attach_function :NetUseDel, %i{LMSTR LMSTR DWORD}, :DWORD
+
+ # NET_API_STATUS NetUseGetInfo(
+ # _In_ LMSTR UncServerName,
+ # _In_ LMSTR UseName,
+ # _In_ DWORD Level,
+ # _Out_ LPBYTE *BufPtr
+ # );
+ safe_attach_function :NetUseGetInfo, %i{LMSTR LMSTR DWORD pointer}, :DWORD
+
+ # NET_API_STATUS NetUseAdd(
+ # _In_ LMSTR UncServerName,
+ # _In_ DWORD Level,
+ # _In_ LPBYTE Buf,
+ # _Out_ LPDWORD ParmError
+ # );
+ safe_attach_function :NetUseAdd, %i{LMSTR DWORD LPBYTE LPDWORD}, :DWORD
end
end
end
diff --git a/lib/chef/win32/api/process.rb b/lib/chef/win32/api/process.rb
index 3568b7e76c..be2c765910 100644
--- a/lib/chef/win32/api/process.rb
+++ b/lib/chef/win32/api/process.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -31,10 +31,10 @@ class Chef
ffi_lib "kernel32"
safe_attach_function :GetCurrentProcess, [], :HANDLE
- safe_attach_function :GetProcessHandleCount, [ :HANDLE, :LPDWORD ], :BOOL
+ safe_attach_function :GetProcessHandleCount, %i{HANDLE LPDWORD}, :BOOL
safe_attach_function :GetProcessId, [ :HANDLE ], :DWORD
safe_attach_function :CloseHandle, [ :HANDLE ], :BOOL
- safe_attach_function :IsWow64Process, [ :HANDLE, :PBOOL ], :BOOL
+ safe_attach_function :IsWow64Process, %i{HANDLE PBOOL}, :BOOL
end
end
diff --git a/lib/chef/win32/api/psapi.rb b/lib/chef/win32/api/psapi.rb
index 9deb68d92e..dff90258d4 100644
--- a/lib/chef/win32/api/psapi.rb
+++ b/lib/chef/win32/api/psapi.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -43,7 +43,7 @@ class Chef
ffi_lib "psapi"
- safe_attach_function :GetProcessMemoryInfo, [ :HANDLE, :pointer, :DWORD ], :BOOL
+ safe_attach_function :GetProcessMemoryInfo, %i{HANDLE pointer DWORD}, :BOOL
end
end
diff --git a/lib/chef/win32/api/registry.rb b/lib/chef/win32/api/registry.rb
index dec25118a3..ac339a3f96 100644
--- a/lib/chef/win32/api/registry.rb
+++ b/lib/chef/win32/api/registry.rb
@@ -1,6 +1,6 @@
#
# Author:: Salim Alam (<salam@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -36,14 +36,14 @@ class Chef
# _In_ REGSAM samDesired,
# _Reserved_ DWORD Reserved
# );
- safe_attach_function :RegDeleteKeyExW, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
- safe_attach_function :RegDeleteKeyExA, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
+ safe_attach_function :RegDeleteKeyExW, %i{HKEY LPCTSTR LONG DWORD}, :LONG
+ safe_attach_function :RegDeleteKeyExA, %i{HKEY LPCTSTR LONG DWORD}, :LONG
# LONG WINAPI RegDeleteValue(
# _In_ HKEY hKey,
# _In_opt_ LPCTSTR lpValueName
# );
- safe_attach_function :RegDeleteValueW, [ :HKEY, :LPCTSTR ], :LONG
+ safe_attach_function :RegDeleteValueW, %i{HKEY LPCTSTR}, :LONG
end
end
diff --git a/lib/chef/win32/api/security.rb b/lib/chef/win32/api/security.rb
index 64df077686..e50a870613 100644
--- a/lib/chef/win32/api/security.rb
+++ b/lib/chef/win32/api/security.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -140,6 +140,8 @@ class Chef
FILE_READ_EA | SYNCHRONIZE
FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE
FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE
+ WRITE = FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA
+ SUBFOLDERS_AND_FILES_ONLY = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
# Access Token Rights (for OpenProcessToken)
# Access Rights for Access-Token Objects (used in OpenProcessToken)
TOKEN_ASSIGN_PRIMARY = 0x0001
@@ -182,18 +184,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
@@ -214,21 +216,21 @@ class Chef
# Win32 API Bindings
###############################################
- SE_OBJECT_TYPE = enum :SE_OBJECT_TYPE, [
- :SE_UNKNOWN_OBJECT_TYPE,
- :SE_FILE_OBJECT,
- :SE_SERVICE,
- :SE_PRINTER,
- :SE_REGISTRY_KEY,
- :SE_LMSHARE,
- :SE_KERNEL_OBJECT,
- :SE_WINDOW_OBJECT,
- :SE_DS_OBJECT,
- :SE_DS_OBJECT_ALL,
- :SE_PROVIDER_DEFINED_OBJECT,
- :SE_WMIGUID_OBJECT,
- :SE_REGISTRY_WOW64_32KEY,
- ]
+ SE_OBJECT_TYPE = enum :SE_OBJECT_TYPE, %i{
+ SE_UNKNOWN_OBJECT_TYPE
+ SE_FILE_OBJECT
+ SE_SERVICE
+ SE_PRINTER
+ SE_REGISTRY_KEY
+ SE_LMSHARE
+ SE_KERNEL_OBJECT
+ SE_WINDOW_OBJECT
+ SE_DS_OBJECT
+ SE_DS_OBJECT_ALL
+ SE_PROVIDER_DEFINED_OBJECT
+ SE_WMIGUID_OBJECT
+ SE_REGISTRY_WOW64_32KEY
+ }
SID_NAME_USE = enum :SID_NAME_USE, [
:SidTypeUser, 1,
@@ -296,13 +298,24 @@ class Chef
end
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572%28v=vs.85%29.aspx
- SECURITY_IMPERSONATION_LEVEL = enum :SECURITY_IMPERSONATION_LEVEL, [
- :SecurityAnonymous,
- :SecurityIdentification,
- :SecurityImpersonation,
- :SecurityDelegation,
+ SECURITY_IMPERSONATION_LEVEL = enum :SECURITY_IMPERSONATION_LEVEL, %i{
+ SecurityAnonymous
+ SecurityIdentification
+ SecurityImpersonation
+ 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
@@ -313,24 +326,24 @@ class Chef
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa374931(v=VS.85).aspx
class ACLStruct < FFI::Struct
layout :AclRevision, :uchar,
- :Sbzl, :uchar,
- :AclSize, :ushort,
- :AceCount, :ushort,
- :Sbz2, :ushort
+ :Sbzl, :uchar,
+ :AclSize, :ushort,
+ :AceCount, :ushort,
+ :Sbz2, :ushort
end
class ACE_HEADER < FFI::Struct
layout :AceType, :uchar,
- :AceFlags, :uchar,
- :AceSize, :ushort
+ :AceFlags, :uchar,
+ :AceSize, :ushort
end
class ACE_WITH_MASK_AND_SID < FFI::Struct
layout :AceType, :uchar,
- :AceFlags, :uchar,
- :AceSize, :ushort,
- :Mask, :uint32,
- :SidStart, :uint32
+ :AceFlags, :uchar,
+ :AceSize, :ushort,
+ :Mask, :uint32,
+ :SidStart, :uint32
# The AceTypes this structure supports
def self.supports?(ace_type)
@@ -345,12 +358,12 @@ class Chef
class LUID < FFI::Struct
layout :LowPart, :DWORD,
- :HighPart, :LONG
+ :HighPart, :LONG
end
class LUID_AND_ATTRIBUTES < FFI::Struct
layout :Luid, LUID,
- :Attributes, :DWORD
+ :Attributes, :DWORD
end
class GENERIC_MAPPING < FFI::Struct
@@ -362,13 +375,13 @@ class Chef
class PRIVILEGE_SET < FFI::Struct
layout :PrivilegeCount, :DWORD,
- :Control, :DWORD,
- :Privilege, [LUID_AND_ATTRIBUTES, 1]
+ :Control, :DWORD,
+ :Privilege, [LUID_AND_ATTRIBUTES, 1]
end
class TOKEN_PRIVILEGES < FFI::Struct
layout :PrivilegeCount, :DWORD,
- :Privileges, LUID_AND_ATTRIBUTES
+ :Privileges, LUID_AND_ATTRIBUTES
def self.size_with_privileges(num_privileges)
offset_of(:Privileges) + LUID_AND_ATTRIBUTES.size * num_privileges
@@ -386,73 +399,82 @@ class Chef
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms721829(v=vs.85).aspx
class LSA_OBJECT_ATTRIBUTES < FFI::Struct
layout :Length, :ULONG,
- :RootDirectory, :HANDLE,
- :ObjectName, :pointer,
- :Attributes, :ULONG,
- :SecurityDescriptor, :PVOID,
- :SecurityQualityOfService, :PVOID
+ :RootDirectory, :HANDLE,
+ :ObjectName, :pointer,
+ :Attributes, :ULONG,
+ :SecurityDescriptor, :PVOID,
+ :SecurityQualityOfService, :PVOID
end
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms721841(v=vs.85).aspx
class LSA_UNICODE_STRING < FFI::Struct
layout :Length, :USHORT,
- :MaximumLength, :USHORT,
- :Buffer, :PWSTR
+ :MaximumLength, :USHORT,
+ :Buffer, :PWSTR
+ end
+
+ # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-lsa_enumeration_information
+ class LSA_ENUMERATION_INFORMATION < FFI::Struct
+ layout :Sid, :PSID
end
ffi_lib "advapi32"
- safe_attach_function :AccessCheck, [:pointer, :HANDLE, :DWORD, :pointer, :pointer, :pointer, :pointer, :pointer], :BOOL
- safe_attach_function :AddAce, [ :pointer, :DWORD, :DWORD, :LPVOID, :DWORD ], :BOOL
- safe_attach_function :AddAccessAllowedAce, [ :pointer, :DWORD, :DWORD, :pointer ], :BOOL
- safe_attach_function :AddAccessAllowedAceEx, [ :pointer, :DWORD, :DWORD, :DWORD, :pointer ], :BOOL
- safe_attach_function :AddAccessDeniedAce, [ :pointer, :DWORD, :DWORD, :pointer ], :BOOL
- safe_attach_function :AddAccessDeniedAceEx, [ :pointer, :DWORD, :DWORD, :DWORD, :pointer ], :BOOL
- safe_attach_function :AdjustTokenPrivileges, [ :HANDLE, :BOOL, :pointer, :DWORD, :pointer, :PDWORD ], :BOOL
- safe_attach_function :ConvertSidToStringSidA, [ :pointer, :pointer ], :BOOL
- safe_attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :BOOL
- safe_attach_function :DeleteAce, [ :pointer, :DWORD ], :BOOL
- safe_attach_function :DuplicateToken, [:HANDLE, :SECURITY_IMPERSONATION_LEVEL, :PHANDLE], :BOOL
- safe_attach_function :EqualSid, [ :pointer, :pointer ], :BOOL
+ safe_attach_function :AccessCheck, %i{pointer HANDLE DWORD pointer pointer pointer pointer pointer}, :BOOL
+ safe_attach_function :AddAce, %i{pointer DWORD DWORD LPVOID DWORD}, :BOOL
+ safe_attach_function :AddAccessAllowedAce, %i{pointer DWORD DWORD pointer}, :BOOL
+ safe_attach_function :AddAccessAllowedAceEx, %i{pointer DWORD DWORD DWORD pointer}, :BOOL
+ safe_attach_function :AddAccessDeniedAce, %i{pointer DWORD DWORD pointer}, :BOOL
+ safe_attach_function :AddAccessDeniedAceEx, %i{pointer DWORD DWORD DWORD pointer}, :BOOL
+ safe_attach_function :AdjustTokenPrivileges, %i{HANDLE BOOL pointer DWORD pointer PDWORD}, :BOOL
+ safe_attach_function :ConvertSidToStringSidA, %i{pointer pointer}, :BOOL
+ safe_attach_function :ConvertStringSidToSidW, %i{pointer pointer}, :BOOL
+ safe_attach_function :DeleteAce, %i{pointer DWORD}, :BOOL
+ safe_attach_function :DuplicateToken, %i{HANDLE SECURITY_IMPERSONATION_LEVEL PHANDLE}, :BOOL
+ safe_attach_function :EqualSid, %i{pointer pointer}, :BOOL
safe_attach_function :FreeSid, [ :pointer ], :pointer
- safe_attach_function :GetAce, [ :pointer, :DWORD, :pointer ], :BOOL
- safe_attach_function :GetFileSecurityW, [:LPCWSTR, :DWORD, :pointer, :DWORD, :pointer], :BOOL
+ safe_attach_function :GetAce, %i{pointer DWORD pointer}, :BOOL
+ safe_attach_function :GetFileSecurityW, %i{LPCWSTR DWORD pointer DWORD pointer}, :BOOL
safe_attach_function :GetLengthSid, [ :pointer ], :DWORD
- safe_attach_function :GetNamedSecurityInfoW, [ :LPWSTR, :SE_OBJECT_TYPE, :DWORD, :pointer, :pointer, :pointer, :pointer, :pointer ], :DWORD
- safe_attach_function :GetSecurityDescriptorControl, [ :pointer, :PWORD, :LPDWORD], :BOOL
- safe_attach_function :GetSecurityDescriptorDacl, [ :pointer, :LPBOOL, :pointer, :LPBOOL ], :BOOL
- safe_attach_function :GetSecurityDescriptorGroup, [ :pointer, :pointer, :LPBOOL], :BOOL
- safe_attach_function :GetSecurityDescriptorOwner, [ :pointer, :pointer, :LPBOOL], :BOOL
- safe_attach_function :GetSecurityDescriptorSacl, [ :pointer, :LPBOOL, :pointer, :LPBOOL ], :BOOL
- safe_attach_function :InitializeAcl, [ :pointer, :DWORD, :DWORD ], :BOOL
- safe_attach_function :InitializeSecurityDescriptor, [ :pointer, :DWORD ], :BOOL
+ safe_attach_function :GetNamedSecurityInfoW, %i{LPWSTR SE_OBJECT_TYPE DWORD pointer pointer pointer pointer pointer}, :DWORD
+ safe_attach_function :GetSecurityDescriptorControl, %i{pointer PWORD LPDWORD}, :BOOL
+ safe_attach_function :GetSecurityDescriptorDacl, %i{pointer LPBOOL pointer LPBOOL}, :BOOL
+ safe_attach_function :GetSecurityDescriptorGroup, %i{pointer pointer LPBOOL}, :BOOL
+ safe_attach_function :GetSecurityDescriptorOwner, %i{pointer pointer LPBOOL}, :BOOL
+ safe_attach_function :GetSecurityDescriptorSacl, %i{pointer LPBOOL pointer LPBOOL}, :BOOL
+ safe_attach_function :InitializeAcl, %i{pointer DWORD DWORD}, :BOOL
+ safe_attach_function :InitializeSecurityDescriptor, %i{pointer DWORD}, :BOOL
safe_attach_function :IsValidAcl, [ :pointer ], :BOOL
safe_attach_function :IsValidSecurityDescriptor, [ :pointer ], :BOOL
safe_attach_function :IsValidSid, [ :pointer ], :BOOL
- safe_attach_function :LookupAccountNameW, [ :LPCWSTR, :LPCWSTR, :pointer, :LPDWORD, :LPWSTR, :LPDWORD, :pointer ], :BOOL
- safe_attach_function :LookupAccountSidW, [ :LPCWSTR, :pointer, :LPWSTR, :LPDWORD, :LPWSTR, :LPDWORD, :pointer ], :BOOL
- safe_attach_function :LookupPrivilegeNameW, [ :LPCWSTR, :PLUID, :LPWSTR, :LPDWORD ], :BOOL
- safe_attach_function :LookupPrivilegeDisplayNameW, [ :LPCWSTR, :LPCWSTR, :LPWSTR, :LPDWORD, :LPDWORD ], :BOOL
- safe_attach_function :LookupPrivilegeValueW, [ :LPCWSTR, :LPCWSTR, :PLUID ], :BOOL
- safe_attach_function :LsaAddAccountRights, [ :pointer, :pointer, :pointer, :ULONG ], :NTSTATUS
+ safe_attach_function :LookupAccountNameW, %i{LPCWSTR LPCWSTR pointer LPDWORD LPWSTR LPDWORD pointer}, :BOOL
+ safe_attach_function :LookupAccountSidW, %i{LPCWSTR pointer LPWSTR LPDWORD LPWSTR LPDWORD pointer}, :BOOL
+ safe_attach_function :LookupPrivilegeNameW, %i{LPCWSTR PLUID LPWSTR LPDWORD}, :BOOL
+ safe_attach_function :LookupPrivilegeDisplayNameW, %i{LPCWSTR LPCWSTR LPWSTR LPDWORD LPDWORD}, :BOOL
+ safe_attach_function :LookupPrivilegeValueW, %i{LPCWSTR LPCWSTR PLUID}, :BOOL
+ safe_attach_function :LsaAddAccountRights, %i{pointer pointer pointer ULONG}, :NTSTATUS
+ safe_attach_function :LsaEnumerateAccountsWithUserRight, %i{LSA_HANDLE PLSA_UNICODE_STRING PVOID PULONG}, :NTSTATUS
+ safe_attach_function :LsaRemoveAccountRights, %i{pointer pointer BOOL pointer ULONG}, :NTSTATUS
safe_attach_function :LsaClose, [ :LSA_HANDLE ], :NTSTATUS
- safe_attach_function :LsaEnumerateAccountRights, [ :LSA_HANDLE, :PSID, :PLSA_UNICODE_STRING, :PULONG ], :NTSTATUS
+ safe_attach_function :LsaEnumerateAccountRights, %i{LSA_HANDLE PSID PLSA_UNICODE_STRING PULONG}, :NTSTATUS
safe_attach_function :LsaFreeMemory, [ :PVOID ], :NTSTATUS
safe_attach_function :LsaNtStatusToWinError, [ :NTSTATUS ], :ULONG
- safe_attach_function :LsaOpenPolicy, [ :PLSA_UNICODE_STRING, :PLSA_OBJECT_ATTRIBUTES, :DWORD, :PLSA_HANDLE ], :NTSTATUS
- safe_attach_function :MakeAbsoluteSD, [ :pointer, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD], :BOOL
- safe_attach_function :MapGenericMask, [ :PDWORD, :PGENERICMAPPING ], :void
- safe_attach_function :OpenProcessToken, [ :HANDLE, :DWORD, :PHANDLE ], :BOOL
- safe_attach_function :QuerySecurityAccessMask, [ :DWORD, :LPDWORD ], :void
- safe_attach_function :SetFileSecurityW, [ :LPWSTR, :DWORD, :pointer ], :BOOL
- safe_attach_function :SetNamedSecurityInfoW, [ :LPWSTR, :SE_OBJECT_TYPE, :DWORD, :pointer, :pointer, :pointer, :pointer ], :DWORD
- safe_attach_function :SetSecurityAccessMask, [ :DWORD, :LPDWORD ], :void
- safe_attach_function :SetSecurityDescriptorDacl, [ :pointer, :BOOL, :pointer, :BOOL ], :BOOL
- safe_attach_function :SetSecurityDescriptorGroup, [ :pointer, :pointer, :BOOL ], :BOOL
- safe_attach_function :SetSecurityDescriptorOwner, [ :pointer, :pointer, :BOOL ], :BOOL
- 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 :LsaOpenPolicy, %i{PLSA_UNICODE_STRING PLSA_OBJECT_ATTRIBUTES DWORD PLSA_HANDLE}, :NTSTATUS
+ safe_attach_function :MakeAbsoluteSD, %i{pointer pointer LPDWORD pointer LPDWORD pointer LPDWORD pointer LPDWORD pointer LPDWORD}, :BOOL
+ safe_attach_function :MapGenericMask, %i{PDWORD PGENERICMAPPING}, :void
+ safe_attach_function :OpenProcessToken, %i{HANDLE DWORD PHANDLE}, :BOOL
+ safe_attach_function :QuerySecurityAccessMask, %i{DWORD LPDWORD}, :void
+ safe_attach_function :SetFileSecurityW, %i{LPWSTR DWORD pointer}, :BOOL
+ safe_attach_function :SetNamedSecurityInfoW, %i{LPWSTR SE_OBJECT_TYPE DWORD pointer pointer pointer pointer}, :DWORD
+ safe_attach_function :SetSecurityAccessMask, %i{DWORD LPDWORD}, :void
+ safe_attach_function :SetSecurityDescriptorDacl, %i{pointer BOOL pointer BOOL}, :BOOL
+ safe_attach_function :SetSecurityDescriptorGroup, %i{pointer pointer BOOL}, :BOOL
+ safe_attach_function :SetSecurityDescriptorOwner, %i{pointer pointer BOOL}, :BOOL
+ safe_attach_function :SetSecurityDescriptorSacl, %i{pointer BOOL pointer BOOL}, :BOOL
+ safe_attach_function :GetTokenInformation, %i{HANDLE TOKEN_INFORMATION_CLASS pointer DWORD PDWORD}, :BOOL
+ safe_attach_function :LogonUserW, %i{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/api/synchronization.rb b/lib/chef/win32/api/synchronization.rb
index 9b5d5c6ab9..cf9a101558 100644
--- a/lib/chef/win32/api/synchronization.rb
+++ b/lib/chef/win32/api/synchronization.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -56,8 +56,8 @@ HANDLE WINAPI CreateMutex(
_In_opt_ LPCTSTR lpName
);
=end
- safe_attach_function :CreateMutexW, [ :LPSECURITY_ATTRIBUTES, :BOOL, :LPCTSTR ], :HANDLE
- safe_attach_function :CreateMutexA, [ :LPSECURITY_ATTRIBUTES, :BOOL, :LPCTSTR ], :HANDLE
+ safe_attach_function :CreateMutexW, %i{LPSECURITY_ATTRIBUTES BOOL LPCTSTR}, :HANDLE
+ safe_attach_function :CreateMutexA, %i{LPSECURITY_ATTRIBUTES BOOL LPCTSTR}, :HANDLE
=begin
DWORD WINAPI WaitForSingleObject(
@@ -65,7 +65,7 @@ DWORD WINAPI WaitForSingleObject(
_In_ DWORD dwMilliseconds
);
=end
- safe_attach_function :WaitForSingleObject, [ :HANDLE, :DWORD ], :DWORD
+ safe_attach_function :WaitForSingleObject, %i{HANDLE DWORD}, :DWORD
=begin
BOOL WINAPI ReleaseMutex(
@@ -81,8 +81,8 @@ HANDLE WINAPI OpenMutex(
_In_ LPCTSTR lpName
);
=end
- safe_attach_function :OpenMutexW, [ :DWORD, :BOOL, :LPCTSTR ], :HANDLE
- safe_attach_function :OpenMutexA, [ :DWORD, :BOOL, :LPCTSTR ], :HANDLE
+ safe_attach_function :OpenMutexW, %i{DWORD BOOL LPCTSTR}, :HANDLE
+ safe_attach_function :OpenMutexA, %i{DWORD BOOL LPCTSTR}, :HANDLE
end
end
end
diff --git a/lib/chef/win32/api/system.rb b/lib/chef/win32/api/system.rb
index 732ed073e6..924c9c1e29 100644
--- a/lib/chef/win32/api/system.rb
+++ b/lib/chef/win32/api/system.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -177,7 +177,7 @@ BOOL WINAPI GetProductInfo(
__out PDWORD pdwReturnedProductType
);
=end
- safe_attach_function :GetProductInfo, [:DWORD, :DWORD, :DWORD, :DWORD, :PDWORD], :BOOL
+ safe_attach_function :GetProductInfo, %i{DWORD DWORD DWORD DWORD PDWORD}, :BOOL
=begin
int WINAPI GetSystemMetrics(
@@ -192,8 +192,8 @@ UINT WINAPI GetSystemWow64Directory(
_In_ UINT uSize
);
=end
- safe_attach_function :GetSystemWow64DirectoryW, [:LPTSTR, :UINT], :UINT
- safe_attach_function :GetSystemWow64DirectoryA, [:LPTSTR, :UINT], :UINT
+ safe_attach_function :GetSystemWow64DirectoryW, %i{LPTSTR UINT}, :UINT
+ safe_attach_function :GetSystemWow64DirectoryA, %i{LPTSTR UINT}, :UINT
=begin
BOOL WINAPI Wow64DisableWow64FsRedirection(
@@ -220,8 +220,8 @@ LRESULT WINAPI SendMessageTimeout(
_Out_opt_ PDWORD_PTR lpdwResult
);
=end
- safe_attach_function :SendMessageTimeoutW, [:HWND, :UINT, :WPARAM, :LPARAM, :UINT, :UINT, :PDWORD_PTR], :LRESULT
- safe_attach_function :SendMessageTimeoutA, [:HWND, :UINT, :WPARAM, :LPARAM, :UINT, :UINT, :PDWORD_PTR], :LRESULT
+ safe_attach_function :SendMessageTimeoutW, %i{HWND UINT WPARAM LPARAM UINT UINT PDWORD_PTR}, :LRESULT
+ safe_attach_function :SendMessageTimeoutA, %i{HWND UINT WPARAM LPARAM UINT UINT PDWORD_PTR}, :LRESULT
=begin
DWORD WINAPI ExpandEnvironmentStrings(
@@ -230,8 +230,8 @@ DWORD WINAPI ExpandEnvironmentStrings(
_In_ DWORD nSize
);
=end
- safe_attach_function :ExpandEnvironmentStringsW, [:pointer, :pointer, :DWORD], :DWORD
- safe_attach_function :ExpandEnvironmentStringsA, [:pointer, :pointer, :DWORD], :DWORD
+ safe_attach_function :ExpandEnvironmentStringsW, %i{pointer pointer DWORD}, :DWORD
+ safe_attach_function :ExpandEnvironmentStringsA, %i{pointer pointer DWORD}, :DWORD
end
end
end
diff --git a/lib/chef/win32/api/unicode.rb b/lib/chef/win32/api/unicode.rb
index 21ddde2865..92cb3a3d66 100644
--- a/lib/chef/win32/api/unicode.rb
+++ b/lib/chef/win32/api/unicode.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/api"
+require_relative "../api"
class Chef
module ReservedNames::Win32
@@ -101,7 +101,7 @@ BOOL IsTextUnicode(
__inout LPINT lpiResult
);
=end
- safe_attach_function :IsTextUnicode, [:pointer, :int, :LPINT], :BOOL
+ safe_attach_function :IsTextUnicode, %i{pointer int LPINT}, :BOOL
=begin
int MultiByteToWideChar(
@@ -113,7 +113,7 @@ int MultiByteToWideChar(
__in int cchWideChar
);
=end
- safe_attach_function :MultiByteToWideChar, [:UINT, :DWORD, :LPCSTR, :int, :LPWSTR, :int], :int
+ safe_attach_function :MultiByteToWideChar, %i{UINT DWORD LPCSTR int LPWSTR int}, :int
=begin
int WideCharToMultiByte(
@@ -127,7 +127,7 @@ int WideCharToMultiByte(
__out LPBOOL lpUsedDefaultChar
);
=end
- safe_attach_function :WideCharToMultiByte, [:UINT, :DWORD, :LPCWSTR, :int, :LPSTR, :int, :LPCSTR, :LPBOOL], :int
+ safe_attach_function :WideCharToMultiByte, %i{UINT DWORD LPCWSTR int LPSTR int LPCSTR LPBOOL}, :int
end
end
diff --git a/lib/chef/win32/crypto.rb b/lib/chef/win32/crypto.rb
index 9832f9e67e..5521f67aee 100644
--- a/lib/chef/win32/crypto.rb
+++ b/lib/chef/win32/crypto.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/win32/error"
-require "chef/win32/api/memory"
-require "chef/win32/api/crypto"
-require "chef/win32/unicode"
-require "digest"
+require_relative "error"
+require_relative "api/memory"
+require_relative "api/crypto"
+require_relative "unicode"
+require "digest" unless defined?(Digest)
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/error.rb b/lib/chef/win32/error.rb
index 83d4583f1d..c2dc7785ea 100644
--- a/lib/chef/win32/error.rb
+++ b/lib/chef/win32/error.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/win32/api/error"
-require "chef/win32/memory"
-require "chef/win32/unicode"
-require "chef/exceptions"
+require_relative "api/error"
+require_relative "memory"
+require_relative "unicode"
+require_relative "../exceptions"
class Chef
module ReservedNames::Win32
@@ -50,7 +50,7 @@ class Chef
# Extract the string
begin
- return buffer.read_pointer.read_wstring(num_chars)
+ buffer.read_pointer.read_wstring(num_chars)
ensure
Chef::ReservedNames::Win32::Memory.local_free(buffer.read_pointer)
end
diff --git a/lib/chef/win32/eventlog.rb b/lib/chef/win32/eventlog.rb
index eae0ae4abf..54e9fc3077 100644
--- a/lib/chef/win32/eventlog.rb
+++ b/lib/chef/win32/eventlog.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -16,16 +16,16 @@
# limitations under the License.
#
-if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
- if !defined? Chef::Win32EventLogLoaded
+if ChefUtils.windows?
+ unless defined? Chef::Win32EventLogLoaded
if defined? Windows::Constants
- [:INFINITE, :WAIT_FAILED, :FORMAT_MESSAGE_IGNORE_INSERTS, :ERROR_INSUFFICIENT_BUFFER].each do |c|
+ %i{INFINITE WAIT_FAILED FORMAT_MESSAGE_IGNORE_INSERTS ERROR_INSUFFICIENT_BUFFER}.each do |c|
# These are redefined in 'win32/eventlog'
Windows::Constants.send(:remove_const, c) if Windows::Constants.const_defined? c
end
end
require "win32/eventlog"
- Chef::Win32EventLogLoaded = true # rubocop:disable Style/ConstantName
+ Chef::Win32EventLogLoaded = true # rubocop:disable Naming/ConstantName
end
end
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index 1009f8c5a9..55fc2461e8 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Author:: Mark Mzyk (<mmzyk@ospcode.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Author:: Mark Mzyk (<mmzyk@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,12 @@
# limitations under the License.
#
-require "chef/mixin/wide_string"
-require "chef/win32/api/file"
-require "chef/win32/api/security"
-require "chef/win32/error"
-require "chef/win32/unicode"
+require_relative "../mixin/wide_string"
+require_relative "api/file"
+require_relative "api/security"
+require_relative "error"
+require_relative "unicode"
+require_relative "version"
class Chef
module ReservedNames::Win32
@@ -40,6 +41,7 @@ class Chef
#
def self.link(old_name, new_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)
@@ -60,6 +62,7 @@ class Chef
# 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 +78,7 @@ class Chef
def self.symlink?(file_name)
is_symlink = false
path = encode_path(file_name)
- if ::File.exists?(file_name) || ::File.symlink?(file_name)
+ if ::File.exist?(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
@@ -87,13 +90,22 @@ class Chef
is_symlink
end
+ def self.realpath(file_name)
+ if symlink?(file_name)
+ readlink(file_name)
+ else
+ file_name
+ end
+ end
+
# Returns the path of the of the symbolic link referred to by +file+.
#
# Requires Windows Vista or later. On older versions of Windows it
# will raise a NotImplementedError, as per MRI.
#
def self.readlink(link_name)
- raise Errno::ENOENT, link_name unless ::File.exists?(link_name) || ::File.symlink?(link_name)
+ raise Errno::ENOENT, link_name unless ::File.exist?(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
@@ -111,8 +123,8 @@ class Chef
# Return the link destination (strip off \??\ at the beginning, which is a local filesystem thing)
link_dest = reparse_buffer.reparse_buffer.substitute_name
- if link_dest =~ /^\\\?\?\\/
- link_dest = link_dest[4..-1]
+ if /^\\\?\?\\/.match?(link_dest)
+ link_dest = link_dest[4..]
end
link_dest
end
@@ -154,16 +166,6 @@ class Chef
VersionInfo.new(file_name)
end
- def self.verify_links_supported!
- begin
- CreateSymbolicLinkW(nil)
- rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
- raise e
- rescue Exception
- # things are ok.
- end
- end
-
def self.file_access_check(path, desired_access)
security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security(path)
token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE |
@@ -172,7 +174,8 @@ class Chef
Chef::ReservedNames::Win32::Security::STANDARD_RIGHTS_READ
token = Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
- token_rights)
+ token_rights
+ )
duplicate_token = token.duplicate_token(:SecurityImpersonation)
mapping = Chef::ReservedNames::Win32::Security::GENERIC_MAPPING.new
@@ -182,7 +185,7 @@ class Chef
mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS
Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token,
- desired_access, mapping)
+ desired_access, mapping)
end
def self.delete_volume_mount_point(mount_point)
@@ -214,5 +217,5 @@ class Chef
end
end
-require "chef/win32/file/info"
-require "chef/win32/file/version_info"
+require_relative "file/info"
+require_relative "file/version_info"
diff --git a/lib/chef/win32/file/info.rb b/lib/chef/win32/file/info.rb
index 55873f8a0b..46a0250c38 100644
--- a/lib/chef/win32/file/info.rb
+++ b/lib/chef/win32/file/info.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/file"
+require_relative "../file"
class Chef
module ReservedNames::Win32
@@ -34,6 +34,7 @@ class Chef
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
def initialize(file_name)
raise Errno::ENOENT, file_name unless ::File.exist?(file_name)
+
@file_info = retrieve_file_info(file_name)
end
diff --git a/lib/chef/win32/file/version_info.rb b/lib/chef/win32/file/version_info.rb
index fa04096cf1..d1b7c70543 100644
--- a/lib/chef/win32/file/version_info.rb
+++ b/lib/chef/win32/file/version_info.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
-require "chef/win32/file"
+require_relative "../file"
class Chef
module ReservedNames::Win32
@@ -28,31 +28,32 @@ class Chef
def initialize(file_name)
raise Errno::ENOENT, file_name unless ::File.exist?(file_name)
+
@file_version_info = retrieve_file_version_info(file_name)
end
# defining method for each predefined version resource string
# see https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
- [
- :Comments,
- :CompanyName,
- :FileDescription,
- :FileVersion,
- :InternalName,
- :LegalCopyright,
- :LegalTrademarks,
- :OriginalFilename,
- :ProductName,
- :ProductVersion,
- :PrivateBuild,
- :SpecialBuild,
- ].each do |method|
+ %i{
+ Comments
+ CompanyName
+ FileDescription
+ FileVersion
+ InternalName
+ LegalCopyright
+ LegalTrademarks
+ OriginalFilename
+ ProductName
+ ProductVersion
+ PrivateBuild
+ SpecialBuild
+ }.each do |method|
define_method method do
- begin
- get_version_info_string(method.to_s)
- rescue Chef::Exceptions::Win32APIError
- return nil
- end
+
+ get_version_info_string(method.to_s)
+ rescue Chef::Exceptions::Win32APIError
+ return nil
+
end
end
diff --git a/lib/chef/win32/handle.rb b/lib/chef/win32/handle.rb
index 3ebb6983c4..1b0257ed68 100644
--- a/lib/chef/win32/handle.rb
+++ b/lib/chef/win32/handle.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/win32/api/process"
-require "chef/win32/api/psapi"
-require "chef/win32/api/system"
-require "chef/win32/error"
+require_relative "api/process"
+require_relative "api/psapi"
+require_relative "api/system"
+require_relative "error"
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/memory.rb b/lib/chef/win32/memory.rb
index 49dcdfbd41..52dcb6cfb7 100644
--- a/lib/chef/win32/memory.rb
+++ b/lib/chef/win32/memory.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/win32/error"
-require "chef/win32/api/memory"
+require_relative "error"
+require_relative "api/memory"
class Chef
module ReservedNames::Win32
@@ -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
@@ -67,7 +67,7 @@ class Chef
# Free memory allocated using local_alloc
def self.local_free(pointer)
result = LocalFree(pointer)
- if !result.null?
+ unless result.null?
Chef::ReservedNames::Win32::Error.raise!
end
end
diff --git a/lib/chef/win32/mutex.rb b/lib/chef/win32/mutex.rb
index a14a160f56..85f4036c87 100644
--- a/lib/chef/win32/mutex.rb
+++ b/lib/chef/win32/mutex.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/win32/api/synchronization"
-require "chef/win32/unicode"
+require_relative "api/synchronization"
+require_relative "unicode"
class Chef
module ReservedNames::Win32
@@ -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.
@@ -68,7 +68,7 @@ class Chef
end
#####################################################
- # Releaes the mutex
+ # Releases the mutex
def release
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms685066(v=vs.85).aspx
# Note that release method needs to be called more than once
@@ -95,7 +95,7 @@ if other threads attempt to acquire the mutex.")
@handle = OpenMutexW(SYNCHRONIZE, true, name.to_wstring)
if @handle == 0
- # Mutext doesn't exist so create one.
+ # Mutex doesn't exist so create one.
# In the initial creation of the mutex initial_owner is set to
# false so that mutex will not be acquired until someone calls
# acquire.
diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb
index 09db2af89d..f985891c17 100644
--- a/lib/chef/win32/net.rb
+++ b/lib/chef/win32/net.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala(<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/api/net"
-require "chef/win32/error"
-require "chef/mixin/wide_string"
+require_relative "api/net"
+require_relative "error"
+require_relative "../mixin/wide_string"
class Chef
module ReservedNames::Win32
@@ -40,6 +40,7 @@ class Chef
usri3_priv: 0,
usri3_home_dir: nil,
usri3_comment: nil,
+ # cspell:disable-next-line
usri3_flags: UF_SCRIPT | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT,
usri3_script_path: nil,
usri3_auth_flags: 0,
@@ -180,6 +181,21 @@ class Chef
end
end
+ def self.net_local_group_set_info(server_name, group_name, comment)
+ server_name = wstring(server_name)
+ group_name = wstring(group_name)
+ comment = wstring(comment)
+
+ buf = LOCALGROUP_INFO_1.new
+ buf[:lgrpi1_name] = FFI::MemoryPointer.from_string(group_name)
+ buf[:lgrpi1_comment] = FFI::MemoryPointer.from_string(comment)
+
+ rc = NetLocalGroupSetInfo(server_name, group_name, 1, buf, nil)
+ if rc != NERR_Success
+ Chef::ReservedNames::Win32::Error.raise!(nil, rc)
+ end
+ end
+
def self.net_user_del(server_name, user_name)
server_name = wstring(server_name)
user_name = wstring(user_name)
@@ -209,7 +225,8 @@ class Chef
buf = FFI::MemoryPointer.new(LOCALGROUP_MEMBERS_INFO_3, members.size)
Array.new(members.size) do |i|
member_info = LOCALGROUP_MEMBERS_INFO_3.new(
- buf + i * LOCALGROUP_MEMBERS_INFO_3.size)
+ buf + i * LOCALGROUP_MEMBERS_INFO_3.size
+ )
member_info[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(wstring(members[i]))
member_info
end
@@ -221,7 +238,8 @@ class Chef
lgrmi3s = members_to_lgrmi3(members)
rc = NetLocalGroupAddMembers(
- server_name, group_name, 3, lgrmi3s[0], members.size)
+ server_name, group_name, 3, lgrmi3s[0], members.size
+ )
if rc != NERR_Success
Chef::ReservedNames::Win32::Error.raise!(nil, rc)
@@ -234,7 +252,8 @@ class Chef
lgrmi3s = members_to_lgrmi3(members)
rc = NetLocalGroupSetMembers(
- server_name, group_name, 3, lgrmi3s[0], members.size)
+ server_name, group_name, 3, lgrmi3s[0], members.size
+ )
if rc != NERR_Success
Chef::ReservedNames::Win32::Error.raise!(nil, rc)
@@ -247,7 +266,8 @@ class Chef
lgrmi3s = members_to_lgrmi3(members)
rc = NetLocalGroupDelMembers(
- server_name, group_name, 3, lgrmi3s[0], members.size)
+ server_name, group_name, 3, lgrmi3s[0], members.size
+ )
if rc != NERR_Success
Chef::ReservedNames::Win32::Error.raise!(nil, rc)
diff --git a/lib/chef/win32/process.rb b/lib/chef/win32/process.rb
index 76e526340b..17621f8518 100644
--- a/lib/chef/win32/process.rb
+++ b/lib/chef/win32/process.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/win32/api/process"
-require "chef/win32/api/psapi"
-require "chef/win32/error"
-require "chef/win32/handle"
-require "ffi"
+require_relative "api/process"
+require_relative "api/psapi"
+require_relative "error"
+require_relative "handle"
+require "ffi" unless defined?(FFI)
class Chef
module ReservedNames::Win32
@@ -82,8 +82,8 @@ class Chef
(call_succeeded != 0) && (is_64_bit_process_result.get_int(0) != 0)
end
- # Must have PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION rights,
- # AND the PROCESS_VM_READ right
+ # Must have PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION rights,
+ # AND the PROCESS_VM_READ right
def self.get_process_memory_info(handle)
memory_info = PROCESS_MEMORY_COUNTERS.new
unless GetProcessMemoryInfo(handle.handle, memory_info, memory_info.size)
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 613994295c..4b5f8ede41 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 (c) 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.
@@ -16,14 +16,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require "chef/reserved_names"
-require "chef/win32/api"
-require "chef/mixin/wide_string"
-
-if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require "chef/monkey_patches/win32/registry"
- require "chef/win32/api/registry"
- require "win32/registry"
+require_relative "../reserved_names"
+require_relative "api"
+require_relative "../mixin/wide_string"
+
+if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
+ Win32.autoload :Registry, File.expand_path("../monkey_patches/win32/registry", __dir__)
+ require_relative "api/registry"
require "win32/api"
end
@@ -31,7 +30,7 @@ class Chef
class Win32
class Registry
- if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
include Chef::ReservedNames::Win32::API::Registry
extend Chef::ReservedNames::Win32::API::Registry
end
@@ -40,7 +39,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
@@ -56,37 +55,37 @@ class Chef
hive, key = get_hive_and_key(key_path)
key_exists!(key_path)
values = hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
- reg.map { |name, type, data| { :name => name, :type => get_name_from_type(type), :data => data } }
+ reg.map { |name, type, data| { name: name, type: get_name_from_type(type), data: data } }
end
end
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,43 +94,44 @@ 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
raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
end
+
hive, key_including_parent = get_hive_and_key(key_path)
# key_including_parent: Software\\Root\\Branch\\Fruit
# key => Fruit
@@ -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
@@ -153,7 +153,7 @@ class Chef
return true
end
rescue ::Win32::Registry::Error => e
- return false
+ false
end
end
@@ -161,6 +161,7 @@ class Chef
unless key_exists?(key_path)
raise Chef::Exceptions::Win32RegKeyMissing, "Registry key #{key_path} does not exist"
end
+
true
end
@@ -170,7 +171,7 @@ class Chef
rescue Chef::Exceptions::Win32RegHiveMissing => e
return false
end
- return true
+ true
end
def has_subkeys?(key_path)
@@ -179,7 +180,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 +190,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 +205,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,13 +220,14 @@ class Chef
end
end
end
- return false
+ false
end
def value_exists!(key_path, value)
unless value_exists?(key_path, value)
raise Chef::Exceptions::Win32RegValueMissing, "Registry key #{key_path} has no value named #{value[:name]}"
end
+
true
end
@@ -233,6 +235,7 @@ class Chef
unless data_exists?(key_path, value)
raise Chef::Exceptions::Win32RegDataMissing, "Registry key #{key_path} has no value named #{value[:name]}, containing type #{value[:type]} and data #{value[:data]}"
end
+
true
end
@@ -249,7 +252,7 @@ class Chef
end
end
end
- return false
+ false
end
def type_matches!(key_path, value)
@@ -279,7 +282,8 @@ class Chef
if val.is_a? String
return val.downcase
end
- return val
+
+ val
end
def node
@@ -316,18 +320,18 @@ 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
{
- :binary => ::Win32::Registry::REG_BINARY,
- :string => ::Win32::Registry::REG_SZ,
- :multi_string => ::Win32::Registry::REG_MULTI_SZ,
- :expand_string => ::Win32::Registry::REG_EXPAND_SZ,
- :dword => ::Win32::Registry::REG_DWORD,
- :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
- :qword => ::Win32::Registry::REG_QWORD,
+ binary: ::Win32::Registry::REG_BINARY,
+ string: ::Win32::Registry::REG_SZ,
+ multi_string: ::Win32::Registry::REG_MULTI_SZ,
+ expand_string: ::Win32::Registry::REG_EXPAND_SZ,
+ dword: ::Win32::Registry::REG_DWORD,
+ dword_big_endian: ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
+ qword: ::Win32::Registry::REG_QWORD,
}
end
@@ -336,7 +340,7 @@ class Chef
end
def get_type_from_num(val_type)
- value = {
+ {
3 => ::Win32::Registry::REG_BINARY,
1 => ::Win32::Registry::REG_SZ,
7 => ::Win32::Registry::REG_MULTI_SZ,
@@ -345,7 +349,6 @@ class Chef
5 => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
11 => ::Win32::Registry::REG_QWORD,
}[val_type]
- return value
end
def create_missing(key_path)
@@ -356,8 +359,8 @@ class Chef
hive, key = get_hive_and_key(key_path)
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}")
+ unless key_exists?(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..3894c65b21 100644
--- a/lib/chef/win32/security.rb
+++ b/lib/chef/win32/security.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require "chef/win32/api/security"
-require "chef/win32/error"
-require "chef/win32/memory"
-require "chef/win32/process"
-require "chef/win32/unicode"
-require "chef/win32/security/token"
-require "chef/mixin/wide_string"
+require_relative "api/security"
+require_relative "error"
+require_relative "memory"
+require_relative "process"
+require_relative "unicode"
+require_relative "security/token"
+require_relative "../mixin/wide_string"
class Chef
module ReservedNames::Win32
@@ -56,8 +56,8 @@ class Chef
granted_access_ptr = FFI::MemoryPointer.new(:ulong)
unless AccessCheck(security_descriptor_ptr, token_handle, rights_ptr.read_ulong,
- generic_mapping, privileges, privileges_length_ptr, granted_access_ptr,
- result_ptr)
+ generic_mapping, privileges, privileges_length_ptr, granted_access_ptr,
+ result_ptr)
Chef::ReservedNames::Win32::Error.raise!
end
result_ptr.read_ulong == 1
@@ -113,10 +113,20 @@ 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
+
+ def self.remove_account_right(name, privilege)
+ privilege_pointer = FFI::MemoryPointer.new LSA_UNICODE_STRING, 1
+ privilege_lsa_string = LSA_UNICODE_STRING.new(privilege_pointer)
+ privilege_lsa_string[:Buffer] = FFI::MemoryPointer.from_string(privilege.to_wstring)
+ privilege_lsa_string[:Length] = privilege.length * 2
+ privilege_lsa_string[:MaximumLength] = (privilege.length + 1) * 2
+
+ with_lsa_policy(name) do |policy_handle, sid|
+ result = LsaRemoveAccountRights(policy_handle.read_pointer, sid, false, privilege_pointer, 1)
+ test_and_raise_lsa_nt_status(result)
end
end
@@ -190,20 +200,55 @@ 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
end
+ def self.get_account_with_user_rights(privilege)
+ privilege_pointer = FFI::MemoryPointer.new LSA_UNICODE_STRING, 1
+ privilege_lsa_string = LSA_UNICODE_STRING.new(privilege_pointer)
+ privilege_lsa_string[:Buffer] = FFI::MemoryPointer.from_string(privilege.to_wstring)
+ privilege_lsa_string[:Length] = privilege.length * 2
+ privilege_lsa_string[:MaximumLength] = (privilege.length + 1) * 2
+
+ buffer = FFI::MemoryPointer.new(:pointer)
+ count = FFI::MemoryPointer.new(:ulong)
+
+ accounts = []
+ with_lsa_policy(nil) do |policy_handle, sid|
+ result = LsaEnumerateAccountsWithUserRight(policy_handle.read_pointer, privilege_pointer, buffer, count)
+ if result == 0
+ win32_error = LsaNtStatusToWinError(result)
+ return [] if win32_error == 1313 # NO_SUCH_PRIVILEGE - https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-
+
+ test_and_raise_lsa_nt_status(result)
+
+ count.read_ulong.times do |i|
+ sid = LSA_ENUMERATION_INFORMATION.new(buffer.read_pointer + i * LSA_ENUMERATION_INFORMATION.size)
+ sid_name = lookup_account_sid(sid[:Sid])
+ domain, name, use = sid_name
+ account_name = (!domain.nil? && domain.length > 0) ? "#{domain}\\#{name}" : name
+ accounts << account_name
+ end
+ end
+
+ result = LsaFreeMemory(buffer.read_pointer)
+ test_and_raise_lsa_nt_status(result)
+ end
+
+ accounts
+ end
+
def self.get_ace(acl, index)
acl = acl.pointer if acl.respond_to?(:pointer)
ace = FFI::Buffer.new :pointer
@@ -239,7 +284,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
@@ -318,6 +363,7 @@ class Chef
elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
Chef::ReservedNames::Win32::Error.raise!
end
+
owner_result_storage = FFI::MemoryPointer.new owner_result_size.read_ulong
unless GetTokenInformation(token.handle.handle, :TokenOwner, owner_result_storage, owner_result_size.read_ulong, owner_result_size)
Chef::ReservedNames::Win32::Error.raise!
@@ -333,6 +379,7 @@ class Chef
elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
Chef::ReservedNames::Win32::Error.raise!
end
+
group_result_storage = FFI::MemoryPointer.new group_result_size.read_ulong
unless GetTokenInformation(token.handle.handle, :TokenPrimaryGroup, group_result_storage, group_result_size.read_ulong, group_result_size)
Chef::ReservedNames::Win32::Error.raise!
@@ -341,6 +388,23 @@ 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)
@@ -379,7 +443,7 @@ class Chef
system_name = system_name.to_wstring if system_name
if LookupAccountNameW(system_name, name.to_wstring, nil, sid_size, nil, referenced_domain_name_size, nil)
raise "Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountName, and got no error!"
- elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
+ elsif !([NO_ERROR, ERROR_INSUFFICIENT_BUFFER].include?(FFI::LastError.error))
Chef::ReservedNames::Win32::Error.raise!
end
@@ -525,20 +589,20 @@ class Chef
# Determine the security_information flags
security_information = 0
- security_information |= OWNER_SECURITY_INFORMATION if args.has_key?(:owner)
- security_information |= GROUP_SECURITY_INFORMATION if args.has_key?(:group)
- security_information |= DACL_SECURITY_INFORMATION if args.has_key?(:dacl)
- security_information |= SACL_SECURITY_INFORMATION if args.has_key?(:sacl)
- if args.has_key?(:dacl_inherits)
+ security_information |= OWNER_SECURITY_INFORMATION if args.key?(:owner)
+ security_information |= GROUP_SECURITY_INFORMATION if args.key?(:group)
+ security_information |= DACL_SECURITY_INFORMATION if args.key?(:dacl)
+ security_information |= SACL_SECURITY_INFORMATION if args.key?(:sacl)
+ if args.key?(:dacl_inherits)
security_information |= (args[:dacl_inherits] ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION)
end
- if args.has_key?(:sacl_inherits)
+ if args.key?(:sacl_inherits)
security_information |= (args[:sacl_inherits] ? UNPROTECTED_SACL_SECURITY_INFORMATION : PROTECTED_SACL_SECURITY_INFORMATION)
end
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 +615,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 +643,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!
@@ -587,26 +651,24 @@ class Chef
end
def self.with_lsa_policy(username)
- sid = lookup_account_name(username)[1]
+ sid = lookup_account_name(username)[1] if username
access = 0
access |= POLICY_CREATE_ACCOUNT
access |= POLICY_LOOKUP_NAMES
+ access |= POLICY_VIEW_LOCAL_INFORMATION if username.nil?
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)
+
+ sid_pointer = username.nil? ? nil : sid.pointer
begin
- yield policy_handle, sid.pointer
+ 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 +689,27 @@ 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)
+ rescue Exception => run_error
+ return false if /Access is denied/.match?(run_error.message)
- # 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)
+ 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,12 +723,19 @@ 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
-require "chef/win32/security/ace"
-require "chef/win32/security/acl"
-require "chef/win32/security/securable_object"
-require "chef/win32/security/security_descriptor"
-require "chef/win32/security/sid"
+require_relative "security/ace"
+require_relative "security/acl"
+require_relative "security/securable_object"
+require_relative "security/security_descriptor"
+require_relative "security/sid"
diff --git a/lib/chef/win32/security/ace.rb b/lib/chef/win32/security/ace.rb
index d593513983..945fcdfdcd 100644
--- a/lib/chef/win32/security/ace.rb
+++ b/lib/chef/win32/security/ace.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/security/sid"
-require "chef/win32/memory"
+require_relative "../security"
+require_relative "sid"
+require_relative "../memory"
-require "ffi"
+require "ffi" unless defined?(FFI)
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/security/acl.rb b/lib/chef/win32/security/acl.rb
index 8a04987e44..31838b6c68 100644
--- a/lib/chef/win32/security/acl.rb
+++ b/lib/chef/win32/security/acl.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/security/ace"
-require "ffi"
+require_relative "../security"
+require_relative "ace"
+require "ffi" unless defined?(FFI)
class Chef
module ReservedNames::Win32
@@ -45,10 +45,11 @@ class Chef
def ==(other)
return false if length != other.length
+
0.upto(length - 1) do |i|
return false if self[i] != other[i]
end
- return true
+ true
end
def pointer
@@ -88,7 +89,7 @@ class Chef
end
def to_s
- "[#{self.collect { |ace| ace.to_s }.join(", ")}]"
+ "[#{collect(&:to_s).join(", ")}]"
end
def self.align_dword(size)
diff --git a/lib/chef/win32/security/securable_object.rb b/lib/chef/win32/security/securable_object.rb
index aef1a72c8c..3dd1470e9e 100644
--- a/lib/chef/win32/security/securable_object.rb
+++ b/lib/chef/win32/security/securable_object.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/security/acl"
-require "chef/win32/security/sid"
+require_relative "../security"
+require_relative "acl"
+require_relative "sid"
class Chef
module ReservedNames::Win32
@@ -42,10 +42,10 @@ class Chef
# compare an existing ACE with one you want to create.
def predict_rights_mask(generic_mask)
mask = generic_mask
- #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_READ if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0
- #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_WRITE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0
- #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_EXECUTE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0
- #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_ALL if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0
+ # mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_READ if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0
+ # mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_WRITE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0
+ # mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_EXECUTE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0
+ # mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_ALL if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0
if type == :SE_FILE_OBJECT
mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0
mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0
@@ -71,36 +71,36 @@ class Chef
end
def dacl=(val)
- Security.set_named_security_info(path, type, :dacl => val)
+ Security.set_named_security_info(path, type, dacl: val)
end
# You don't set dacl_inherits without also setting dacl,
# because Windows gets angry and denies you access. So
# if you want to do that, you may as well do both at once.
def set_dacl(dacl, dacl_inherits)
- Security.set_named_security_info(path, type, :dacl => dacl, :dacl_inherits => dacl_inherits)
+ Security.set_named_security_info(path, type, dacl: dacl, dacl_inherits: dacl_inherits)
end
def group=(val)
- Security.set_named_security_info(path, type, :group => val)
+ Security.set_named_security_info(path, type, group: val)
end
def owner=(val)
# TODO to fix serious permissions problems, we may need to enable SeBackupPrivilege. But we might need it (almost) everywhere else, too.
Security.with_privileges("SeTakeOwnershipPrivilege", "SeRestorePrivilege") do
- Security.set_named_security_info(path, type, :owner => val)
+ Security.set_named_security_info(path, type, owner: val)
end
end
def sacl=(val)
Security.with_privileges("SeSecurityPrivilege") do
- Security.set_named_security_info(path, type, :sacl => val)
+ Security.set_named_security_info(path, type, sacl: val)
end
end
def set_sacl(sacl, sacl_inherits)
Security.with_privileges("SeSecurityPrivilege") do
- Security.set_named_security_info(path, type, :sacl => sacl, :sacl_inherits => sacl_inherits)
+ Security.set_named_security_info(path, type, sacl: sacl, sacl_inherits: sacl_inherits)
end
end
end
diff --git a/lib/chef/win32/security/security_descriptor.rb b/lib/chef/win32/security/security_descriptor.rb
index 8bfd8b8287..ee2d44862f 100644
--- a/lib/chef/win32/security/security_descriptor.rb
+++ b/lib/chef/win32/security/security_descriptor.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/security/acl"
-require "chef/win32/security/sid"
+require_relative "../security"
+require_relative "acl"
+require_relative "sid"
class Chef
module ReservedNames::Win32
@@ -41,7 +41,8 @@ class Chef
end
def dacl
- raise "DACL not present" if !dacl_present?
+ raise "DACL not present" unless dacl_present?
+
present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_dacl(self)
acl
end
@@ -65,7 +66,8 @@ class Chef
end
def sacl
- raise "SACL not present" if !sacl_present?
+ raise "SACL not present" unless sacl_present?
+
Security.with_privileges("SeSecurityPrivilege") do
present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_sacl(self)
acl
diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb
index f6b88c60ce..aaf3532fc4 100644
--- a/lib/chef/win32/security/sid.rb
+++ b/lib/chef/win32/security/sid.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/api/net"
-require "chef/win32/api/error"
+require_relative "../security"
+require_relative "../api/net"
+require_relative "../api/error"
require "wmi-lite/wmi"
@@ -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
@@ -226,25 +231,63 @@ class Chef
end
def self.None
- SID.from_account("#{::ENV['COMPUTERNAME']}\\None")
+ SID.from_account("#{::ENV["COMPUTERNAME"]}\\None")
end
def self.Administrator
- SID.from_account("#{::ENV['COMPUTERNAME']}\\#{SID.admin_account_name}")
+ SID.from_account("#{::ENV["COMPUTERNAME"]}\\#{SID.admin_account_name}")
end
def self.Guest
- SID.from_account("#{::ENV['COMPUTERNAME']}\\Guest")
+ SID.from_account("#{::ENV["COMPUTERNAME"]}\\Guest")
end
def self.current_user
- SID.from_account("#{::ENV['USERDOMAIN']}\\#{::ENV['USERNAME']}")
+ SID.from_account("#{::ENV["USERDOMAIN"]}\\#{::ENV["USERNAME"]}")
+ end
+
+ SERVICE_ACCOUNT_USERS = [self.LocalSystem,
+ self.NtLocal,
+ self.NtNetwork].flat_map do |user_type|
+ [user_type.account_simple_name.upcase,
+ user_type.account_name.upcase]
+ end.freeze
+
+ BUILT_IN_GROUPS = [self.BuiltinAdministrators,
+ self.BuiltinUsers, self.Guests].flat_map do |user_type|
+ [user_type.account_simple_name.upcase,
+ user_type.account_name.upcase]
+ end.freeze
+
+ SYSTEM_USER = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
+
+ # Check if the user belongs to service accounts category
+ #
+ # @return [Boolean] True or False
+ #
+ def self.service_account_user?(user)
+ SERVICE_ACCOUNT_USERS.include?(user.to_s.upcase)
+ end
+
+ # Check if the user is in builtin system group
+ #
+ # @return [Boolean] True or False
+ #
+ def self.group_user?(user)
+ BUILT_IN_GROUPS.include?(user.to_s.upcase)
+ end
+
+ # Check if the user belongs to system users category
+ #
+ # @return [Boolean] True or False
+ #
+ def self.system_user?(user)
+ SYSTEM_USER.include?(user.to_s.upcase)
end
# 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)
@@ -278,11 +321,11 @@ class Chef
while status == ERROR_MORE_DATA
status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle)
- if status == NERR_Success || status == ERROR_MORE_DATA
+ if [NERR_Success, ERROR_MORE_DATA].include?(status)
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
+ # RID for the Administrator account is always 500 and it's privilege is set to USER_PRIV_ADMIN
if user_info[:usri3_user_id] == 500 && user_info[:usri3_priv] == 2 # USER_PRIV_ADMIN (2) - Administrator
admin_account_name = user_info[:usri3_name].read_wstring
break
@@ -295,6 +338,7 @@ class Chef
end
raise "Can not determine the administrator account name." if admin_account_name.nil?
+
admin_account_name
end
end
diff --git a/lib/chef/win32/security/token.rb b/lib/chef/win32/security/token.rb
index 38ef03b33c..70835d0ffe 100644
--- a/lib/chef/win32/security/token.rb
+++ b/lib/chef/win32/security/token.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require "chef/win32/security"
-require "chef/win32/api/security"
-require "chef/win32/unicode"
-require "ffi"
+require_relative "../security"
+require_relative "../api/security"
+require_relative "../unicode"
+require "ffi" unless defined?(FFI)
class Chef
module ReservedNames::Win32
@@ -35,7 +35,8 @@ class Chef
def enable_privileges(*privilege_names)
# Build the list of privileges we want to set
new_privileges = Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.new(
- FFI::MemoryPointer.new(Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.size_with_privileges(privilege_names.length)))
+ FFI::MemoryPointer.new(Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.size_with_privileges(privilege_names.length))
+ )
new_privileges[:PrivilegeCount] = 0
privilege_names.each do |privilege_name|
luid = Chef::ReservedNames::Win32::API::Security::LUID.new
@@ -64,6 +65,7 @@ class Chef
unless Chef::ReservedNames::Win32::API::Security.DuplicateToken(handle.handle, security_impersonation_level, duplicate_token_handle)
raise Chef::ReservedNames::Win32::Error.raise!
end
+
Token.new(Handle.new(duplicate_token_handle.read_ulong))
end
end
diff --git a/lib/chef/win32/system.rb b/lib/chef/win32/system.rb
index ec2e5d3457..a217ee984a 100755..100644
--- a/lib/chef/win32/system.rb
+++ b/lib/chef/win32/system.rb
@@ -1,6 +1,6 @@
#
# Author:: Salim Alam (<salam@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require "chef/win32/api/system"
-require "chef/win32/error"
-require "ffi"
+require_relative "api/system"
+require_relative "error"
+require "ffi" unless defined?(FFI)
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/unicode.rb b/lib/chef/win32/unicode.rb
index d531463be0..731fe2fbd9 100644
--- a/lib/chef/win32/unicode.rb
+++ b/lib/chef/win32/unicode.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef/mixin/wide_string"
-require "chef/win32/api/unicode"
+require_relative "../mixin/wide_string"
+require_relative "api/unicode"
class Chef
module ReservedNames::Win32
@@ -40,13 +40,19 @@ 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..]
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
+
+ def read_utf16string
+ offset = 0
+ offset += 2 while get_bytes(offset, 2) != "\x00\x00"
+ get_bytes(0, offset).force_encoding("utf-16le").encode("utf-8")
end
end
end
diff --git a/lib/chef/win32/version.rb b/lib/chef/win32/version.rb
index 303fe1531d..c83e52e4fc 100644
--- a/lib/chef/win32/version.rb
+++ b/lib/chef/win32/version.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require "chef/win32/api"
-require "chef/win32/api/system"
+require_relative "api"
+require_relative "api/system"
require "wmi-lite/wmi"
class Chef
@@ -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
@@ -41,29 +43,29 @@ class Chef
private_class_method :get_system_metrics
def self.method_name_from_marketing_name(marketing_name)
- "#{marketing_name.gsub(/\s/, '_').tr('.', '_').downcase}?"
- # "#{marketing_name.gsub(/\s/, '_').gsub(//, '_').downcase}?"
+ "#{marketing_name.gsub(/\s/, "_").tr(".", "_").downcase}?"
end
private_class_method :method_name_from_marketing_name
WIN_VERSIONS = {
- "Windows 10" => { :major => 10, :minor => 0, :callable => lambda { |product_type, suite_mask| product_type == VER_NT_WORKSTATION } },
- "Windows Server 2016" => { :major => 10, :minor => 0, :callable => lambda { |product_type, suite_mask| product_type != VER_NT_WORKSTATION } },
- "Windows 8.1" => { :major => 6, :minor => 3, :callable => lambda { |product_type, suite_mask| product_type == VER_NT_WORKSTATION } },
- "Windows Server 2012 R2" => { :major => 6, :minor => 3, :callable => lambda { |product_type, suite_mask| product_type != VER_NT_WORKSTATION } },
- "Windows 8" => { :major => 6, :minor => 2, :callable => lambda { |product_type, suite_mask| product_type == VER_NT_WORKSTATION } },
- "Windows Server 2012" => { :major => 6, :minor => 2, :callable => lambda { |product_type, suite_mask| product_type != VER_NT_WORKSTATION } },
- "Windows 7" => { :major => 6, :minor => 1, :callable => lambda { |product_type, suite_mask| product_type == VER_NT_WORKSTATION } },
- "Windows Server 2008 R2" => { :major => 6, :minor => 1, :callable => lambda { |product_type, suite_mask| product_type != VER_NT_WORKSTATION } },
- "Windows Server 2008" => { :major => 6, :minor => 0, :callable => lambda { |product_type, suite_mask| product_type != VER_NT_WORKSTATION } },
- "Windows Vista" => { :major => 6, :minor => 0, :callable => lambda { |product_type, suite_mask| product_type == VER_NT_WORKSTATION } },
- "Windows Server 2003 R2" => { :major => 5, :minor => 2, :callable => lambda { |product_type, suite_mask| get_system_metrics(SM_SERVERR2) != 0 } },
- "Windows Home Server" => { :major => 5, :minor => 2, :callable => lambda { |product_type, suite_mask| (suite_mask & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER } },
- "Windows Server 2003" => { :major => 5, :minor => 2, :callable => lambda { |product_type, suite_mask| get_system_metrics(SM_SERVERR2) == 0 } },
- "Windows XP" => { :major => 5, :minor => 1 },
- "Windows 2000" => { :major => 5, :minor => 0 },
- }
+ "Windows Server 2019" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number >= 17763 } },
+ "Windows 10" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION } },
+ "Windows Server 2016" => { major: 10, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION && build_number <= 14393 } },
+ "Windows 8.1" => { major: 6, minor: 3, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION } },
+ "Windows Server 2012 R2" => { major: 6, minor: 3, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION } },
+ "Windows 8" => { major: 6, minor: 2, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION } },
+ "Windows Server 2012" => { major: 6, minor: 2, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION } },
+ "Windows 7" => { major: 6, minor: 1, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION } },
+ "Windows Server 2008 R2" => { major: 6, minor: 1, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION } },
+ "Windows Server 2008" => { major: 6, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type != VER_NT_WORKSTATION } },
+ "Windows Vista" => { major: 6, minor: 0, callable: lambda { |product_type, suite_mask, build_number| product_type == VER_NT_WORKSTATION } },
+ "Windows Server 2003 R2" => { major: 5, minor: 2, callable: lambda { |product_type, suite_mask, build_number| get_system_metrics(SM_SERVERR2) != 0 } },
+ "Windows Home Server" => { major: 5, minor: 2, callable: lambda { |product_type, suite_mask, build_number| (suite_mask & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER } },
+ "Windows Server 2003" => { major: 5, minor: 2, callable: lambda { |product_type, suite_mask, build_number| get_system_metrics(SM_SERVERR2) == 0 } },
+ "Windows XP" => { major: 5, minor: 1 },
+ "Windows 2000" => { major: 5, minor: 0 },
+ }.freeze
def initialize
@major_version, @minor_version, @build_number = get_version
@@ -74,18 +76,11 @@ 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
+ marketing_names = []
# General Windows checks
WIN_VERSIONS.each do |k, v|
@@ -93,14 +88,14 @@ class Chef
define_method(method_name) do
(@major_version == v[:major]) &&
(@minor_version == v[:minor]) &&
- (v[:callable] ? v[:callable].call(@product_type, @suite_mask) : true)
+ (v[:callable] ? v[:callable].call(@product_type, @suite_mask, @build_number) : true)
end
marketing_names << [k, method_name]
end
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
@@ -129,7 +128,7 @@ class Chef
# The operating system version is a string in the following form
# that can be split into components based on the '.' delimiter:
# MajorVersionNumber.MinorVersionNumber.BuildNumber
- os_version.split(".").collect { |version_string| version_string.to_i }
+ os_version.split(".").collect(&:to_i)
end
def get_version_ex
@@ -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/lib/chef/workstation_config_loader.rb b/lib/chef/workstation_config_loader.rb
index 97f41240f3..ce918ba861 100644
--- a/lib/chef/workstation_config_loader.rb
+++ b/lib/chef/workstation_config_loader.rb
@@ -1,6 +1,6 @@
#
# Author:: Claire McQuin (<claire@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/omnibus/.gitignore b/omnibus/.gitignore
index 1a2c556f8d..91d2b32b21 100644
--- a/omnibus/.gitignore
+++ b/omnibus/.gitignore
@@ -1,6 +1,8 @@
+binstubs
+.bundle
vendor/bundle
pkg/*
-.kitchen.local.yml
+kitchen.local.yml
bin/*
files/chef-server-cookbooks/cache/
files/msi/ChefClient-Config.wxi
@@ -9,3 +11,4 @@ vendor/cookbooks
build_timestamp
ldd.out
Berksfile.lock
+Gemfile.local
diff --git a/omnibus/.kitchen.vmware.yml b/omnibus/.kitchen.vmware.yml
deleted file mode 100644
index 69f001ef97..0000000000
--- a/omnibus/.kitchen.vmware.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-driver:
- name: vagrant
- provider: vmware_fusion
- customize:
- numvcpus: 4
- memsize: 4096
diff --git a/omnibus/.kitchen.yml b/omnibus/.kitchen.yml
deleted file mode 100644
index a14d3498de..0000000000
--- a/omnibus/.kitchen.yml
+++ /dev/null
@@ -1,137 +0,0 @@
-#
-# NOTE: this runs the omnibus cookbook, but does not actually run Omnibus. Use
-# 'kichen converge' to setup the virtual machine and then `kitchen login` to
-# SSH into the machine and run Omnibus.
-#
-
-driver:
- name: vagrant
- forward_agent: yes
- customize:
- cpus: 4
- memory: 4096
- synced_folders:
- - ['../..', '/vagrant/code']
- - ['../../omnibus', '/home/vagrant/omnibus']
- - ['../../omnibus-software', '/home/vagrant/omnibus-software']
-
-provisioner:
- name: chef_zero
- # Always install the latest version of Chef.
- # This is not the version of chef that we're building - this is the version
- # of chef that omnibus needs to build chef/chef.
- require_chef_omnibus: true
- attributes:
- vagrant:
- this_key_exists_so_we_have_a_vagrant_key: true
- omnibus:
- build_user: vagrant
- build_user_group: vagrant
- build_user_password: vagrant
- product_name: angrychef
- product_version: latest
- chef_omnibus_root: /opt/angrychef
-
-platforms:
- - name: centos-5.11
- run_list: yum-epel::default
- - name: centos-6.7
- 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
- run_list:
- - freebsd::portsnap
- - freebsd::pkgng
- - name: freebsd-10.2
- run_list: freebsd::portsnap
- - name: ubuntu-10.04
- run_list: apt::default
- - name: ubuntu-12.04
- run_list: apt::default
- - name: ubuntu-14.04
- run_list: apt::default
- # The following (private) boxes are shared via Atlas and are only
- # available to users working for Chef. Sorry, it's about software licensing.
- #
- # Chef-internal users, you will need to:
- # 1. Create an Atlas account: https://atlas.hashicorp.com/
- # 2. Ping #eng-services-support with your Atlas account name
- # to be added to the relevant team in Atlas,
- # 3. Do `vagrant login` with your Atlas creds so that you can download
- # the private boxes.
- #
- # The Mac OS X boxes are VMware only also. You can enable VMware Fusion
- # by activating the `.kitchen.vmware.yml` file with the `KITCHEN_LOCAL_YAML`
- # environment variable:
- #
- # KITCHEN_LOCAL_YAML=.kitchen.vmware.yml kitchen converge chefdk-macosx-109
- #
- <% %w(
- 10.9
- 10.10
- 10.11
- ).each do |mac_version| %>
- - name: macosx-<%= mac_version %>
- driver:
- box: chef/macosx-<%= mac_version %> # private
- synced_folders:
- - ['..', '/Users/vagrant/chef']
- - ['../../omnibus', '/Users/vagrant/omnibus']
- - ['../../omnibus-software', '/Users/vagrant/omnibus-software']
- <% end %>
- - name: windows-2012r2-standard
- driver:
- box: chef/windows-server-2012r2-standard # 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
- # mounts at `C:\vagrant\code` and the Chef source folder is available
- # 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
- # 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-2012r2-standard-i386
- driver:
- box: chef/windows-server-2012r2-standard # private
- synced_folders:
- # We have to mount this repos enclosing folder as the Omnibus build
- # gets cranky if the mounted ChefDK source folder is a symlink. This
- # mounts at `C:\vagrant\code` and the ChefDK source folder is available
- # at `C:\vagrant\code\chef-dk`
- - ['../..', '/vagrant/code']
- provisioner:
- attributes:
- omnibus:
- build_user: vagrant
- build_user_group: Administrators
- build_user_password: vagrant
- chef_omnibus_root: /opscode/angrychef
-
-suites:
- # - name: angrychef
- # attributes:
- # omnibus:
- # <<: *attribute_defaults
- # install_dir: /opt/angrychef
- # run_list:
- # - omnibus::default
- - name: chef
- attributes:
- omnibus:
- install_dir: /opt/chef
- run_list:
- - omnibus::default
diff --git a/omnibus/Berksfile b/omnibus/Berksfile
index 614c6da643..4e6a878bf4 100644
--- a/omnibus/Berksfile
+++ b/omnibus/Berksfile
@@ -3,10 +3,10 @@ source "https://supermarket.chef.io"
cookbook "omnibus"
# Uncomment to use the latest version of the Omnibus cookbook from GitHub
-# cookbook 'omnibus', github: 'opscode-cookbooks/omnibus'
+# cookbook 'omnibus', github: 'chef-cookbooks/omnibus'
group :integration do
- cookbook "apt", "~> 2.3"
- cookbook "freebsd", "~> 0.1"
- cookbook "yum-epel", "~> 0.3"
+ cookbook "apt"
+ cookbook "freebsd"
+ cookbook "yum-epel"
end
diff --git a/omnibus/CHEF-EULA.md b/omnibus/CHEF-EULA.md
new file mode 100644
index 0000000000..8911f3ebdc
--- /dev/null
+++ b/omnibus/CHEF-EULA.md
@@ -0,0 +1,48 @@
+
+# Software End User License Agreement
+(Personal, Non-Commercial, Experimental)
+
+**April 2, 2019** - The most recent edition of this license is available at https://www.chef.io/end-user-license-agreement/
+
+This Software End User License Agreement (this “**Agreement**“), is a binding agreement between Chef Software Inc. (“**Chef**“) and You (as defined below).
+
+IF YOU REPRESENT A CORPORATION, GOVERNMENTAL ORGANIZATION, OR OTHER LEGAL ENTITY, OR YOU INTEND TO USE THE SOFTWARE FOR COMMERCIAL PURPOSES, YOU MUST CONTACT CHEF DIRECTLY TO OBTAIN A COMMERCIAL LICENSE FOR THIS SOFTWARE. PLEASE VISIT [https://www.chef.io/eula-inquiry/](https://www.chef.io/eula-inquiry/) TO INQUIRE.
+
+LICENSOR PROVIDES THE SOFTWARE SOLELY ON THE TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT AND ON THE CONDITION THAT YOU ACCEPT THEM. BY CLICKING THE “ACCEPT” BUTTON YOU (A) ACCEPT THIS AGREEMENT AND AGREE TO BE LEGALLY BOUND BY ITS TERMS; AND (B) REPRESENT AND WARRANT THAT YOU HAVE THE LEGAL CAPACITY TO ENTER INTO A BINDING AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS OF THIS AGREEMENT, YOU MUST NOT INSTALL OR USE THE SOFTWARE.
+
+<u>Definitions</u>. For purposes of this Agreement, the following terms have the following meanings:
+
+“**Intellectual Property Rights**” means patent, copyright, trademark, trade secret, database protection, or other intellectual property rights laws, and all similar or equivalent rights or forms of protection.
+
+“**Business**” means any Person other than a natural person.
+
+“**Commercial Purpose**” means for the benefit of (i) any Business, or (ii) any undertaking intended, directly or indirectly, for profit.
+
+“**Experimental Use**” means using the Software to learn, train, experiment with, or test viability of the Software. Experimental Use excludes pre-production and production environments as well as making the Software available to others, whether or not in exchange for any consideration.
+
+“**Person**” means an individual, corporation, partnership, joint venture, limited liability company, governmental authority, non-profit organization, unincorporated organization, trust, association, or other entity.
+
+“**Software**” means the software programs made available under this License.
+
+“**Term**” has the meaning set forth in Section 6.
+
+“**Third Party**” means any Person other than You or Chef.
+
+“**You**” means the Person exercising permissions granted by this Agreement.
+
+1. **<u>License Grant and Scope</u>**. Chef hereby grants to You a non-exclusive, non-transferable, limited license during the Term to use the Software solely as set forth in this Section 1 and subject to the terms of Section 3\. Chef hereby grants You the non-exclusive, non-transferable, non-sublicensable, royalty free right to:
+ * Download, copy, and install the Software on computers owned or leased, and controlled by, You. In addition to the foregoing, You may make copies of the Software for archival or backup purposes. All copies of the Software made by You must include all trademark, copyright, patent, and other Intellectual Property Rights notices contained in the original.
+ * Use and run the Software on such computers solely for Your personal, non-Commercial Purposes or Experimental Use.
+2. **<u>Third-Party Materials</u>**. The Software includes software, content, data, or other materials, including related documentation, that are owned by Persons other than Chef and that are provided to You on license terms that are in addition to and/or different from those contained in this Agreement (“**Third-Party Licenses**“). A list of all materials included in the Software and provided under Third-Party Licenses can be found at [https://www.chef.io/3rd-party-licenses/](https://www.chef.io/3rd-party-licenses/). You must comply with all Third-Party Licenses.
+3. **<u>Use Restrictions</u>**. You must not, directly or indirectly: (a) modify, translate, adapt, or otherwise create derivative works or improvements, whether or not patentable, of the Software or any part thereof; (b) reverse engineer, disassemble, decompile, decode, or otherwise attempt to derive or gain access to the source code of the Software or any part thereof; (c) remove, delete, alter, or obscure any trademarks or any copyright, trademark, patent, or other intellectual property or proprietary rights notices provided on or with the Software, including any copy thereof; (d) rent, lease, lend, sell, sublicense, assign, distribute, publish, transfer, or otherwise make available the Software, or any features or functionality of the Software, to any Third Party for any reason; (e) use the Software in violation of any law, regulation, or rule; or (f) use the Software for purposes of competitive analysis of the Software, the development of a competing software product or service, or any other purpose that is to the Chef’s commercial disadvantage.
+4. **<u>Collection and Use of Information</u>**. You hereby consent to Chef receiving data and information directly from the Software for the sole purpose of obtaining information regarding Your use of the Software (e.g., when You install an update or upgrade), as well as any Software bugs, errors, and other similar technical support issues. Chef will only use such data and information (“Software Usage and Technical Support Data”) for Chef’s own business purposes, including but not limited to the purposes of (i) gathering information about how You use the Software, which may be combined with information about how others use the Software, in order to help Chef better understand trends and Your needs in order to better consider new features, and (ii) improving the Software and Your use experience. Chef will use Software Usage and Technical Support Data solely in aggregate, anonymized form.
+5. **<u>Intellectual Property Rights</u>**. You acknowledge that the Software is provided under license, and not sold, to You. Chef reserves all right, title, and interest in and to the Software and all Intellectual Property Rights in or to Software, except as expressly granted to You in this Agreement. **Some portions of the Software may be separately available as source code from Chef under open source software licenses. Nothing in this Agreement affects any rights you may have separately under such licenses.**
+6. **<u>Term and Termination</u>**. This Agreement and the license granted hereunder shall remain in effect until terminated as set forth herein (the “**Term**“).
+ * You may terminate this Agreement by ceasing to use and destroying all copies of the Software.
+ * Chef may terminate this Agreement for convenience.
+ * If You institute any litigation against Chef (including a cross-claim or counterclaim in a lawsuit) then the licenses granted to You under this Agreement shall terminate automatically as of the date such litigation is filed.
+ * Upon termination of this Agreement, the license granted hereunder shall also terminate, and You shall cease using and destroy all copies of the Software.
+7. **<u>Warranty Disclaimer</u>**. THE SOFTWARE IS PROVIDED TO YOU “AS IS” AND WITH ALL FAULTS AND DEFECTS WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, CHEF, ON ITS OWN BEHALF AND ON BEHALF OF ITS AFFILIATES AND ITS AND THEIR RESPECTIVE LICENSORS EXPRESSLY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT, AND WARRANTIES THAT MAY ARISE OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE, USAGE, OR TRADE PRACTICE. WITHOUT LIMITING THE FOREGOING, CHEF PROVIDES NO WARRANTY OR UNDERTAKING, AND MAKES NO REPRESENTATION OF ANY KIND THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, ACHIEVE ANY INTENDED RESULTS, BE COMPATIBLE, OR WORK WITH ANY OTHER SOFTWARE, APPLICATIONS, SYSTEMS, OR SERVICES, OPERATE WITHOUT INTERRUPTION, MEET ANY PERFORMANCE OR RELIABILITY STANDARDS OR BE ERROR FREE, OR THAT ANY ERRORS OR DEFECTS CAN OR WILL BE CORRECTED. YOU MAY HAVE ADDITIONAL RIGHTS THAT VARY FROM STATE TO STATE.
+8. **<u>Limitation of Liability</u>**. TO THE FULLEST EXTENT PERMITTED UNDER APPLICABLE LAW: IN NO EVENT WILL CHEF OR ITS AFFILIATES, OR ANY OF ITS OR THEIR RESPECTIVE LICENSORS OR SERVICE PROVIDERS, BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, INDIRECT, EXEMPLARY, SPECIAL, OR PUNITIVE DAMAGES, WHETHER ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT, BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, REGARDLESS OF WHETHER SUCH DAMAGES WERE FORESEEABLE AND WHETHER OR NOT CHEF WAS ADVISED OF THE POSSIBILITY OF SUCH YOU MAY HAVE ADDITIONAL RIGHTS THAT VARY FROM STATE TO STATE.
+9. **<u>Export Regulation</u>**. The Software may be subject to US export control laws, including the US Export Administration Act and its associated regulations. You shall not, directly or indirectly, export, re-export, or release the Software to, or make the Software accessible from, any jurisdiction or country to which export, re-export, or release is prohibited by law, rule, or regulation.
+10. **<u>Miscellaneous</u>**. All matters arising out of or relating to this Agreement shall be governed by and construed in accordance with the internal laws of the State of Washington without giving effect to any conflict of law provision. Any legal action arising out of or relating to this Agreement will the subject to the exclusive jurisdiction of the state or federal courts located in King County. This Agreement constitutes the sole and entire agreement between You and Chef with respect to the subject matter contained herein, and supersedes all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, with respect to such subject matter. If any provision of this Agreement is determined by a court of law to be unenforceable, this Agreement and the license granted herein will terminate automatically.
diff --git a/omnibus/Gemfile b/omnibus/Gemfile
index 6e9e9cce71..df94943a3d 100644
--- a/omnibus/Gemfile
+++ b/omnibus/Gemfile
@@ -1,12 +1,10 @@
source "https://rubygems.org"
-gem "omnibus", github: "chef/omnibus", branch: "rhass/COOL-502_with_gcc_investigate"
-gem "omnibus-software", github: "chef/omnibus-software", branch: "lcg/ruby23"
-gem "license_scout", github: "chef/license_scout"
+gem "omnibus", git: "https://github.com/chef/omnibus", branch: "master"
+gem "omnibus-software", git: "https://github.com/chef/omnibus-software", branch: "master"
+gem "artifactory"
-# 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
@@ -14,13 +12,13 @@ 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", "~> 4.0"
+ gem "berkshelf", ">= 7.0"
+
+ # We pin here to the last release Ohai so prevent more current chef coming in
+ gem "ohai"
# Use Test Kitchen with Vagrant for converging the build environment
- gem "test-kitchen", "~> 1.13"
- gem "kitchen-vagrant", "~> 0.19.0"
+ gem "test-kitchen", ">= 1.23"
+ gem "kitchen-vagrant", ">= 1.3.1"
gem "winrm-fs", "~> 1.0"
- gem "pry"
- gem "pry-byebug"
- gem "pry-stack_explorer"
-end
+end \ No newline at end of file
diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock
index 1512f36319..54f191cbbb 100644
--- a/omnibus/Gemfile.lock
+++ b/omnibus/Gemfile.lock
@@ -1,271 +1,409 @@
GIT
- remote: git://github.com/chef/license_scout.git
- revision: dbd17c0a99a12e16dd7af27798df863366efc289
+ remote: https://github.com/chef/omnibus
+ revision: 44f13035ff8aa40ea15e4483dfcfde09b8c82e5c
+ branch: master
specs:
- license_scout (0.1.2)
+ omnibus (8.0.11)
+ aws-sdk-s3 (~> 1)
+ chef-cleanroom (~> 1.0)
+ chef-utils (>= 15.4)
ffi-yajl (~> 2.2)
- mixlib-shellout (~> 2.2)
-
-GIT
- remote: git://github.com/chef/omnibus-software.git
- revision: f0f4bb4beab18a9b6adbc0a34bcd2f0caf10be5c
- branch: lcg/ruby23
- specs:
- omnibus-software (4.0.0)
- chef-sugar (>= 3.4.0)
- omnibus (>= 5.5.0)
-
-GIT
- remote: git://github.com/chef/omnibus.git
- revision: 98c9af20a0f79b6ac205613f509b4725eea47f7b
- branch: rhass/COOL-502_with_gcc_investigate
- specs:
- omnibus (5.5.0)
- aws-sdk (~> 2)
- chef-sugar (~> 3.3)
- cleanroom (~> 1.0)
- ffi-yajl (~> 2.2)
- license_scout
- mixlib-shellout (~> 2.0)
+ license_scout (~> 1.0)
+ mixlib-shellout (>= 2.0, < 4.0)
mixlib-versioning
- ohai (~> 8.0)
+ ohai (>= 15)
+ pedump
ruby-progressbar (~> 1.7)
- thor (~> 0.18)
+ thor (>= 0.18, < 2.0)
GIT
- remote: https://github.com/ksubrama/pedump.git
- revision: b4319556e18c80d2cba064ffe57fe0dea549dfe2
- branch: patch-1
+ remote: https://github.com/chef/omnibus-software
+ revision: 869ef4e1ece0d53c267b8ca0b4b86e2644ef7693
+ 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)
+ omnibus (>= 8.0.0)
GEM
remote: https://rubygems.org/
specs:
- addressable (2.4.0)
- artifactory (2.5.0)
- awesome_print (1.7.0)
- aws-sdk (2.6.1)
- aws-sdk-resources (= 2.6.1)
- aws-sdk-core (2.6.1)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
+ artifactory (3.0.15)
+ awesome_print (1.8.0)
+ aws-eventstream (1.1.0)
+ aws-partitions (1.414.0)
+ aws-sdk-core (3.110.0)
+ aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (~> 1, >= 1.239.0)
+ aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
- aws-sdk-resources (2.6.1)
- aws-sdk-core (= 2.6.1)
- 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-io (~> 0.16.1)
+ aws-sdk-kms (1.40.0)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sigv4 (~> 1.1)
+ aws-sdk-s3 (1.87.0)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.1)
+ aws-sigv4 (1.2.2)
+ aws-eventstream (~> 1, >= 1.0.2)
+ bcrypt_pbkdf (1.1.0.rc1)
+ berkshelf (7.1.0)
+ chef (>= 15.7.32)
+ chef-config
cleanroom (~> 1.0)
- faraday (~> 0.9)
- httpclient (~> 2.7)
- minitar (~> 0.5, >= 0.5.4)
- mixlib-archive (~> 0.1)
+ concurrent-ruby (~> 1.0)
+ minitar (>= 0.6)
+ mixlib-archive (>= 0.4, < 2.0)
+ mixlib-config (>= 2.2.5)
+ mixlib-shellout (>= 2.0, < 4.0)
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)
- binding_of_caller (0.7.2)
- debug_inspector (>= 0.0.1)
- 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)
- byebug (9.0.5)
- celluloid (0.16.0)
- timers (~> 4.0.0)
- celluloid-io (0.16.2)
- celluloid (>= 0.16.0)
- nio4r (>= 1.1.0)
- chef-config (12.14.60)
+ retryable (>= 2.0, < 4.0)
+ solve (~> 4.0)
+ thor (>= 0.20)
+ builder (3.2.4)
+ chef (16.7.61)
addressable
+ bcrypt_pbkdf (= 1.1.0.rc1)
+ bundler (>= 1.10)
+ chef-config (= 16.7.61)
+ chef-utils (= 16.7.61)
+ chef-vault
+ chef-zero (>= 14.0.11)
+ diff-lcs (>= 1.2.4, < 1.4.0)
+ ed25519 (~> 1.2)
+ erubis (~> 2.7)
+ ffi (>= 1.9.25)
+ ffi-libarchive (~> 1.0, >= 1.0.3)
+ ffi-yajl (~> 2.2)
+ highline (>= 1.6.9, < 3)
+ iniparse (~> 1.4)
+ license-acceptance (>= 1.0.5, < 3)
+ mixlib-archive (>= 0.4, < 2.0)
+ mixlib-authentication (>= 2.1, < 4)
+ mixlib-cli (>= 2.1.1, < 3.0)
+ mixlib-log (>= 2.0.3, < 4.0)
+ mixlib-shellout (>= 3.1.1, < 4.0)
+ net-sftp (>= 2.1.2, < 4.0)
+ net-ssh (>= 4.2, < 7)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 16.0)
+ pastel
+ plist (~> 3.2)
+ proxifier (~> 1.0)
+ syslog-logger (~> 1.6)
+ train-core (~> 3.2, >= 3.2.28)
+ train-winrm (>= 0.2.5)
+ tty-prompt (~> 0.21)
+ tty-screen (~> 0.6)
+ tty-table (~> 0.11)
+ uuidtools (~> 2.1.5)
+ chef (16.7.61-universal-mingw32)
+ addressable
+ bcrypt_pbkdf (= 1.1.0.rc1)
+ bundler (>= 1.10)
+ chef-config (= 16.7.61)
+ chef-utils (= 16.7.61)
+ chef-vault
+ chef-zero (>= 14.0.11)
+ diff-lcs (>= 1.2.4, < 1.4.0)
+ ed25519 (~> 1.2)
+ erubis (~> 2.7)
+ ffi (>= 1.9.25)
+ ffi-libarchive (~> 1.0, >= 1.0.3)
+ ffi-yajl (~> 2.2)
+ highline (>= 1.6.9, < 3)
+ iniparse (~> 1.4)
+ iso8601 (>= 0.12.1, < 0.14)
+ license-acceptance (>= 1.0.5, < 3)
+ mixlib-archive (>= 0.4, < 2.0)
+ mixlib-authentication (>= 2.1, < 4)
+ mixlib-cli (>= 2.1.1, < 3.0)
+ mixlib-log (>= 2.0.3, < 4.0)
+ mixlib-shellout (>= 3.1.1, < 4.0)
+ net-sftp (>= 2.1.2, < 4.0)
+ net-ssh (>= 4.2, < 7)
+ net-ssh-multi (~> 1.2, >= 1.2.1)
+ ohai (~> 16.0)
+ pastel
+ plist (~> 3.2)
+ proxifier (~> 1.0)
+ syslog-logger (~> 1.6)
+ train-core (~> 3.2, >= 3.2.28)
+ train-winrm (>= 0.2.5)
+ tty-prompt (~> 0.21)
+ tty-screen (~> 0.6)
+ tty-table (~> 0.11)
+ uuidtools (~> 2.1.5)
+ win32-api (~> 1.5.3)
+ win32-certstore (~> 0.3)
+ win32-event (~> 0.6.1)
+ win32-eventlog (= 0.6.3)
+ win32-mmap (~> 0.4.1)
+ win32-mutex (~> 0.4.2)
+ win32-process (~> 0.9)
+ win32-service (>= 2.1.5, < 3.0)
+ win32-taskscheduler (~> 2.0)
+ wmi-lite (~> 1.0)
+ chef-cleanroom (1.0.2)
+ chef-config (16.7.61)
+ addressable
+ chef-utils (= 16.7.61)
fuzzyurl
- mixlib-config (~> 2.0)
- mixlib-shellout (~> 2.0)
- chef-sugar (3.4.0)
+ mixlib-config (>= 2.2.12, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ tomlrb (~> 1.2)
+ chef-utils (16.7.61)
+ chef-vault (4.1.0)
+ chef-zero (15.0.3)
+ ffi-yajl (~> 2.2)
+ hashie (>= 2.0, < 5.0)
+ mixlib-log (>= 2.0, < 4.0)
+ rack (~> 2.0, >= 2.0.6)
+ uuidtools (~> 2.1)
+ citrus (3.0.2)
cleanroom (1.0.0)
- coderay (1.1.1)
- debug_inspector (0.0.2)
+ concurrent-ruby (1.1.7)
+ diff-lcs (1.3)
+ ed25519 (1.2.4)
+ erubi (1.10.0)
erubis (2.7.0)
- faraday (0.9.2)
+ faraday (1.3.0)
+ faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
- ffi (1.9.14)
- ffi (1.9.14-x86-mingw32)
- ffi-yajl (2.3.0)
+ ruby2_keywords
+ faraday-net_http (1.0.0)
+ ffi (1.14.2)
+ ffi (1.14.2-x64-mingw32)
+ ffi (1.14.2-x86-mingw32)
+ ffi-libarchive (1.0.4)
+ ffi (~> 1.0)
+ ffi-win32-extensions (1.0.4)
+ ffi
+ ffi-yajl (2.3.4)
libyajl2 (~> 1.2)
fuzzyurl (0.9.0)
- gssapi (1.2.0)
+ gssapi (1.3.1)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
- hashie (3.4.6)
- hitimes (1.2.4)
- hitimes (1.2.4-x86-mingw32)
- httpclient (2.7.2)
+ hashie (4.1.0)
+ highline (2.0.3)
+ httpclient (2.8.3)
+ iniparse (1.5.0)
iostruct (0.0.4)
ipaddress (0.8.3)
- jmespath (1.3.1)
- json (2.0.2)
- kitchen-vagrant (0.19.0)
- test-kitchen (~> 1.4)
+ iso8601 (0.13.0)
+ jmespath (1.4.0)
+ json (2.5.1)
+ kitchen-vagrant (1.7.2)
+ test-kitchen (>= 1.4, < 3)
libyajl2 (1.2.0)
+ license-acceptance (2.1.13)
+ pastel (~> 0.7)
+ tomlrb (>= 1.2, < 3.0)
+ tty-box (~> 0.6)
+ tty-prompt (~> 0.20)
+ license_scout (1.2.7)
+ ffi-yajl (~> 2.2)
+ mixlib-shellout (>= 2.2, < 4.0)
+ toml-rb (>= 1, < 3)
little-plugger (1.1.4)
- logging (2.1.0)
+ logging (2.3.0)
little-plugger (~> 1.1)
- multi_json (~> 1.10)
- method_source (0.8.2)
- minitar (0.5.4)
- mixlib-archive (0.2.0)
+ multi_json (~> 1.14)
+ minitar (0.9)
+ mixlib-archive (1.0.7)
mixlib-log
- mixlib-authentication (1.4.1)
+ mixlib-archive (1.0.7-universal-mingw32)
mixlib-log
- mixlib-cli (1.7.0)
- mixlib-config (2.2.4)
- mixlib-install (1.2.0)
- artifactory
+ mixlib-authentication (3.0.7)
+ mixlib-cli (2.1.8)
+ mixlib-config (3.0.9)
+ tomlrb
+ mixlib-install (3.12.5)
mixlib-shellout
mixlib-versioning
- mixlib-log (1.7.1)
- mixlib-shellout (2.2.7)
- mixlib-shellout (2.2.7-universal-mingw32)
- win32-process (~> 0.8.2)
+ thor
+ mixlib-log (3.0.9)
+ mixlib-shellout (3.2.2)
+ chef-utils
+ mixlib-shellout (3.2.2-universal-mingw32)
+ chef-utils
+ ffi-win32-extensions (~> 1.0.3)
+ win32-process (~> 0.9)
wmi-lite (~> 1.0)
- mixlib-versioning (1.1.0)
- molinillo (0.4.5)
- multi_json (1.12.1)
- multipart-post (1.2.0)
- net-scp (1.2.1)
- net-ssh (>= 2.6.5)
- net-ssh (3.2.0)
- net-ssh-gateway (1.2.0)
+ mixlib-versioning (1.2.12)
+ molinillo (0.7.0)
+ multi_json (1.15.0)
+ multipart-post (2.1.1)
+ net-scp (3.0.0)
+ net-ssh (>= 2.6.5, < 7.0.0)
+ net-sftp (3.0.0)
+ net-ssh (>= 5.0.0, < 7.0.0)
+ net-ssh (6.1.0)
+ net-ssh-gateway (2.0.0)
+ net-ssh (>= 4.0.0)
+ net-ssh-multi (1.2.1)
net-ssh (>= 2.6.5)
- nio4r (1.2.1)
+ net-ssh-gateway (>= 1.2.0)
nori (2.6.0)
- octokit (4.3.0)
- sawyer (~> 0.7.0, >= 0.5.3)
- ohai (8.20.0)
- chef-config (>= 12.5.0.alpha.1, < 13)
+ octokit (4.20.0)
+ faraday (>= 0.9)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ ohai (16.8.1)
+ chef-config (>= 12.8, < 17)
+ chef-utils (>= 16.0, < 17)
ffi (~> 1.9)
ffi-yajl (~> 2.2)
ipaddress
- mixlib-cli
- mixlib-config (~> 2.0)
- mixlib-log (>= 1.7.1, < 2.0)
- mixlib-shellout (~> 2.0)
+ mixlib-cli (>= 1.7.0)
+ mixlib-config (>= 2.0, < 4.0)
+ mixlib-log (>= 2.0.1, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
plist (~> 3.1)
- systemu (~> 2.6.4)
+ train-core
wmi-lite (~> 1.0)
- plist (3.2.0)
- progressbar (0.21.0)
- pry (0.10.4)
- coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.0)
- byebug (~> 9.0)
- pry (~> 0.10)
- pry-stack_explorer (0.4.9.2)
- binding_of_caller (>= 0.7)
- pry (>= 0.9.11)
- retryable (2.0.4)
- ridley (4.6.1)
- addressable
- buff-config (~> 1.0)
- buff-extensions (~> 1.0)
- buff-ignore (~> 1.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)
- ruby-progressbar (1.8.1)
- rubyntlm (0.6.1)
- rubyzip (1.2.0)
- safe_yaml (1.0.4)
- sawyer (0.7.0)
- addressable (>= 2.3.5, < 2.5)
- faraday (~> 0.8, < 0.10)
- semverse (1.2.1)
- slop (3.6.0)
- solve (2.0.3)
- molinillo (~> 0.4.2)
- semverse (~> 1.1)
- systemu (2.6.5)
- test-kitchen (1.13.0)
- mixlib-install (~> 1.2)
- mixlib-shellout (>= 1.2, < 3.0)
- net-scp (~> 1.1)
- net-ssh (>= 2.9, < 4.0)
- net-ssh-gateway (~> 1.2.0)
- safe_yaml (~> 1.0)
- thor (~> 0.18)
- thor (0.19.1)
- timers (4.0.4)
- hitimes
- varia_model (0.4.1)
- buff-extensions (~> 1.0)
- hashie (>= 2.0.2, < 4.0.0)
- win32-process (0.8.3)
+ pastel (0.8.0)
+ tty-color (~> 0.5)
+ pedump (0.6.1)
+ awesome_print
+ iostruct (>= 0.0.4)
+ multipart-post (>= 2.0.0)
+ rainbow
+ zhexdump (>= 0.0.2)
+ plist (3.6.0)
+ proxifier (1.0.3)
+ public_suffix (4.0.6)
+ rack (2.2.3)
+ rainbow (3.0.0)
+ retryable (3.0.5)
+ ruby-progressbar (1.11.0)
+ ruby2_keywords (0.0.2)
+ rubyntlm (0.6.2)
+ rubyzip (2.3.0)
+ sawyer (0.8.2)
+ addressable (>= 2.3.5)
+ faraday (> 0.8, < 2.0)
+ semverse (3.0.0)
+ solve (4.0.4)
+ molinillo (~> 0.6)
+ semverse (>= 1.1, < 4.0)
+ strings (0.2.0)
+ strings-ansi (~> 0.2)
+ unicode-display_width (~> 1.5)
+ unicode_utils (~> 1.4)
+ strings-ansi (0.2.0)
+ structured_warnings (0.4.0)
+ syslog-logger (1.6.8)
+ test-kitchen (2.9.0)
+ bcrypt_pbkdf (~> 1.0)
+ ed25519 (~> 1.2)
+ license-acceptance (>= 1.0.11, < 3.0)
+ mixlib-install (~> 3.6)
+ mixlib-shellout (>= 1.2, < 4.0)
+ net-scp (>= 1.1, < 4.0)
+ net-ssh (>= 2.9, < 7.0)
+ net-ssh-gateway (>= 1.2, < 3.0)
+ thor (>= 0.19, < 2.0)
+ winrm (~> 2.0)
+ winrm-elevated (~> 1.0)
+ winrm-fs (~> 1.1)
+ thor (1.0.1)
+ toml-rb (2.0.1)
+ citrus (~> 3.0, > 3.0)
+ tomlrb (1.3.0)
+ train-core (3.4.4)
+ addressable (~> 2.5)
+ ffi (!= 1.13.0)
+ json (>= 1.8, < 3.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ net-scp (>= 1.2, < 4.0)
+ net-ssh (>= 2.9, < 7.0)
+ train-winrm (0.2.11)
+ winrm (~> 2.0)
+ winrm-elevated (~> 1.2.2)
+ winrm-fs (~> 1.0)
+ tty-box (0.7.0)
+ pastel (~> 0.8)
+ strings (~> 0.2.0)
+ tty-cursor (~> 0.7)
+ tty-color (0.6.0)
+ tty-cursor (0.7.1)
+ tty-prompt (0.23.0)
+ pastel (~> 0.8)
+ tty-reader (~> 0.8)
+ tty-reader (0.9.0)
+ tty-cursor (~> 0.7)
+ tty-screen (~> 0.8)
+ wisper (~> 2.0)
+ tty-screen (0.8.1)
+ tty-table (0.12.0)
+ pastel (~> 0.8)
+ strings (~> 0.2.0)
+ tty-screen (~> 0.8)
+ unicode-display_width (1.7.0)
+ unicode_utils (1.4.0)
+ uuidtools (2.1.5)
+ win32-api (1.5.3-universal-mingw32)
+ win32-certstore (0.4.1)
+ ffi
+ mixlib-shellout
+ win32-event (0.6.3)
+ win32-ipc (>= 0.6.0)
+ win32-eventlog (0.6.3)
+ ffi
+ win32-ipc (0.7.0)
+ ffi
+ win32-mmap (0.4.2)
+ ffi
+ win32-mutex (0.4.3)
+ win32-ipc (>= 0.6.0)
+ win32-process (0.9.0)
ffi (>= 1.0.0)
- winrm (2.0.2)
+ win32-service (2.2.0)
+ ffi
+ ffi-win32-extensions
+ win32-taskscheduler (2.0.4)
+ ffi
+ structured_warnings
+ winrm (2.3.5)
builder (>= 2.1.2)
- erubis (~> 2.7)
+ erubi (~> 1.8)
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, >= 0.6.1)
- winrm-fs (1.0.0)
- erubis (~> 2.7)
+ winrm-elevated (1.2.3)
+ erubi (~> 1.8)
+ winrm (~> 2.0)
+ winrm-fs (~> 1.0)
+ winrm-fs (1.3.5)
+ erubi (~> 1.8)
logging (>= 1.6.1, < 3.0)
- rubyzip (~> 1.1)
+ rubyzip (~> 2.0)
winrm (~> 2.0)
- wmi-lite (1.0.0)
+ wisper (2.0.1)
+ wmi-lite (1.0.5)
zhexdump (0.0.2)
PLATFORMS
ruby
+ x64-mingw32
x86-mingw32
DEPENDENCIES
- berkshelf (~> 4.0)
- kitchen-vagrant (~> 0.19.0)
- license_scout!
+ artifactory
+ berkshelf (>= 7.0)
+ kitchen-vagrant (>= 1.3.1)
+ ohai
omnibus!
omnibus-software!
- pedump!
- pry
- pry-byebug
- pry-stack_explorer
- test-kitchen (~> 1.13)
+ pedump
+ test-kitchen (>= 1.23)
winrm-fs (~> 1.0)
BUNDLED WITH
- 1.12.5
+ 2.1.4
diff --git a/omnibus/README.md b/omnibus/README.md
index 98a9094325..a10190c136 100644
--- a/omnibus/README.md
+++ b/omnibus/README.md
@@ -1,22 +1,20 @@
-Client Tools Omnibus project
-============================
+# Chef Infra Client Omnibus project
+
This project creates full-stack platform-specific packages for the following projects:
-* AngryChef
-* Chef
-* Chef with FIPS enabled
+- AngryChef
+- Chef
+
+## Installation
-Installation
-------------
-You must have a sane Ruby 1.9+ environment with Bundler installed. Ensure all
-the required gems are installed:
+You must have a sane Ruby environment with Bundler installed. Ensure all the required gems are installed:
```shell
-$ bundle install --without development
+bundle install --without development
```
-Usage
------
+## Usage
+
### Build
You create a platform-specific package using the `build project` command:
@@ -25,23 +23,17 @@ You create a platform-specific package using the `build project` command:
$ bundle exec omnibus build <PROJECT>
```
-The platform/architecture type of the package created will match the platform
-where the `build project` command is invoked. For example, running this command
-on a MacBook Pro will generate a Mac OS X package. After the build completes
-packages will be available in the `pkg/` folder.
+The platform/architecture type of the package created will match the platform where the `build project` command is invoked. For example, running this command on a MacBook Pro will generate a macOS package. After the build completes packages will be available in the `pkg/` folder.
### Clean
-You can clean up all temporary files generated during the build process with
-the `clean` command:
+You can clean up all temporary files generated during the build process with the `clean` command:
```shell
$ bundle exec omnibus clean <PROJECT>
```
-Adding the `--purge` purge option removes __ALL__ files generated during the
-build including the project install directory (`/opt/chef`) and
-the package cache directory (`/var/cache/omnibus/pkg`):
+Adding the `--purge` purge option removes **ALL** files generated during the build including the project install directory (`/opt/chef`) and the package cache directory (`/var/cache/omnibus/pkg`):
```shell
$ bundle exec omnibus clean <PROJECT> --purge
@@ -49,9 +41,7 @@ $ bundle exec omnibus clean <PROJECT> --purge
### Publish
-Omnibus has a built-in mechanism for releasing to a variety of "backends", such
-as Amazon S3 and Artifactory. You must set the proper credentials in your `omnibus.rb`
-config file or specify them via the command line.
+Omnibus has a built-in mechanism for releasing to a variety of "backends", such as Amazon S3 and Artifactory. You must set the proper credentials in your `omnibus.rb` config file or specify them via the command line.
```shell
$ bundle exec omnibus publish path/to/*.deb --backend s3
@@ -59,40 +49,33 @@ $ bundle exec omnibus publish path/to/*.deb --backend s3
### Help
-Full help for the Omnibus command line interface can be accessed with the
-`help` command:
+Full help for the Omnibus command line interface can be accessed with the `help` command:
```shell
$ bundle exec omnibus help
```
-Kitchen-based Build Environment
--------------------------------
-Every Omnibus project ships will a project-specific
-[Berksfile](http://berkshelf.com/) that will allow you to build your omnibus projects on all of the projects listed
-in the `.kitchen.yml`. You can add/remove additional platforms as needed by
-changing the list found in the `.kitchen.yml` `platforms` YAML stanza.
+## Kitchen-based Build Environment
-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
-your desired platform and execute the build steps listed above.
+Every Omnibus project ships will a project-specific [Berksfile](https://docs.chef.io/berkshelf/) that will allow you to build your omnibus projects on all of the projects listed in the `kitchen.yml`. You can add/remove additional platforms as needed by changing the list found in the `kitchen.yml` `platforms` YAML stanza.
-The default build environment requires Test Kitchen and VirtualBox for local
-development. Test Kitchen also exposes the ability to provision instances using
-various cloud providers like AWS, DigitalOcean, or OpenStack. For more
-information, please see the [Test Kitchen documentation](http://kitchen.ci).
+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/chef-cookbooks/omnibus) to setup your desired platform and execute the build steps listed above.
-Once you have tweaked your `.kitchen.yml` (or `.kitchen.local.yml`) to your
-liking, you can bring up an individual build environment using the `kitchen`
-command.
+The default build environment requires Test Kitchen and VirtualBox for local development. Test Kitchen also exposes the ability to provision instances using various cloud providers like AWS, DigitalOcean, or OpenStack. For more information, please see the [Test Kitchen documentation](http://kitchen.ci).
+
+Once you have tweaked your `kitchen.yml` (or `kitchen.local.yml`) to your liking, you can bring up an individual build environment using the `kitchen` command.
```shell
-$ bundle exec kitchen converge chef-ubuntu-1404
+$ bundle exec kitchen converge chef-ubuntu-1604
+```
+
+Additional settings are required if using the kitchen-vagrant driver with the Hyper-V provider:
+
+```
+PS> $env:KITCHEN_LOCAL_YAML="kitchen.hyperv.yml"; kitchen converge chef-windows-server-2012r2-standard
```
-Then login to the instance and build the project as described in the Usage
-section:
+Then login to the instance and build the project as described in the Usage section:
```shell
$ bundle exec kitchen login <PROJECT>-ubuntu-1204
@@ -101,8 +84,9 @@ $ bundle exec kitchen login <PROJECT>-ubuntu-1204
[vagrant@ubuntu...] $ ...
[vagrant@ubuntu...] $ bundle exec omnibus build <PROJECT> -l internal
```
+
```shell
-$ kitchen login chef-ubuntu-1404
+$ kitchen login chef-ubuntu-1604
[vagrant@ubuntu...] $ source load-omnibus-toolchain.sh
[vagrant@ubuntu...] $ cd chef/omnibus
[vagrant@ubuntu...] $ bundle install --without development # Don't install dev tools!
@@ -110,10 +94,7 @@ $ kitchen login chef-ubuntu-1404
[vagrant@ubuntu...] $ bundle exec omnibus build chef -l internal
```
-You can also login to Windows instances but will have to manually call the
-`load-omnibus-toolchain.bat` script which initializes the build environment.
-Please note the mounted code directory is also at `C:\home\vagrant\chef\omnibus`
-as opposed to `C:\Users\vagrant\chef\omnibus`.
+You can also login to Windows instances but will have to manually call the `load-omnibus-toolchain.ps1` script from an administrative PowerShell session which initializes the build environment. You will also need to `git clone https://github.com/chef/chef` into the `c:\vagrant` folder to workaround the lack of a shared folder.
```shell
$ bundle exec kitchen login <PROJECT>-windows-81-professional
@@ -121,22 +102,21 @@ Last login: Sat Sep 13 10:19:04 2014 from 172.16.27.1
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
-C:\>C:\vagrant\load-omnibus-toolchain.bat
+C:\>C:\vagrant\load-omnibus-toolchain.ps1
-C:\>cd C:\vagrant\code\chef\omnibus
+C:\>cd C:\vagrant\chef\omnibus
-C:\vagrant\code\chef\omnibus>bundle install --without development
+C:\vagrant\chef\omnibus>bundle install --without development
-C:\vagrant\code\chef\omnibus>bundle exec omnibus build chef -l internal
+C:\vagrant\chef\omnibus>bundle exec omnibus build chef -l internal
```
-For a complete list of all commands and platforms, run `kitchen list` or
-`kitchen help`.
+For a complete list of all commands and platforms, run `kitchen list` or `kitchen help`.
+
+## License
-License
--------
```text
-Copyright 2012-2016, Chef Software, Inc.
+Copyright:: 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.
diff --git a/omnibus/config/projects/angrychef.rb b/omnibus/config/projects/angrychef.rb
index 48902e6607..57f2c023fe 100644
--- a/omnibus/config/projects/angrychef.rb
+++ b/omnibus/config/projects/angrychef.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -20,8 +20,8 @@
# `config/project/chef.rb`.
#
current_file = __FILE__
-chef_project_contents = IO.read(File.expand_path("../chef.rb", __FILE__))
-self.instance_eval chef_project_contents
+chef_project_contents = IO.read(File.expand_path("chef.rb", __dir__))
+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 3d92d290a0..5a8c8e8da0 100644
--- a/omnibus/config/projects/chef.rb
+++ b/omnibus/config/projects/chef.rb
@@ -1,5 +1,5 @@
#
-# Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -15,11 +15,11 @@
#
name "chef"
-friendly_name "Chef Client"
+friendly_name "Chef Infra Client"
maintainer "Chef Software, Inc. <maintainers@chef.io>"
homepage "https://www.chef.io"
-license "Apache-2.0"
-license_file "../LICENSE"
+license "Chef EULA"
+license_file "CHEF-EULA.md"
build_iteration 1
# Do not use __FILE__ after this point, use current_file. If you use __FILE__
@@ -39,31 +39,55 @@ 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"
+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
+
+dependency "ruby-cleanup"
+
+# further gem cleanup other projects might not yet want to use
+dependency "more-ruby-cleanup"
package :rpm do
signing_passphrase ENV["OMNIBUS_RPM_SIGNING_PASSPHRASE"]
+ compression_level 1
+ compression_type :xz
+end
+
+package :deb do
+ compression_level 1
+ compression_type :xz
end
-proj_to_work_around_cleanroom = self
+proj_to_work_around_cleanroom = self # wat voodoo hackery is this?
package :pkg do
identifier "com.getchef.pkg.#{proj_to_work_around_cleanroom.name}"
- signing_identity "Developer ID Installer: Chef Software, Inc. (EU3VF8YLX2)"
+ signing_identity "Chef Software, Inc. (EU3VF8YLX2)"
end
compress :dmg
@@ -74,11 +98,12 @@ package :msi do
upgrade_code msi_upgrade_code
wix_candle_extension "WixUtilExtension"
wix_light_extension "WixUtilExtension"
- signing_identity "F74E1A68005E8A9C465C3D2FF7B41F3988F0EA09", machine_store: true
+ signing_identity "AF21BA8C9E50AE20DA9907B6E2D4B0CC3306CA03", 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
+# We don't support appx builds, and they eat a lot of time.
package :appx do
- signing_identity "F74E1A68005E8A9C465C3D2FF7B41F3988F0EA09", machine_store: true
+ skip_packager true
end
diff --git a/omnibus/config/software/chef-appbundle.rb b/omnibus/config/software/chef-appbundle.rb
deleted file mode 100644
index 8ea21103fb..0000000000
--- a/omnibus/config/software/chef-appbundle.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-name "chef-appbundle"
-default_version "local_source"
-
-license :project_license
-skip_transitive_dependency_licensing true
-
-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 8ca370c832..0000000000
--- a/omnibus/config/software/chef-complete.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-name "chef-complete"
-
-license :project_license
-skip_transitive_dependency_licensing true
-
-dependency "chef"
-dependency "chef-appbundle"
-dependency "chef-remove-docs"
-
-dependency "gem-permissions"
-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
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 3e7a9f9c70..0000000000
--- a/omnibus/config/software/chef-gem-binding_of_caller.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "MIT"
-license_file "https://github.com/banister/binding_of_caller/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-byebug.rb b/omnibus/config/software/chef-gem-byebug.rb
deleted file mode 100644
index 3aef706e82..0000000000
--- a/omnibus/config/software/chef-gem-byebug.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "MIT"
-license_file "https://github.com/deivid-rodriguez/byebug/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
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 ab818768ea..0000000000
--- a/omnibus/config/software/chef-gem-debug_inspector.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "MIT"
-license_file "https://github.com/banister/debug_inspector/blob/master/README.md"
-skip_transitive_dependency_licensing true
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 44f98446bd..0000000000
--- a/omnibus/config/software/chef-gem-ffi-yajl.rb
+++ /dev/null
@@ -1,12 +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__)
-
-license "MIT"
-license_file "https://github.com/chef/ffi-yajl/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
-
-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 ea8879c2ac..0000000000
--- a/omnibus/config/software/chef-gem-ffi.rb
+++ /dev/null
@@ -1,12 +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__)
-
-license "BSD-3-Clause"
-license_file "https://github.com/ffi/ffi/blob/master/LICENSE"
-license_file "https://github.com/ffi/ffi/blob/master/COPYING"
-license_file "https://github.com/ffi/ffi/blob/master/LICENSE.SPECS"
-skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-json.rb b/omnibus/config/software/chef-gem-json.rb
deleted file mode 100644
index 9217359ba2..0000000000
--- a/omnibus/config/software/chef-gem-json.rb
+++ /dev/null
@@ -1,11 +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__)
-
-license "Ruby"
-license_file "https://github.com/flori/json/blob/master/README.md"
-license_file "https://www.ruby-lang.org/en/about/license.txt"
-skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-libyajl2.rb b/omnibus/config/software/chef-gem-libyajl2.rb
deleted file mode 100644
index 47ef42e1cf..0000000000
--- a/omnibus/config/software/chef-gem-libyajl2.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "Apache-2.0"
-license_file "https://github.com/chef/libyajl2-gem/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
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 36a2b833dd..0000000000
--- a/omnibus/config/software/chef-gem-mini_portile2.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "MIT"
-license_file "https://github.com/flavorjones/mini_portile/blob/master/LICENSE.txt"
-skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-nokogiri.rb b/omnibus/config/software/chef-gem-nokogiri.rb
deleted file mode 100644
index c6b8d03822..0000000000
--- a/omnibus/config/software/chef-gem-nokogiri.rb
+++ /dev/null
@@ -1,13 +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__)
-
-license "MIT"
-license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
-
-dependency "chef-gem-pkg-config"
-dependency "chef-gem-mini_portile2"
diff --git a/omnibus/config/software/chef-gem-pkg-config.rb b/omnibus/config/software/chef-gem-pkg-config.rb
deleted file mode 100644
index 051091b73f..0000000000
--- a/omnibus/config/software/chef-gem-pkg-config.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "LGPL-2.1"
-license_file "https://github.com/ruby-gnome2/pkg-config/blob/master/LGPL-2.1"
-skip_transitive_dependency_licensing true
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 af90212d23..0000000000
--- a/omnibus/config/software/chef-gem-ruby-prof.rb
+++ /dev/null
@@ -1,10 +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__)
-
-license "BSD-2-Clause"
-license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE"
-skip_transitive_dependency_licensing true
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 02fc906d9d..0000000000
--- a/omnibus/config/software/chef-gem-ruby-shadow.rb
+++ /dev/null
@@ -1,11 +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__)
-
-license "Public-Domain"
-license_file "https://github.com/apalmblad/ruby-shadow/blob/master/LICENSE"
-license_file "http://creativecommons.org/licenses/publicdomain/"
-skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-remove-docs.rb b/omnibus/config/software/chef-remove-docs.rb
deleted file mode 100644
index 31e2797afd..0000000000
--- a/omnibus/config/software/chef-remove-docs.rb
+++ /dev/null
@@ -1,34 +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
-skip_transitive_dependency_licensing true
-
-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 c6ced7e566..0000000000
--- a/omnibus/config/software/chef.rb
+++ /dev/null
@@ -1,83 +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
-
- # 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/more-ruby-cleanup.rb b/omnibus/config/software/more-ruby-cleanup.rb
new file mode 100644
index 0000000000..d7c3b6000f
--- /dev/null
+++ b/omnibus/config/software/more-ruby-cleanup.rb
@@ -0,0 +1,143 @@
+#
+# Copyright:: Copyright (c) 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 "fileutils"
+
+name "more-ruby-cleanup"
+
+skip_transitive_dependency_licensing true
+license :project_license
+
+source path: "#{project.files_path}/#{name}"
+
+dependency "ruby"
+
+build do
+ block "Removing console and setup binaries" do
+ Dir.glob("#{install_dir}/embedded/lib/ruby/gems/*/gems/*/bin/{console,setup}").each do |f|
+ puts "Deleting #{f}"
+ FileUtils.rm_rf(f)
+ end
+ end
+
+ block "remove any .gitkeep files" do
+ Dir.glob("#{install_dir}/**/{.gitkeep,.keep}").each do |f|
+ puts "Deleting #{f}"
+ File.delete(f)
+ end
+ end
+
+ block "Removing additional non-code files from installed gems" do
+ # find the embedded ruby gems dir and clean it up for globbing
+ target_dir = "#{install_dir}/embedded/lib/ruby/gems/*/gems".tr('\\', "/")
+ files = %w{
+ .rspec-tm
+ .sitearchdir.time
+ *-public_cert.pem
+ bootstrap.sh
+ diagrams
+ example
+ examples
+ ext
+ Gemfile.lock
+ install.rb
+ java
+ patches
+ perf
+ rakelib
+ README-json-jruby.md
+ references
+ sample
+ samples
+ script
+ site
+ vendor
+ VERSION
+ }
+
+ Dir.glob("#{target_dir}/*/{#{files.join(",")}}").each do |f|
+ # chef stores the powershell dlls in the ext dir
+ next if File.basename(File.expand_path("..", f)).start_with?("chef-")
+
+ puts "Deleting #{f}"
+ FileUtils.rm_rf(f)
+ end
+ end
+
+ block "Removing Gemspec / Rakefile / Gemfile unless there's a bin dir / not a chef gem" do
+ # find the embedded ruby gems dir and clean it up for globbing
+ target_dir = "#{install_dir}/embedded/lib/ruby/gems/*/gems".tr('\\', "/")
+ files = %w{
+ *.gemspec
+ Gemfile
+ Rakefile
+ tasks
+ }
+
+ Dir.glob("#{target_dir}/*/{#{files.join(",")}}").each do |f|
+ # don't delete these files if there's a non-empty bin dir in the same dir
+ next if Dir.exist?(File.join(File.dirname(f), "bin")) && !Dir.empty?(File.join(File.dirname(f), "bin"))
+
+ # don't perform this cleanup in chef gems
+ next if File.basename(File.expand_path("..", f)).start_with?("chef-")
+
+ puts "Deleting #{f}"
+ FileUtils.rm_rf(f)
+ end
+ end
+
+ block "Removing spec dirs from non-Chef gems" do
+ Dir.glob("#{install_dir}/embedded/lib/ruby/gems/*/gems/*/spec".tr('\\', "/")).each do |f|
+ # if we're in a chef- gem then don't remove the specs
+ next if File.basename(File.expand_path("..", f)).start_with?("chef-")
+
+ puts "Deleting #{f}"
+ FileUtils.rm_rf(f)
+ end
+ end
+
+ block "Remove extra unused binaries that are built with libraries we ship" do
+ %w{
+ xml2-config
+ xmlcatalog
+ xmllint
+ xslt-config
+ xsltproc
+ coderay
+ ffi-yajl-bench
+ }.each do |f|
+ file_path = "#{install_dir}/embedded/bin/#{f}"
+
+ if ::File.exist?(file_path)
+ puts "Deleting binary at #{file_path}"
+ FileUtils.rm_f(file_path)
+ else
+ puts "Binary #{file_path} not found. Skipping."
+ end
+ end
+ end
+
+ block "Remove deprecated fauxhai dumps we don't need for running chef-utils specs" do
+ require "json"
+ Dir.glob("#{install_dir}/embedded/lib/ruby/gems/*/gems/fauxhai*/lib/fauxhai/platforms/**/*.json") do |file_path|
+ if JSON.parse(File.read(file_path))["deprecated"]
+ puts "Deleted deprecated Fauxhai definition at #{file_path}"
+ FileUtils.rm_f(file_path)
+ end
+ end
+ end
+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/openssl-customization/windows/ssl_env_hack.rb b/omnibus/files/openssl-customization/windows/ssl_env_hack.rb
index 26b68e4191..27ba267e36 100644
--- a/omnibus/files/openssl-customization/windows/ssl_env_hack.rb
+++ b/omnibus/files/openssl-customization/windows/ssl_env_hack.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
#
# This script sets the SSL_CERT_FILE environment variable to the CA cert bundle
-# that ships with omnibus packages of Chef and Chef DK. If this environment
-# variable is already configured, this script is a no-op.
+# that ships with omnibus packages of Chef Infra Client and Chef Workstation. If
+# this environment variable is already configured, this script is a no-op.
#
# This is required to make Chef tools use https URLs out of the box.
unless ENV.key?("SSL_CERT_FILE")
- base_dirs = File.dirname(__FILE__).split(File::SEPARATOR)
+ base_dirs = __dir__.split(File::SEPARATOR)
(base_dirs.length - 1).downto(0) do |i|
candidate_ca_bundle = File.join(base_dirs[0..i] + [ "ssl/certs/cacert.pem" ])
diff --git a/omnibus/kitchen.yml b/omnibus/kitchen.yml
new file mode 100644
index 0000000000..a3db5b8499
--- /dev/null
+++ b/omnibus/kitchen.yml
@@ -0,0 +1,118 @@
+#
+# NOTE: this runs the omnibus cookbook, but does not actually run Omnibus. Use
+# 'kichen converge' to setup the virtual machine and then `kitchen login` to
+# SSH into the machine and run Omnibus.
+#
+
+driver:
+ name: vagrant
+ forward_agent: yes
+ customize:
+ cpus: 4
+ memory: 4096
+ synced_folders:
+ - ['../..', '/vagrant/code']
+ - ['../../omnibus', '/home/vagrant/omnibus']
+ - ['../../omnibus-software', '/home/vagrant/omnibus-software']
+
+provisioner:
+ name: chef_zero
+ attributes:
+ vagrant:
+ this_key_exists_so_we_have_a_vagrant_key: true
+ omnibus:
+ build_user: vagrant
+ build_user_group: vagrant
+ build_user_password: vagrant
+ product_name: angrychef
+ product_version: latest
+ chef_omnibus_root: /opt/angrychef
+ chef_license: accept-no-persist
+
+platforms:
+ - name: centos-6
+ run_list: yum-epel::default
+ - name: centos-7
+ run_list: yum-epel::default
+ - name: debian-9
+ run_list: apt::default
+ - name: freebsd-11
+ run_list: freebsd::portsnap
+ - name: ubuntu-18.04
+ run_list: apt::default
+
+ # macOS
+ - name: macos-10.15
+ driver:
+ customize:
+ numvcpus: 4
+ memsize: 4096
+ box: tas50/macos_10.15
+ synced_folders:
+ - ['..', '/Users/vagrant/chef']
+ - ['../../omnibus', '/Users/vagrant/omnibus']
+ - ['../../omnibus-software', '/Users/vagrant/omnibus-software']
+
+ <% %w(
+ 10
+ 2012r2
+ ).each do |win_version| %>
+ # Windows 64-bit
+ - name: windows-<%= win_version %>
+ driver:
+ box: tas50/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
+ # mounts at `C:\vagrant\code` and the Chef source folder is available
+ # at `C:\vagrant\code\chef`
+ - ['../..', '/vagrant/code']
+ communicator: winrm
+ provisioner:
+ attributes:
+ omnibus:
+ build_user: vagrant
+ build_user_group: Administrators
+ build_user_password: vagrant
+ chef_omnibus_root: /opscode/angrychef
+ transport:
+ name: winrm
+ elevated: true
+
+ # 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: tas50/windows-<%= win_version %> # private
+ synced_folders:
+ - ['../..', '/vagrant/code']
+ communicator: winrm
+ provisioner:
+ attributes:
+ omnibus:
+ build_user: vagrant
+ build_user_group: Administrators
+ build_user_password: vagrant
+ chef_omnibus_root: /opscode/angrychef
+ transport:
+ name: winrm
+ elevated: true
+ <% end %>
+
+suites:
+ # - name: angrychef
+ # attributes:
+ # omnibus:
+ # <<: *attribute_defaults
+ # install_dir: /opt/angrychef
+ # run_list:
+ # - omnibus::default
+ - name: chef
+ attributes:
+ omnibus:
+ toolchain_channel: stable
+ toolchain_version: latest
+ install_dir: /opt/chef
+ run_list:
+ - omnibus::default
diff --git a/omnibus/omnibus-test.ps1 b/omnibus/omnibus-test.ps1
new file mode 100644
index 0000000000..aa23a6442a
--- /dev/null
+++ b/omnibus/omnibus-test.ps1
@@ -0,0 +1,126 @@
+# Stop script execution when a non-terminating error occurs
+$ErrorActionPreference = "Stop"
+
+# install chocolatey
+Set-ExecutionPolicy Bypass -Scope Process -Force
+[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
+
+# install powershell core
+Invoke-WebRequest "https://github.com/PowerShell/PowerShell/releases/download/v7.0.3/PowerShell-7.0.3-win-x64.msi" -UseBasicParsing -OutFile powershell.msi
+Start-Process msiexec.exe -Wait -ArgumentList "/package PowerShell.msi /quiet"
+$env:path += ";C:\Program Files\PowerShell\7"
+
+$channel = "$Env:CHANNEL"
+If ([string]::IsNullOrEmpty($channel)) { $channel = "unstable" }
+
+$product = "$Env:PRODUCT"
+If ([string]::IsNullOrEmpty($product)) { $product = "chef" }
+
+$version = "$Env:VERSION"
+If ([string]::IsNullOrEmpty($version)) { $version = "latest" }
+
+Write-Output "--- Installing $channel $product $version"
+$package_file = $(C:\opscode\omnibus-toolchain\bin\install-omnibus-product.ps1 -Product "$product" -Channel "$channel" -Version "$version" | Select-Object -Last 1)
+
+Write-Output "--- Verifying omnibus package is signed"
+C:\opscode\omnibus-toolchain\bin\check-omnibus-package-signed.ps1 "$package_file"
+
+Write-Output "--- Running verification for $channel $product $version"
+
+# We don't want to add the embedded bin dir to the main PATH as this
+# could mask issues in our binstub shebangs.
+$embedded_bin_dir = "C:\opscode\$product\embedded\bin"
+
+# Set TEMP and TMP environment variables to a short path because buildkite-agent user's default path is so long it causes tests to fail
+$Env:TEMP = "C:\cheftest"
+$Env:TMP = "C:\cheftest"
+Remove-Item -Recurse -Force $Env:TEMP -ErrorAction SilentlyContinue
+New-Item -ItemType directory -Path $Env:TEMP
+
+# FIXME: we should really use Bundler.with_unbundled_env in the caller instead of re-inventing it here
+Remove-Item Env:_ORIGINAL_GEM_PATH -ErrorAction SilentlyContinue
+Remove-Item Env:BUNDLE_BIN_PATH -ErrorAction SilentlyContinue
+Remove-Item Env:BUNDLE_GEMFILE -ErrorAction SilentlyContinue
+Remove-Item Env:GEM_HOME -ErrorAction SilentlyContinue
+Remove-Item Env:GEM_PATH -ErrorAction SilentlyContinue
+Remove-Item Env:GEM_ROOT -ErrorAction SilentlyContinue
+Remove-Item Env:RUBYLIB -ErrorAction SilentlyContinue
+Remove-Item Env:RUBYOPT -ErrorAction SilentlyContinue
+Remove-Item Env:RUBY_ENGINE -ErrorAction SilentlyContinue
+Remove-Item Env:RUBY_ROOT -ErrorAction SilentlyContinue
+Remove-Item Env:RUBY_VERSION -ErrorAction SilentlyContinue
+Remove-Item Env:BUNDLER_VERSION -ErrorAction SilentlyContinue
+
+ForEach ($b in
+ "chef-client",
+ "knife",
+ "chef-solo",
+ "ohai"
+) {
+ Write-Output "Checking for existence of binfile $b..."
+
+ If (Test-Path -PathType Leaf -Path "C:\opscode\$product\bin\$b") {
+ Write-Output "Found $b!"
+ }
+ Else {
+ Write-Output "Error: Could not find $b"
+ exit 1
+ }
+}
+
+$Env:PATH = "C:\opscode\$product\bin;$Env:PATH"
+
+chef-client --version
+
+# Exercise various packaged tools to validate binstub shebangs
+& $embedded_bin_dir\ruby --version
+& $embedded_bin_dir\gem.bat --version
+& $embedded_bin_dir\bundle.bat --version
+& $embedded_bin_dir\rspec.bat --version
+
+$Env:PATH = "C:\opscode\$product\bin;C:\opscode\$product\embedded\bin;$Env:PATH"
+
+# Test against the vendored chef gem (cd into the output of "gem which chef")
+$chefdir = gem which chef
+$chefdir = Split-Path -Path "$chefdir" -Parent
+$chefdir = Split-Path -Path "$chefdir" -Parent
+Set-Location -Path $chefdir
+
+Get-Location
+
+# ffi-yajl must run in c-extension mode for perf, so force it so we don't accidentally fall back to ffi
+$Env:FORCE_FFI_YAJL = "ext"
+
+# accept license
+$Env:CHEF_LICENSE = "accept-no-persist"
+
+# some tests need winrm configured
+winrm quickconfig -quiet
+
+bundle
+If ($lastexitcode -ne 0) { Exit $lastexitcode }
+
+# buildkite changes the casing of the Path variable to PATH
+# It is not clear how or where that happens, but it breaks the choco
+# tests. Removing the PATH and resetting it with the expected casing
+$p = $env:PATH
+$env:PATH = $null
+$env:Path = $p
+
+# Running the specs separately fixes an edge case on 2012R2-i386 where the desktop heap's
+# allocated limit is hit and any test's attempt to create a new process is met with
+# exit code -1073741502 (STATUS_DLL_INIT_FAILED). after much research and troubleshooting,
+# desktop heap exhaustion seems likely (https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/desktop-heap-overview)
+$exit = 0
+
+bundle exec rspec -f progress --profile -- ./spec/unit
+If ($lastexitcode -ne 0) { $exit = 1 }
+
+bundle exec rspec -f progress --profile -- ./spec/functional
+If ($lastexitcode -ne 0) { $exit = 1 }
+
+bundle exec rspec -f progress --profile -- ./spec/integration
+If ($lastexitcode -ne 0) { $exit = 1 }
+
+Exit $exit
diff --git a/omnibus/omnibus-test.sh b/omnibus/omnibus-test.sh
new file mode 100644
index 0000000000..2c1313681c
--- /dev/null
+++ b/omnibus/omnibus-test.sh
@@ -0,0 +1,154 @@
+#!/bin/bash
+set -ueo pipefail
+
+channel="${CHANNEL:-unstable}"
+product="${PRODUCT:-chef}"
+version="${VERSION:-latest}"
+
+export INSTALL_DIR="/opt/$product"
+
+echo "--- Installing $channel $product $version"
+package_file="$("/opt/$TOOLCHAIN/bin/install-omnibus-product" -c "$channel" -P "$product" -v "$version" | tail -1)"
+
+echo "--- Verifying omnibus package is signed"
+"/opt/$TOOLCHAIN/bin/check-omnibus-package-signed" "$package_file"
+
+sudo rm -f "$package_file"
+
+echo "--- Verifying ownership of package files"
+
+NONROOT_FILES="$(find "$INSTALL_DIR" ! -user 0 -print)"
+if [[ "$NONROOT_FILES" == "" ]]; then
+ echo "Packages files are owned by root. Continuing verification."
+else
+ echo "Exiting with an error because the following files are not owned by root:"
+ echo "$NONROOT_FILES"
+ exit 1
+fi
+
+echo "--- Running verification for $channel $product $version"
+
+# Our tests hammer YUM pretty hard and the EL6 testers get corrupted
+# after some period of time. Rebuilding the RPM database clears
+# up the underlying corruption. We'll do this each test run just to
+# be safe.
+if [[ -f /etc/redhat-release ]]; then
+ major_version="$(sed 's/^.\+ release \([0-9]\+\).*/\1/' /etc/redhat-release)"
+ if [[ "$major_version" -lt "7" ]]; then
+ sudo rm -rf /var/lib/rpm/__db*
+ sudo db_verify /var/lib/rpm/Packages
+ sudo rpm --rebuilddb
+ sudo yum clean all
+ fi
+fi
+
+# Set up a custom tmpdir, and clean it up before and after the tests
+export TMPDIR="${TMPDIR:-/tmp}/cheftest"
+sudo rm -rf "$TMPDIR"
+mkdir -p "$TMPDIR"
+
+# Verify that we kill any orphaned test processes. Kill any orphaned rspec processes.
+sudo kill -9 $(ps ax | grep 'rspec' | grep -v grep | awk '{ print $1 }') || true
+
+export PATH="/opt/$product/bin:$PATH"
+
+export BIN_DIR="/opt/$product/bin"
+
+# We don't want to add the embedded bin dir to the main PATH as this
+# could mask issues in our binstub shebangs.
+export EMBEDDED_BIN_DIR="/opt/$product/embedded/bin"
+
+# If we are on Mac our symlinks are located under /usr/local/bin
+# otherwise they are under /usr/bin
+if [[ -f /usr/bin/sw_vers ]]; then
+ export USR_BIN_DIR="/usr/local/bin"
+else
+ export USR_BIN_DIR="/usr/bin"
+fi
+
+# sanity check that we're getting the correct symlinks from the pre-install script
+# solaris doesn't have readlink or test -e. ls -n is different on BSD. proceed with caution.
+if [[ ! -L $USR_BIN_DIR/chef-client ]] || [[ $(ls -l $USR_BIN_DIR/chef-client | awk '{print$NF}') != "$BIN_DIR/chef-client" ]]; then
+ echo "$USR_BIN_DIR/chef-client symlink to $BIN_DIR/chef-client was not correctly created by the pre-install script!"
+ exit 1
+fi
+
+if [[ ! -L $USR_BIN_DIR/knife ]] || [[ $(ls -l $USR_BIN_DIR/knife | awk '{print$NF}') != "$BIN_DIR/knife" ]]; then
+ echo "$USR_BIN_DIR/knife symlink to $BIN_DIR/knife was not correctly created by the pre-install script!"
+ exit 1
+fi
+
+if [[ ! -L $USR_BIN_DIR/chef-solo ]] || [[ $(ls -l $USR_BIN_DIR/chef-solo | awk '{print$NF}') != "$BIN_DIR/chef-solo" ]]; then
+ echo "$USR_BIN_DIR/chef-solo symlink to $BIN_DIR/chef-solo was not correctly created by the pre-install script!"
+ exit 1
+fi
+
+if [[ ! -L $USR_BIN_DIR/ohai ]] || [[ $(ls -l $USR_BIN_DIR/ohai | awk '{print$NF}') != "$BIN_DIR/ohai" ]]; then
+ echo "$USR_BIN_DIR/ohai symlink to $BIN_DIR/ohai was not correctly created by the pre-install script!"
+ exit 1
+fi
+
+if [[ ! -x $EMBEDDED_BIN_DIR/inspec ]]; then
+ echo "$EMBEDDED_BIN_DIR/inspec does not exist!"
+ exit 1
+fi
+
+# Ensure the calling environment (disapproval look Bundler) does not
+# infect our Ruby environment created by the `chef-client` cli.
+for ruby_env_var in _ORIGINAL_GEM_PATH \
+ BUNDLE_BIN_PATH \
+ BUNDLE_GEMFILE \
+ GEM_HOME \
+ GEM_PATH \
+ GEM_ROOT \
+ RUBYLIB \
+ RUBYOPT \
+ RUBY_ENGINE \
+ RUBY_ROOT \
+ RUBY_VERSION \
+ BUNDLER_VERSION
+
+do
+ unset $ruby_env_var
+done
+
+chef-client --version
+
+# Exercise various packaged tools to validate binstub shebangs
+"$EMBEDDED_BIN_DIR/ruby" --version
+"$EMBEDDED_BIN_DIR/gem" --version
+"$EMBEDDED_BIN_DIR/bundle" --version
+"$EMBEDDED_BIN_DIR/rspec" --version
+
+# ffi-yajl must run in c-extension mode or we take perf hits, so we force it
+# before running rspec so that we don't wind up testing the ffi mode
+export FORCE_FFI_YAJL=ext
+
+# chef-shell smoke tests require "rb-readline" which requires "infocmp"
+# most platforms provide "infocmp" by default via an "ncurses" package but SLES 12 provide it via "ncurses-devel" which
+# isn't typically installed. omnibus-toolchain has "infocmp" built-in so we add omnibus-toolchain to the PATH to ensure
+# tests will function properly.
+export PATH="/opt/$TOOLCHAIN/bin:/usr/local/bin:/opt/$TOOLCHAIN/embedded/bin:$PATH"
+
+# add chef's bin paths to PATH to ensure tests function properly
+export PATH="/opt/$product/bin:/opt/$product/embedded/bin:$PATH"
+
+gem_list="$(gem which chef)"
+lib_dir="$(dirname "$gem_list")"
+chef_gem="$(dirname "$lib_dir")"
+
+# ensure that PATH doesn't get reset by sudoers
+if [[ -d /etc/sudoers.d ]]; then
+ echo "Defaults:$(id -un) !secure_path, exempt_group += $(id -gn)" | sudo tee "/etc/sudoers.d/$(id -un)-preserve_path"
+ sudo chmod 440 "/etc/sudoers.d/$(id -un)-preserve_path"
+elif [[ -d /usr/local/etc/sudoers.d ]]; then
+ echo "Defaults:$(id -un) !secure_path, exempt_group += $(id -gn)" | sudo tee "/usr/local/etc/sudoers.d/$(id -un)-preserve_path"
+ sudo chmod 440 "/usr/local/etc/sudoers.d/$(id -un)-preserve_path"
+fi
+
+# accept license
+export CHEF_LICENSE=accept-no-persist
+
+cd "$chef_gem"
+sudo -E bundle install --jobs=3 --retry=3
+sudo -E bundle exec rspec --profile -f progress
diff --git a/omnibus/omnibus.rb b/omnibus/omnibus.rb
index c6f883adbc..99817f7caf 100644
--- a/omnibus/omnibus.rb
+++ b/omnibus/omnibus.rb
@@ -1,5 +1,5 @@
#
-# This file is used to configure the Omnibus projects in this repo. It contains
+# This file is used to configure the Chef Infra Client project. It contains
# some minimal configuration examples for working with Omnibus. For a full list
# of configurable options, please see the documentation for +omnibus/config.rb+.
#
@@ -29,11 +29,9 @@
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
+windows_arch env_omnibus_windows_arch
-# Disable git caching
-# ------------------------------
-# use_git_caching false
+use_git_caching true
# Enable S3 asset caching
# ------------------------------
@@ -52,3 +50,4 @@ fetcher_read_timeout 120
# 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/package-scripts/angrychef/postinst b/omnibus/package-scripts/angrychef/postinst
index a173efeaef..c8f75105ce 100755
--- a/omnibus/package-scripts/angrychef/postinst
+++ b/omnibus/package-scripts/angrychef/postinst
@@ -23,17 +23,10 @@ error_exit()
is_darwin()
{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
+ uname -a | grep "^Darwin" 2>&1 >/dev/null
}
-is_smartos()
-{
- uname -v | grep "^joyent" 2>&1 >/dev/null
-}
-
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
+if is_darwin; then
PREFIX="/usr/local"
mkdir -p "$PREFIX/bin"
else
@@ -106,6 +99,6 @@ ln -sf $INSTALLER_DIR/bin/chef-client $PREFIX/bin || error_exit "Cannot link che
# be manually fixed.
chown -Rh 0:0 $INSTALLER_DIR
-echo "Thank you for installing Chef!"
+echo "Thank you for installing Chef Infra Client! For help getting started visit https://learn.chef.io"
exit 0
diff --git a/omnibus/package-scripts/angrychef/postrm b/omnibus/package-scripts/angrychef/postrm
index 247688074e..43b5f22c98 100755
--- a/omnibus/package-scripts/angrychef/postrm
+++ b/omnibus/package-scripts/angrychef/postrm
@@ -7,18 +7,20 @@
# this programming language. do not touch.
# - if you are under 40, get peer review from your elders.
-is_smartos() {
- uname -v | grep "^joyent" 2>&1 >/dev/null
+is_darwin() {
+ uname -a | grep "^Darwin" 2>&1 >/dev/null
}
-is_darwin()
-{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
+is_suse() {
+ if [ -f /etc/os-release ]; then
+ . /etc/os-release
+ [ "$ID_LIKE" = "sles" ] || [ "$ID_LIKE" = "suse" ]
+ else
+ [ -f /etc/SuSE-release ]
+ fi
}
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
+if is_darwin; then
PREFIX="/usr/local"
else
PREFIX="/usr"
@@ -33,10 +35,10 @@ cleanup_symlinks() {
# Clean up binary symlinks if they exist
# see: http://tickets.opscode.com/browse/CHEF-3022
-if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then
+if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! is_suse ]; then
# not a redhat-ish RPM-based system
cleanup_symlinks
elif [ "x$1" = "x0" ]; then
- # RPM-based system and we're deinstalling rather than upgrading
+ # RPM-based system and we're uninstalling rather than upgrading
cleanup_symlinks
fi
diff --git a/omnibus/package-scripts/angrychef/preinst b/omnibus/package-scripts/angrychef/preinst
new file mode 100755
index 0000000000..e38c3f7a3e
--- /dev/null
+++ b/omnibus/package-scripts/angrychef/preinst
@@ -0,0 +1,23 @@
+#!/bin/sh
+# WARNING: REQUIRES /bin/sh
+#
+# - must run on /bin/sh on solaris 9
+# - must run on /bin/sh on AIX 6.x
+# - if you think you are a bash wizard, you probably do not understand
+# this programming language. do not touch.
+# - if you are under 40, get peer review from your elders.
+
+INSTALLER_DIR=/opt/chef
+if [ -e $INSTALLER_DIR ]; then
+ echo "removing $INSTALLER_DIR..."
+
+ # have to do this dance of moving /opt/chef to a tmp dir since files may be in use
+ tmp_dir="/opt/.chef.$$"
+ # if we can't create the tmp_dir then fail hard to prevent any possible security hole
+ (umask 077 && mkdir $tmp_dir) || exit 1
+ # now we can clean up the tmp_dir we created safely
+ mv $INSTALLER_DIR $tmp_dir
+ # ignore errors which must be EBUSY issues, this may crate some litter, which may
+ # be unavoidable
+ rm -rf $tmp_dir || true
+fi
diff --git a/omnibus/package-scripts/chef-fips/postinst b/omnibus/package-scripts/chef-fips/postinst
deleted file mode 100755
index 8aa6f19ec1..0000000000
--- a/omnibus/package-scripts/chef-fips/postinst
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/bin/sh
-# WARNING: REQUIRES /bin/sh
-#
-# - must run on /bin/sh on solaris 9
-# - must run on /bin/sh on AIX 6.x
-# - if you think you are a bash wizard, you probably do not understand
-# this programming language. do not touch.
-# - if you are under 40, get peer review from your elders.
-#
-# Install a full Opscode Client
-#
-
-PROGNAME=`basename $0`
-INSTALLER_DIR=/opt/chef-fips
-CONFIG_DIR=/etc/chef
-USAGE="usage: $0 [-v validation_key] ([-o organization] || [-u url])"
-
-error_exit()
-{
- echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
- exit 1
-}
-
-is_darwin()
-{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
-}
-
-is_smartos()
-{
- uname -v | grep "^joyent" 2>&1 >/dev/null
-}
-
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
- PREFIX="/usr/local"
- mkdir -p "$PREFIX/bin"
-else
- PREFIX="/usr"
-fi
-
-validation_key=
-organization=
-chef_url=
-
-while getopts o:u:v: opt
-do
- case "$opt" in
- v) validation_key="${OPTARG}";;
- o) organization="${OPTARG}"; chef_url="https://api.opscode.com/organizations/${OPTARG}";;
- u) chef_url="${OPTARG}";;
- \?) # unknown flag
- echo >&2 ${USAGE}
- exit 1;;
- esac
-done
-shift `expr ${OPTIND} - 1`
-
-if [ "" != "$chef_url" ]; then
- mkdir -p ${CONFIG_DIR} || error_exit "Cannot create ${CONFIG_DIR}!"
- (
- cat <<'EOP'
-log_level :info
-log_location STDOUT
-EOP
- ) > ${CONFIG_DIR}/client.rb
- if [ "" != "$chef_url" ]; then
- echo "chef_server_url '${chef_url}'" >> ${CONFIG_DIR}/client.rb
- fi
- if [ "" != "$organization" ]; then
- echo "validation_client_name '${organization}-validator'" >> ${CONFIG_DIR}/client.rb
- fi
- chmod 644 ${CONFIG_DIR}/client.rb
-fi
-
-if [ "" != "$validation_key" ]; then
- cp ${validation_key} ${CONFIG_DIR}/validation.pem || error_exit "Cannot copy the validation key!"
- chmod 600 ${CONFIG_DIR}/validation.pem
-fi
-
-# rm -f before ln -sf is required for solaris 9
-rm -f $PREFIX/bin/chef-client
-rm -f $PREFIX/bin/chef-solo
-rm -f $PREFIX/bin/chef-apply
-rm -f $PREFIX/bin/chef-shell
-rm -f $PREFIX/bin/knife
-rm -f $PREFIX/bin/ohai
-
-ln -sf $INSTALLER_DIR/bin/chef-solo $PREFIX/bin || error_exit "Cannot link chef-solo to $PREFIX/bin"
-if [ -f "$INSTALLER_DIR/bin/chef-apply" ]; then
- ln -sf $INSTALLER_DIR/bin/chef-apply $PREFIX/bin || error_exit "Cannot link chef-apply to $PREFIX/bin"
-fi
-if [ -f "$INSTALLER_DIR/bin/chef-shell" ]; then
- ln -sf $INSTALLER_DIR/bin/chef-shell $PREFIX/bin || error_exit "Cannot link chef-shell to $PREFIX/bin"
-fi
-ln -sf $INSTALLER_DIR/bin/knife $PREFIX/bin || error_exit "Cannot link knife to $PREFIX/bin"
-ln -sf $INSTALLER_DIR/bin/ohai $PREFIX/bin || error_exit "Cannot link ohai to $PREFIX/bin"
-
-# We test for the presence of /usr/bin/chef-client to know if this script succeeds, so this
-# must appear as the last real action in the script
-ln -sf $INSTALLER_DIR/bin/chef-client $PREFIX/bin || error_exit "Cannot link chef-client to $PREFIX/bin"
-
-# Ensure all files/directories in $INSTALLER_DIR are owned by root. This
-# has been fixed on new installs but upgrades from old installs need to
-# be manually fixed.
-chown -Rh 0:0 $INSTALLER_DIR
-
-echo "Thank you for installing Chef!"
-
-exit 0
diff --git a/omnibus/package-scripts/chef-fips/postrm b/omnibus/package-scripts/chef-fips/postrm
deleted file mode 100755
index 247688074e..0000000000
--- a/omnibus/package-scripts/chef-fips/postrm
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-# WARNING: REQUIRES /bin/sh
-#
-# - must run on /bin/sh on solaris 9
-# - must run on /bin/sh on AIX 6.x
-# - if you think you are a bash wizard, you probably do not understand
-# this programming language. do not touch.
-# - if you are under 40, get peer review from your elders.
-
-is_smartos() {
- uname -v | grep "^joyent" 2>&1 >/dev/null
-}
-
-is_darwin()
-{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
-}
-
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
- PREFIX="/usr/local"
-else
- PREFIX="/usr"
-fi
-
-cleanup_symlinks() {
- binaries="chef-client chef-solo chef-apply chef-shell knife ohai"
- for binary in $binaries; do
- rm -f $PREFIX/bin/$binary
- done
-}
-
-# Clean up binary symlinks if they exist
-# see: http://tickets.opscode.com/browse/CHEF-3022
-if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then
- # not a redhat-ish RPM-based system
- cleanup_symlinks
-elif [ "x$1" = "x0" ]; then
- # RPM-based system and we're deinstalling rather than upgrading
- cleanup_symlinks
-fi
diff --git a/omnibus/package-scripts/chef/postinst b/omnibus/package-scripts/chef/postinst
index 45893241b3..1500feac0c 100755
--- a/omnibus/package-scripts/chef/postinst
+++ b/omnibus/package-scripts/chef/postinst
@@ -23,17 +23,10 @@ error_exit()
is_darwin()
{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
+ uname -a | grep "^Darwin" 2>&1 >/dev/null
}
-is_smartos()
-{
- uname -v | grep "^joyent" 2>&1 >/dev/null
-}
-
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
+if is_darwin; then
PREFIX="/usr/local"
mkdir -p "$PREFIX/bin"
else
@@ -106,6 +99,6 @@ ln -sf $INSTALLER_DIR/bin/chef-client $PREFIX/bin || error_exit "Cannot link che
# be manually fixed.
chown -Rh 0:0 $INSTALLER_DIR
-echo "Thank you for installing Chef!"
+echo "Thank you for installing Chef Infra Client! For help getting started visit https://learn.chef.io"
exit 0
diff --git a/omnibus/package-scripts/chef/postrm b/omnibus/package-scripts/chef/postrm
index 247688074e..43b5f22c98 100755
--- a/omnibus/package-scripts/chef/postrm
+++ b/omnibus/package-scripts/chef/postrm
@@ -7,18 +7,20 @@
# this programming language. do not touch.
# - if you are under 40, get peer review from your elders.
-is_smartos() {
- uname -v | grep "^joyent" 2>&1 >/dev/null
+is_darwin() {
+ uname -a | grep "^Darwin" 2>&1 >/dev/null
}
-is_darwin()
-{
- uname -v | grep "^Darwin" 2>&1 >/dev/null
+is_suse() {
+ if [ -f /etc/os-release ]; then
+ . /etc/os-release
+ [ "$ID_LIKE" = "sles" ] || [ "$ID_LIKE" = "suse" ]
+ else
+ [ -f /etc/SuSE-release ]
+ fi
}
-if is_smartos; then
- PREFIX="/opt/local"
-elif is_darwin; then
+if is_darwin; then
PREFIX="/usr/local"
else
PREFIX="/usr"
@@ -33,10 +35,10 @@ cleanup_symlinks() {
# Clean up binary symlinks if they exist
# see: http://tickets.opscode.com/browse/CHEF-3022
-if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then
+if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! is_suse ]; then
# not a redhat-ish RPM-based system
cleanup_symlinks
elif [ "x$1" = "x0" ]; then
- # RPM-based system and we're deinstalling rather than upgrading
+ # RPM-based system and we're uninstalling rather than upgrading
cleanup_symlinks
fi
diff --git a/omnibus/package-scripts/chef/preinst b/omnibus/package-scripts/chef/preinst
new file mode 100755
index 0000000000..e38c3f7a3e
--- /dev/null
+++ b/omnibus/package-scripts/chef/preinst
@@ -0,0 +1,23 @@
+#!/bin/sh
+# WARNING: REQUIRES /bin/sh
+#
+# - must run on /bin/sh on solaris 9
+# - must run on /bin/sh on AIX 6.x
+# - if you think you are a bash wizard, you probably do not understand
+# this programming language. do not touch.
+# - if you are under 40, get peer review from your elders.
+
+INSTALLER_DIR=/opt/chef
+if [ -e $INSTALLER_DIR ]; then
+ echo "removing $INSTALLER_DIR..."
+
+ # have to do this dance of moving /opt/chef to a tmp dir since files may be in use
+ tmp_dir="/opt/.chef.$$"
+ # if we can't create the tmp_dir then fail hard to prevent any possible security hole
+ (umask 077 && mkdir $tmp_dir) || exit 1
+ # now we can clean up the tmp_dir we created safely
+ mv $INSTALLER_DIR $tmp_dir
+ # ignore errors which must be EBUSY issues, this may crate some litter, which may
+ # be unavoidable
+ rm -rf $tmp_dir || true
+fi
diff --git a/omnibus/resources/chef/dmg/background.png b/omnibus/resources/chef/dmg/background.png
index 82c605ae51..91e16a08ad 100644
--- a/omnibus/resources/chef/dmg/background.png
+++ b/omnibus/resources/chef/dmg/background.png
Binary files differ
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/assets/LICENSE.rtf b/omnibus/resources/chef/msi/assets/LICENSE.rtf
index b18e6f59b8..dae4848a44 100644
--- a/omnibus/resources/chef/msi/assets/LICENSE.rtf
+++ b/omnibus/resources/chef/msi/assets/LICENSE.rtf
@@ -1,194 +1,125 @@
{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
-{\*\generator Msftedit 5.41.21.2500;}\viewkind4\uc1\pard\qc\lang1033\f0\fs20 Apache License\par
+{\*\generator Msftedit 5.41.21.2500;}\viewkind4\uc1\pard\qc\lang1033\f0\fs20 Software End User License Agreement\par
-Version 2.0, January 2004\par
+(Personal, Non-Commercial, Experimental)\par
-http://www.apache.org/licenses/\par
+April 2, 2019 - The most recent edition of this license is available at https://www.chef.io/end-user-license-agreement/\par
\pard\par
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\par
+This Software End User License Agreement (this "Agreement"), is a binding agreement between Chef Software Inc. ("Chef") and You (as defined below).\par
\par
-1. Definitions.\par
+IF YOU REPRESENT A CORPORATION, GOVERNMENTAL ORGANIZATION, OR OTHER LEGAL ENTITY, OR YOU INTEND TO USE THE SOFTWARE FOR COMMERCIAL PURPOSES, YOU MUST CONTACT CHEF DIRECTLY TO OBTAIN A COMMERCIAL LICENSE FOR THIS SOFTWARE. PLEASE VISIT https://www.chef.io/eula-inquiry/ TO INQUIRE.\par
\par
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\par
+LICENSOR PROVIDES THE SOFTWARE SOLELY ON THE TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT AND ON THE CONDITION THAT YOU ACCEPT THEM. BY CLICKING THE "ACCEPT" BUTTON YOU (A) ACCEPT THIS AGREEMENT AND AGREE TO BE LEGALLY BOUND BY ITS TERMS; AND (B) REPRESENT AND WARRANT THAT YOU HAVE THE LEGAL CAPACITY TO ENTER INTO A BINDING AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS OF THIS AGREEMENT, YOU MUST NOT INSTALL OR USE THE SOFTWARE.\par
\par
-"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\par
+Definitions. For purposes of this Agreement, the following terms have the following meanings:\par
\par
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the 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.\par
+"Intellectual Property Rights" means patent, copyright, trademark, trade secret, database protection, or other intellectual property rights laws, and all similar or equivalent rights or forms of protection.\par
\par
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.\par
+"Business" means any Person other than a natural person.\par
\par
-"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation, source, and configuration files.\par
+"Commercial Purpose" means for the benefit of (i) any Business, or (ii) any undertaking intended, directly or indirectly, for profit.\par
\par
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\par
+"Experimental Use" means using the Software to learn, train, experiment with, or test viability of the Software. Experimental Use excludes pre-production and production environments as well as making the Software available to others, whether or not in exchange for any consideration.\par
\par
-"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\par
+"Person" means an individual, corporation, partnership, joint venture, limited liability company, governmental authority, non-profit organization, unincorporated organization, trust, association, or other entity.\par
\par
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\par
+"Software" means the software programs made available under this License.\par
\par
-"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."\par
+"Term" has the meaning set forth in Section 6.\par
\par
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\par
+"Third Party" means any Person other than You or Chef.\par
\par
-2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\par
+"You" means the Person exercising permissions granted by this Agreement.\par
\par
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license plies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\par
+1. License Grant and Scope. Chef hereby grants to You a non-exclusive, non-transferable, limited license during the Term to use the Software solely as set forth in this Section 1 and subject to the terms of Section 3. Chef hereby grants You the non-exclusive, non-transferable, non-sublicensable, royalty free right to:\par
\par
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\par
+ * Download, copy, and install the Software on computers owned or leased, and controlled by, You. In addition to the foregoing, You may make copies of the Software for archival or backup purposes. All copies of the Software made by You must include all trademark, copyright, patent, and other Intellectual Property Rights notices contained in the original.\par
\par
- (a) You must give any other recipients of the Work or\par
-
- Derivative Works a copy of this License; and\par
-
-\par
-
- (b) You must cause any modified files to carry prominent notices\par
-
- stating that You changed the files; and\par
-
-\par
-
- (c) You must retain, in the Source form of any Derivative Works\par
-
- that You distribute, all copyright, patent, trademark, and\par
-
- attribution notices from the Source form of the Work,\par
-
- excluding those notices that do not pertain to any part of\par
-
- the Derivative Works; and\par
-
-\par
-
- (d) If the Work includes a "NOTICE" text file as part of its\par
-
- distribution, then any Derivative Works that You distribute\par
-
- must include a readable copy of the attribution notices\par
-
- contained within such NOTICE file, excluding those notices\par
-
- that do not pertain to any part of the Derivative Works, in\par
-
- at least one of the following places: within a NOTICE text\par
-
- file distributed as part of the Derivative Works; within the\par
-
- Source form or documentation, if provided along with the\par
-
- Derivative Works; or, within a display generated by the\par
-
- Derivative Works, if and wherever such third-party notices\par
-
- normally appear. The contents of the NOTICE file are for\par
-
- informational purposes only and do not modify the License.\par
-
- You may add Your own attribution notices within Derivative\par
-
- Works that You distribute, alongside or as an addendum to the\par
-
- NOTICE text from the Work, provided that such additional\par
-
- attribution notices cannot be construed as modifying the\par
-
- License.\par
+ * Use and run the Software on such computers solely for Your personal, non-Commercial Purposes or Experimental Use.\par
\par
-You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\par
+2. Third-Party Materials. The Software includes software, content, data, or other materials, including related documentation, that are owned by Persons other than Chef and that are provided to You on license terms that are in addition to and/or different from those contained in this Agreement ("Third-Party Licenses"). A list of all materials included in the Software and provided under Third-Party Licenses can be found at https://www.chef.io/3rd-party-licenses/. You must comply with all Third-Party Licenses.\par
\par
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\par
+3. Use Restrictions. You must not, directly or indirectly: (a) modify, translate, adapt, or otherwise create derivative works or improvements, whether or not patentable, of the Software or any part thereof; (b) reverse engineer, disassemble, decompile, decode, or otherwise attempt to derive or gain access to the source code of the Software or any part thereof; (c) remove, delete, alter, or obscure any trademarks or any copyright, trademark, patent, or other intellectual property or proprietary rights notices provided on or with the Software, including any copy thereof; (d) rent, lease, lend, sell, sublicense, assign, distribute, publish, transfer, or otherwise make available the Software, or any features or functionality of the Software, to any Third Party for any reason; (e) use the Software in violation of any law, regulation, or rule; or (f) use the Software for purposes of competitive analysis of the Software, the development of a competing software product or service, or any other purpose that is to the Chef’s commercial disadvantage.\par
\par
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\par
+4. Collection and Use of Information. You hereby consent to Chef receiving data and information directly from the Software for the sole purpose of obtaining information regarding Your use of the Software (e.g., when You install an update or upgrade), as well as any Software bugs, errors, and other similar technical support issues. Chef will only use such data and information ("Software Usage and Technical Support Data") for Chef’s own business purposes, including but not limited to the purposes of (i) gathering information about how You use the Software, which may be combined with information about how others use the Software, in order to help Chef better understand trends and Your needs in order to better consider new features, and (ii) improving the Software and Your use experience. Chef will use Software Usage and Technical Support Data solely in aggregate, anonymized form.\par
\par
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\par
+5. Intellectual Property Rights. You acknowledge that the Software is provided under license, and not sold, to You. Chef reserves all right, title, and interest in and to the Software and all Intellectual Property Rights in or to Software, except as expressly granted to You in this Agreement. Some portions of the Software may be separately available as source code from Chef under open source software licenses. Nothing in this Agreement affects any rights you may have separately under such licenses.\par
\par
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\par
+6. Term and Termination. This Agreement and the license granted hereunder shall remain in effect until terminated as set forth herein (the "Term").\par
\par
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\par
+ * You may terminate this Agreement by ceasing to use and destroying all copies of the Software.\par
\par
-END OF TERMS AND CONDITIONS\par
+ * Chef may terminate this Agreement for convenience.\par
\par
-
-APPENDIX: How to apply the Apache License to your work.\par
+ * If You institute any litigation against Chef (including a cross-claim or counterclaim in a lawsuit) then the licenses granted to You under this Agreement shall terminate automatically as of the date such litigation is filed.\par
\par
-To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.\par
+ * Upon termination of this Agreement, the license granted hereunder shall also terminate, and You shall cease using and destroy all copies of the Software.\par
\par
- Copyright [yyyy] [name of copyright owner]\par
+7. Warranty Disclaimer. THE SOFTWARE IS PROVIDED TO YOU "AS IS" AND WITH ALL FAULTS AND DEFECTS WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, CHEF, ON ITS OWN BEHALF AND ON BEHALF OF ITS AFFILIATES AND ITS AND THEIR RESPECTIVE LICENSORS EXPRESSLY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT, AND WARRANTIES THAT MAY ARISE OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE, USAGE, OR TRADE PRACTICE. WITHOUT LIMITING THE FOREGOING, CHEF PROVIDES NO WARRANTY OR UNDERTAKING, AND MAKES NO REPRESENTATION OF ANY KIND THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, ACHIEVE ANY INTENDED RESULTS, BE COMPATIBLE, OR WORK WITH ANY OTHER SOFTWARE, APPLICATIONS, SYSTEMS, OR SERVICES, OPERATE WITHOUT INTERRUPTION, MEET ANY PERFORMANCE OR RELIABILITY STANDARDS OR BE ERROR FREE, OR THAT ANY ERRORS OR DEFECTS CAN OR WILL BE CORRECTED. YOU MAY HAVE ADDITIONAL RIGHTS THAT VARY FROM STATE TO STATE.\par
\par
- Licensed under the Apache License, Version 2.0 (the "License");\par
-
- you may not use this file except in compliance with the License.\par
-
- You may obtain a copy of the License at\par
+8. Limitation of Liability. TO THE FULLEST EXTENT PERMITTED UNDER APPLICABLE LAW: IN NO EVENT WILL CHEF OR ITS AFFILIATES, OR ANY OF ITS OR THEIR RESPECTIVE LICENSORS OR SERVICE PROVIDERS, BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, INDIRECT, EXEMPLARY, SPECIAL, OR PUNITIVE DAMAGES, WHETHER ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT, BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, REGARDLESS OF WHETHER SUCH DAMAGES WERE FORESEEABLE AND WHETHER OR NOT CHEF WAS ADVISED OF THE POSSIBILITY OF SUCH YOU MAY HAVE ADDITIONAL RIGHTS THAT VARY FROM STATE TO STATE.\par
\par
- http://www.apache.org/licenses/LICENSE-2.0\par
+9. Export Regulation. The Software may be subject to US export control laws, including the US Export Administration Act and its associated regulations. You shall not, directly or indirectly, export, re-export, or release the Software to, or make the Software accessible from, any jurisdiction or country to which export, re-export, or release is prohibited by law, rule, or regulation.\par
\par
- Unless required by applicable law or agreed to in writing, software\par
-
- distributed under the License is distributed on an "AS IS" BASIS,\par
-
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\par
-
- implied. See the License for the specific language governing\par
-
- permissions and limitations under the License.\par
+10. Miscellaneous. All matters arising out of or relating to this Agreement shall be governed by and construed in accordance with the internal laws of the State of Washington without giving effect to any conflict of law provision. Any legal action arising out of or relating to this Agreement will the subject to the exclusive jurisdiction of the state or federal courts located in King County. This Agreement constitutes the sole and entire agreement between You and Chef with respect to the subject matter contained herein, and supersedes all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, with respect to such subject matter. If any provision of this Agreement is determined by a court of law to be unenforceable, this Agreement and the license granted herein will terminate automatically.\par
\par
diff --git a/omnibus/resources/chef/msi/localization-en-us.wxl.erb b/omnibus/resources/chef/msi/localization-en-us.wxl.erb
index 62c27b99f6..e6b055984f 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 Infra Client Scheduled Task</String>
+ <String Id="CustomizeDlgSecondRadioButtonText">Chef Infra Client Service</String>
+ <String Id="CustomizeDlgThirdRadioButtonText">None</String>
+
+ <String Id="CustomizeDlgOptionsMsg">The installer can configure the Chef Infra 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/.</String>
</WixLocalization>
diff --git a/omnibus/resources/chef/msi/source.wxs.erb b/omnibus/resources/chef/msi/source.wxs.erb
index bdde02687e..8b8deaabaa 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>
@@ -135,6 +177,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>
@@ -151,8 +203,24 @@
<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>
<!--
@@ -168,17 +236,97 @@
-->
<Icon Id="oc.ico" SourceFile="Resources\assets\oc_16x16.ico"/>
<Property Id="ARPPRODUCTICON" Value="oc.ico" />
- <Property Id="ARPHELPLINK" Value="http://www.getchef.com/support/" />
+ <Property Id="ARPHELPLINK" Value="https://www.chef.io/support/" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION" />
<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/resources/chef/pkg/entitlements.plist b/omnibus/resources/chef/pkg/entitlements.plist
new file mode 100644
index 0000000000..bb87459e76
--- /dev/null
+++ b/omnibus/resources/chef/pkg/entitlements.plist
@@ -0,0 +1,8 @@
+<?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>
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+ <true/>
+ </dict>
+</plist> \ No newline at end of file
diff --git a/omnibus/resources/chef/pkg/license.html.erb b/omnibus/resources/chef/pkg/license.html.erb
index 21b7991abf..a4fc95c80a 100644
--- a/omnibus/resources/chef/pkg/license.html.erb
+++ b/omnibus/resources/chef/pkg/license.html.erb
@@ -1,202 +1,243 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the 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.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright <%= Time.new.year %> 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.
+Software End User License Agreement
+(Personal, Non-Commercial, Experimental)
+
+April 2, 2019 - The most recent edition of this license is
+available at https://www.chef.io/end-user-license-agreement/
+
+This Software End User License Agreement (this “Agreement“),
+is a binding agreement between Chef Software Inc. (“Chef“)
+and You (as defined below).
+
+IF YOU REPRESENT A CORPORATION, GOVERNMENTAL
+ORGANIZATION, OR OTHER LEGAL ENTITY, OR YOU
+INTEND TO USE THE SOFTWARE FOR COMMERCIAL
+PURPOSES, YOU MUST CONTACT CHEF DIRECTLY TO
+OBTAIN A COMMERCIAL LICENSE FOR THIS
+SOFTWARE. PLEASE VISIT
+https://www.chef.io/eula-inquiry/ TO INQUIRE.
+
+LICENSOR PROVIDES THE SOFTWARE SOLELY ON THE
+TERMS AND CONDITIONS SET FORTH IN THIS
+AGREEMENT AND ON THE CONDITION THAT YOU
+ACCEPT THEM. BY CLICKING THE “ACCEPT” BUTTON
+YOU (A) ACCEPT THIS AGREEMENT AND AGREE
+TO BE LEGALLY BOUND BY ITS TERMS; AND
+(B) REPRESENT AND WARRANT THAT YOU HAVE THE
+LEGAL CAPACITY TO ENTER INTO A BINDING
+AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS
+OF THIS AGREEMENT, YOU MUST NOT INSTALL OR
+USE THE SOFTWARE.
+
+Definitions. For purposes of this Agreement, the following
+terms have the following meanings:
+
+“Intellectual Property Rights” means patent, copyright,
+trademark, trade secret, database protection, or other
+intellectual property rights laws, and all similar or
+equivalent rights or forms of protection.
+
+“Business” means any Person other than a natural person.
+
+“Commercial Purpose” means for the benefit of (i) any
+Business, or (ii) any undertaking intended, directly
+or indirectly, for profit.
+
+“Experimental Use” means using the Software to learn,
+train, experiment with, or test viability of the
+Software. Experimental Use excludes pre-production and
+production environments as well as making the Software
+available to others, whether or not in exchange for any
+consideration.
+
+“Person” means an individual, corporation, partnership,
+joint venture, limited liability company, governmental
+authority, non-profit organization, unincorporated
+organization, trust, association, or other entity.
+
+“Software” means the software programs made available
+under this License.
+
+“Term” has the meaning set forth in Section 6.
+
+“Third Party” means any Person other than You or Chef.
+
+“You” means the Person exercising permissions granted by
+this Agreement.
+
+ 1. License Grant and Scope. Chef hereby grants to You a
+ non-exclusive, non-transferable, limited license
+ during the Term to use the Software solely as set
+ forth in this Section 1 and subject to the terms of
+ Section 3. Chef hereby grants You the non-exclusive,
+ non-transferable, non-sublicensable, royalty free
+ right to:
+
+ * Download, copy, and install the Software on computers
+ owned or leased, and controlled by, You. In addition
+ to the foregoing, You may make copies of the Software
+ for archival or backup purposes. All copies of the
+ Software made by You must include all trademark,
+ copyright, patent, and other Intellectual Property
+ Rights notices contained in the original.
+
+ * Use and run the Software on such computers solely
+ for Your personal, non-Commercial Purposes or
+ Experimental Use.
+
+ 2. Third-Party Materials. The Software includes software,
+ content, data, or other materials, including related
+ documentation, that are owned by Persons other than
+ Chef and that are provided to You on license terms
+ that are in addition to and/or different from those
+ contained in this Agreement (“Third-Party Licenses“).
+ A list of all materials included in the Software and
+ provided under Third-Party Licenses can be found
+ at https://www.chef.io/3rd-party-licenses/. You must
+ comply with all Third-Party Licenses.
+
+ 3. Use Restrictions. You must not, directly or
+ indirectly: (a) modify, translate, adapt, or otherwise
+ create derivative works or improvements, whether or
+ not patentable, of the Software or any part thereof;
+ (b) reverse engineer, disassemble, decompile, decode,
+ or otherwise attempt to derive or gain access to the
+ source code of the Software or any part thereof; (c)
+ remove, delete, alter, or obscure any trademarks or
+ any copyright, trademark, patent, or other
+ intellectual property or proprietary rights notices
+ provided on or with the Software, including any copy
+ thereof; (d) rent, lease, lend, sell, sublicense,
+ assign, distribute, publish, transfer, or otherwise
+ make available the Software, or any features or
+ functionality of the Software, to any Third Party for
+ any reason; (e) use the Software in violation of any
+ law, regulation, or rule; or (f) use the Software for
+ purposes of competitive analysis of the Software, the
+ development of a competing software product or
+ service, or any other purpose that is to the Chef’s
+ commercial disadvantage.
+
+ 4. Collection and Use of Information. You hereby consent
+ to Chef receiving data and information directly from
+ the Software for the sole purpose of obtaining
+ information regarding Your use of the Software
+ (e.g., when You install an update or upgrade), as well
+ as any Software bugs, errors, and other similar
+ technical support issues. Chef will only use such data
+ and information (“Software Usage and Technical Support
+ Data”) for Chef’s own business purposes, including but
+ not limited to the purposes of (i) gathering
+ information about how You use the Software, which may
+ be combined with information about how others use the
+ Software, in order to help Chef better understand
+ trends and Your needs in order to better consider new
+ features, and (ii) improving the Software and Your use
+ experience. Chef will use Software Usage and Technical
+ Support Data solely in aggregate, anonymized form.
+
+ 5. Intellectual Property Rights. You acknowledge that the
+ Software is provided under license, and not sold, to
+ You. Chef reserves all right, title, and interest in
+ and to the Software and all Intellectual Property
+ Rights in or to Software, except as expressly granted
+ to You in this Agreement. Some portions of the
+ Software may be separately available as source code
+ from Chef under open source software licenses. Nothing
+ in this Agreement affects any rights you may have
+ separately under such licenses.
+
+ 6. Term and Termination. This Agreement and the license
+ granted hereunder shall remain in effect until
+ terminated as set forth herein (the “Term“).
+
+ * You may terminate this Agreement by ceasing to use
+ and destroying all copies of the Software.
+
+ * Chef may terminate this Agreement for convenience.
+
+ * If You institute any litigation against Chef
+ (including a cross-claim or counterclaim in a
+ lawsuit) then the licenses granted to You under this
+ Agreement shall terminate automatically as of the
+ date such litigation is filed.
+
+ * Upon termination of this Agreement, the license
+ granted hereunder shall also terminate, and You
+ shall cease using and destroy all copies of the
+ Software.
+
+ 7. Warranty Disclaimer. THE SOFTWARE IS
+ PROVIDED TO YOU “AS IS” AND WITH ALL
+ FAULTS AND DEFECTS WITHOUT WARRANTY OF
+ ANY KIND. TO THE MAXIMUM EXTENT
+ PERMITTED UNDER APPLICABLE LAW, CHEF,
+ ON ITS OWN BEHALF AND ON BEHALF OF ITS
+ AFFILIATES AND ITS AND THEIR RESPECTIVE
+ LICENSORS EXPRESSLY DISCLAIMS ALL
+ WARRANTIES, WHETHER EXPRESS, IMPLIED,
+ STATUTORY, OR OTHERWISE, WITH RESPECT
+ TO THE SOFTWARE, INCLUDING ALL IMPLIED
+ WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE, TITLE, AND
+ NON-INFRINGEMENT, AND WARRANTIES THAT
+ MAY ARISE OUT OF COURSE OF DEALING,
+ COURSE OF PERFORMANCE, USAGE, OR TRADE
+ PRACTICE. WITHOUT LIMITING THE
+ FOREGOING, CHEF PROVIDES NO WARRANTY OR
+ UNDERTAKING, AND MAKES NO REPRESENTATION
+ OF ANY KIND THAT THE SOFTWARE WILL MEET
+ YOUR REQUIREMENTS, ACHIEVE ANY INTENDED
+ RESULTS, BE COMPATIBLE, OR WORK WITH ANY
+ OTHER SOFTWARE, APPLICATIONS, SYSTEMS,
+ OR SERVICES, OPERATE WITHOUT
+ INTERRUPTION, MEET ANY PERFORMANCE OR
+ RELIABILITY STANDARDS OR BE ERROR FREE,
+ OR THAT ANY ERRORS OR DEFECTS CAN OR
+ WILL BE CORRECTED. YOU MAY HAVE
+ ADDITIONAL RIGHTS THAT VARY FROM STATE
+ TO STATE.
+
+ 8. Limitation of Liability. TO THE FULLEST
+ EXTENT PERMITTED UNDER APPLICABLE LAW:
+ IN NO EVENT WILL CHEF OR ITS AFFILIATES,
+ OR ANY OF ITS OR THEIR RESPECTIVE
+ LICENSORS OR SERVICE PROVIDERS, BE
+ LIABLE TO YOU FOR ANY CONSEQUENTIAL,
+ INCIDENTAL, INDIRECT, EXEMPLARY, SPECIAL,
+ OR PUNITIVE DAMAGES, WHETHER ARISING OUT
+ OF OR IN CONNECTION WITH THIS AGREEMENT,
+ BREACH OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), OR OTHERWISE,
+ REGARDLESS OF WHETHER SUCH DAMAGES WERE
+ FORESEEABLE AND WHETHER OR NOT CHEF WAS
+ ADVISED OF THE POSSIBILITY OF SUCH YOU MAY
+ HAVE ADDITIONAL RIGHTS THAT VARY FROM
+ STATE TO STATE.
+
+ 9. Export Regulation. The Software may be subject to US
+ export control laws, including the US Export
+ Administration Act and its associated regulations.
+ You shall not, directly or indirectly, export,
+ re-export, or release the Software to, or make the
+ Software accessible from, any jurisdiction or country
+ to which export, re-export, or release is prohibited
+ by law, rule, or regulation.
+
+10. Miscellaneous. All matters arising out of or relating
+ to this Agreement shall be governed by and construed
+ in accordance with the internal laws of the State of
+ Washington without giving effect to any conflict of
+ law provision. Any legal action arising out of or
+ relating to this Agreement will the subject to the
+ exclusive jurisdiction of the state or federal courts
+ located in King County. This Agreement constitutes the
+ sole and entire agreement between You and Chef with
+ respect to the subject matter contained herein, and
+ supersedes all prior and contemporaneous
+ understandings, agreements, representations, and
+ warranties, both written and oral, with respect
+ to such subject matter. If any provision of this
+ Agreement is determined by a court of law to be
+ unenforceable, this Agreement and the license granted
+ herein will terminate automatically. \ No newline at end of file
diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb
index 44134bfc9a..008614bdeb 100644
--- a/omnibus_overrides.rb
+++ b/omnibus_overrides.rb
@@ -1,19 +1,31 @@
-# DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead.
-override :rubygems, version: "2.6.6"
-override :bundler, version: "1.12.5"
-override "libffi", version: "3.2.1"
-override "libiconv", version: "1.14"
-override "liblzma", version: "5.2.2"
+# THIS IS NOW HAND MANAGED, JUST EDIT THE THING
+# keep it machine-parsable since CI uses it
+#
+# NOTE: You MUST update omnibus-software when adding new versions of
+# software here: bundle exec rake dependencies:update_omnibus_gemfile_lock
+override "libarchive", version: "3.5.0"
+override "libffi", version: "3.3"
+override "libiconv", version: "1.16"
+override "liblzma", version: "5.2.5"
override "libtool", version: "2.4.2"
-override "libxml2", version: "2.9.4"
-override "libxslt", version: "1.1.29"
-override "libyaml", version: "0.1.6"
+override "libxml2", version: "2.9.10"
+override "libxslt", version: "1.1.34"
+override "libyaml", version: "0.1.7"
override "makedepend", version: "1.0.5"
override "ncurses", version: "5.9"
+override "nokogiri", version: "1.11.0"
+override "openssl", version: "1.0.2x"
override "pkg-config-lite", version: "0.28-1"
-override "ruby", version: "2.3.1"
+override "ruby", version: "2.7.2"
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 "openssl", version: "1.0.2h"
+override "zlib", version: "1.2.11"
+
+# We build both chef and ohai omnibus-software definitions which creates the
+# chef-client and ohai binstubs. Out of the box the ohai definition uses whatever
+# is in master, which won't match what's in the Gemfile.lock and used by the chef
+# definition. This pin will ensure that ohai and chef-client commands use the
+# same (released) version of ohai.
+gemfile_lock = File.join(File.expand_path(__dir__), "Gemfile.lock")
+override "ohai", version: "#{::File.readlines(gemfile_lock).find { |l| l =~ /^\s+ohai \((\d+\.\d+\.\d+)\)/ }; "v" + $1}" # rubocop: disable Layout/SpaceInsideStringInterpolation
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/apache2/metadata.json b/spec/data/cookbooks/apache2/metadata.json
new file mode 100644
index 0000000000..18f5e50bb3
--- /dev/null
+++ b/spec/data/cookbooks/apache2/metadata.json
@@ -0,0 +1,33 @@
+{
+ "name": "apache2",
+ "description": "",
+ "long_description": "",
+ "maintainer": "",
+ "maintainer_email": "",
+ "license": "All rights reserved",
+ "platforms": {
+
+ },
+ "dependencies": {
+
+ },
+ "providing": {
+
+ },
+ "recipes": {
+
+ },
+ "version": "0.0.1",
+ "source_url": "",
+ "issues_url": "",
+ "privacy": false,
+ "chef_versions": [
+
+ ],
+ "ohai_versions": [
+
+ ],
+ "gems": [
+
+ ]
+}
diff --git a/spec/data/cookbooks/irssi/files/default/irssi.response b/spec/data/cookbooks/irssi/files/default/irssi.response
new file mode 100644
index 0000000000..6b67a12758
--- /dev/null
+++ b/spec/data/cookbooks/irssi/files/default/irssi.response
@@ -0,0 +1,2 @@
+# Hi, I'm pretending to be the preseed file for installing the irssi on debian
+# or Ubuntu
diff --git a/spec/data/cookbooks/java/metadata.json b/spec/data/cookbooks/java/metadata.json
new file mode 100644
index 0000000000..9d46842f3c
--- /dev/null
+++ b/spec/data/cookbooks/java/metadata.json
@@ -0,0 +1,33 @@
+{
+ "name": "java",
+ "description": "",
+ "long_description": "",
+ "maintainer": "",
+ "maintainer_email": "",
+ "license": "All rights reserved",
+ "platforms": {
+
+ },
+ "dependencies": {
+
+ },
+ "providing": {
+
+ },
+ "recipes": {
+
+ },
+ "version": "0.0.1",
+ "source_url": "",
+ "issues_url": "",
+ "privacy": false,
+ "chef_versions": [
+
+ ],
+ "ohai_versions": [
+
+ ],
+ "gems": [
+
+ ]
+}
diff --git a/spec/data/cookbooks/openldap/libraries/openldap.rb b/spec/data/cookbooks/openldap/libraries/openldap.rb
index 6a3f058f95..0b9389c688 100644
--- a/spec/data/cookbooks/openldap/libraries/openldap.rb
+++ b/spec/data/cookbooks/openldap/libraries/openldap.rb
@@ -1,4 +1,4 @@
-require_relative './openldap/version.rb'
+require_relative './openldap/version'
class OpenLDAP
end
diff --git a/spec/data/cookbooks/openldap/metadata.rb b/spec/data/cookbooks/openldap/metadata.rb
index ab0dface9d..fc132946f2 100644
--- a/spec/data/cookbooks/openldap/metadata.rb
+++ b/spec/data/cookbooks/openldap/metadata.rb
@@ -1,6 +1,6 @@
name "openldap"
-maintainer "Opscode, Inc."
-maintainer_email "cookbooks@opscode.com"
+maintainer "Chef Software, Inc."
+maintainer_email "cookbooks@chef.io"
license "Apache 2.0"
description "Installs and configures all aspects of openldap using Debian style symlinks with helper definitions"
long_description "The long description for the openldap cookbook from metadata.rb"
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/cookbooks/starter/chefignore b/spec/data/cookbooks/starter/chefignore
new file mode 100644
index 0000000000..b9d6c768c2
--- /dev/null
+++ b/spec/data/cookbooks/starter/chefignore
@@ -0,0 +1,8 @@
+#
+# The ignore file allows you to skip files in cookbooks with the same name that appear
+# later in the search path.
+#
+
+recipes/default.rb
+ # comments can be indented
+ignored
diff --git a/spec/data/cookbooks/starter/files/sample.txt b/spec/data/cookbooks/starter/files/sample.txt
new file mode 100644
index 0000000000..e635a0f018
--- /dev/null
+++ b/spec/data/cookbooks/starter/files/sample.txt
@@ -0,0 +1 @@
+This is a Chef cookbook file. It is used to copy content verbatim on to a server. \ No newline at end of file
diff --git a/spec/data/cookbooks/starter/metadata.rb b/spec/data/cookbooks/starter/metadata.rb
new file mode 100644
index 0000000000..fbd288e9c4
--- /dev/null
+++ b/spec/data/cookbooks/starter/metadata.rb
@@ -0,0 +1,2 @@
+name "starter"
+version "1.0.0"
diff --git a/spec/data/cookbooks/starter/recipes/default.rb b/spec/data/cookbooks/starter/recipes/default.rb
new file mode 100644
index 0000000000..4b5f712879
--- /dev/null
+++ b/spec/data/cookbooks/starter/recipes/default.rb
@@ -0,0 +1,4 @@
+# This is a Chef recipe file. It can be used to specify resources which will
+# apply configuration to a server.
+
+# For more information, see the documentation: https://docs.chef.io/recipes/
diff --git a/spec/data/cookbooks/wget/files/default/wget.response b/spec/data/cookbooks/wget/files/default/wget.response
new file mode 100644
index 0000000000..b5f22f4d10
--- /dev/null
+++ b/spec/data/cookbooks/wget/files/default/wget.response
@@ -0,0 +1,2 @@
+# Hi, I'm pretending to be the preseed file for installing the wget on debian
+# or Ubuntu
diff --git a/spec/data/lwrp/providers/buck_passer.rb b/spec/data/lwrp/providers/buck_passer.rb
index 2bbca07bf7..9d764277ce 100644
--- a/spec/data/lwrp/providers/buck_passer.rb
+++ b/spec/data/lwrp/providers/buck_passer.rb
@@ -4,13 +4,13 @@ def without_deprecation_warnings(&block)
old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
Chef::Config[:treat_deprecation_warnings_as_errors] = false
begin
- block.call
+ yield
ensure
Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
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..0b8f49f7bd 100644
--- a/spec/data/lwrp/providers/buck_passer_2.rb
+++ b/spec/data/lwrp/providers/buck_passer_2.rb
@@ -2,13 +2,13 @@ def without_deprecation_warnings(&block)
old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
Chef::Config[:treat_deprecation_warnings_as_errors] = false
begin
- block.call
+ yield
ensure
Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
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..b86dc860d0 100644
--- a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
+++ b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
@@ -7,13 +7,13 @@ def without_deprecation_warnings(&block)
old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
Chef::Config[:treat_deprecation_warnings_as_errors] = false
begin
- block.call
+ yield
ensure
Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
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/mac_users/10.7-8.plist.xml b/spec/data/mac_users/10.7-8.plist.xml
deleted file mode 100644
index 4ed294eb38..0000000000
--- a/spec/data/mac_users/10.7-8.plist.xml
+++ /dev/null
@@ -1,559 +0,0 @@
-<?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>
- <key>KerberosKeys</key>
- <array>
- <data>
- MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCBxHUxawMNiov49kfZn
- M38ddgXFivE9SNpYgPamy+6prKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVB
- OUJFMDZCQzExQjAxODdEMzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQw
- Y6EbMBmgAwIBEaESBBBD9mGbvFTNIUKAvAbnjh8ookQwQqADAgEDoTsEOUxL
- REM6U0hBMS40RUE5QkUwNkJDMTFCMDE4N0QzNDUyQjdBMDk2MTdCMEI5MjY5
- NjhFdmFncmFudDBroSMwIaADAgEQoRoEGG4TEFIf416UH7MvFW7sAXC8ArC6
- AhbCraJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVBOUJFMDZCQzExQjAxODdE
- MzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQ=
- </data>
- </array>
- <key>ShadowHashData</key>
- <array>
- <data>
- YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50
- cm9weVRzYWx0Wml0ZXJhdGlvbnNPEIDqTC0mXYAboOwN/M0lPfwd6Ry+CAa0
- rMHtf+Iq689r61NE0PRC5ZD/oE1nkHXaOvsRnkG3K16vCO5KpUaTciZG1Rnu
- BIQ964o+l3Qo0z9iXoOIeRPlwTtwA1lhXgCte8PnoMmK/D4Z0TYCckVPjTOp
- IU0vvovmjR+YIbJmiTEjZk8QIPmU7y9zt8VZTr0VUzAJdrIHM84OJNZZeD2H
- 89gcu7apEZugCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA
- AAAAAOo=
- </data>
- </array>
- <key>_writers_hint</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_jpegphoto</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_passwd</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_picture</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>authentication_authority</key>
- <array>
- <string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512-PBKDF2&gt;</string>
- <string>;Kerberosv5;;vagrant@LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E;LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E</string>
- </array>
- <key>generateduid</key>
- <array>
- <string>11112222-3333-4444-AAAA-BBBBCCCCDDDD</string>
- </array>
- <key>gid</key>
- <array>
- <string>80</string>
- </array>
- <key>home</key>
- <array>
- <string>/Users/vagrant</string>
- </array>
- <key>jpegphoto</key>
- <array>
- <data>
- /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw
- bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs
- AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA
- AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH
- MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk
- AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH
- ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv
- ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo
- AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA
- AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv
- S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD
- Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa
- AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA
- AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp
- RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE
- 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg
- AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA
- QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo
- AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E
- FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy
- AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA
- RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl
- AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA
- bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg
- AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA
- bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs
- AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA
- RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv
- AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD
- wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl
- AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A
- ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH
- AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA
- bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs
- AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E
- RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn
- BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA
- ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz
- AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo
- dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN
- AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A
- ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA
- /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH
- BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA
- QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O
- Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A
- AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE
- AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX
- GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3
- eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI
- ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB
- AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR
- BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico
- KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG
- h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW
- 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0
- 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ
- H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i
- P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4
- mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK
- ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W
- gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q
- fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7
- j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8
- zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6
- 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX
- 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4
- 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3
- XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a
- ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus
- K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW
- VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A
- Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux
- +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D
- CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ
- 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS
- MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/
- x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP
- FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/
- pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r
- 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY
- LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f
- TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i
- yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx
- nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi
- eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE
- aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE
- ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE
- bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R
- rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT
- XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC
- MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ
- bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN
- 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA
- Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL
- LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp
- yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2
- JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl
- JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF
- FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy
- e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY
- jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP
- JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2
- a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW
- tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9
- yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM
- yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A
- Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz
- MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z
- b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n
- w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR
- /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f
- iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv
- 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z
- ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe
- RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr
- CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca
- UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU
- tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL
- eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX
- x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2
- x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1
- gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN
- N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3
- i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC
- as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8
- KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm
- OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9
- Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY
- 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT
- QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H
- s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz
- /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm
- r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+
- XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw
- dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7
- PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op
- a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc
- R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6
- INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW
- MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo
- ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry
- P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y
- TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51
- H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX
- n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG//
- ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se
- B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H
- bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ
- z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl
- 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt
- P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4
- vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+
- Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy
- zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A
- 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U
- Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7
- J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd
- zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba
- tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V
- /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx
- H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf
- q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK
- eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz
- 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf
- GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR
- QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf
- DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5
- EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR
- 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP
- paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf
- EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG
- 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry
- f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95
- 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO
- FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m
- Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g
- kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9
- ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM
- x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7
- x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F
- fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm
- qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf
- FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z
- m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG
- lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A
- UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro
- /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm
- t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y
- H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O
- oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY
- NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs
- PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7
- iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf
- Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw
- F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/
- X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m
- e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv
- DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V
- 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V
- JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx
- TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d
- qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv
- +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw
- 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg
- i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6
- jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB
- 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u
- qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu
- gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX
- qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ
- M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk
- blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb
- MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A
- fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0
- vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r
- zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV
- rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+
- 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/
- 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79
- Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP
- ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp
- Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ
- llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/
- 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps
- 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb
- WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+
- 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG
- w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ
- 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF
- FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX
- 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af
- u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A
- yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT
- +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V
- 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5
- MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH
- pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP
- sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r
- /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP
- JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f
- 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd
- 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH
- SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av
- QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B
- X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t
- ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk
- 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ
- ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e
- XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8
- mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc
- SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO
- UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+
- BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf
- aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy
- 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo
- or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn
- u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg
- CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2
- PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt
- LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+
- dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob
- BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s
- cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F
- /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+
- vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv
- 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ
- 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/
- AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN
- s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh
- VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY
- 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn
- +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3
- xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP
- 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH
- QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep
- fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/
- SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS
- GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5
- J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/
- ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz
- +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1
- zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa
- 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci
- iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf
- 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj
- XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf
- 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY
- AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3
- XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9
- x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi
- KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC
- d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c
- KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF
- AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2
- 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU
- rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+
- Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw
- wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL
- 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL
- XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+
- NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/
- 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a
- hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux
- 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH
- tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6
- 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi
- ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6
- +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt
- RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A
- Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw
- J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9
- F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA
- BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ
- fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf
- 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw
- EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA
- H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN
- ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ
- /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA
- q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j
- X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K
- PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A
- MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT
- gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff
- n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO
- c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4
- /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE
- fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O
- v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA
- CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO
- ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM
- kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ
- WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/
- mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB
- 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo
- oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK
- KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY
- 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky
- 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr
- FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6
- D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j
- aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj
- b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v
- mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF
- FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN
- 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f
- fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG
- wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP
- xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw
- /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY
- OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J
- 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/
- m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n
- u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF
- 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4
- hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS
- vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T
- 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l
- osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z
- YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU
- UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV
- l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/
- 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U
- up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S
- ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63
- hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW
- htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC
- 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9
- ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j
- dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk
- +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB
- cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ
- eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe
- CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q
- SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0
- HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af
- +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F
- anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp
- avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA
- rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi
- IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt
- B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG
- GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
- D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A
- kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv
- dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t
- Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3
- OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1
- JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O
- qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs
- qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u
- /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz
- 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y
- /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40
- rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i
- rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3
- cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD
- xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0
- VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn
- 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN
- I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP
- FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA
- CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ
- dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH
- /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV
- /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz
- kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM
- 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX
- gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj
- rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R
- 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+
- 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U
- 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9
- 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0
- iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl
- okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb
- V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6
- /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA
- FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh
- mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf
- RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1
- /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW
- p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS
- vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV
- tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs
- +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+
- Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ
- pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0
- m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia
- /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le
- FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9
- 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0
- uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX
- 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V
- 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv
- QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva
- Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu
- By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU
- UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9
- piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s
- fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/
- xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL
- /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc
- 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b
- /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o
- 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB
- k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A
- tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI
- TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm
- Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF
- FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
- AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR
- WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q==
- </data>
- </array>
- <key>name</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>passwd</key>
- <array>
- <string>********</string>
- </array>
- <key>passwordpolicyoptions</key>
- <array>
- <data>
- PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU
- WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO
- IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w
- LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp
- bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr
- ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt
- MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8
- L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp
- Y3Q+CjwvcGxpc3Q+Cg==
- </data>
- </array>
- <key>realname</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>shell</key>
- <array>
- <string>/bin/bash</string>
- </array>
- <key>uid</key>
- <array>
- <string>501</string>
- </array>
-</dict>
-</plist>
diff --git a/spec/data/mac_users/10.7-8.shadow.xml b/spec/data/mac_users/10.7-8.shadow.xml
deleted file mode 100644
index 8c3b6dd3d7..0000000000
--- a/spec/data/mac_users/10.7-8.shadow.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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>
- <key>SALTED-SHA512</key>
- <data>
- b3XXGQRB+sw0KR676h/HVrJC1P6bz/FBvMuE8ZeeJ+U5U5qjH599zJLAzqlZ6hjhi3IO
- NY5/vjz76qVhRW9roAiTejA=
- </data>
-</dict>
-</plist>
diff --git a/spec/data/mac_users/10.7.plist.xml b/spec/data/mac_users/10.7.plist.xml
deleted file mode 100644
index 5c7a98fada..0000000000
--- a/spec/data/mac_users/10.7.plist.xml
+++ /dev/null
@@ -1,559 +0,0 @@
-<?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>
- <key>KerberosKeys</key>
- <array>
- <data>
- MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCCBdluECwTg7Fe5bsZ+
- kxWTdvLPPtNGBCZOK2+aEFrkBaJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuREZG
- QTkxRjM1QjUxNjMzMDNDMDc5RTk5ODc2NDAzMEQwOTU2QUYyNnZhZ3JhbnQw
- Y6EbMBmgAwIBEaESBBAHZXv8koch6fiOdgRkDXyjokQwQqADAgEDoTsEOUxL
- REM6U0hBMS5ERkZBOTFGMzVCNTE2MzMwM0MwNzlFOTk4NzY0MDMwRDA5NTZB
- RjI2dmFncmFudDBroSMwIaADAgEQoRoEGKs+5dPs07zLf/0Vhu+YWCXZ6iwg
- NLpkqKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuREZGQTkxRjM1QjUxNjMzMDND
- MDc5RTk5ODc2NDAzMEQwOTU2QUYyNnZhZ3JhbnQ=
- </data>
- </array>
- <key>ShadowHashData</key>
- <array>
- <data>
- YnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRG911xkEQfrMNCkeu+ofx1ay
- QtT+m8/xQbzLhPGXniflOVOaox+ffcySwM6pWeoY4YtyDjWOf748++qlYUVv
- a6AIk3owCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA=
- </data>
- </array>
- <key>_writers_LinkedIdentity</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_hint</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_jpegphoto</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_passwd</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_picture</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>authentication_authority</key>
- <array>
- <string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512&gt;</string>
- <string>;Kerberosv5;;vagrant@LKDC:SHA1.DFFA91F35B5163303C079E998764030D0956AF26;LKDC:SHA1.DFFA91F35B5163303C079E998764030D0956AF26</string>
- </array>
- <key>generateduid</key>
- <array>
- <string>11112222-3333-4444-AAAA-BBBBCCCCDDDD</string>
- </array>
- <key>gid</key>
- <array>
- <string>80</string>
- </array>
- <key>home</key>
- <array>
- <string>/Users/vagrant</string>
- </array>
- <key>jpegphoto</key>
- <array>
- <data>
- /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw
- bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs
- AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA
- AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH
- MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk
- AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH
- ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv
- ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo
- AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA
- AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv
- S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD
- Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa
- AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA
- AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp
- RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE
- 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg
- AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA
- QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo
- AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E
- FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy
- AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA
- RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl
- AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA
- bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg
- AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA
- bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs
- AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA
- RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv
- AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD
- wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl
- AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A
- ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH
- AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA
- bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs
- AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E
- RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn
- BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA
- ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz
- AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo
- dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN
- AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A
- ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA
- /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH
- BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA
- QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O
- Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A
- AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE
- AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX
- GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3
- eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI
- ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB
- AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR
- BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico
- KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG
- h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW
- 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0
- 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ
- H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i
- P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4
- mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK
- ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W
- gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q
- fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7
- j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8
- zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6
- 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX
- 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4
- 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3
- XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a
- ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus
- K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW
- VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A
- Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux
- +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D
- CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ
- 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS
- MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/
- x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP
- FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/
- pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r
- 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY
- LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f
- TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i
- yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx
- nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi
- eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE
- aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE
- ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE
- bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R
- rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT
- XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC
- MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ
- bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN
- 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA
- Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL
- LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp
- yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2
- JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl
- JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF
- FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy
- e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY
- jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP
- JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2
- a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW
- tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9
- yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM
- yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A
- Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz
- MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z
- b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n
- w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR
- /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f
- iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv
- 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z
- ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe
- RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr
- CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca
- UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU
- tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL
- eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX
- x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2
- x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1
- gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN
- N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3
- i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC
- as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8
- KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm
- OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9
- Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY
- 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT
- QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H
- s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz
- /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm
- r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+
- XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw
- dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7
- PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op
- a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc
- R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6
- INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW
- MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo
- ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry
- P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y
- TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51
- H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX
- n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG//
- ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se
- B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H
- bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ
- z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl
- 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt
- P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4
- vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+
- Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy
- zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A
- 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U
- Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7
- J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd
- zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba
- tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V
- /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx
- H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf
- q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK
- eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz
- 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf
- GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR
- QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf
- DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5
- EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR
- 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP
- paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf
- EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG
- 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry
- f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95
- 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO
- FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m
- Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g
- kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9
- ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM
- x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7
- x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F
- fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm
- qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf
- FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z
- m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG
- lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A
- UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro
- /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm
- t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y
- H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O
- oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY
- NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs
- PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7
- iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf
- Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw
- F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/
- X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m
- e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv
- DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V
- 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V
- JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx
- TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d
- qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv
- +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw
- 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg
- i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6
- jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB
- 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u
- qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu
- gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX
- qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ
- M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk
- blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb
- MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A
- fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0
- vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r
- zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV
- rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+
- 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/
- 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79
- Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP
- ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp
- Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ
- llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/
- 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps
- 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb
- WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+
- 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG
- w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ
- 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF
- FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX
- 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af
- u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A
- yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT
- +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V
- 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5
- MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH
- pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP
- sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r
- /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP
- JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f
- 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd
- 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH
- SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av
- QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B
- X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t
- ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk
- 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ
- ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e
- XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8
- mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc
- SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO
- UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+
- BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf
- aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy
- 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo
- or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn
- u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg
- CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2
- PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt
- LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+
- dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob
- BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s
- cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F
- /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+
- vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv
- 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ
- 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/
- AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN
- s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh
- VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY
- 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn
- +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3
- xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP
- 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH
- QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep
- fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/
- SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS
- GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5
- J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/
- ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz
- +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1
- zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa
- 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci
- iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf
- 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj
- XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf
- 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY
- AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3
- XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9
- x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi
- KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC
- d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c
- KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF
- AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2
- 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU
- rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+
- Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw
- wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL
- 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL
- XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+
- NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/
- 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a
- hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux
- 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH
- tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6
- 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi
- ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6
- +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt
- RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A
- Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw
- J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9
- F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA
- BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ
- fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf
- 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw
- EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA
- H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN
- ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ
- /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA
- q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j
- X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K
- PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A
- MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT
- gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff
- n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO
- c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4
- /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE
- fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O
- v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA
- CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO
- ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM
- kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ
- WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/
- mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB
- 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo
- oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK
- KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY
- 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky
- 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr
- FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6
- D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j
- aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj
- b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v
- mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF
- FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN
- 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f
- fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG
- wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP
- xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw
- /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY
- OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J
- 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/
- m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n
- u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF
- 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4
- hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS
- vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T
- 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l
- osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z
- YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU
- UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV
- l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/
- 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U
- up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S
- ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63
- hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW
- htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC
- 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9
- ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j
- dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk
- +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB
- cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ
- eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe
- CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q
- SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0
- HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af
- +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F
- anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp
- avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA
- rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi
- IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt
- B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG
- GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
- D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A
- kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv
- dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t
- Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3
- OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1
- JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O
- qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs
- qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u
- /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz
- 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y
- /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40
- rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i
- rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3
- cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD
- xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0
- VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn
- 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN
- I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP
- FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA
- CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ
- dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH
- /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV
- /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz
- kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM
- 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX
- gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj
- rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R
- 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+
- 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U
- 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9
- 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0
- iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl
- okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb
- V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6
- /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA
- FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh
- mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf
- RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1
- /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW
- p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS
- vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV
- tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs
- +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+
- Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ
- pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0
- m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia
- /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le
- FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9
- 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0
- uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX
- 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V
- 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv
- QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva
- Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu
- By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU
- UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9
- piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s
- fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/
- xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL
- /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc
- 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b
- /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o
- 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB
- k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A
- tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI
- TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm
- Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF
- FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
- AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR
- WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q==
- </data>
- </array>
- <key>name</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>passwd</key>
- <array>
- <string>********</string>
- </array>
- <key>passwordpolicyoptions</key>
- <array>
- <data>
- PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU
- WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO
- IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w
- LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp
- bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr
- ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt
- MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8
- L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp
- Y3Q+CjwvcGxpc3Q+Cg==
- </data>
- </array>
- <key>realname</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>shell</key>
- <array>
- <string>/bin/bash</string>
- </array>
- <key>uid</key>
- <array>
- <string>501</string>
- </array>
-</dict>
-</plist>
diff --git a/spec/data/mac_users/10.7.shadow.xml b/spec/data/mac_users/10.7.shadow.xml
deleted file mode 100644
index 8c3b6dd3d7..0000000000
--- a/spec/data/mac_users/10.7.shadow.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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>
- <key>SALTED-SHA512</key>
- <data>
- b3XXGQRB+sw0KR676h/HVrJC1P6bz/FBvMuE8ZeeJ+U5U5qjH599zJLAzqlZ6hjhi3IO
- NY5/vjz76qVhRW9roAiTejA=
- </data>
-</dict>
-</plist>
diff --git a/spec/data/mac_users/10.8.plist.xml b/spec/data/mac_users/10.8.plist.xml
deleted file mode 100644
index 4ed294eb38..0000000000
--- a/spec/data/mac_users/10.8.plist.xml
+++ /dev/null
@@ -1,559 +0,0 @@
-<?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>
- <key>KerberosKeys</key>
- <array>
- <data>
- MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCBxHUxawMNiov49kfZn
- M38ddgXFivE9SNpYgPamy+6prKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVB
- OUJFMDZCQzExQjAxODdEMzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQw
- Y6EbMBmgAwIBEaESBBBD9mGbvFTNIUKAvAbnjh8ookQwQqADAgEDoTsEOUxL
- REM6U0hBMS40RUE5QkUwNkJDMTFCMDE4N0QzNDUyQjdBMDk2MTdCMEI5MjY5
- NjhFdmFncmFudDBroSMwIaADAgEQoRoEGG4TEFIf416UH7MvFW7sAXC8ArC6
- AhbCraJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVBOUJFMDZCQzExQjAxODdE
- MzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQ=
- </data>
- </array>
- <key>ShadowHashData</key>
- <array>
- <data>
- YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50
- cm9weVRzYWx0Wml0ZXJhdGlvbnNPEIDqTC0mXYAboOwN/M0lPfwd6Ry+CAa0
- rMHtf+Iq689r61NE0PRC5ZD/oE1nkHXaOvsRnkG3K16vCO5KpUaTciZG1Rnu
- BIQ964o+l3Qo0z9iXoOIeRPlwTtwA1lhXgCte8PnoMmK/D4Z0TYCckVPjTOp
- IU0vvovmjR+YIbJmiTEjZk8QIPmU7y9zt8VZTr0VUzAJdrIHM84OJNZZeD2H
- 89gcu7apEZugCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA
- AAAAAOo=
- </data>
- </array>
- <key>_writers_hint</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_jpegphoto</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_passwd</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>_writers_picture</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>authentication_authority</key>
- <array>
- <string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512-PBKDF2&gt;</string>
- <string>;Kerberosv5;;vagrant@LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E;LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E</string>
- </array>
- <key>generateduid</key>
- <array>
- <string>11112222-3333-4444-AAAA-BBBBCCCCDDDD</string>
- </array>
- <key>gid</key>
- <array>
- <string>80</string>
- </array>
- <key>home</key>
- <array>
- <string>/Users/vagrant</string>
- </array>
- <key>jpegphoto</key>
- <array>
- <data>
- /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw
- bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs
- AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA
- AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH
- MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk
- AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH
- ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv
- ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo
- AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA
- AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv
- S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD
- Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa
- AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA
- AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp
- RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE
- 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg
- AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA
- QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo
- AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E
- FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy
- AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA
- RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl
- AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA
- bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg
- AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA
- bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs
- AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA
- RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv
- AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD
- wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl
- AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A
- ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH
- AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA
- bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs
- AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E
- RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn
- BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA
- ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz
- AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo
- dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN
- AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A
- ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA
- /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH
- BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA
- QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O
- Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A
- AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE
- AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX
- GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3
- eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI
- ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB
- AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR
- BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico
- KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG
- h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW
- 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0
- 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ
- H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i
- P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4
- mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK
- ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W
- gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q
- fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7
- j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8
- zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6
- 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX
- 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4
- 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3
- XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a
- ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus
- K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW
- VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A
- Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux
- +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D
- CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ
- 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS
- MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/
- x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP
- FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/
- pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r
- 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY
- LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f
- TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i
- yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx
- nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi
- eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE
- aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE
- ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE
- bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R
- rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT
- XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC
- MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ
- bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN
- 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA
- Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL
- LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp
- yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2
- JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl
- JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF
- FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy
- e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY
- jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP
- JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2
- a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW
- tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9
- yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM
- yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A
- Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz
- MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z
- b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n
- w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR
- /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f
- iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv
- 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z
- ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe
- RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr
- CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca
- UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU
- tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL
- eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX
- x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2
- x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1
- gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN
- N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3
- i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC
- as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8
- KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm
- OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9
- Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY
- 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT
- QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H
- s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz
- /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm
- r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+
- XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw
- dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7
- PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op
- a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc
- R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6
- INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW
- MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo
- ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry
- P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y
- TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51
- H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX
- n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG//
- ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se
- B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H
- bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ
- z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl
- 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt
- P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4
- vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+
- Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy
- zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A
- 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U
- Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7
- J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd
- zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba
- tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V
- /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx
- H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf
- q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK
- eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz
- 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf
- GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR
- QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf
- DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5
- EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR
- 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP
- paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf
- EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG
- 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry
- f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95
- 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO
- FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m
- Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g
- kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9
- ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM
- x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7
- x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F
- fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm
- qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf
- FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z
- m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG
- lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A
- UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro
- /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm
- t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y
- H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O
- oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY
- NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs
- PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7
- iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf
- Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw
- F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/
- X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m
- e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv
- DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V
- 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V
- JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx
- TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d
- qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv
- +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw
- 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg
- i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6
- jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB
- 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u
- qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu
- gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX
- qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ
- M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk
- blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb
- MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A
- fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0
- vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r
- zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV
- rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+
- 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/
- 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79
- Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP
- ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp
- Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ
- llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/
- 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps
- 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb
- WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+
- 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG
- w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ
- 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF
- FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX
- 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af
- u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A
- yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT
- +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V
- 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5
- MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH
- pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP
- sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r
- /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP
- JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f
- 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd
- 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH
- SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av
- QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B
- X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t
- ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk
- 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ
- ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e
- XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8
- mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc
- SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO
- UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+
- BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf
- aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy
- 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo
- or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn
- u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg
- CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2
- PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt
- LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+
- dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob
- BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s
- cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F
- /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+
- vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv
- 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ
- 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/
- AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN
- s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh
- VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY
- 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn
- +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3
- xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP
- 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH
- QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep
- fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/
- SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS
- GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5
- J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/
- ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz
- +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1
- zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa
- 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci
- iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf
- 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj
- XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf
- 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY
- AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3
- XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9
- x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi
- KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC
- d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c
- KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF
- AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2
- 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU
- rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+
- Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw
- wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL
- 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL
- XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+
- NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/
- 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a
- hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux
- 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH
- tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6
- 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi
- ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6
- +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt
- RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A
- Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw
- J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9
- F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA
- BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ
- fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf
- 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw
- EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA
- H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN
- ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ
- /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA
- q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j
- X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K
- PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A
- MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT
- gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff
- n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO
- c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4
- /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE
- fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O
- v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA
- CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO
- ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM
- kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ
- WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/
- mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB
- 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo
- oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK
- KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY
- 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky
- 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr
- FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6
- D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j
- aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj
- b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v
- mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF
- FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN
- 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f
- fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG
- wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP
- xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw
- /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY
- OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J
- 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/
- m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n
- u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF
- 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4
- hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS
- vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T
- 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l
- osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z
- YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU
- UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV
- l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/
- 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U
- up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S
- ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63
- hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW
- htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC
- 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9
- ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j
- dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk
- +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB
- cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ
- eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe
- CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q
- SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0
- HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af
- +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F
- anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp
- avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA
- rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi
- IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt
- B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG
- GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
- D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A
- kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv
- dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t
- Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3
- OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1
- JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O
- qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs
- qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u
- /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz
- 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y
- /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40
- rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i
- rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3
- cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD
- xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0
- VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn
- 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN
- I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP
- FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA
- CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ
- dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH
- /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV
- /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz
- kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM
- 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX
- gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj
- rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R
- 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+
- 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U
- 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9
- 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0
- iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl
- okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb
- V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6
- /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA
- FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh
- mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf
- RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1
- /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW
- p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS
- vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV
- tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs
- +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+
- Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ
- pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0
- m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia
- /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le
- FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9
- 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0
- uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX
- 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V
- 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv
- QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva
- Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu
- By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU
- UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9
- piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s
- fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/
- xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL
- /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc
- 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b
- /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o
- 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB
- k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A
- tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI
- TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm
- Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF
- FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
- AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR
- WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q==
- </data>
- </array>
- <key>name</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>passwd</key>
- <array>
- <string>********</string>
- </array>
- <key>passwordpolicyoptions</key>
- <array>
- <data>
- PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU
- WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO
- IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w
- LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp
- bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr
- ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt
- MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8
- L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp
- Y3Q+CjwvcGxpc3Q+Cg==
- </data>
- </array>
- <key>realname</key>
- <array>
- <string>vagrant</string>
- </array>
- <key>shell</key>
- <array>
- <string>/bin/bash</string>
- </array>
- <key>uid</key>
- <array>
- <string>501</string>
- </array>
-</dict>
-</plist>
diff --git a/spec/data/mac_users/10.8.shadow.xml b/spec/data/mac_users/10.8.shadow.xml
deleted file mode 100644
index c4b8ec8363..0000000000
--- a/spec/data/mac_users/10.8.shadow.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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>
- <key>SALTED-SHA512-PBKDF2</key>
- <dict>
- <key>entropy</key>
- <data>
- 6kwtJl2AG6DsDfzNJT38HekcvggGtKzB7X/iKuvPa+tTRND0QuWQ/6BNZ5B1
- 2jr7EZ5BtyterwjuSqVGk3ImRtUZ7gSEPeuKPpd0KNM/Yl6DiHkT5cE7cANZ
- YV4ArXvD56DJivw+GdE2AnJFT40zqSFNL76L5o0fmCGyZokxI2Y=
- </data>
- <key>iterations</key>
- <integer>39840</integer>
- <key>salt</key>
- <data>
- +ZTvL3O3xVlOvRVTMAl2sgczzg4k1ll4PYfz2By7tqk=
- </data>
- </dict>
-</dict>
-</plist>
diff --git a/spec/data/metadata/quick_start/metadata.rb b/spec/data/metadata/quick_start/metadata.rb
index e74eedba0f..e7ae9d1749 100644
--- a/spec/data/metadata/quick_start/metadata.rb
+++ b/spec/data/metadata/quick_start/metadata.rb
@@ -1,5 +1,5 @@
-maintainer "Opscode, Inc."
-maintainer_email "cookbooks@opscode.com"
+maintainer "Chef Software, Inc."
+maintainer_email "cookbooks@chef.io"
license "Apache 2.0"
description "Example cookbook for quick_start wiki document"
version "0.7"
@@ -12,8 +12,3 @@ version "0.7"
}.each do |os|
supports os
end
-
-attribute "quick_start/deep_thought",
- :display_name => "Quick Start Deep Thought",
- :description => "A deep thought",
- :default => "If a tree falls in the forest..."
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/rubygems.org/latest_specs.4.8.gz b/spec/data/rubygems.org/latest_specs.4.8.gz
new file mode 100644
index 0000000000..ab6a175f32
--- /dev/null
+++ b/spec/data/rubygems.org/latest_specs.4.8.gz
Binary files differ
diff --git a/spec/data/rubygems.org/nonexistent_gem b/spec/data/rubygems.org/nonexistent_gem
new file mode 100644
index 0000000000..0ba94359df
--- /dev/null
+++ b/spec/data/rubygems.org/nonexistent_gem
Binary files differ
diff --git a/spec/data/rubygems.org/sexp_processor b/spec/data/rubygems.org/sexp_processor
new file mode 100644
index 0000000000..37c6e97769
--- /dev/null
+++ b/spec/data/rubygems.org/sexp_processor
Binary files differ
diff --git a/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz b/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz
new file mode 100644
index 0000000000..38840f2682
--- /dev/null
+++ b/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz
Binary files differ
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file b/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
diff --git a/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file b/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/definitions/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
diff --git a/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file b/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/libraries/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
diff --git a/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file b/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/providers/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
diff --git a/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file b/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/recipes/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
diff --git a/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file b/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file
new file mode 100644
index 0000000000..60fee07cc6
--- /dev/null
+++ b/spec/data/run_context/cookbooks/dependency1/resources/unparsed_file
@@ -0,0 +1 @@
+raise "this should not be parsed by the loader"
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..1ace5efd21 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" << "languages" << "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 __dir__
+cookbook_path "#{chef_repo_path}/cookbooks"
diff --git a/spec/data/snap_package/async_result_success.json b/spec/data/snap_package/async_result_success.json
new file mode 100644
index 0000000000..09781ad5bd
--- /dev/null
+++ b/spec/data/snap_package/async_result_success.json
@@ -0,0 +1,6 @@
+{
+ "type": "async",
+ "status-code": 202,
+ "status": "Accepted",
+ "change": "401"
+}
diff --git a/spec/data/snap_package/change_id_result.json b/spec/data/snap_package/change_id_result.json
new file mode 100644
index 0000000000..cd823d7cbc
--- /dev/null
+++ b/spec/data/snap_package/change_id_result.json
@@ -0,0 +1,175 @@
+{
+ "type": "sync",
+ "status-code": 200,
+ "status": "OK",
+ "result": {
+ "id": "15",
+ "kind": "install-snap",
+ "summary": "Install snap \"hello\"",
+ "status": "Done",
+ "tasks": [{
+ "id": "165",
+ "kind": "prerequisites",
+ "summary": "Ensure prerequisites for \"hello\" are available",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.22104314Z",
+ "ready-time": "2018-09-22T20:25:25.231090966Z"
+ }, {
+ "id": "166",
+ "kind": "download-snap",
+ "summary": "Download snap \"hello\" (20) from channel \"stable\"",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221070859Z",
+ "ready-time": "2018-09-22T20:25:25.24321909Z"
+ }, {
+ "id": "167",
+ "kind": "validate-snap",
+ "summary": "Fetch and check assertions for snap \"hello\" (20)",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221080163Z",
+ "ready-time": "2018-09-22T20:25:25.55308904Z"
+ }, {
+ "id": "168",
+ "kind": "mount-snap",
+ "summary": "Mount snap \"hello\" (20)",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221082984Z",
+ "ready-time": "2018-09-22T20:25:25.782452658Z"
+ }, {
+ "id": "169",
+ "kind": "copy-snap-data",
+ "summary": "Copy snap \"hello\" data",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221085677Z",
+ "ready-time": "2018-09-22T20:25:25.790911883Z"
+ }, {
+ "id": "170",
+ "kind": "setup-profiles",
+ "summary": "Setup snap \"hello\" (20) security profiles",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221088261Z",
+ "ready-time": "2018-09-22T20:25:25.972796111Z"
+ }, {
+ "id": "171",
+ "kind": "link-snap",
+ "summary": "Make snap \"hello\" (20) available to the system",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221090669Z",
+ "ready-time": "2018-09-22T20:25:25.986931331Z"
+ }, {
+ "id": "172",
+ "kind": "auto-connect",
+ "summary": "Automatically connect eligible plugs and slots of snap \"hello\"",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221093357Z",
+ "ready-time": "2018-09-22T20:25:25.996914144Z"
+ }, {
+ "id": "173",
+ "kind": "set-auto-aliases",
+ "summary": "Set automatic aliases for snap \"hello\"",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221097651Z",
+ "ready-time": "2018-09-22T20:25:26.009155888Z"
+ }, {
+ "id": "174",
+ "kind": "setup-aliases",
+ "summary": "Setup snap \"hello\" aliases",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221100379Z",
+ "ready-time": "2018-09-22T20:25:26.021062388Z"
+ }, {
+ "id": "175",
+ "kind": "run-hook",
+ "summary": "Run install hook of \"hello\" snap if present",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221103116Z",
+ "ready-time": "2018-09-22T20:25:26.031383884Z"
+ }, {
+ "id": "176",
+ "kind": "start-snap-services",
+ "summary": "Start snap \"hello\" (20) services",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221110251Z",
+ "ready-time": "2018-09-22T20:25:26.039564637Z"
+ }, {
+ "id": "177",
+ "kind": "run-hook",
+ "summary": "Run configure hook of \"hello\" snap if present",
+ "status": "Done",
+ "progress": {
+ "label": "",
+ "done": 1,
+ "total": 1
+ },
+ "spawn-time": "2018-09-22T20:25:25.221115952Z",
+ "ready-time": "2018-09-22T20:25:26.05069451Z"
+ }
+ ],
+ "ready": true,
+ "spawn-time": "2018-09-22T20:25:25.221130149Z",
+ "ready-time": "2018-09-22T20:25:26.050696298Z",
+ "data": {
+ "snap-names": ["hello"]
+ }
+ }
+}
diff --git a/spec/data/snap_package/find_result_failure.json b/spec/data/snap_package/find_result_failure.json
new file mode 100644
index 0000000000..ec0d82a3b8
--- /dev/null
+++ b/spec/data/snap_package/find_result_failure.json
@@ -0,0 +1,10 @@
+{
+ "type": "error",
+ "status-code": 404,
+ "status": "Not Found",
+ "result": {
+ "message": "snap not found",
+ "kind": "snap-not-found",
+ "value": "hello2"
+ }
+}
diff --git a/spec/data/snap_package/find_result_success.json b/spec/data/snap_package/find_result_success.json
new file mode 100644
index 0000000000..f19f24dcef
--- /dev/null
+++ b/spec/data/snap_package/find_result_success.json
@@ -0,0 +1,70 @@
+{
+ "type": "sync",
+ "status-code": 200,
+ "status": "OK",
+ "result": [{
+ "id": "mVyGrEwiqSi5PugCwyH7WgpoQLemtTd6",
+ "title": "hello",
+ "summary": "GNU Hello, the \"hello world\" snap",
+ "description": "GNU hello prints a friendly greeting. This is part of the snapcraft tour at https://snapcraft.io/",
+ "download-size": 65536,
+ "name": "hello",
+ "publisher": {
+ "id": "canonical",
+ "username": "canonical",
+ "display-name": "Canonical",
+ "validation": "verified"
+ },
+ "developer": "canonical",
+ "status": "available",
+ "type": "app",
+ "version": "2.10",
+ "channel": "stable",
+ "ignore-validation": false,
+ "revision": "20",
+ "confinement": "strict",
+ "private": false,
+ "devmode": false,
+ "jailmode": false,
+ "contact": "mailto:snaps@canonical.com",
+ "license": "GPL-3.0",
+ "channels": {
+ "latest/beta": {
+ "revision": "29",
+ "confinement": "strict",
+ "version": "2.10.1",
+ "channel": "beta",
+ "epoch": "0",
+ "size": 65536
+ },
+ "latest/candidate": {
+ "revision": "20",
+ "confinement": "strict",
+ "version": "2.10",
+ "channel": "candidate",
+ "epoch": "0",
+ "size": 65536
+ },
+ "latest/edge": {
+ "revision": "34",
+ "confinement": "strict",
+ "version": "2.10.42",
+ "channel": "edge",
+ "epoch": "0",
+ "size": 65536
+ },
+ "latest/stable": {
+ "revision": "20",
+ "confinement": "strict",
+ "version": "2.10",
+ "channel": "stable",
+ "epoch": "0",
+ "size": 65536
+ }
+ },
+ "tracks": ["latest"]
+ }
+ ],
+ "sources": ["store"],
+ "suggested-currency": "USD"
+}
diff --git a/spec/data/snap_package/get_by_name_result_failure.json b/spec/data/snap_package/get_by_name_result_failure.json
new file mode 100644
index 0000000000..c8c1bb7342
--- /dev/null
+++ b/spec/data/snap_package/get_by_name_result_failure.json
@@ -0,0 +1,10 @@
+{
+ "type": "error",
+ "status-code": 404,
+ "status": "Not Found",
+ "result": {
+ "message": "snap not installed",
+ "kind": "snap-not-found",
+ "value": "aws-cliasdfasdf"
+ }
+}
diff --git a/spec/data/snap_package/get_by_name_result_success.json b/spec/data/snap_package/get_by_name_result_success.json
new file mode 100644
index 0000000000..05517362ab
--- /dev/null
+++ b/spec/data/snap_package/get_by_name_result_success.json
@@ -0,0 +1,38 @@
+{
+ "type": "sync",
+ "status-code": 200,
+ "status": "OK",
+ "result": {
+ "id": "CRrJViJiSuDcCkU31G0xpNRVNaj4P960",
+ "summary": "Universal Command Line Interface for Amazon Web Services",
+ "description": "This package provides a unified command line interface to Amazon Web\nServices.\n",
+ "installed-size": 15851520,
+ "name": "aws-cli",
+ "publisher": {
+ "id": "S7iQ7mKDXBDliQqRcgefvc2TKXIH9pYk",
+ "username": "aws",
+ "display-name": "Amazon Web Services",
+ "validation": "verified"
+ },
+ "developer": "aws",
+ "status": "active",
+ "type": "app",
+ "version": "1.15.71",
+ "channel": "",
+ "tracking-channel": "stable",
+ "ignore-validation": false,
+ "revision": "135",
+ "confinement": "classic",
+ "private": false,
+ "devmode": false,
+ "jailmode": false,
+ "apps": [{
+ "snap": "aws-cli",
+ "name": "aws"
+ }
+ ],
+ "contact": "",
+ "mounted-from": "/var/lib/snapd/snaps/aws-cli_135.snap",
+ "install-date": "2018-09-17T20:39:38.516Z"
+ }
+}
diff --git a/spec/data/snap_package/get_conf_success.json b/spec/data/snap_package/get_conf_success.json
new file mode 100644
index 0000000000..e83ffbfbe3
--- /dev/null
+++ b/spec/data/snap_package/get_conf_success.json
@@ -0,0 +1,10 @@
+{
+ "type": "sync",
+ "status-code": 200,
+ "status": "OK",
+ "result": {
+ "address": "0.0.0.0",
+ "allow-privileged": true,
+ "anonymous-auth": false
+ }
+}
diff --git a/spec/data/snap_package/result_failure.json b/spec/data/snap_package/result_failure.json
new file mode 100644
index 0000000000..e65120ad33
--- /dev/null
+++ b/spec/data/snap_package/result_failure.json
@@ -0,0 +1,9 @@
+{
+ "type": "error",
+ "status-code": 401,
+ "status": "Unauthorized",
+ "result": {
+ "message": "access denied",
+ "kind": "login-required"
+ }
+}
diff --git a/spec/data/ssl/binary/chef-rspec-der.cert b/spec/data/ssl/binary/chef-rspec-der.cert
new file mode 100644
index 0000000000..e49df6252a
--- /dev/null
+++ b/spec/data/ssl/binary/chef-rspec-der.cert
Binary files differ
diff --git a/spec/data/ssl/binary/chef-rspec-der.key b/spec/data/ssl/binary/chef-rspec-der.key
new file mode 100644
index 0000000000..d8adadc5c9
--- /dev/null
+++ b/spec/data/ssl/binary/chef-rspec-der.key
Binary files differ
diff --git a/spec/data/ssl/chef-rspec.cert b/spec/data/ssl/chef-rspec.cert
index 08ec684520..9215a39362 100644
--- a/spec/data/ssl/chef-rspec.cert
+++ b/spec/data/ssl/chef-rspec.cert
@@ -1,9 +1,9 @@
-----BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIJAKBJr4wSRUVvMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
+MIIEkjCCA3qgAwIBAgIJAOEDC5RFoEUZMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEN
MAsGA1UEChMEQ2hlZjETMBEGA1UECxMKZGV2ZWxvcGVyczESMBAGA1UEAxMJa2Fs
-bGlzdGVjMR4wHAYJKoZIhvcNAQkBFg9kYW5Ab3BzY29kZS5jb20wHhcNMTAwNDEw
-MTkxMTMxWhcNMjAwNDA3MTkxMTMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
+bGlzdGVjMR4wHAYJKoZIhvcNAQkBFg9kYW5Ab3BzY29kZS5jb20wHhcNMjAwODEy
+MTEyOTUxWhcNMzAwODEwMTEyOTUxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
Cldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzAR
BgNVBAsTCmRldmVsb3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3
DQEJARYPZGFuQG9wc2NvZGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
@@ -12,16 +12,16 @@ SxuFR7Fnp2OM8ed7iPIKSrcM0vQ+g7vYKCv5Z8UR3sbLY8UHm9AgZ/bLAHEHS2if
1WHPD5DOe1B7HwW0IfEiW4/WakkVn4uoWw5rCZ87f4YCrETomXIo1n/rMFHf+yoY
guuEfGQxRcQdlEZM9YMlMByQvXlVR5IVhpiMHBCyV6KzxjZVCgTlvS8nPMiiHpoO
pgB6BGEQ/nn4Kapk40baPqpT4EP/DnBnbhhR3kBQ6MQRlh7bl5vjH5xFSFwGUUA9
-IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABo4H0MIHxMB0GA1UdDgQWBBS88Zxt
-vG+FTu1U+VFA47ffzwStbjCBwQYDVR0jBIG5MIG2gBS88ZxtvG+FTu1U+VFA47ff
-zwStbqGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
-EDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzARBgNVBAsTCmRldmVs
-b3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3DQEJARYPZGFuQG9w
-c2NvZGUuY29tggkAoEmvjBJFRW8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUF
-AAOCAQEAwwMrbuJAhP5uawJi5OYEaJKSbJGyahCcOAl4+ONgsdDoCy/9AZKzuFNc
-C8vM/Ee6jyugrKMdckvZ883kJ4770HU6nbomCUVKMHMzJBE1Guvsn8wZP3nKyeSZ
-eXXbH1b/NfstNyo6XLucaBRQvyvQYDUnk6osrBh+Gekvqsahr0wkVa8VUY2UySyY
-60lYt4O92XJ1jWtYoFjRxeeUgo5E0TfIWj74kXhdMqwMf4Iv9VatfYR87ps5VMdf
-Hp+nrCRaquDAs87LdO9e7M8l+W1ryPfP2inuGjIozsN5lLmwBdT+O6NkpmuxGPEG
-ArIbYatR7+4MsDn+CjfkYblnmGLuug==
+IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABo4H0MIHxMAwGA1UdEwQFMAMBAf8w
+HQYDVR0OBBYEFLzxnG28b4VO7VT5UUDjt9/PBK1uMIHBBgNVHSMEgbkwgbaAFLzx
+nG28b4VO7VT5UUDjt9/PBK1uoYGSpIGPMIGMMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTENMAsGA1UEChMEQ2hlZjET
+MBEGA1UECxMKZGV2ZWxvcGVyczESMBAGA1UEAxMJa2FsbGlzdGVjMR4wHAYJKoZI
+hvcNAQkBFg9kYW5Ab3BzY29kZS5jb22CCQDhAwuURaBFGTANBgkqhkiG9w0BAQsF
+AAOCAQEAoTCRzUbZOPrJdpd928fQPt/HsYODDmgWQJIPucdLKmlY5wb3zSc1B5H6
+zBUmSFylnDLKhZlO+gojBuQDhr2h9bMXn8RvE0A/Dqq9XcNsblMn0HjoJmjv/x8Q
+wFfrwIWj1wHoGHromkJaedWCRGlVW1oLZa99JmQCNee4bjcwkK2H0xRqX8STbqJV
+z+uEBf5fDc4EioULwfxa6B15XDD09k14uHtlV6JwTmahDjpdKV/ICKBi/WN0aQg1
+9k7OAkW5cnzmS6uFFjrvWuNy4ey4j1c4U5GogxEgCsattshHNO+icWRCN2gPg2Nx
+SKEXNcUA4jRWGF7PRgY/oyjULObFqw==
-----END CERTIFICATE-----
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/templates/failed.erb b/spec/data/templates/failed.erb
new file mode 100644
index 0000000000..e077ac8684
--- /dev/null
+++ b/spec/data/templates/failed.erb
@@ -0,0 +1,5 @@
+This is a template
+
+Which includes some content
+
+And will fail <%= nil[] %>
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/data/windows_certificates/base64_test.cer b/spec/data/windows_certificates/base64_test.cer
new file mode 100644
index 0000000000..763216f86a
--- /dev/null
+++ b/spec/data/windows_certificates/base64_test.cer
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIQPAc5ZRAOLL1PCvdo8CoWDTANBgkqhkiG9w0BAQsFADAh
+MR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVzdHMuY29tMCAXDTIwMDMwNjEyMDUz
+MVoYDzI4OTkxMjMxMTgzMDAwWjAhMR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVz
+dHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6uZ0V5zobMQm
+JPtZxt4vYtL/As7U6sUBVe9oR9OCYvyIDmpuPcNnrJ26L+iu2W5Kd+840Dv6tHS4
+yOV07bYBU+nVHiCdEn/K7Q5ITv/8uXv39dvlSuSrIn4P+I2vhSQjIy/B94QPD/xE
+dD0WDym1ySY2zQsL4T+yKoaXc5tiBoWBwAdl6/RiXeOm2kBXhIDcW4MLlB0BXtDJ
+l7syB30mOvNsQT6UlymI1q7fpsaPBTo8V3lUWooVVmQciiYquoD34gq7XpdGQOLJ
+V7aSIch1BoQyeQJfWsKzv/R5yzAzw+2zeRf301USunBXwhoac/Sx4xrJxjRknGTs
+7tsCNQUmRQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
+BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFnd3dy5kdW1teWNoZWZ0ZXN0cy5j
+b20wHQYDVR0OBBYEFGQa7l1ZPNbhj0s64g1/nyY+xULdMA0GCSqGSIb3DQEBCwUA
+A4IBAQCS3chRs1LUvlq7Hj1kx3CtAhjTE75eEWz8wzWZ+DGppGnMUQg0vwrw7JPd
+s3ODAFor62J97Fmb1sQ9/lSGan0CwBtCMqzHr3hoKbpVR9aFKu/Kt21zE4pEvFgZ
+NVrxOFofmZ072VRdRpRK3RcnV58I02Xyb+5VR8lTbHpIsUOj+i9+y5ZuuOXoRDpI
+G+AdIAfvcBbshPkI62gSFvBUdic0fcMVPZ5rFWaDjW2XFXZ6s/e5mPHNjpGpSZy7
+2y9ku9kB6ywBQXx9U21DBoIDxfprSylQGxtUuXaeCwnRvpT0Ifto5/KaeH4IzJQq
+ZYGdPzBO7WBpk/AsO6buw3kQ9M5h
+-----END CERTIFICATE-----
diff --git a/spec/data/windows_certificates/othertest.cer b/spec/data/windows_certificates/othertest.cer
new file mode 100644
index 0000000000..f4ff69eb08
--- /dev/null
+++ b/spec/data/windows_certificates/othertest.cer
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVjCCAj6gAwIBAgIQEavqVx6t6J9MQFWBGNLMAzANBgkqhkiG9w0BAQsFADAo
+MSYwJAYDVQQDDB13d3cuZHVtbXljaGVmdGVzdHNhbm90aGVyLmNvbTAgFw0yMDAz
+MDYxMDA2NDVaGA8yODk5MTIzMTE4MzAwMFowKDEmMCQGA1UEAwwdd3d3LmR1bW15
+Y2hlZnRlc3RzYW5vdGhlci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDwvGtD46qB4T9Fyt0oEXX2C0zgMQuFPWSutAKWArQCXjDlSD2+a4DvFHvz
+czv8P5P9XZM52cYDIk1nsdG75ZsgzLoiFvk2MtGpH3A1J8tXxJAROZCj8mAwoVpQ
+e791otvHH6LYK4iABHUN3PvuQEfbLTqcPPDFB3jgqV7mkI3wiGxZ5txrjBJo4f8Q
+9ZcqhR33zhp5+eBUH46T7ZY524/CI2dv+1a58LX9y1neqe6Bg0K51Rw0O8Zm/8kD
+31paD75qkq1qWS0BD0OOhVXXzsO1C4OH8L53nAtrwGceTmNRKPgMD5+Rwe6zaw94
+G7m+UeMyEkFJ87Y8+HeSU7F09TcVAgMBAAGjejB4MA4GA1UdDwEB/wQEAwIFoDAd
+BgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKAYDVR0RBCEwH4Idd3d3LmR1
+bW15Y2hlZnRlc3RzYW5vdGhlci5jb20wHQYDVR0OBBYEFLzhpImP5MmDZ0s60syY
+agCrc9NoMA0GCSqGSIb3DQEBCwUAA4IBAQArFhqQQ0U3yzLr47Bcu5WwooULCarW
+caroDqJySdQV+abAMf0aqr8TTl+o9dVyl7Lzt+G4WQgl8Ay7P5dlEv3mGRl7J2PF
+o55PPPmZQkORzwv772d7Nzv9bQemMcYZF38su9+k3mcMh0vxdHoaz39TpUQt5Tz1
+3WC56dflaCbRMq8/fQhYivxfByq3BOf4ghW+BswZMjrV9wMDwKv21ebYAULmxaU3
+/Z0igXN3O4V4RKavONUwRMyRspLFlxm0EEirP+FruQS+/ABIPxuaReYLSqnD9Jqa
+03UdwrqC3bymfKI1zYYToSAqBO8qjim8+cjvdEaXGILW7YBKXNEvyJ20
+-----END CERTIFICATE-----
diff --git a/spec/data/windows_certificates/test.cer b/spec/data/windows_certificates/test.cer
new file mode 100644
index 0000000000..1c358b5035
--- /dev/null
+++ b/spec/data/windows_certificates/test.cer
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIQX3zqNCJbsKlEvzCz3Z9aNDANBgkqhkiG9w0BAQsFADAh
+MR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVzdHMuY29tMCAXDTIwMDMwNTEwMjcw
+NVoYDzIxMjAwMzA1MTAzNzA2WjAhMR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVz
+dHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtuYKDb6woWIH
+HPPOrcVpgJFVxbkjgk+tsYwbIiqR9jtRaKE6nM/awOgn9/dFF4k8KB8Em0sUx7Vq
+J3YhK2N2cAacgP2Frqqf5znpNBBOg968RoZzGx0EiXFvLsqC4y8ggApWTbMXPRk4
+1a7GlpUpSqI3y5cLeEbzwGQKu8I1I+v7P2fTlnJPHarM7sBbL8bieukkFHYu78iV
+u1wpKOCCfs5DTmJu8WN+z1Mar9vyrWMBlt2wBBgNHPz5mcXUzJHTzaI/D9RGgBgF
+V0IkNqISx/IzR62jjj2g6MgTH4G/0mM6O5sxduM4yGmWZNZpVzh0yMLgH619MZlj
+SMQIN3U/SQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
+BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFnd3dy5kdW1teWNoZWZ0ZXN0cy5j
+b20wHQYDVR0OBBYEFHwS3gs03m6RcpR+66u4OqGiZdYnMA0GCSqGSIb3DQEBCwUA
+A4IBAQCFHqMjHUfBZahIsKHQIcFCbC1NFh1ZHlJKZzrRBRwRzX19OttHGMyLpDd6
+tM9Ac6LLR8S4QIWg+HF3IrkN+vfTRDZAccj+tIwBRstmdsEz/rAJ79Vb/00mXZQx
+0FPiBDR3hE7On2oo24DU8kJP3v6TrunwtIomVGqrrkwZzvxqyW+WJMB2shGNFw5J
+mKYBiiXsHl4Bi7V4zhXssrLp877sqpNLeXloXBmAlT39SwQTP9ImZaV5R6udqlvo
+Gfgm5PH/WeK6MV3n5ik0v1rS0LwR2o82WlIB6a4iSEbzY3qSLsWOwt8o5QjAVzCR
+tNdbdS3U8nrG73iA2clmF57ARQWC
+-----END CERTIFICATE-----
diff --git a/spec/data/windows_certificates/test.p7b b/spec/data/windows_certificates/test.p7b
new file mode 100644
index 0000000000..bd46d5eccd
--- /dev/null
+++ b/spec/data/windows_certificates/test.p7b
Binary files differ
diff --git a/spec/data/windows_certificates/test.pem b/spec/data/windows_certificates/test.pem
new file mode 100644
index 0000000000..1c358b5035
--- /dev/null
+++ b/spec/data/windows_certificates/test.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIQX3zqNCJbsKlEvzCz3Z9aNDANBgkqhkiG9w0BAQsFADAh
+MR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVzdHMuY29tMCAXDTIwMDMwNTEwMjcw
+NVoYDzIxMjAwMzA1MTAzNzA2WjAhMR8wHQYDVQQDDBZ3d3cuZHVtbXljaGVmdGVz
+dHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtuYKDb6woWIH
+HPPOrcVpgJFVxbkjgk+tsYwbIiqR9jtRaKE6nM/awOgn9/dFF4k8KB8Em0sUx7Vq
+J3YhK2N2cAacgP2Frqqf5znpNBBOg968RoZzGx0EiXFvLsqC4y8ggApWTbMXPRk4
+1a7GlpUpSqI3y5cLeEbzwGQKu8I1I+v7P2fTlnJPHarM7sBbL8bieukkFHYu78iV
+u1wpKOCCfs5DTmJu8WN+z1Mar9vyrWMBlt2wBBgNHPz5mcXUzJHTzaI/D9RGgBgF
+V0IkNqISx/IzR62jjj2g6MgTH4G/0mM6O5sxduM4yGmWZNZpVzh0yMLgH619MZlj
+SMQIN3U/SQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
+BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFnd3dy5kdW1teWNoZWZ0ZXN0cy5j
+b20wHQYDVR0OBBYEFHwS3gs03m6RcpR+66u4OqGiZdYnMA0GCSqGSIb3DQEBCwUA
+A4IBAQCFHqMjHUfBZahIsKHQIcFCbC1NFh1ZHlJKZzrRBRwRzX19OttHGMyLpDd6
+tM9Ac6LLR8S4QIWg+HF3IrkN+vfTRDZAccj+tIwBRstmdsEz/rAJ79Vb/00mXZQx
+0FPiBDR3hE7On2oo24DU8kJP3v6TrunwtIomVGqrrkwZzvxqyW+WJMB2shGNFw5J
+mKYBiiXsHl4Bi7V4zhXssrLp877sqpNLeXloXBmAlT39SwQTP9ImZaV5R6udqlvo
+Gfgm5PH/WeK6MV3n5ik0v1rS0LwR2o82WlIB6a4iSEbzY3qSLsWOwt8o5QjAVzCR
+tNdbdS3U8nrG73iA2clmF57ARQWC
+-----END CERTIFICATE-----
diff --git a/spec/data/windows_certificates/test.pfx b/spec/data/windows_certificates/test.pfx
new file mode 100644
index 0000000000..2c208bf7c6
--- /dev/null
+++ b/spec/data/windows_certificates/test.pfx
Binary files differ
diff --git a/spec/functional/application_spec.rb b/spec/functional/application_spec.rb
index 19a23e0e22..91060d49b5 100644
--- a/spec/functional/application_spec.rb
+++ b/spec/functional/application_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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/inittest b/spec/functional/assets/inittest
new file mode 100644
index 0000000000..3284d27a1e
--- /dev/null
+++ b/spec/functional/assets/inittest
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+TMPDIR="${TMPDIR:-/tmp}"
+
+create_chef_txt() {
+ touch "$TMPDIR"/inittest.txt
+}
+
+
+delete_chef_txt() {
+ rm "$TMPDIR"/inittest.txt
+}
+
+rename_chef_txt() {
+ mv "$TMPDIR"/inittest.txt "$TMPDIR"/"$1"
+}
+
+case "$1" in
+start )
+ create_chef_txt
+ ;;
+stop )
+ delete_chef_txt
+ ;;
+status )
+ [ -f "$TMPDIR"/inittest.txt ] || [ -f "$TMPDIR"/inittest_reload.txt ] || [ -f "$TMPDIR"/inittest_restart.txt ]
+ ;;
+reload )
+ rename_chef_txt "inittest_reload.txt"
+ ;;
+restart )
+ rename_chef_txt "inittest_restart.txt"
+ ;;
+* )
+ echo "Usage: $0 (start | stop | restart | reload)"
+ exit 1
+esac
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/assets/zypprepo/chef_rpm-1.10-1.aarch64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.aarch64.rpm
new file mode 100644
index 0000000000..808e5c64c9
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.aarch64.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.i686.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.i686.rpm
new file mode 100644
index 0000000000..ed7b6ddc8e
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.i686.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64.rpm
new file mode 100644
index 0000000000..f3683b3c89
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64le.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64le.rpm
new file mode 100644
index 0000000000..4f8de433d9
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64le.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.s390x.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.s390x.rpm
new file mode 100644
index 0000000000..98538293ff
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.s390x.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.src.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.src.rpm
new file mode 100644
index 0000000000..03a92f1cfc
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.src.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.10-1.x86_64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.x86_64.rpm
new file mode 100644
index 0000000000..3533640780
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.10-1.x86_64.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.aarch64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.aarch64.rpm
new file mode 100644
index 0000000000..73d59bf3e2
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.aarch64.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.i686.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.i686.rpm
new file mode 100644
index 0000000000..6637756abb
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.i686.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64.rpm
new file mode 100644
index 0000000000..677e43f83b
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64le.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64le.rpm
new file mode 100644
index 0000000000..df21a68780
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64le.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.s390x.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.s390x.rpm
new file mode 100644
index 0000000000..02a771c6f5
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.s390x.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.src.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.src.rpm
new file mode 100644
index 0000000000..5d4d13e18a
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.src.rpm
Binary files differ
diff --git a/spec/functional/assets/zypprepo/chef_rpm-1.2-1.x86_64.rpm b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.x86_64.rpm
new file mode 100644
index 0000000000..314c52f22e
--- /dev/null
+++ b/spec/functional/assets/zypprepo/chef_rpm-1.2-1.x86_64.rpm
Binary files differ
diff --git a/spec/functional/audit/rspec_formatter_spec.rb b/spec/functional/audit/rspec_formatter_spec.rb
deleted file mode 100644
index 209694ae70..0000000000
--- a/spec/functional/audit/rspec_formatter_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@chef.io>)
-# Author:: Claire McQuin (<claire@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 "rspec/core/sandbox"
-require "chef/audit/runner"
-require "rspec/support/spec/in_sub_process"
-require "rspec/support/spec/stderr_splitter"
-require "chef/audit/rspec_formatter"
-
-describe Chef::Audit::RspecFormatter do
- include RSpec::Support::InSubProcess
-
- let(:events) { double("events").as_null_object }
- let(:audits) { {} }
- let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) }
- let(:runner) { Chef::Audit::Runner.new(run_context) }
-
- let(:output) { double("output") }
- # aggressively define this so we can mock out the new call later
- let!(:formatter) { Chef::Audit::RspecFormatter.new(output) }
-
- around(:each) do |ex|
- RSpec::Core::Sandbox.sandboxed { ex.run }
- end
-
- it "should not close the output using our formatter" do
- in_sub_process do
- expect_any_instance_of(Chef::Audit::RspecFormatter).to receive(:new).and_return(formatter)
- expect(formatter).to receive(:close).and_call_original
- expect(output).to_not receive(:close)
-
- runner.run
- end
- end
-
-end
diff --git a/spec/functional/audit/runner_spec.rb b/spec/functional/audit/runner_spec.rb
deleted file mode 100644
index 54f014e28f..0000000000
--- a/spec/functional/audit/runner_spec.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "rspec/core/sandbox"
-require "chef/audit/runner"
-require "rspec/support/spec/in_sub_process"
-require "rspec/support/spec/stderr_splitter"
-require "tempfile"
-
-##
-# This functional test ensures that our runner can be setup to not interfere with existing RSpec
-# configuration and world objects. When normally running Chef, there is only 1 RSpec instance
-# so this isn't needed. In unit testing the Runner should be mocked appropriately.
-
-describe Chef::Audit::Runner do
-
- # The functional tests must be run in a sub_process. Including Serverspec includes the Serverspec DSL - this
- # conflicts with our `package` DSL (among others) when we try to test `package` inside an RSpec example.
- # Our DSL leverages `method_missing` while the Serverspec DSL defines a method on the RSpec::Core::ExampleGroup.
- # The defined method wins our and returns before our `method_missing` DSL can be called.
- #
- # Running in a sub_process means the serverspec libraries will only be included in a forked process, not the main one.
- include RSpec::Support::InSubProcess
-
- let(:events) { double("events").as_null_object }
- let(:runner) { Chef::Audit::Runner.new(run_context) }
- let(:stdout) { StringIO.new }
-
- around(:each) do |ex|
- RSpec::Core::Sandbox.sandboxed { ex.run }
- end
-
- describe "#run" do
-
- let(:audits) { {} }
- let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) }
- let(:control_group_name) { "control_group_name" }
-
- # Set cookbook path to include our parent, so that it will recognize this
- # rspec file as one that should show up in the backtrace.
- before(:each) { Chef::Config[:cookbook_path] = File.dirname(__FILE__) }
-
- shared_context "passing audit" do
- let(:audits) do
- should_pass = lambda do
- it "should pass" do
- expect(2 - 2).to eq(0)
- end
- end
- { control_group_name => Struct.new(:args, :block).new([control_group_name], should_pass) }
- end
- end
-
- shared_context "failing audit" do
- let(:audits) do
- should_fail = lambda do
- it "should fail" do
- expect(2 - 1).to eq(0)
- end
- end
- { control_group_name => Struct.new(:args, :block).new([control_group_name], should_fail) }
- end
- end
-
- describe "log location is stdout" do
- before do
- allow(Chef::Log).to receive(:info) do |msg|
- stdout.puts(msg)
- end
- end
-
- it "Correctly runs an empty controls block" do
- in_sub_process do
- runner.run
- end
- end
-
- context "there is a single successful control" do
- include_context "passing audit"
- it "correctly runs" do
- in_sub_process do
- runner.run
-
- expect(stdout.string).to match(/1 example, 0 failures/)
- end
- end
- end
-
- context "there is a single failing control" do
- include_context "failing audit"
- it "correctly runs" do
- in_sub_process do
- runner.run
-
- expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/)
- expect(stdout.string).to match(/1 example, 1 failure/)
- expect(stdout.string).to match(/# control_group_name should fail/)
- end
- end
- end
- end
-
- end
-
-end
diff --git a/spec/functional/dsl/reboot_pending_spec.rb b/spec/functional/dsl/reboot_pending_spec.rb
index c7a93c6822..9ff3f5495c 100644
--- a/spec/functional/dsl/reboot_pending_spec.rb
+++ b/spec/functional/dsl/reboot_pending_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,13 @@
#
require "chef/dsl/reboot_pending"
+require "chef/dsl/registry_helper"
require "chef/win32/registry"
require "spec_helper"
describe Chef::DSL::RebootPending, :windows_only do
+ include Chef::DSL::RegistryHelper
+
def run_ohai
node.consume_external_attrs(OHAI_SYSTEM.data, {})
end
@@ -36,44 +39,21 @@ describe Chef::DSL::RebootPending, :windows_only do
let(:reg_key) { nil }
let(:original_set) { false }
- before(:all) { @any_flag = Hash.new }
-
- after { @any_flag[reg_key] = original_set }
-
describe 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations' do
let(:reg_key) { 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager' }
- let(:original_set) { registry.value_exists?(reg_key, { :name => "PendingFileRenameOperations" }) }
+ let(:original_set) { registry.value_exists?(reg_key, { name: "PendingFileRenameOperations" }) }
it "returns true if the registry value exists" do
skip "found existing registry key" if original_set
registry.set_value(reg_key,
- { :name => "PendingFileRenameOperations", :type => :multi_string, :data => ['\??\C:\foo.txt|\??\C:\bar.txt'] })
+ { name: "PendingFileRenameOperations", type: :multi_string, data: ['\??\C:\foo.txt|\??\C:\bar.txt'] })
expect(recipe.reboot_pending?).to be_truthy
end
after do
unless original_set
- registry.delete_value(reg_key, { :name => "PendingFileRenameOperations" })
- end
- end
- end
-
- describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' do
- let(:reg_key) { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' }
- let(:original_set) { registry.key_exists?(reg_key) }
-
- it "returns true if the registry key exists" do
- skip "found existing registry key" if original_set
- pending "Permissions are limited to 'TrustedInstaller' by default"
- registry.create_key(reg_key, false)
-
- expect(recipe.reboot_pending?).to be_truthy
- end
-
- after do
- unless original_set
- registry.delete_key(reg_key, false)
+ registry.delete_value(reg_key, { name: "PendingFileRenameOperations" })
end
end
end
@@ -98,7 +78,9 @@ describe Chef::DSL::RebootPending, :windows_only do
describe "when there is nothing to indicate a reboot is pending" do
it "should return false" do
- skip "reboot pending" if @any_flag.any? { |_, v| v == true }
+ skip "reboot pending" if registry_value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { name: "PendingFileRenameOperations" }) ||
+ registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') ||
+ registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending')
expect(recipe.reboot_pending?).to be_falsey
end
end
diff --git a/spec/functional/dsl/registry_helper_spec.rb b/spec/functional/dsl/registry_helper_spec.rb
index d90d5090e4..9d043dd35f 100644
--- a/spec/functional/dsl/registry_helper_spec.rb
+++ b/spec/functional/dsl/registry_helper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Resource::RegistryKey, :windows_only do
- before (:all) do
+ before(:all) do
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root"
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch"
::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg|
@@ -42,7 +42,7 @@ describe Chef::Resource::RegistryKey, :windows_only do
end
it "returns true if registry has specified value" do
values = @resource.registry_get_values("HKCU\\Software\\Root")
- expect(values.include?({ :name => "RootType1", :type => :string, :data => "fibrous" })).to eq(true)
+ expect(values.include?({ name: "RootType1", type: :string, data: "fibrous" })).to eq(true)
end
it "returns true if specified registry_has_subkey" do
expect(@resource.registry_has_subkeys?("HKCU\\Software\\Root")).to eq(true)
@@ -52,10 +52,10 @@ describe Chef::Resource::RegistryKey, :windows_only do
expect(subkeys.include?("Branch")).to eq(true)
end
it "returns true if registry_value_exists" do
- expect(@resource.registry_value_exists?("HKCU\\Software\\Root", { :name => "RootType1", :type => :string, :data => "fibrous" })).to eq(true)
+ expect(@resource.registry_value_exists?("HKCU\\Software\\Root", { name: "RootType1", type: :string, data: "fibrous" })).to eq(true)
end
it "returns true if data_value_exists" do
- expect(@resource.registry_data_exists?("HKCU\\Software\\Root", { :name => "RootType1", :type => :string, :data => "fibrous" })).to eq(true)
+ expect(@resource.registry_data_exists?("HKCU\\Software\\Root", { name: "RootType1", type: :string, data: "fibrous" })).to eq(true)
end
end
end
diff --git a/spec/functional/event_loggers/windows_eventlog_spec.rb b/spec/functional/event_loggers/windows_eventlog_spec.rb
index 019595ea8d..b9f10fd4dc 100644
--- a/spec/functional/event_loggers/windows_eventlog_spec.rb
+++ b/spec/functional/event_loggers/windows_eventlog_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
@@ -19,12 +19,13 @@
require "spec_helper"
require "securerandom"
require "chef/event_loggers/windows_eventlog"
-if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
+require "chef-utils"
+if ChefUtils.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
@@ -46,19 +47,21 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
end
it "writes run_start event with event_id 10000 and contains version" do
- logger.run_start(version)
+ logger.run_start(version, run_status)
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
+ e.source == ChefUtils::Dist::Infra::SHORT && 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? do |e|
- e.source == "Chef" && e.event_id == 10001 &&
- e.string_inserts[0].include?(run_id) end).to be_truthy
+ e.source == ChefUtils::Dist::Infra::SHORT && 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
@@ -66,7 +69,7 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
logger.run_completed(node)
expect(event_log.read(flags, offset).any? do |e|
- e.source == "Chef" && e.event_id == 10002 &&
+ e.source == ChefUtils::Dist::Infra::SHORT && e.event_id == 10002 &&
e.string_inserts[0].include?(run_id) &&
e.string_inserts[1].include?(elapsed_time.to_s)
end).to be_truthy
@@ -77,7 +80,7 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
logger.run_failed(mock_exception)
expect(event_log.read(flags, offset).any? do |e|
- e.source == "Chef" && e.event_id == 10003 &&
+ e.source == ChefUtils::Dist::Infra::SHORT && e.event_id == 10003 &&
e.string_inserts[0].include?(run_id) &&
e.string_inserts[1].include?(elapsed_time.to_s) &&
e.string_inserts[2].include?(mock_exception.class.name) &&
@@ -91,7 +94,7 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
logger.run_failed(mock_exception)
expect(event_log.read(flags, offset).any? do |e|
- e.source == "Chef" && e.event_id == 10003 &&
+ e.source == ChefUtils::Dist::Infra::SHORT && e.event_id == 10003 &&
e.string_inserts[0].include?("UNKNOWN") &&
e.string_inserts[1].include?("UNKNOWN") &&
e.string_inserts[2].include?(mock_exception.class.name) &&
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index 9e2131388f..c5a5e351ae 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -113,7 +113,7 @@ shared_examples_for "a content deploy strategy" do
path
end
- def unix_invariant_properies(stat_struct)
+ def unix_invariant_properties(stat_struct)
unix_invariants.inject({}) do |property_map, property|
property_map[property] = stat_struct.send(property)
property_map
@@ -137,7 +137,7 @@ shared_examples_for "a content deploy strategy" do
content_deployer.deploy(staging_file_path, target_file_path)
updated_info = File.stat(target_file_path)
- expect(unix_invariant_properies(original_info)).to eq(unix_invariant_properies(updated_info))
+ expect(unix_invariant_properties(original_info)).to eq(unix_invariant_properties(updated_info))
end
it "maintains invariant properties on Windows", :windows_only do
@@ -164,7 +164,7 @@ shared_examples_for "a content deploy strategy" do
content_deployer.deploy(staging_file_path, target_file_path)
updated_info = File.stat(target_file_path)
- expect(unix_invariant_properies(original_info)).to eq(unix_invariant_properies(updated_info))
+ expect(unix_invariant_properties(original_info)).to eq(unix_invariant_properties(updated_info))
end
end
@@ -175,20 +175,20 @@ end
describe Chef::FileContentManagement::Deploy::Cp do
let(:unix_invariants) do
- [
- :uid,
- :gid,
- :mode,
- :ino,
- ]
+ %i{
+ uid
+ gid
+ mode
+ ino
+ }
end
let(:security_descriptor_invariants) do
- [
- :owner,
- :group,
- :dacl,
- ]
+ %i{
+ owner
+ group
+ dacl
+ }
end
it_should_behave_like "a content deploy strategy"
@@ -198,11 +198,11 @@ end
describe Chef::FileContentManagement::Deploy::MvUnix, :unix_only do
let(:unix_invariants) do
- [
- :uid,
- :gid,
- :mode,
- ]
+ %i{
+ uid
+ gid
+ mode
+ }
end
it_should_behave_like "a content deploy strategy"
@@ -216,11 +216,11 @@ describe Chef::FileContentManagement::Deploy::MvWindows, :windows_only do
context "when a file has no sacl" do
let(:security_descriptor_invariants) do
- [
- :owner,
- :group,
- :dacl,
- ]
+ %i{
+ owner
+ group
+ dacl
+ }
end
it_should_behave_like "a content deploy strategy"
diff --git a/spec/functional/http/simple_spec.rb b/spec/functional/http/simple_spec.rb
index 421045693a..a94c8304e8 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,13 +24,19 @@ describe Chef::HTTP::Simple do
include ChefHTTPShared
let(:http_client) { described_class.new(source) }
- let(:http_client_disable_gzip) { described_class.new(source, { :disable_gzip => true } ) }
+ let(:http_client_disable_gzip) { described_class.new(source, { disable_gzip: true } ) }
+
+ before(:all) do
+ start_tiny_server(RequestTimeout: 1)
+ end
before(:each) do
- start_tiny_server
+ Chef::Config[:rest_timeout] = 2
+ Chef::Config[:http_retry_delay] = 1
+ Chef::Config[:http_retry_count] = 2
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -46,46 +52,46 @@ 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
shared_examples_for "an endpoint that 403s" do
- it "fails with a Net::HTTPServerException for a streaming request" do
- expect { http_client.streaming_request(source) }.to raise_error(Net::HTTPServerException)
+ it "fails with a Net::HTTPClientException for a streaming request" do
+ expect { http_client.streaming_request(source) }.to raise_error(Net::HTTPClientException)
end
- it "fails with a Net::HTTPServerException for a GET request" do
- expect { http_client.get(source) }.to raise_error(Net::HTTPServerException)
+ it "fails with a Net::HTTPClientException for a GET request" do
+ expect { http_client.get(source) }.to raise_error(Net::HTTPClientException)
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 for a streaming request" do
+ it "fails with a Net::HTTPClientException for 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))
- expect { http_client.streaming_request(source2) }.to raise_error(Net::HTTPServerException)
+ expect { http_client.streaming_request(source2) }.to raise_error(Net::HTTPClientException)
end
- it "fails with a Net::HTTPServerException for a GET request" do
+ it "fails with a Net::HTTPClientException for a GET request" do
expect(Digest::MD5.hexdigest(http_client.get(source))).to eq(Digest::MD5.hexdigest(expected_content))
- expect { http_client.get(source2) }.to raise_error(Net::HTTPServerException)
+ expect { http_client.get(source2) }.to raise_error(Net::HTTPClientException)
end
end
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" }
@@ -114,7 +120,7 @@ describe Chef::HTTP::Simple do
it "Logs the request and response and bodies for 400 response" do
expect do
http_client.get("http://localhost:9000/bad_request")
- end.to raise_error(Net::HTTPServerException)
+ end.to raise_error(Net::HTTPClientException)
expect(@debug_log).to match(/400/)
expect(@debug_log).to match(/HTTP Request Header Data/)
expect(@debug_log).to match(/HTTP Status and Header Data/)
@@ -127,7 +133,7 @@ describe Chef::HTTP::Simple do
it "Logs the request and response and bodies for 400 POST response" do
expect do
http_client.post("http://localhost:9000/bad_request", "hithere")
- end.to raise_error(Net::HTTPServerException)
+ end.to raise_error(Net::HTTPClientException)
expect(@debug_log).to match(/400/)
expect(@debug_log).to match(/HTTP Request Header Data/)
expect(@debug_log).to match(/HTTP Status and Header Data/)
diff --git a/spec/functional/knife/configure_spec.rb b/spec/functional/knife/configure_spec.rb
index f1da2d660e..8f2a5b4d6e 100644
--- a/spec/functional/knife/configure_spec.rb
+++ b/spec/functional/knife/configure_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
require "chef/knife/configure"
describe "knife configure" do
- let (:ohai) do
+ let(:ohai) do
OHAI_SYSTEM
end
diff --git a/spec/functional/knife/cookbook_delete_spec.rb b/spec/functional/knife/cookbook_delete_spec.rb
index 99f3309752..650db0ede5 100644
--- a/spec/functional/knife/cookbook_delete_spec.rb
+++ b/spec/functional/knife/cookbook_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,86 +20,75 @@ require "spec_helper"
require "tiny_server"
describe Chef::Knife::CookbookDelete do
- before(:each) do
- @server = TinyServer::Manager.new
- @server.start
- end
-
- after(:each) do
- @server.stop
+ 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
- context "when the cookbook doesn't exist" do
- let(:log_output) { StringIO.new }
+ after(:each) do
+ server.stop
+ end
+ context "when the cookbook doesn't exist" do
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 ac8f617a90..3905798317 100644
--- a/spec/functional/knife/exec_spec.rb
+++ b/spec/functional/knife/exec_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "tiny_server"
describe Chef::Knife::Exec do
before(:each) do
- @server = TinyServer::Manager.new #(:debug => true)
+ @server = TinyServer::Manager.new # (:debug => true)
@server.start
end
@@ -49,7 +49,7 @@ describe Chef::Knife::Exec do
code = "$output.puts nodes.all"
@knife.config[:exec] = code
@knife.run
- expect($output.string).to match(%r{node\[ohai-world\]})
+ expect($output.string).to match(/node\[ohai-world\]/)
end
end
diff --git a/spec/functional/knife/rehash_spec.rb b/spec/functional/knife/rehash_spec.rb
index 22ffa125fa..8f59eec270 100644
--- a/spec/functional/knife/rehash_spec.rb
+++ b/spec/functional/knife/rehash_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/knife/smoke_test.rb b/spec/functional/knife/smoke_test.rb
index 350644d177..99b61f42f2 100644
--- a/spec/functional/knife/smoke_test.rb
+++ b/spec/functional/knife/smoke_test.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/knife/ssh_spec.rb b/spec/functional/knife/ssh_spec.rb
index 065b646ac6..1d4aff15b5 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,27 +31,16 @@ describe Chef::Knife::Ssh do
@server.stop
end
- let(:ssh_config) { Hash.new }
+ let(:ssh_config) { {} }
before do
allow(Net::SSH).to receive(:configuration_for).and_return(ssh_config)
end
- # Force log level to info.
- around do |ex|
- old_level = Chef::Log.level
- begin
- Chef::Log.level = :info
- ex.run
- ensure
- Chef::Log.level = old_level
- end
- end
-
describe "identity file" do
context "when knife[:ssh_identity_file] is set" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/aws.rsa"
+ setup_knife(["*:*", "uptime"])
end
it "uses the ssh_identity_file" do
@@ -62,8 +51,8 @@ describe Chef::Knife::Ssh do
context "when knife[:ssh_identity_file] is set and frozen" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/aws.rsa".freeze
+ setup_knife(["*:*", "uptime"])
end
it "uses the ssh_identity_file" do
@@ -74,8 +63,8 @@ describe Chef::Knife::Ssh do
context "when -i is provided" do
before do
- setup_knife(["-i ~/.ssh/aws.rsa", "*:*", "uptime"])
Chef::Config[:knife][:ssh_identity_file] = nil
+ setup_knife(["-i ~/.ssh/aws.rsa", "*:*", "uptime"])
end
it "should use the value on the command line" do
@@ -85,6 +74,7 @@ describe Chef::Knife::Ssh do
it "should override what is set in knife.rb" do
Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/other.rsa"
+ @knife.merge_configs
@knife.run
expect(@knife.config[:ssh_identity_file]).to eq("~/.ssh/aws.rsa")
end
@@ -92,8 +82,8 @@ describe Chef::Knife::Ssh do
context "when knife[:ssh_identity_file] is not provided]" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_identity_file] = nil
+ setup_knife(["*:*", "uptime"])
end
it "uses the default" do
@@ -119,8 +109,8 @@ describe Chef::Knife::Ssh do
describe "user" do
context "when knife[:ssh_user] is set" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_user] = "ubuntu"
+ setup_knife(["*:*", "uptime"])
end
it "uses the ssh_user" do
@@ -131,8 +121,8 @@ describe Chef::Knife::Ssh do
context "when knife[:ssh_user] is set and frozen" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_user] = "ubuntu".freeze
+ setup_knife(["*:*", "uptime"])
end
it "uses the ssh_user" do
@@ -143,8 +133,8 @@ describe Chef::Knife::Ssh do
context "when -x is provided" do
before do
- setup_knife(["-x ubuntu", "*:*", "uptime"])
Chef::Config[:knife][:ssh_user] = nil
+ setup_knife(["-x ubuntu", "*:*", "uptime"])
end
it "should use the value on the command line" do
@@ -154,6 +144,7 @@ describe Chef::Knife::Ssh do
it "should override what is set in knife.rb" do
Chef::Config[:knife][:ssh_user] = "root"
+ @knife.merge_configs
@knife.run
expect(@knife.config[:ssh_user]).to eq("ubuntu")
end
@@ -161,8 +152,8 @@ describe Chef::Knife::Ssh do
context "when knife[:ssh_user] is not provided]" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_user] = nil
+ setup_knife(["*:*", "uptime"])
end
it "uses the default (current user)" do
@@ -175,46 +166,95 @@ describe Chef::Knife::Ssh do
describe "attribute" do
context "when knife[:ssh_attribute] is set" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_attribute] = "ec2.public_hostname"
+ setup_knife(["*:*", "uptime"])
end
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
+ setup_knife(["*:*", "uptime"])
end
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"])
Chef::Config[:knife][:ssh_attribute] = nil
+ setup_knife(["-a", "ec2.public_hostname", "*:*", "uptime"])
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"
+ @knife.merge_configs
# 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[:ssh_attribute]).to eq("ec2.public_hostname")
+ end
+ end
+ end
+
+ describe "prefix" do
+ context "when knife[:prefix_attribute] is set" do
+ before do
+ Chef::Config[:knife][:prefix_attribute] = "name"
+ setup_knife(["*:*", "uptime"])
+ 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
+ Chef::Config[:knife][:prefix_attribute] = nil
+ setup_knife(["*:*", "uptime"])
+ end
+
+ it "falls back to nil" do
@knife.run
- expect(@knife.config[:attribute]).to eq("ec2.public_hostname")
+ expect(@knife.get_prefix_attribute({})).to eq(nil)
+ end
+ end
+
+ context "when --prefix-attribute ec2.public_public_hostname is provided" do
+ before do
+ Chef::Config[:knife][:prefix_attribute] = nil
+ setup_knife(["--prefix-attribute", "ec2.public_hostname", "*:*", "uptime"])
+ 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"
+ @knife.merge_configs
+ # 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
@@ -222,12 +262,12 @@ describe Chef::Knife::Ssh do
describe "gateway" do
context "when knife[:ssh_gateway] is set" do
before do
- setup_knife(["*:*", "uptime"])
Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
+ setup_knife(["*:*", "uptime"])
end
it "uses the ssh_gateway" do
- expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", {})
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { append_all_supported_algorithms: true })
@knife.run
expect(@knife.config[:ssh_gateway]).to eq("user@ec2.public_hostname")
end
@@ -235,28 +275,56 @@ describe Chef::Knife::Ssh do
context "when -G user@ec2.public_hostname is provided" do
before do
- setup_knife(["-G user@ec2.public_hostname", "*:*", "uptime"])
Chef::Config[:knife][:ssh_gateway] = nil
+ setup_knife(["-G user@ec2.public_hostname", "*:*", "uptime"])
end
it "uses the ssh_gateway" do
- expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", {})
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { append_all_supported_algorithms: true })
@knife.run
expect(@knife.config[:ssh_gateway]).to eq("user@ec2.public_hostname")
end
end
+ context "when knife[:ssh_gateway_identity] is set" do
+ before do
+ Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
+ Chef::Config[:knife][:ssh_gateway_identity] = "~/.ssh/aws-gateway.rsa"
+ setup_knife(["*:*", "uptime"])
+ end
+
+ it "uses the ssh_gateway_identity file" do
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { append_all_supported_algorithms: true, 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
+ Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
+ Chef::Config[:knife][:ssh_gateway_identity] = nil
+ setup_knife(["--ssh-gateway-identity", "~/.ssh/aws-gateway.rsa", "*:*", "uptime"])
+ end
+
+ it "uses the ssh_gateway_identity file" do
+ expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", { append_all_supported_algorithms: true, 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"])
Chef::Config[:knife][:ssh_gateway] = nil
+ setup_knife(["-G user@ec2.public_hostname", "*:*", "uptime"])
allow(@knife.session).to receive(:via) do |host, user, options|
raise Net::SSH::AuthenticationFailed unless options[:password]
end
end
it "should prompt the user for a password" do
- expect(@knife.ui).to receive(:ask).with("Enter the password for user@ec2.public_hostname: ").and_return("password")
+ expect(@knife.ui).to receive(:ask).with("Enter the password for user@ec2.public_hostname: ", echo: false).and_return("password")
@knife.run
end
end
@@ -276,8 +344,8 @@ 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) do
- %({"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
diff --git a/spec/functional/mixin/from_file_spec.rb b/spec/functional/mixin/from_file_spec.rb
new file mode 100644
index 0000000000..17371513e7
--- /dev/null
+++ b/spec/functional/mixin/from_file_spec.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright (c) 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")
+ DIRECTORY = File.expand_path("")
+
+ 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
+
+ it "should fail if it's a directory not a file" do
+ datum = TestData.new
+ expect { datum.from_file(DIRECTORY) }.to raise_error(IOError)
+ end
+
+ it "should fail class if it's a directory not a file" do
+ datum = ClassTestData
+ expect { datum.from_file(DIRECTORY) }.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 66214cb0c7..801910cd87 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,12 +18,20 @@
require "spec_helper"
require "chef/mixin/powershell_out"
-describe Chef::Mixin::PowershellOut, windows_only: true do
+describe Chef::Mixin::PowershellOut, :windows_only do
include Chef::Mixin::PowershellOut
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+/
+ expect(powershell_out("get-process").run_command.stdout).to match(/Handles/)
+ end
+
+ it "uses :powershell by default" do
+ expect(powershell_out("$PSVersionTable").run_command.stdout).to match(/CLRVersion/)
+ end
+
+ it ":pwsh interpreter uses core edition", :pwsh_installed do
+ expect(powershell_out("$PSVersionTable", :pwsh).run_command.stdout).to match(/Core/)
end
it "does not raise exceptions when the command is invalid" do
@@ -33,7 +41,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+/
+ 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/shell_out_spec.rb b/spec/functional/mixin/shell_out_spec.rb
index 48f6b7d912..f52dc5f5ae 100644
--- a/spec/functional/mixin/shell_out_spec.rb
+++ b/spec/functional/mixin/shell_out_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,13 +20,13 @@ require "spec_helper"
describe Chef::Mixin::ShellOut do
include Chef::Mixin::ShellOut
- describe "shell_out_with_systems_locale" do
+ describe "shell_out default_env: false" do
describe "when environment['LC_ALL'] is not set" do
it "should use the default shell_out setting" do
cmd = if windows?
- shell_out_with_systems_locale("echo %LC_ALL%")
+ shell_out("echo %LC_ALL%", default_env: false)
else
- shell_out_with_systems_locale("echo $LC_ALL")
+ shell_out("echo $LC_ALL", default_env: false)
end
expect(cmd.stdout.chomp).to match_environment_variable("LC_ALL")
@@ -36,9 +36,9 @@ describe Chef::Mixin::ShellOut do
describe "when environment['LC_ALL'] is set" do
it "should use the option's setting" do
cmd = if windows?
- shell_out_with_systems_locale("echo %LC_ALL%", :environment => { "LC_ALL" => "POSIX" })
+ shell_out("echo %LC_ALL%", environment: { "LC_ALL" => "POSIX" }, default_env: false)
else
- shell_out_with_systems_locale("echo $LC_ALL", :environment => { "LC_ALL" => "POSIX" })
+ shell_out("echo $LC_ALL", environment: { "LC_ALL" => "POSIX" }, default_env: false)
end
expect(cmd.stdout.chomp).to eq "POSIX"
diff --git a/spec/functional/mixin/user_context_spec.rb b/spec/functional/mixin/user_context_spec.rb
new file mode 100644
index 0000000000..e9e6329bb8
--- /dev/null
+++ b/spec/functional/mixin/user_context_spec.rb
@@ -0,0 +1,119 @@
+#
+# Copyright:: Copyright (c) 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 ChefUtils.windows?
+require "chef/win32/api/error" if ChefUtils.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, %i{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 8d8b2d970c..87afbd8359 100644
--- a/spec/functional/notifications_spec.rb
+++ b/spec/functional/notifications_spec.rb
@@ -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/remote_file/cache_control_data_spec.rb b/spec/functional/provider/remote_file/cache_control_data_spec.rb
index c56787e7e5..c6c7daae2e 100755
--- a/spec/functional/provider/remote_file/cache_control_data_spec.rb
+++ b/spec/functional/provider/remote_file/cache_control_data_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
index 1bb36f2cf6..881eeb64af 100644
--- a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
+++ b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/rebooter_spec.rb b/spec/functional/rebooter_spec.rb
index 0006f3bcdb..689570c580 100644
--- a/spec/functional/rebooter_spec.rb
+++ b/spec/functional/rebooter_spec.rb
@@ -22,9 +22,9 @@ describe Chef::Platform::Rebooter do
let(:reboot_info) do
{
- :delay_mins => 5,
- :requested_by => "reboot resource functional test",
- :reason => "rebooter spec test",
+ delay_mins: 5,
+ requested_by: "reboot resource functional test",
+ reason: "rebooter spec test",
}
end
@@ -35,16 +35,18 @@ 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
@@ -69,9 +71,11 @@ describe Chef::Platform::Rebooter do
end
shared_context "test a reboot method" do
- def test_rebooter_method(method_sym, is_windows, expected_reboot_str)
- allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ def test_rebooter_method(method_sym, is_windows, is_solaris, expected_reboot_str)
+ allow(ChefUtils).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
@@ -81,11 +85,15 @@ describe Chef::Platform::Rebooter 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
@@ -93,11 +101,15 @@ describe Chef::Platform::Rebooter 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/aix_service_spec.rb b/spec/functional/resource/aix_service_spec.rb
index 5fff3e00d7..16d830b88a 100755
--- a/spec/functional/resource/aix_service_spec.rb
+++ b/spec/functional/resource/aix_service_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
shared_examples "src service" do
@@ -77,12 +75,21 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
include Chef::Mixin::ShellOut
def get_user_id
- shell_out("id -u #{ENV['USER']}").stdout.chomp
+ shell_out("id -u #{ENV["USER"]}").stdout.chomp
+ end
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
end
describe "When service is a subsystem" do
before(:all) do
- script_dir = File.join(File.dirname(__FILE__), "/../assets/")
+ script_dir = File.join(__dir__, "/../assets/")
shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q")
end
@@ -110,7 +117,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Cannot run this test on a WPAR
describe "When service is a group", :not_wpar do
before(:all) do
- script_dir = File.join(File.dirname(__FILE__), "/../assets/")
+ script_dir = File.join(__dir__, "/../assets/")
shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q -G ctestgrp")
end
diff --git a/spec/functional/resource/aixinit_service_spec.rb b/spec/functional/resource/aixinit_service_spec.rb
index bf50046b03..c568d40a8d 100755
--- a/spec/functional/resource/aixinit_service_spec.rb
+++ b/spec/functional/resource/aixinit_service_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
require "fileutils"
@@ -29,18 +27,18 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Platform specific validation routines.
def service_should_be_started(file_name)
# The existence of this file indicates that the service was started.
- expect(File.exists?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
end
def service_should_be_stopped(file_name)
- expect(File.exists?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
end
def valide_symlinks(expected_output, run_level = nil, status = nil, priority = nil)
directory = []
if priority.is_a? Hash
priority.each do |level, o|
- directory << "/etc/rc.d/rc#{level}.d/#{(o[0] == :start ? 'S' : 'K')}#{o[1]}#{new_resource.service_name}"
+ directory << "/etc/rc.d/rc#{level}.d/#{(o[0] == :start ? "S" : "K")}#{o[1]}#{new_resource.service_name}"
end
directory
else
@@ -57,9 +55,10 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Actual tests
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::Service.new("chefinittest", run_context)
new_resource.provider Chef::Provider::Service::AixInit
- new_resource.supports({ :status => true, :restart => true, :reload => true })
+ new_resource.supports({ status: true, restart: true, reload: true })
new_resource
end
@@ -69,12 +68,12 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
before(:all) do
- File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest")
- FileUtils.cp("#{File.join(File.dirname(__FILE__), "/../assets/chefinittest")}", "/etc/rc.d/init.d/chefinittest")
+ File.delete("/etc/rc.d/init.d/chefinittest") if File.exist?("/etc/rc.d/init.d/chefinittest")
+ FileUtils.cp((File.join(__dir__, "/../assets/chefinittest")).to_s, "/etc/rc.d/init.d/chefinittest")
end
after(:all) do
- File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest")
+ File.delete("/etc/rc.d/init.d/chefinittest") if File.exist?("/etc/rc.d/init.d/chefinittest")
end
before(:each) do
@@ -166,7 +165,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "creates symlink with status K" do
@@ -182,7 +181,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "creates a symlink with status K and a priority" do
@@ -199,7 +198,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "create symlink with status stop (K) and a priority " do
diff --git a/spec/functional/resource/apt_package_spec.rb b/spec/functional/resource/apt_package_spec.rb
new file mode 100644
index 0000000000..9f10e27731
--- /dev/null
+++ b/spec/functional/resource/apt_package_spec.rb
@@ -0,0 +1,383 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 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 "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 [trusted=yes] http://localhost:9000/ sid main"
+ end
+ # Magic to update apt cache for only our repo
+ shell_out!("apt-get update " +
+ '-o Dir::Etc::sourcelist="sources.list.d/chef-integration-test.list" ' +
+ '-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"')
+ end
+
+ def disable_testing_apt_source
+ FileUtils.rm("/etc/apt/sources.list.d/chef-integration-test.list")
+ rescue Errno::ENOENT
+ puts("Attempted to remove integration test from /etc/apt/sources.list.d but it didn't exist")
+ end
+
+ def tcp_test_port(hostname, port)
+ tcp_socket = TCPSocket.new(hostname, port)
+ true
+ rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED
+ false
+ ensure
+ tcp_socket && tcp_socket.close
+ end
+
+ def apt_server
+ @apt_server ||= WEBrick::HTTPServer.new(
+ Port: 9000,
+ DocumentRoot: apt_data_dir + "/var/www/apt",
+ # Make WEBrick quiet, comment out for debug.
+ Logger: Logger.new(StringIO.new),
+ AccessLog: [ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ]
+ )
+ end
+
+ def run_apt_server
+ apt_server.start
+ end
+
+ def start_apt_server
+ @apt_server_thread = Thread.new do
+ run_apt_server
+ end
+ until tcp_test_port("localhost", 9000)
+ if @apt_server_thread.alive?
+ sleep 1
+ else
+ @apt_server_thread.join
+ raise "apt server failed to start"
+ end
+ end
+ end
+
+ def stop_apt_server
+ apt_server.shutdown
+ @apt_server_thread.join
+ end
+
+ def apt_data_dir
+ File.join(CHEF_SPEC_DATA, "apt")
+ end
+end
+
+metadata = { unix_only: true,
+ requires_root: true,
+ provider: { package: Chef::Provider::Package::Apt },
+ arch: "x86_64", # test packages are 64bit
+}
+
+describe Chef::Resource::AptPackage, metadata do
+ include Chef::Mixin::ShellOut
+
+ context "with a remote package source" do
+
+ include AptServer
+
+ before(:all) do
+ # Disable mixlib-shellout live streams
+ Chef::Log.level = :warn
+ start_apt_server
+ enable_testing_apt_source
+ end
+
+ after(:all) do
+ stop_apt_server
+ disable_testing_apt_source
+ shell_out!("apt-get clean")
+ end
+
+ after do
+ shell_out!("dpkg -r chef-integration-test")
+ shell_out("dpkg --clear-avail")
+ shell_out!("apt-get clean")
+ end
+
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ n
+ end
+
+ let(:events) do
+ Chef::EventDispatch::Dispatcher.new
+ end
+
+ # TODO: lots of duplication from client.rb;
+ # All of this must be setup for preseed files to get found
+ let(:cookbook_collection) do
+ cookbook_path = File.join(CHEF_SPEC_DATA, "cookbooks")
+ cl = Chef::CookbookLoader.new(cookbook_path)
+ cl.load_cookbooks
+ Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_path)
+ Chef::CookbookCollection.new(cl)
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(node, cookbook_collection, events)
+ end
+
+ def base_resource
+ 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")
+ r
+ end
+
+ let(:package_resource) do
+ base_resource
+ end
+
+ context "when the package is not yet installed" do
+ it "installs the package with action :install" do
+ package_resource.run_action(:install)
+ shell_out!("dpkg -l chef-integration-test")
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ it "installs the package for action :upgrade" do
+ package_resource.run_action(:upgrade)
+ shell_out!("dpkg -l chef-integration-test")
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ it "does nothing for action :remove" do
+ package_resource.run_action(:remove)
+ shell_out!("dpkg -l chef-integration-test", returns: [1])
+ expect(package_resource).not_to be_updated_by_last_action
+ end
+
+ it "does nothing for action :purge" do
+ package_resource.run_action(:purge)
+ shell_out!("dpkg -l chef-integration-test", returns: [1])
+ expect(package_resource).not_to be_updated_by_last_action
+ end
+
+ context "and a not-available package version is specified" do
+ let(:package_resource) do
+ r = base_resource
+ r.version("2.0")
+ r
+ end
+
+ it "raises a reasonable error for action :install" do
+ expect do
+ package_resource.run_action(:install)
+ end.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
+ end
+
+ describe "when preseeding the install" do
+
+ let(:file_cache_path) { Dir.mktmpdir }
+
+ before do
+ Chef::Config[:file_cache_path] = file_cache_path
+ debconf_reset = 'chef-integration-test chef-integration-test/sample-var string "INVALID"'
+ shell_out!("echo #{debconf_reset} |debconf-set-selections")
+ end
+
+ after do
+ FileUtils.rm_rf(file_cache_path)
+ end
+
+ context "with a preseed file" do
+
+ let(:package_resource) do
+ r = base_resource
+ r.cookbook_name = "preseed"
+ r.response_file("preseed-file.seed")
+ r
+ end
+
+ it "preseeds the package, then installs it" do
+ package_resource.run_action(:install)
+ cmd = shell_out!("debconf-show chef-integration-test")
+ expect(cmd.stdout).to include('chef-integration-test/sample-var: "hello world"')
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ context "and the preseed file exists and is up-to-date" do
+
+ before do
+ # Code here is duplicated from the implementation. Not great, but
+ # it should at least fail if the code gets out of sync.
+ source = File.join(CHEF_SPEC_DATA, "cookbooks/preseed/files/default/preseed-file.seed")
+ file_cache_dir = Chef::FileCache.create_cache_path("preseed/preseed")
+ dest = "#{file_cache_dir}/chef-integration-test-1.1-1.seed"
+ FileUtils.cp(source, dest)
+ end
+
+ it "does not update the package configuration" do
+ package_resource.run_action(:install)
+ cmd = shell_out!("debconf-show chef-integration-test")
+ expect(cmd.stdout).to include("chef-integration-test/sample-var: INVALID")
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ end
+
+ end
+
+ context "with a preseed template" do
+
+ # NOTE: in the fixtures, there is also a cookbook_file named
+ # "preseed-template.seed". This implicitly tests that templates are
+ # preferred over cookbook_files when both are present.
+
+ let(:package_resource) do
+ r = base_resource
+ r.cookbook_name = "preseed"
+ r.response_file("preseed-template.seed")
+ r
+ end
+
+ before do
+ node.normal[:preseed_value] = "FROM TEMPLATE"
+ end
+
+ it "preseeds the package, then installs it" do
+ package_resource.run_action(:install)
+ cmd = shell_out!("debconf-show chef-integration-test")
+ expect(cmd.stdout).to include('chef-integration-test/sample-var: "FROM TEMPLATE"')
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ context "with variables" do
+ let(:package_resource) do
+ r = base_resource
+ r.cookbook_name = "preseed"
+ r.response_file("preseed-template-variables.seed")
+ r.response_file_variables({ template_variable: "SUPPORTS VARIABLES" })
+ r
+ end
+
+ it "preseeds the package, then installs it" do
+ package_resource.run_action(:install)
+ cmd = shell_out!("debconf-show chef-integration-test")
+ expect(cmd.stdout).to include('chef-integration-test/sample-var: "SUPPORTS VARIABLES"')
+ expect(package_resource).to be_updated_by_last_action
+ end
+ end
+
+ end
+ end # installing w/ preseed
+ end # when package not installed
+
+ context "and the desired version of the package is installed" do
+
+ before do
+ v_1_1_package = File.expand_path("apt/chef-integration-test_1.1-1_amd64.deb", CHEF_SPEC_DATA)
+ shell_out!("dpkg -i #{v_1_1_package}")
+ end
+
+ it "does nothing for action :install" do
+ package_resource.run_action(:install)
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(package_resource).not_to be_updated_by_last_action
+ end
+
+ it "does nothing for action :upgrade" do
+ package_resource.run_action(:upgrade)
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(package_resource).not_to be_updated_by_last_action
+ end
+
+ # Verify that the package is removed by running `dpkg -l PACKAGE`
+ # On Ubuntu 12.10 and newer, the command exits 1.
+ #
+ # On Ubuntu 12.04 and older, the `dpkg -l` command will exit 0 and
+ # display a package status message like this:
+ #
+ # Desired=Unknown/Install/Remove/Purge/Hold
+ # | Status=Not/Inst/Cfg-files/Unpacked/Failed-cfg/Half-inst/trig-aWait/Trig-pend
+ # |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
+ # ||/ Name Version Description
+ # +++-=================================-=========================================-============================================
+ # un chef-integration-test <none> (no description available)
+ def pkg_should_be_removed
+ # will raise if exit code != 0,1
+ pkg_check = shell_out!("dpkg -l chef-integration-test", returns: [0, 1])
+
+ if pkg_check.exitstatus == 0
+ expect(pkg_check.stdout).to match(/un\s+chef-integration-test/)
+ end
+ end
+
+ it "removes the package for action :remove" do
+ package_resource.run_action(:remove)
+ pkg_should_be_removed
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ it "removes the package for action :purge" do
+ package_resource.run_action(:purge)
+ pkg_should_be_removed
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ end
+
+ context "and an older version of the package is installed" do
+ before do
+ v_1_0_package = File.expand_path("apt/chef-integration-test_1.0-1_amd64.deb", CHEF_SPEC_DATA)
+ shell_out!("dpkg -i #{v_1_0_package}")
+ end
+
+ it "does nothing for action :install" do
+ package_resource.run_action(:install)
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(package_resource).not_to be_updated_by_last_action
+ end
+
+ it "upgrades the package for action :upgrade" do
+ package_resource.run_action(:upgrade)
+ dpkg_l = shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(dpkg_l.stdout).to match(/chef\-integration\-test\s+1\.1\-1/)
+ expect(package_resource).to be_updated_by_last_action
+ end
+
+ context "and the resource specifies the new version" do
+ let(:package_resource) do
+ r = base_resource
+ r.version("1.1-1")
+ r
+ end
+
+ it "upgrades the package for action :install" do
+ package_resource.run_action(:install)
+ dpkg_l = shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(dpkg_l.stdout).to match(/chef\-integration\-test\s+1\.1\-1/)
+ expect(package_resource).to be_updated_by_last_action
+ end
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/functional/resource/base.rb b/spec/functional/resource/base.rb
deleted file mode 100644
index 38175e81c0..0000000000
--- a/spec/functional/resource/base.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# 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.
-#
-
-def run_context
- @run_context ||= begin
- node = Chef::Node.new
- node.default[:platform] = ohai[:platform]
- node.default[:platform_version] = ohai[:platform_version]
- node.default[:os] = ohai[:os]
- events = Chef::EventDispatch::Dispatcher.new
- Chef::RunContext.new(node, {}, events)
- end
-end
diff --git a/spec/functional/resource/bash_spec.rb b/spec/functional/resource/bash_spec.rb
index a2e174d557..abb88f499f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,65 +17,32 @@
#
require "spec_helper"
-require "functional/resource/base"
describe Chef::Resource::Bash, :unix_only do
let(:code) { "echo hello" }
let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
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 setting the command property" do
+ let(:command) { "wizard racket" }
- 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..9ec1385175 100644
--- a/spec/functional/resource/batch_spec.rb
+++ b/spec/functional/resource/batch_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..cdcc086180 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "functional/resource/base"
require "chef/mixin/shell_out"
# Run the test only for AIX platform.
-describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform] != "aix" do
+describe Chef::Resource::BffPackage, :requires_root, external: ohai[:platform] != "aix" do
include Chef::Mixin::ShellOut
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::BffPackage.new(@pkg_name, run_context)
new_resource.source @pkg_path
new_resource
@@ -31,12 +31,12 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
def bff_pkg_should_be_installed(resource)
expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(0)
- ::File.exists?("/usr/PkgA/bin/acommand")
+ ::File.exist?("/usr/PkgA/bin/acommand")
end
def bff_pkg_should_be_removed(resource)
expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(1)
- !::File.exists?("/usr/PkgA/bin/acommand")
+ !::File.exist?("/usr/PkgA/bin/acommand")
end
before(:all) do
@@ -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 7bb6698daf..e55c1a453c 100644
--- a/spec/functional/resource/chocolatey_package_spec.rb
+++ b/spec/functional/resource/chocolatey_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,22 +16,19 @@
# limitations under the License.
#
require "spec_helper"
-require "chef/mixin/powershell_out"
+require "chef/mixin/shell_out"
-describe Chef::Resource::ChocolateyPackage, :windows_only 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
+describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
+ include Chef::Mixin::ShellOut
let(:package_name) { "test-A" }
- let(:package_list) { proc { powershell_out!("choco list -lo -r #{Array(package_name).join(' ')}").stdout.chomp } }
+ let(:package_list) { proc { shell_out!("choco list -lo -r #{Array(package_name).join(" ")}").stdout.chomp } }
let(:package_source) { File.join(CHEF_SPEC_ASSETS, "chocolatey_feed") }
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
subject do
new_resource = Chef::Resource::ChocolateyPackage.new("test choco package", run_context)
new_resource.package_name package_name
@@ -39,6 +36,11 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
new_resource
end
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
context "installing a package" do
after { remove_package }
@@ -70,7 +72,7 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
end
context "installing multiple packages" do
- let(:package_name) { [ "test-A", "test-B" ] }
+ let(:package_name) { %w{test-A test-B} }
it "installs both packages" do
subject.run_action(:install)
@@ -82,6 +84,48 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
subject.package_name "blah"
expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package
end
+
+ it "installs with an option as a string" do
+ subject.options "--force --confirm"
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
+
+ it "installs with multiple options as a string" do
+ subject.options "--force --confirm"
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
+
+ context "when multiple options passed as string" do
+ before do
+ subject.options "--force --confirm"
+ subject.source nil
+ end
+
+ it "splits a string into an array of options" do
+ expect(provider.send(:cmd_args)).to eq(["--force", "--confirm"])
+ end
+
+ it "calls command_line_to_argv_w_helper method" do
+ expect(provider).to receive(:command_line_to_argv_w_helper).with(subject.options).and_return(["--force", "--confirm"])
+ provider.send(:cmd_args)
+ end
+ end
+
+ context "when multiple options passed as array" do
+ it "Does not call command_line_to_argv_w_helper method" do
+ subject.options [ "--force", "--confirm" ]
+ expect(provider).not_to receive(:command_line_to_argv_w_helper)
+ provider.send(:cmd_args)
+ end
+ end
+
+ it "installs with multiple options as an array" do
+ subject.options [ "--force", "--confirm" ]
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
end
context "upgrading a package" do
diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb
index d127413c73..8dbf22d611 100644
--- a/spec/functional/resource/cookbook_file_spec.rb
+++ b/spec/functional/resource/cookbook_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,9 +25,7 @@ describe Chef::Resource::CookbookFile do
let(:source) { "java.response" }
let(:cookbook_name) { "java" }
let(:expected_content) do
- content = File.open(File.join(CHEF_SPEC_DATA, "cookbooks", "java", "files", "default", "java.response"), "rb") do |f|
- f.read
- end
+ content = File.open(File.join(CHEF_SPEC_DATA, "cookbooks", "java", "files", "default", "java.response"), "rb", &:read)
content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
content
end
@@ -70,11 +68,11 @@ describe Chef::Resource::CookbookFile do
let(:path) { File.join(windows_non_temp_dir, make_tmpname(file_base)) }
before do
- FileUtils.mkdir_p(windows_non_temp_dir) if Chef::Platform.windows?
+ FileUtils.mkdir_p(windows_non_temp_dir) if ChefUtils.windows?
end
after do
- FileUtils.rm_r(windows_non_temp_dir) if Chef::Platform.windows? && File.exists?(windows_non_temp_dir)
+ FileUtils.rm_r(windows_non_temp_dir) if ChefUtils.windows? && File.exist?(windows_non_temp_dir)
end
end
diff --git a/spec/functional/resource/cron_spec.rb b/spec/functional/resource/cron_spec.rb
index f5948191c5..fa53eb08a1 100644
--- a/spec/functional/resource/cron_spec.rb
+++ b/spec/functional/resource/cron_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
describe Chef::Resource::Cron, :requires_root, :unix_only do
@@ -28,7 +26,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
# Platform specific validation routines.
def cron_should_exists(cron_name, command)
case ohai[:platform]
- when "aix", "solaris", "opensolaris", "solaris2", "omnios"
+ when "aix", "solaris2", "omnios"
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(0)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").stdout.lines.to_a.size).to eq(1)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{command}\"").exitstatus).to eq(0)
@@ -43,7 +41,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
def cron_should_not_exists(cron_name)
case ohai[:platform]
- when "aix", "solaris", "opensolaris", "solaris2", "omnios"
+ when "aix", "solaris2", "omnios"
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(1)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{new_resource.command}\"").stdout.lines.to_a.size).to eq(0)
else
@@ -53,19 +51,20 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
end
# Actual tests
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Cron.new("Chef functional test cron", run_context)
- new_resource.user "root"
- # @hourly is not supported on solaris, aix
- if ohai[:platform] == "solaris" || ohai[:platform] == "solaris2" || ohai[:platform] == "aix"
- new_resource.minute "0 * * * *"
- else
- new_resource.minute "@hourly"
- end
- new_resource.hour ""
- new_resource.day ""
- new_resource.month ""
- new_resource.weekday ""
+ new_resource.user "root"
+ new_resource.minute "0"
new_resource.command "/bin/true"
new_resource
end
@@ -89,6 +88,16 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
5.times { new_resource.run_action(:create) }
cron_should_exists(new_resource.name, new_resource.command)
end
+
+ # Test cron for day of week
+ weekdays = { Mon: 1, tuesday: 2, '3': 3, 'thursday': 4, 'Fri': 5, 6 => 6 }
+ weekdays.each do |key, value|
+ it "should create crontab entry and set #{value} for #{key} as weekday" do
+ new_resource.weekday key
+ expect { new_resource.run_action(:create) }.not_to raise_error
+ cron_should_exists(new_resource.name, new_resource.command)
+ end
+ end
end
describe "delete action" do
@@ -104,9 +113,9 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
end
end
- exclude_solaris = %w{solaris opensolaris solaris2 omnios}.include?(ohai[:platform])
- describe "create action with various attributes", :external => exclude_solaris do
- def create_and_validate_with_attribute(resource, attribute, value)
+ exclude_solaris = %w{solaris solaris2 omnios}.include?(ohai[:platform])
+ describe "create action with various attributes", external: exclude_solaris do
+ def create_and_validate_with_property(resource, attribute, value)
if ohai[:platform] == "aix"
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron, /Aix cron entry does not support environment variables. Please set them in script and use script in cron./)
else
@@ -118,6 +127,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
def cron_attribute_should_exists(cron_name, attribute, value)
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)
@@ -129,28 +139,28 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
it "should create a crontab entry for mailto attribute" do
new_resource.mailto "cheftest@example.com"
- create_and_validate_with_attribute(new_resource, "mailto", "cheftest@example.com")
+ create_and_validate_with_property(new_resource, "mailto", "cheftest@example.com")
end
it "should create a crontab entry for path attribute" do
new_resource.path "/usr/local/bin"
- create_and_validate_with_attribute(new_resource, "path", "/usr/local/bin")
+ create_and_validate_with_property(new_resource, "path", "/usr/local/bin")
end
it "should create a crontab entry for shell attribute" do
new_resource.shell "/bin/bash"
- create_and_validate_with_attribute(new_resource, "shell", "/bin/bash")
+ create_and_validate_with_property(new_resource, "shell", "/bin/bash")
end
it "should create a crontab entry for home attribute" do
new_resource.home "/home/opscode"
- create_and_validate_with_attribute(new_resource, "home", "/home/opscode")
+ create_and_validate_with_property(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, "")
+ create_and_validate_with_property(new_resource, attr.to_s, "")
end
end
end
@@ -160,20 +170,10 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
new_resource.run_action(:delete)
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/)
- cron_should_not_exists(new_resource.name)
- end
-
- it "should not create cron with invalid minute" do
- new_resource.minute "invalid"
- cron_create_should_raise_exception
- end
-
it "should not create cron with invalid user" do
new_resource.user "1-really-really-invalid-user-name"
- cron_create_should_raise_exception
+ expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron)
+ cron_should_not_exists(new_resource.name)
end
-
end
end
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
deleted file mode 100644
index 572609d8ff..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 "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/directory_spec.rb b/spec/functional/resource/directory_spec.rb
index 0c1345d57f..b4791226f8 100644
--- a/spec/functional/resource/directory_spec.rb
+++ b/spec/functional/resource/directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/resource/dnf_package_spec.rb b/spec/functional/resource/dnf_package_spec.rb
new file mode 100644
index 0000000000..e0a69da4f9
--- /dev/null
+++ b/spec/functional/resource/dnf_package_spec.rb
@@ -0,0 +1,1277 @@
+#
+# Copyright:: Copyright (c) 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/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora amazon}.include?(ohai[:platform_family]) && File.exist?("/usr/bin/dnf"))
+describe Chef::Resource::DnfPackage, :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
+ # 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(:all) do
+ shell_out!("dnf -y install dnf-plugins-core")
+ 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 --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 yum garbage left around
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-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-dnf-localtesting.repo"
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:dnf_package) { Chef::Resource::DnfPackage.new(package_name, run_context) }
+
+ 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
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.run_action(:install)
+ expect(dnf_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 "expanded idempotency checks with version variants" do
+ %w{1.10 1* 1.10-1 1*-1 1.10-* 1*-* 0:1.10 0:1* 0:1.10-1 0:1*-1 *:1.10-* *:1*-*}.each do |vstring|
+ it "installs the rpm when #{vstring} is in the package_name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the package_name" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the rpm when #{vstring} is in the version property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the version property" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the rpm when #{vstring} is in the package_name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the package_name" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the rpm when #{vstring} is in the version property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the version property" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ %w{1.2 1* 1.2-1 1*-1 1.2-* 1*-* 0:1.2 0:1* 0:1.2-1 0:1*-1 *:1.2-* *:1*-*}.each do |vstring|
+ it "is idempotent when #{vstring} is in the version property and there is a candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_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
+
+ %w{1.2 1.2-1 1.2-* 0:1.2 0:1.2-1 *:1.2-*}.each do |vstring|
+ it "is idempotent when #{vstring} is in the version property on upgrade and it doesn't match the candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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
+
+ %w{1* 1*-1 1*-* 0:1* 0:1*-1 *:1*-*}.each do |vstring|
+ it "upgrades when #{vstring} is in the version property on upgrade and it matches the candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with an evra" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-0:1.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with version and release" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.2-1")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades when the installed version does not match the version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrades when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches with a vr glob", :rhel_gte_8 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches with an evr glob", :rhel_gte_8 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "downgrades" do
+ it "downgrades the package when allow_downgrade" do
+ flush_cache
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2-1")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with arches", :intel_64bit do
+ it "installs with 64-bit arch in the name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs with 64-bit arch in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.arch((pkg_arch).to_s)
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs when the 32-bit arch is in the name and the version is in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.i686")
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs when the 64-bit arch is in the name and the version is in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.#{pkg_arch}")
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.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 --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" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with an equality constraint, when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with an equality constraint, when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.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 --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" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.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 --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" 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.#{pkg_arch}.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
+
+ it "with a less than constraint, when nothing is installed, it installs" 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a less than constraint, when the install version matches, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_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" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ 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 --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrade on a local file with allow_downgrade true works" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.version "1.2-1"
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not downgrade the package with :install" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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 and there is a version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.version "1.2-1"
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "multipackage with arches", :intel_64bit do
+ it "installs two rpms" do
+ flush_cache
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "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.#{pkg_arch}.rpm")
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_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$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "repo controls" do
+ it "should fail with the repo disabled" do
+ flush_cache
+ dnf_package.options("--disablerepo=chef-dnf-localtesting")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "should work with disablerepo first" do
+ flush_cache
+ dnf_package.options(["--disablerepo=*", "--enablerepo=chef-dnf-localtesting"])
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "should work to enable a disabled repo" do
+ shell_out!("dnf config-manager --set-disabled chef-dnf-localtesting")
+ flush_cache
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ flush_cache
+ dnf_package.options("--enablerepo=chef-dnf-localtesting")
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 need to do this).
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.options("--disablerepo=chef-dnf-localtesting")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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 dnf_helper.py cache and we test to see if the
+ # repo that we temporarily disabled is enabled on this pass.
+ dnf_package.package_name("chef_rpm-1.10-1.#{pkg_arch}")
+ dnf_package.options(nil)
+ dnf_package.run_action(:install)
+ expect(dnf_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}$")
+ 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.#{pkg_arch}.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 match("^chef_rpm-1.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.version("1.2")
+ dnf_package.allow_downgrade true
+ 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.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrades the package when allow_downgrade is true" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "version pinning" do
+ it "with a full version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.10-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.2-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a prco equality pin in the name it upgrades a prior package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a prco equality pin in the name it downgrades a later package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a > pin in the name and no rpm installed it installs" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a < pin in the name and no rpm installed it installs" do
+ flush_cache
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a > pin in the name and matching rpm installed it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a < pin in the name and non-matching rpm installed it downgrades" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_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}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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
+ dnf_package.run_action(:remove)
+ expect(dnf_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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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 --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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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")
+ dnf_package.run_action(:remove)
+ expect(dnf_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}$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ 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.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_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$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ describe ":lock and :unlock" do
+ before(:all) do
+ shell_out("dnf -y install python3-dnf-plugin-versionlock")
+ end
+
+ before(:each) do
+ shell_out("dnf versionlock delete 'chef_rpm-*'") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not lock if its already locked" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ end
+
+ it "unlocks an rpm" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not unlock an already locked rpm" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ end
+
+ it "check that we can lock based on provides" do
+ flush_cache
+ dnf_package.package_name("chef_rpm_provides")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "check that we can unlock based on provides" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm_provides")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+end
diff --git a/spec/functional/resource/dpkg_package_spec.rb b/spec/functional/resource/dpkg_package_spec.rb
index 1988fd0c7d..0a8202127c 100644
--- a/spec/functional/resource/dpkg_package_spec.rb
+++ b/spec/functional/resource/dpkg_package_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -273,7 +273,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove both packages when called with two" do
shell_out!("dpkg -i #{test1_0} #{test2_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -282,7 +282,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove a package when only the first one is installed" do
shell_out!("dpkg -i #{test1_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -291,7 +291,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove a package when only the second one is installed" do
shell_out!("dpkg -i #{test2_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -299,7 +299,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
end
it "should do nothing when both packages are not installed" do
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).not_to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
index bb3cf2157d..227811a5ef 100644
--- a/spec/functional/resource/dsc_resource_spec.rb
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,11 +33,11 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only 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
+ context "when PowerShell does not support Invoke-DscResource"
+ context "when PowerShell supports Invoke-DscResource" do
before do
if !Chef::Platform.supports_dsc_invoke_resource?(node)
- skip "Requires Powershell >= 5.0.10018.0"
+ skip "Requires PowerShell >= 5.0.10018.0"
elsif !Chef::Platform.supports_refresh_mode_enabled?(node) && !Chef::Platform.dsc_refresh_mode_disabled?(node)
skip "Requires LCM RefreshMode is Disabled"
end
@@ -46,7 +46,8 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
it "raises an exception if the resource is not found" do
new_resource.resource "thisdoesnotexist"
expect { new_resource.run_action(:run) }.to raise_error(
- Chef::Exceptions::ResourceNotFound)
+ Chef::Exceptions::ResourceNotFound
+ )
end
end
@@ -61,7 +62,7 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
end
after do
- File.delete(tmp_file_name) if File.exists? tmp_file_name
+ File.delete(tmp_file_name) if File.exist? tmp_file_name
end
it "converges the resource if it is not converged" do
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index e2b58f6432..b22599266b 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,14 +17,13 @@
#
require "spec_helper"
-require "chef/mixin/powershell_out"
-require "chef/mixin/shell_out"
+require "chef/mixin/powershell_exec"
require "chef/mixin/windows_architecture_helper"
require "support/shared/integration/integration_helper"
-describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
+describe Chef::Resource::DscScript, :windows_powershell_dsc_only, :ruby64_only do
include Chef::Mixin::WindowsArchitectureHelper
- include Chef::Mixin::PowershellOut
+ include Chef::Mixin::PowershellExec
before(:all) do
@temp_dir = ::Dir.mktmpdir("dsc-functional-test")
# enable the HTTP listener if it is not already enabled needed by underlying DSC engine
@@ -34,7 +33,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
winrm create winrm/config/Listener?Address=*+Transport=HTTP
}
CODE
- powershell_out!(winrm_code)
+ powershell_exec!(winrm_code)
end
after(:all) do
@@ -65,10 +64,8 @@ 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" }
@@ -76,10 +73,11 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
let(:env_value2) { "value2" }
let(:dsc_test_run_context) do
node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ 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
@@ -104,7 +102,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
ValueData = '#{test_registry_data}'
Ensure = 'Present'
}
-EOH
+ EOH
end
let(:dsc_code) { dsc_reg_code }
@@ -112,7 +110,7 @@ EOH
<<-EOH
param($testregkeyname, $testregvaluename)
#{dsc_reg_code}
-EOH
+ EOH
end
let(:dsc_user_prefix) { "dsc" }
@@ -139,7 +137,7 @@ EOH
$#{dsc_user_prefix_param_name},
$#{dsc_user_suffix_param_name}
)
-EOH
+ EOH
end
let(:config_param_section) { "" }
@@ -148,59 +146,59 @@ EOH
let(:dsc_user_suffix_code) { dsc_user_suffix }
let(:dsc_script_environment_attribute) { nil }
let(:dsc_user_resources_code) do
- <<-EOH
- #{config_param_section}
-node localhost
-{
-$testuser = #{dsc_user_code}
-$testpassword = ConvertTo-SecureString -String "jf9a8m49jrajf4#" -AsPlainText -Force
-$testcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testuser, $testpassword
-
-User dsctestusercreate
-{
- UserName = $testuser
- Password = $testcred
- Description = "DSC test user"
- Ensure = "Present"
- Disabled = $false
- PasswordNeverExpires = $true
- PasswordChangeRequired = $false
-}
-}
-EOH
+ <<~EOH
+ #{config_param_section}
+ node localhost
+ {
+ $testuser = #{dsc_user_code}
+ $testpassword = ConvertTo-SecureString -String "jf9a8m49jrajf4#" -AsPlainText -Force
+ $testcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testuser, $testpassword
+
+ User dsctestusercreate
+ {
+ UserName = $testuser
+ Password = $testcred
+ Description = "DSC test user"
+ Ensure = "Present"
+ Disabled = $false
+ PasswordNeverExpires = $true
+ PasswordChangeRequired = $false
+ }
+ }
+ EOH
end
let(:dsc_user_config_data) do
- <<-EOH
-@{
- AllNodes = @(
- @{
- NodeName = "localhost";
- PSDscAllowPlainTextPassword = $true
- }
- )
-}
+ <<~EOH
+ @{
+ AllNodes = @(
+ @{
+ NodeName = "localhost";
+ PSDscAllowPlainTextPassword = $true
+ }
+ )
+ }
-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(: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) 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}'
-}
-environment "whatsmydir"
-{
- Name = '#{dsc_environment_env_var_name}'
- Value = $pwd.path
- Ensure = 'Present'
-}
-EOH
+ <<~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}'
+ }
+ environment "whatsmydir"
+ {
+ Name = '#{dsc_environment_env_var_name}'
+ Value = $pwd.path
+ Ensure = 'Present'
+ }
+ EOH
end
let(:dsc_config_name) do
@@ -235,7 +233,7 @@ EOH
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false)
dsc_test_resource.run_action(:run)
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true)
- expect(dsc_test_resource.registry_value_exists?(test_registry_key, { :name => test_registry_value, :type => :string, :data => test_registry_data })).to eq(true)
+ expect(dsc_test_resource.registry_value_exists?(test_registry_key, { name: test_registry_value, type: :string, data: test_registry_data })).to eq(true)
end
it_should_behave_like "a dsc_script resource with configuration affected by cwd"
@@ -244,13 +242,13 @@ EOH
shared_examples_for "a dsc_script resource with configuration affected by cwd" do
after(:each) do
removal_resource = Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
- removal_resource.code <<-EOH
-environment 'removethis'
-{
- Name = '#{dsc_environment_env_var_name}'
- Ensure = 'Absent'
-}
-EOH
+ removal_resource.code <<~EOH
+ environment 'removethis'
+ {
+ Name = '#{dsc_environment_env_var_name}'
+ Ensure = 'Absent'
+ }
+ EOH
removal_resource.run_action(:run)
end
@@ -263,12 +261,9 @@ EOH
it "should raise an exception if the cwd is etc" do
dsc_test_resource.cwd(dsc_environment_fail_etc_directory)
- expect { dsc_test_resource.run_action(:run) }.to raise_error(Chef::Exceptions::PowershellCmdletException)
- begin
+ expect {
dsc_test_resource.run_action(:run)
- rescue Chef::Exceptions::PowershellCmdletException => e
- expect(e.message).to match(exception_message_signature)
- end
+ }.to raise_error(Chef::PowerShell::CommandFailed, /#{exception_message_signature}/)
end
end
end
@@ -311,11 +306,11 @@ EOH
it "should set a registry key according to parameters passed to the configuration" do
dsc_test_resource.configuration_name(config_name_value)
- dsc_test_resource.flags({ :"#{reg_key_name_param_name}" => test_registry_key, :"#{reg_key_value_param_name}" => test_registry_value })
+ dsc_test_resource.flags({ "#{reg_key_name_param_name}": test_registry_key, "#{reg_key_value_param_name}": test_registry_value })
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false)
dsc_test_resource.run_action(:run)
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true)
- expect(dsc_test_resource.registry_value_exists?(test_registry_key, { :name => test_registry_value, :type => :string, :data => test_registry_data })).to eq(true)
+ expect(dsc_test_resource.registry_value_exists?(test_registry_key, { name: test_registry_value, type: :string, data: test_registry_data })).to eq(true)
end
end
end
@@ -348,11 +343,9 @@ EOH
shared_examples_for "a dsc_script with configuration data that takes parameters" do
let(:dsc_user_code) { dsc_user_param_code }
let(:config_param_section) { config_params }
- let(:config_flags) { { :"#{dsc_user_prefix_param_name}" => "#{dsc_user_prefix}", :"#{dsc_user_suffix_param_name}" => "#{dsc_user_suffix}" } }
+ let(:config_flags) { { "#{dsc_user_prefix_param_name}": (dsc_user_prefix).to_s, "#{dsc_user_suffix_param_name}": (dsc_user_suffix).to_s } }
it "does not directly contain the user name" do
- configuration_script_content = ::File.open(dsc_test_resource.command) do |file|
- file.read
- end
+ configuration_script_content = ::File.open(dsc_test_resource.command, &:read)
expect(configuration_script_content.include?(dsc_user)).to be(false)
end
it_behaves_like "a dsc_script with configuration data"
@@ -362,9 +355,7 @@ EOH
let(:dsc_user_code) { dsc_user_env_code }
it "does not directly contain the user name" do
- configuration_script_content = ::File.open(dsc_test_resource.command) do |file|
- file.read
- end
+ configuration_script_content = ::File.open(dsc_test_resource.command, &:read)
expect(configuration_script_content.include?(dsc_user)).to be(false)
end
it_behaves_like "a dsc_script with configuration data"
@@ -410,46 +401,46 @@ EOH
end
let(:dsc_configuration_script) do
- <<-MYCODE
-cd c:\\
-configuration LCM
-{
- param ($thumbprint)
- localconfigurationmanager
- {
- RebootNodeIfNeeded = $false
- ConfigurationMode = 'ApplyOnly'
- CertificateID = $thumbprint
- }
-}
-$cert = ls Cert:\\LocalMachine\\My\\ |
- Where-Object {$_.Subject -match "ChefTest"} |
- Select -first 1
-
-if($cert -eq $null) {
- $pfxpath = '#{self_signed_cert_path}'
- $password = ''
- $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxpath, $password, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset))
- $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "My", ([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
- $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
- $store.Add($cert)
- $store.Close()
-}
-
-lcm -thumbprint $cert.thumbprint
-set-dsclocalconfigurationmanager -path ./LCM
-$ConfigurationData = @"
-@{
-AllNodes = @(
- @{
- NodeName = "localhost";
- CertificateID = '$($cert.thumbprint)';
- };
-);
-}
-"@
-$ConfigurationData | out-file '#{configuration_data_path}' -force
- MYCODE
+ <<~MYCODE
+ cd c:\\
+ configuration LCM
+ {
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ CertificateID = $thumbprint
+ }
+ }
+ $cert = ls Cert:\\LocalMachine\\My\\ |
+ Where-Object {$_.Subject -match "ChefTest"} |
+ Select -first 1
+
+ if($cert -eq $null) {
+ $pfxpath = '#{self_signed_cert_path}'
+ $password = ''
+ $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxpath, $password, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset))
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "My", ([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ $store.Add($cert)
+ $store.Close()
+ }
+
+ lcm -thumbprint $cert.thumbprint
+ set-dsclocalconfigurationmanager -path ./LCM
+ $ConfigurationData = @"
+ @{
+ AllNodes = @(
+ @{
+ NodeName = "localhost";
+ CertificateID = '$($cert.thumbprint)';
+ };
+ );
+ }
+ "@
+ $ConfigurationData | out-file '#{configuration_data_path}' -force
+ MYCODE
end
let(:powershell_script_resource) do
@@ -461,14 +452,14 @@ $ConfigurationData | out-file '#{configuration_data_path}' -force
let(:dsc_script_resource) do
dsc_test_resource_base.tap do |r|
- r.code <<-EOF
-User dsctestusercreate
-{
- UserName = '#{dsc_user}'
- Password = #{r.ps_credential('jf9a8m49jrajf4#')}
- Ensure = "Present"
-}
-EOF
+ r.code <<~EOF
+ User dsctestusercreate
+ {
+ UserName = '#{dsc_user}'
+ Password = #{r.ps_credential("jf9a8m49jrajf4#")}
+ Ensure = "Present"
+ }
+ EOF
r.configuration_data_script(configuration_data_path)
end
end
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
deleted file mode 100755
index 4b0ff70c0b..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) 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::Env.new("unknown", test_run_context)
- end
-
- 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 3c31537ebe..3d7e185e17 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,11 @@
#
require "spec_helper"
-require "functional/resource/base"
require "timeout"
describe Chef::Resource::Execute do
let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
resource = Chef::Resource::Execute.new("foo_resource", run_context)
resource.command("echo hello")
resource
@@ -87,8 +87,8 @@ describe Chef::Resource::Execute do
describe "when parent resource sets :environment" do
before do
resource.environment({
- "SAWS_SECRET" => "supersecret",
- "SAWS_KEY" => "qwerty",
+ "SAWS_SECRET" => "supersecret",
+ "SAWS_KEY" => "qwerty",
})
end
@@ -106,7 +106,7 @@ describe Chef::Resource::Execute do
it "guard adds additional values in its :environment and runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] != "regularsecret"'}, {
- :environment => { "SGCE_SECRET" => "regularsecret" },
+ environment: { "SGCE_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).to be_updated_by_last_action
@@ -114,7 +114,7 @@ describe Chef::Resource::Execute do
it "guard adds additional values in its :environment and does not run" do
resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] == "regularsecret"'}, {
- :environment => { "SGCE_SECRET" => "regularsecret" },
+ environment: { "SGCE_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).not_to be_updated_by_last_action
@@ -122,7 +122,7 @@ describe Chef::Resource::Execute do
it "guard overwrites value with its :environment and runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "regularsecret"'}, {
- :environment => { "SAWS_SECRET" => "regularsecret" },
+ environment: { "SAWS_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).to be_updated_by_last_action
@@ -130,13 +130,25 @@ describe Chef::Resource::Execute do
it "guard overwrites value with its :environment and does not runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "regularsecret"'}, {
- :environment => { "SAWS_SECRET" => "regularsecret" },
+ environment: { "SAWS_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).not_to be_updated_by_last_action
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/file_spec.rb b/spec/functional/resource/file_spec.rb
index 0fa1317032..c0d68e1762 100644
--- a/spec/functional/resource/file_spec.rb
+++ b/spec/functional/resource/file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,7 +51,7 @@ describe Chef::Resource::File do
end
let(:resource_with_relative_path) do
- create_resource(:use_relative_path => true)
+ create_resource(use_relative_path: true)
end
let(:unmanaged_content) do
diff --git a/spec/functional/resource/git_spec.rb b/spec/functional/resource/git_spec.rb
index 6808898c29..ab05947d29 100644
--- a/spec/functional/resource/git_spec.rb
+++ b/spec/functional/resource/git_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,30 +17,18 @@
#
require "spec_helper"
-require "chef/mixin/shell_out"
require "tmpdir"
-require "shellwords"
# Deploy relies heavily on symlinks, so it doesn't work on windows.
-describe Chef::Resource::Git, :requires_git => true do
- include Chef::Mixin::ShellOut
- let(:file_cache_path) { Dir.mktmpdir }
+describe Chef::Resource::Git do
+ include RecipeDSLHelper
+
# Some versions of git complains when the deploy directory is
# already created. Here we intentionally don't create the deploy
# directory beforehand.
let(:base_dir_path) { Dir.mktmpdir }
let(:deploy_directory) { File.join(base_dir_path, make_tmpname("git_base")) }
- 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.
#
@@ -64,33 +52,31 @@ describe Chef::Resource::Git, :requires_git => true do
let(:rev_testing) { "972d153654503bccec29f630c5dd369854a561e8" }
let(:rev_head) { "d294fbfd05aa7709ad9a9b8ef6343b17d355bf5f" }
- let(:git_user_config) do
- <<-E
-[user]
- name = frodoTbaggins
- email = frodo@shire.org
-E
- end
-
before(:each) do
- Chef::Log.level = :warn # silence git command live streams
- @old_file_cache_path = Chef::Config[:file_cache_path]
- shell_out!("git clone \"#{git_bundle_repo}\" example", :cwd => origin_repo_dir)
- File.open("#{origin_repo}/.git/config", "a+") { |f| f.print(git_user_config) }
- Chef::Config[:file_cache_path] = file_cache_path
+ shell_out!("git", "clone", git_bundle_repo, "example", cwd: origin_repo_dir)
+ File.open("#{origin_repo}/.git/config", "a+") do |f|
+ f.print <<~EOF
+ [user]
+ name = frodoTbaggins
+ email = frodo@shire.org
+ EOF
+ end
end
after(:each) 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 base_dir_path
- FileUtils.remove_entry_secure file_cache_path
FileUtils.remove_entry_secure origin_repo_dir
end
- before(:all) do
- @ohai = Ohai::System.new
- @ohai.all_plugins(%w{platform os})
+ def expect_revision_to_be(revision, version)
+ rev_ver = shell_out!("git", "rev-parse", revision, cwd: deploy_directory).stdout.strip
+ expect(rev_ver).to eq(version)
+ end
+
+ def expect_branch_to_be(branch)
+ head_branch = shell_out!("git name-rev --name-only HEAD", cwd: deploy_directory).stdout.strip
+ expect(head_branch).to eq(branch)
end
context "working with pathes with special characters" do
@@ -102,156 +88,242 @@ E
end
it "clones a repository with a space in the path" do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository "#{path_with_spaces}/example-repo.gitbundle"
- end.run_action(:sync)
+ repo = "#{path_with_spaces}/example-repo.gitbundle"
+ git(deploy_directory) do
+ repository repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
end
end
context "when deploying from an annotated tag" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "v1.0.0"
- end
- end
-
- # We create a copy of the basic_git_resource so that we can run
- # the resource again and verify that it doesn't update.
- let(:copy_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "v1.0.0"
- end
- end
-
it "checks out the revision pointed to by the tag commit, not the tag commit itself" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(v1_commit)
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", v1_commit)
+ expect_branch_to_be("tags/v1.0.0^0") # detached
# also verify the tag commit itself is what we expect as an extra sanity check
- rev = shell_out!("git rev-parse v1.0.0", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(rev).to eq(v1_tag)
+ expect_revision_to_be("v1.0.0", v1_tag)
end
it "doesn't update if up-to-date" do
- # this used to fail because we didn't resolve the annotated tag
- # properly to the pointed to commit.
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(v1_commit)
-
- copy_git_resource.run_action(:sync)
- expect(copy_git_resource).not_to be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ expect_branch_to_be("tags/v1.0.0^0") # detached
+ end.should_not_be_updated
end
end
context "when deploying from a SHA revision" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository git_bundle_repo
- end
- end
-
- # We create a copy of the basic_git_resource so that we can run
- # the resource again and verify that it doesn't update.
- let(:copy_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- end
+ it "checks out the expected revision ed18" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+ expect_branch_to_be("master~1") # detached
end
- it "checks out the expected revision ed18" do
- basic_git_resource.revision rev_foo
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_foo)
+ it "checks out the expected revision ed18 to a local branch" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+ expect_branch_to_be("deploy") # detached
end
it "doesn't update if up-to-date" do
- basic_git_resource.revision rev_foo
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_foo)
-
- copy_git_resource.revision rev_foo
- copy_git_resource.run_action(:sync)
- expect(copy_git_resource).not_to be_updated
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+
+ git deploy_directory do
+ repository origin_repo
+ revision rev_foo
+ end.should_not_be_updated
+ expect_branch_to_be("master~1") # detached
end
it "checks out the expected revision 972d" do
- basic_git_resource.revision rev_testing
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_testing)
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_testing
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_testing)
+ expect_branch_to_be("master~2") # detached
+ end
+
+ it "checks out the expected revision 972d to a local branch" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_testing
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_testing)
+ expect_branch_to_be("deploy")
end
end
context "when deploying from a revision named 'HEAD'" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "HEAD"
- end
+ it "checks out the expected revision" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out the expected revision" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out the expected revision, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out the expected revision to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
end
end
context "when deploying from the default revision" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- # use default
- end
+ it "checks out HEAD as the default revision" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out HEAD as the default revision" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out HEAD as the default revision, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out HEAD as the default revision to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
+ end
+ end
+
+ context "when updating a branch that's already checked out out" do
+ it "checks out master, commits to the repo, and checks out the latest changes" do
+ git deploy_directory do
+ repository origin_repo
+ revision "master"
+ action :sync
+ end.should_be_updated
+
+ # We don't have a way to test a commit in the git bundle
+ # Revert to a previous commit in the same branch and make sure we can still sync.
+ shell_out!("git", "reset", "--hard", rev_foo, cwd: deploy_directory)
+
+ git deploy_directory do
+ repository origin_repo
+ revision "master"
+ action :sync
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
end
context "when dealing with a repo with a degenerate tag named 'HEAD'" do
before do
- shell_out!("git tag -m\"degenerate tag\" HEAD ed181b3419b6f489bedab282348162a110d6d3a1",
- :cwd => origin_repo)
+ shell_out!("git", "tag", "-m\"degenerate tag\"", "HEAD", "ed181b3419b6f489bedab282348162a110d6d3a1", cwd: origin_repo)
end
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "HEAD"
- end
+ it "checks out the (master) HEAD revision and ignores the tag" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- let(:git_resource_default_rev) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- # use default of revision
- end
+ it "checks out the (master) HEAD revision and ignores the tag, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out the (master) HEAD revision and ignores the tag" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD",
- :cwd => deploy_directory,
- :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out the (master) HEAD revision and ignores the tag to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
end
it "checks out the (master) HEAD revision when no revision is specified (ignores tag)" do
- git_resource_default_rev.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD",
- :cwd => deploy_directory,
- :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out the (master) HEAD revision when no revision is specified (ignores tag), and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
+ it "checks out the (master) HEAD revision when no revision is specified (ignores tag) to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
+ end
end
end
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index aa5a29f92c..a682e9c0c7 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Chirag Jog (<chirag@clogeny.com>)
# Author:: Siddheshwar More (<siddheshwar.more@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,19 +18,14 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
-# Chef::Resource::Group are turned off on Mac OS X 10.6 due to caching
-# issues around Etc.getgrnam() not picking up the group membership
-# changes that are done on the system. Etc.endgrent is not functioning
-# correctly on certain 10.6 boxes.
-describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supported_on_mac_osx_106 do
+describe Chef::Resource::Group, :requires_root_or_running_windows do
include Chef::Mixin::ShellOut
def group_should_exist(group)
- case ohai[:platform_family]
- when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch"
+ case ohai[:os]
+ when "linux"
expect { Etc.getgrnam(group) }.not_to raise_error
expect(group).to eq(Etc.getgrnam(group).name)
when "windows"
@@ -54,8 +49,8 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
def group_should_not_exist(group)
- case ohai[:platform_family]
- when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch"
+ case ohai[:os]
+ when "linux"
expect { Etc.getgrnam(group) }.to raise_error(ArgumentError, "can't find group for #{group}")
when "windows"
expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.to raise_error(ArgumentError, /The group name could not be found./)
@@ -81,7 +76,7 @@ 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
@@ -99,13 +94,17 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
usr
end
- def create_user(username)
- user(username).run_action(:create) if ! windows_domain_user?(username)
+ def create_user(username, uid = nil)
+ unless 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)
- if ! windows_domain_user?(username)
+ unless 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)
@@ -113,6 +112,15 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
# TODO: User shouldn't exist
end
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
shared_examples_for "correct group management" do
def add_members_to_group(members)
temp_resource = group_resource.dup
@@ -146,13 +154,15 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
# dscl doesn't perform any error checking and will let you add users that don't exist.
- describe "when no users exist", :not_supported_on_mac_osx do
+ describe "when no users exist", :not_supported_on_macos do
describe "when append is not set" do
# excluded_members can only be used when append is set. It is ignored otherwise.
let(:excluded_members) { [] }
+ let(:expected_error_class) { windows? ? ArgumentError : Mixlib::ShellOut::ShellCommandFailed }
+
it "should raise an error" do
- expect { group_resource.run_action(tested_action) }.to raise_error()
+ expect { group_resource.run_action(tested_action) }.to raise_error(expected_error_class)
end
end
@@ -161,16 +171,21 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
group_resource.append(true)
end
+ let(:expected_error_class) { windows? ? Chef::Exceptions::Win32APIError : Mixlib::ShellOut::ShellCommandFailed }
+
it "should raise an error" do
- expect { group_resource.run_action(tested_action) }.to raise_error()
+ expect { group_resource.run_action(tested_action) }.to raise_error(expected_error_class)
end
end
end
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
@@ -289,13 +304,27 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
end
- let(:group_name) { "group#{SecureRandom.random_number(9999)}" }
- let(:included_members) { nil }
- let(:excluded_members) { nil }
+ let(:number) do
+ # Loop until we pick a gid that is not in use.
+ loop do
+
+ gid = rand(2000..9999) # avoid low group numbers
+ return nil if Etc.getgrgid(gid).nil? # returns nil on windows
+ rescue ArgumentError # group does not exist
+ return gid
+
+ end
+ end
+
+ let(:group_name) { "grp#{number}" } # group name should be 8 characters or less for Solaris, and possibly others
+ # https://community.aegirproject.org/developing/architecture/unix-group-limitations/index.html#Group_name_length_limits
+ let(:included_members) { [] }
+ let(:excluded_members) { [] }
let(:group_resource) do
group = Chef::Resource::Group.new(group_name, run_context)
group.members(included_members)
group.excluded_members(excluded_members)
+ group.gid(number) unless ohai[:platform_family] == "mac_os_x"
group
end
@@ -316,10 +345,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
describe "when group name is length 256", :windows_only do
let!(:group_name) do
- "theoldmanwalkingdownthestreetalwayshadagood\
-smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
-theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
+ "theoldmanwalkingdownthestreetalwayshadagood"\
+ "smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface"\
+ "theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking"\
+ "downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree"
+ end
it "should create a group" do
group_resource.run_action(:create)
@@ -327,19 +357,6 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
end
end
- describe "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)
- 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
@@ -351,6 +368,26 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
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
@@ -400,7 +437,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
end
- describe "group manage action", :not_supported_on_solaris do
+ describe "group manage action" do
let(:spec_members) { %w{mnou5sdz htulrvwq x4c3g1lu} }
let(:included_members) { [spec_members[0], spec_members[1]] }
let(:excluded_members) { [spec_members[2]] }
@@ -417,6 +454,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
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
@@ -437,37 +475,4 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
end
- describe "group resource with Usermod provider", :solaris_only do
- describe "when excluded_members is set" do
- let(:excluded_members) { ["x4c3g1lu"] }
-
- it ":manage should raise an error" do
- expect { group_resource.run_action(:manage) }.to raise_error
- end
-
- it ":modify should raise an error" do
- expect { group_resource.run_action(:modify) }.to raise_error
- end
-
- it ":create should raise an error" do
- expect { group_resource.run_action(:create) }.to raise_error
- end
- end
-
- describe "when append is not set" do
- let(:included_members) { %w{gordon eric} }
-
- before(:each) do
- group_resource.append(false)
- end
-
- it ":manage should raise an error" do
- expect { group_resource.run_action(:manage) }.to raise_error
- end
-
- it ":modify should raise an error" do
- expect { group_resource.run_action(:modify) }.to raise_error
- end
- end
- end
end
diff --git a/spec/functional/resource/ifconfig_spec.rb b/spec/functional/resource/ifconfig_spec.rb
index a30dcff641..127e4e6331 100644
--- a/spec/functional/resource/ifconfig_spec.rb
+++ b/spec/functional/resource/ifconfig_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,24 @@
# limitations under the License.
#
-require "functional/resource/base"
+require "spec_helper"
require "chef/mixin/shell_out"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix}.include?(ohai[:platform]))
-
-describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => include_flag do
- # This test does not work in travis because there is no eth0
+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, :requires_ifconfig, external: include_flag do
include Chef::Mixin::ShellOut
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Ifconfig.new("10.10.0.1", run_context)
new_resource
@@ -51,11 +58,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 |cut -d'@' -f 1").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
@@ -115,7 +128,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
exclude_test = ohai[:platform] != "ubuntu"
- describe "#action_add", :external => exclude_test do
+ describe "#action_add", external: exclude_test do
after do
new_resource.run_action(:delete)
end
@@ -127,7 +140,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_enable", :external => exclude_test do
+ describe "#action_enable", external: exclude_test do
after do
new_resource.run_action(:disable)
end
@@ -138,7 +151,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_disable", :external => exclude_test do
+ describe "#action_disable", external: exclude_test do
before do
setup_enable_interface(new_resource)
new_resource.run_action(:enable)
@@ -150,7 +163,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_delete", :external => exclude_test do
+ describe "#action_delete", external: exclude_test do
before do
setup_add_interface(new_resource)
new_resource.run_action(:add)
diff --git a/spec/functional/resource/insserv_spec.rb b/spec/functional/resource/insserv_spec.rb
new file mode 100644
index 0000000000..e5c74c68ac
--- /dev/null
+++ b/spec/functional/resource/insserv_spec.rb
@@ -0,0 +1,206 @@
+#
+# Author:: Dheeraj Dubey (<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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/shell_out"
+require "fileutils"
+
+describe Chef::Resource::Service, :requires_root, :opensuse do
+
+ include Chef::Mixin::ShellOut
+
+ def service_should_be_enabled
+ expect(shell_out!("/sbin/insserv -r -f #{new_resource.service_name}").exitstatus).to eq(0)
+ expect(shell_out!("/sbin/insserv -d -f #{new_resource.service_name}").exitstatus).to eq(0)
+ !Dir.glob("/etc/rc*/**/S*#{service_name}").empty?
+ end
+
+ def service_should_be_disabled
+ expect(shell_out!("/sbin/insserv -r -f #{new_resource.service_name}").exitstatus).to eq(0)
+ Dir.glob("/etc/rc*/**/S*#{service_name}").empty?
+ end
+
+ # Platform specific validation routines.
+ def service_should_be_started(file_name)
+ # The existence of this file indicates that the service was started.
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
+ end
+
+ def service_should_be_stopped(file_name)
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
+ end
+
+ def delete_test_files
+ files = Dir.glob("#{Dir.tmpdir}/init[a-z_]*.txt")
+ File.delete(*files)
+ end
+
+ # Actual tests
+ let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
+ new_resource = Chef::Resource::Service.new("inittest", run_context)
+ new_resource.provider Chef::Provider::Service::Insserv
+ new_resource.supports({ status: true, restart: true, reload: true })
+ new_resource
+ end
+
+ let(:provider) do
+ provider = new_resource.provider_for_action(new_resource.action)
+ provider
+ end
+
+ let(:service_name) { "Chef::Util::PathHelper.escape_glob_dir(current_resource.service_name)" }
+
+ let(:current_resource) do
+ provider.load_current_resource
+ provider.current_resource
+ end
+
+ before(:all) do
+ File.delete("/etc/init.d/inittest") if File.exist?("/etc/init.d/inittest")
+ FileUtils.cp((File.join(__dir__, "/../assets/inittest")).to_s, "/etc/init.d/inittest")
+ FileUtils.chmod(0755, "/etc/init.d/inittest")
+ end
+
+ after(:all) do
+ File.delete("/etc/init.d/inittest") if File.exist?("/etc/init.d/inittest")
+ end
+
+ before(:each) do
+ delete_test_files
+ end
+
+ after(:each) do
+ delete_test_files
+ end
+
+ describe "start service" do
+ it "should start the service" do
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "stop service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should stop the service" do
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "restart service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should restart the service" do
+ new_resource.run_action(:restart)
+ service_should_be_started("inittest_restart.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ skip "FIXME: restart is not idempotent"
+ new_resource.run_action(:restart)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:restart)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "reload service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should reload the service" do
+ new_resource.run_action(:reload)
+ service_should_be_started("inittest_reload.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ skip "FIXME: reload is not idempotent"
+ new_resource.run_action(:reload)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:reload)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "enable service" do
+ it "should enable the service" do
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "disable_service" do
+ it "should disable the service" do
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+end
diff --git a/spec/functional/resource/launchd_spec.rb b/spec/functional/resource/launchd_spec.rb
new file mode 100644
index 0000000000..70399d310d
--- /dev/null
+++ b/spec/functional/resource/launchd_spec.rb
@@ -0,0 +1,232 @@
+#
+# Copyright:: Copyright (c) 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"
+
+# Deploy relies heavily on symlinks, so it doesn't work on windows.
+describe Chef::Resource::Launchd, :macos_only, requires_root: true do
+ include RecipeDSLHelper
+
+ before(:each) do
+ shell_out("launchctl unload -wF /Library/LaunchDaemons/io.chef.testing.fake.plist")
+ FileUtils.rm_f "/Library/LaunchDaemons/io.chef.testing.fake.plist"
+ end
+
+ after(:each) do
+ shell_out("launchctl unload -wF /Library/LaunchDaemons/io.chef.testing.fake.plist")
+ FileUtils.rm_f "/Library/LaunchDaemons/io.chef.testing.fake.plist"
+ end
+
+ context ":enable" do
+ it "enables a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out!("launchctl list io.chef.testing.fake").stdout).to match('"PID" = \d+')
+ expect(shell_out!("launchctl list io.chef.testing.fake").stdout).not_to match('"PID" = 0')
+ end
+
+ it "should be idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_not_be_updated
+ end
+ end
+
+ context ":create" do
+ it "creates a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+
+ it "should be idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_not_be_updated
+ end
+ end
+
+ context ":create_if_missing" do
+ it "creates a service if it is missing" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_not_be_updated
+ end
+ end
+
+ context ":delete" do
+ it "deletes a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be false
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_not_be_updated
+ end
+ it "works if the file does not exist" do
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_not_be_updated
+ end
+ end
+
+ context ":disable" do
+ it "deletes a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "1",
+ ]
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "1",
+ ]
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_not_be_updated
+ end
+ it "should work if the plist does not exist" do
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_not_be_updated
+ end
+ end
+end
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index de6976448e..c1de3bf99d 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,13 +19,15 @@
require "spec_helper"
if windows?
- require "chef/win32/file" #probably need this in spec_helper
+ 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.
@@ -53,13 +55,22 @@ describe Chef::Resource::Link do
end
after(:each) do
- begin
- cleanup_link(to) if File.exists?(to)
- cleanup_link(target_file) if File.exists?(target_file)
- cleanup_link(CHEF_SPEC_BACKUP_PATH) if File.exists?(CHEF_SPEC_BACKUP_PATH)
- rescue
- puts "Could not remove a file: #{$!}"
- end
+
+ cleanup_link(to) if File.exist?(to)
+ cleanup_link(target_file) if File.exist?(target_file)
+ cleanup_link(CHEF_SPEC_BACKUP_PATH) if File.exist?(CHEF_SPEC_BACKUP_PATH)
+ rescue
+ puts "Could not remove a file: #{$!}"
+
+ end
+
+ def user(user)
+ node = Chef::Node.new
+ node.consume_external_attrs(ohai.data, {})
+ run_context = Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new)
+ usr = Chef::Resource.resource_for_node(:user, node).new(user, run_context)
+ usr.password("ComplexPass11!") if windows?
+ usr
end
def cleanup_link(path)
@@ -108,12 +119,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.is_a?(String)
+ Chef::ReservedNames::Win32::Security::SID.from_account(value)
+ elsif value.is_a?(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 +171,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 +183,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 +204,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 +225,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 +247,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,11 +269,11 @@ 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
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
# Writing to one hardlinked file should cause both
# to have the new value.
@@ -244,11 +294,11 @@ 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(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
# Writing to one hardlinked file should cause both
# to have the new value.
@@ -288,16 +338,26 @@ describe Chef::Resource::Link do
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(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)
@@ -306,7 +366,7 @@ describe Chef::Resource::Link do
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(to)).to be_truthy
end
end
context "pointing nowhere" do
@@ -323,7 +383,7 @@ describe Chef::Resource::Link do
context "and the link already exists and is a hard link to the file" do
before(:each) do
link(to, target_file)
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
end
include_context "create symbolic link succeeds"
@@ -343,7 +403,7 @@ describe Chef::Resource::Link do
it "create errors out" do
if windows?
expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
- elsif os_x? || solaris? || freebsd? || aix?
+ elsif macos? || solaris? || freebsd? || aix?
expect { resource.run_action(:create) }.to raise_error(Errno::EPERM)
else
expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR)
@@ -354,11 +414,11 @@ describe Chef::Resource::Link do
it_behaves_like "a securable resource without existing target" do
let(:path) { target_file }
- def allowed_acl(sid, expected_perms)
+ def allowed_acl(sid, expected_perms, _flags = 0)
[ ACE.access_allowed(sid, expected_perms[:specific]) ]
end
- def denied_acl(sid, expected_perms)
+ def denied_acl(sid, expected_perms, _flags = 0)
[ ACE.access_denied(sid, expected_perms[:specific]) ]
end
@@ -522,14 +582,14 @@ describe Chef::Resource::Link do
context "and the link already exists and is a hard link to the file" do
before(:each) do
link(to, target_file)
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
end
include_context "create hard link is noop"
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(to)).to be_truthy
end
end
context "and the link already exists and is a file" do
@@ -546,7 +606,7 @@ describe Chef::Resource::Link do
it "errors out" do
if windows?
expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
- elsif os_x? || solaris? || freebsd? || aix?
+ elsif macos? || solaris? || freebsd? || aix?
expect { resource.run_action(:create) }.to raise_error(Errno::EPERM)
else
expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR)
@@ -593,10 +653,10 @@ 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("macOS/FreeBSD/AIX/Solaris symlink? and readlink working on hard links to symlinks") if macos? || 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.
+ expect(File.exist?(target_file)).to be_truthy
+ # macOS gets angry about this sort of link. Bug in macOS, IMO.
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
@@ -612,9 +672,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("macOS/FreeBSD/AIX/Solaris fails to create hardlinks to broken symlinks") if macos? || freebsd? || aix? || solaris?
resource.run_action(:create)
- expect(File.exists?(target_file) || File.symlink?(target_file)).to be_truthy
+ expect(File.exist?(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
@@ -633,10 +693,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/locale_spec.rb b/spec/functional/resource/locale_spec.rb
new file mode 100644
index 0000000000..5258f08696
--- /dev/null
+++ b/spec/functional/resource/locale_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Nimesh Patni (<nimesh.patni@msystechnologies.com>)
+# Copyright:: Copyright (c) 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::Locale, :requires_root do
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data, {})
+ n
+ end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Locale.new("fakey_fakerton", run_context) }
+
+ context "on debian/ubuntu", :debian_family_only do
+ def sets_system_locale(*locales)
+ system_locales = File.readlines("/etc/locale.conf")
+ expect(system_locales.map(&:strip)).to eq(locales)
+ end
+
+ def unsets_system_locale(*locales)
+ system_locales = File.readlines("/etc/locale.conf")
+ expect(system_locales.map(&:strip)).not_to eq(locales)
+ end
+
+ describe "action: update" do
+ context "Sets system variable" do
+ it "when LC var is given" do
+ resource.lc_env({ "LC_MESSAGES" => "en_US" })
+ resource.run_action(:update)
+ sets_system_locale("LC_MESSAGES=en_US")
+ end
+ it "when lang is given" do
+ resource.lang("en_US")
+ resource.run_action(:update)
+ sets_system_locale("LANG=en_US")
+ end
+ it "when both lang & LC vars are given" do
+ resource.lang("en_US")
+ resource.lc_env({ "LC_TIME" => "en_IN" })
+ resource.run_action(:update)
+ sets_system_locale("LANG=en_US", "LC_TIME=en_IN")
+ end
+ end
+
+ context "Unsets system variable" do
+ it "when LC var is not given" do
+ resource.lc_env
+ resource.run_action(:update)
+ unsets_system_locale("LC_MESSAGES=en_US")
+ end
+ it "when lang is not given" do
+ resource.lang
+ resource.run_action(:update)
+ unsets_system_locale("LANG=en_US")
+ end
+ it "when both lang & LC vars are not given" do
+ resource.lang
+ resource.lc_env
+ resource.run_action(:update)
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+ end
+
+ context "on rhel", :rhel do
+ it "raises an exception due lacking the locale-gen tool" do
+ resource.lang("en_US")
+ expect { resource.run_action(:update) }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end
+ end
+
+ context "on macos", :macos_only do
+ it "raises an exception due to being an unsupported platform" do
+ resource.lang("en_US")
+ expect { resource.run_action(:update) }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end
+ end
+
+ # @TODO we need to enable these again
+ # context "on windows", :windows_only, requires_root: false do
+ # describe "action: update" do
+ # context "Sets system locale" do
+ # it "when lang is given" do
+ # resource.lang("en-US")
+ # resource.run_action(:update)
+ # end
+ # end
+ # end
+ # end
+end
diff --git a/spec/functional/resource/mount_spec.rb b/spec/functional/resource/mount_spec.rb
index c756b0d3d4..f11f97d6c7 100644
--- a/spec/functional/resource/mount_spec.rb
+++ b/spec/functional/resource/mount_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,33 +17,32 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
require "tmpdir"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix solaris2}.include?(ohai[:platform]))
-
-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
- # exist even after loading the kernel module
+include_flag = !(%w{debian rhel amazon aix solaris2}.include?(ohai[:platform_family]))
+describe Chef::Resource::Mount, :requires_root, external: include_flag do
include Chef::Mixin::ShellOut
# Platform specific setup, cleanup and validation helpers.
-
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.
@@ -69,7 +68,7 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
def mount_should_exist(mount_point, device, fstype = nil, options = nil)
validation_cmd = "mount | grep #{mount_point} | grep #{device} "
validation_cmd << " | grep #{fstype} " unless fstype.nil?
- validation_cmd << " | grep #{options.join(',')} " unless options.nil? || options.empty?
+ validation_cmd << " | grep #{options.join(",")} " unless options.nil? || options.empty?
expect(shell_out(validation_cmd).exitstatus).to eq(0)
end
@@ -101,6 +100,15 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
expect(shell_out("cat #{unix_mount_config_file}").stdout).not_to include("#{mount_point}:")
end
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Mount.new(@mount_point, run_context)
new_resource.device @device
@@ -121,8 +129,9 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
end
# Actual tests begin here.
- before(:all) do
+ before do |test|
@device, @fstype = setup_device_for_mount
+ @device = "/" if test.metadata[:skip_before]
@mount_point = Dir.mktmpdir("testmount")
@@ -137,13 +146,20 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
end
after(:all) do
- Dir.rmdir(@mount_point)
+ Dir.rmdir(@mount_point) if @mount_point
end
after(:each) do
cleanup_mount(new_resource.mount_point)
end
+ describe "when device is '/'" do
+ it "should mount the filesystem if device is '/'", :skip_before do
+ new_resource.run_action(:mount)
+ mount_should_exist(new_resource.mount_point, new_resource.device)
+ end
+ end
+
describe "when the target state is a mounted filesystem" do
it "should mount the filesystem if it isn't mounted" do
expect(current_resource.enabled).to be_falsey
@@ -157,7 +173,7 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
# don't run the remount tests on solaris2 (tmpfs does not support remount)
# Need to make sure the platforms we've already excluded are considered:
skip_remount = include_flag || (ohai[:platform] == "solaris2")
- describe "when the filesystem should be remounted and the resource supports remounting", :external => skip_remount do
+ describe "when the filesystem should be remounted and the resource supports remounting", external: skip_remount do
it "should remount the filesystem if it is mounted" do
new_resource.run_action(:mount)
mount_should_exist(new_resource.mount_point, new_resource.device)
diff --git a/spec/functional/resource/msu_package_spec.rb b/spec/functional/resource/msu_package_spec.rb
new file mode 100644
index 0000000000..2e6fcdcbca
--- /dev/null
+++ b/spec/functional/resource/msu_package_spec.rb
@@ -0,0 +1,107 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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(:package_identity) { "Package_for_KB2959977~31bf3856ad364e35~amd64~~6.3.1.1" }
+ let(:timeout) { 3600 }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ let(:new_resource) { Chef::Resource::CabPackage.new("windows_test_pkg") }
+ let(:cab_provider) do
+ 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.timeout timeout
+ 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_identity }
+ 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_identity }
+ 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
+
+ context "when an msu package is not applicable to the image." do
+ def package_name
+ "Package_for_KB4019990"
+ end
+
+ def package_source
+ "http://download.windowsupdate.com/c/msdownload/update/software/updt/2017/05/windows8-rt-kb4019990-x64_a77f4e3e1f2d47205824763e7121bb11979c2716.msu"
+ end
+
+ it "raises error while installing" do
+ expect { subject.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /The specified package is not applicable to this image./)
+ 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 06bccfc398..836a4f6da3 100644
--- a/spec/functional/resource/ohai_spec.rb
+++ b/spec/functional/resource/ohai_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb
deleted file mode 100644
index 0f01a751ec..0000000000
--- a/spec/functional/resource/package_spec.rb
+++ /dev/null
@@ -1,386 +0,0 @@
-# encoding: UTF-8
-#
-# Author:: Daniel DeLeo (<dan@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 "spec_helper"
-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"
- end
- # Magic to update apt cache for only our repo
- shell_out!("apt-get update " +
- '-o Dir::Etc::sourcelist="sources.list.d/chef-integration-test.list" ' +
- '-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"')
- end
-
- def disable_testing_apt_source
- FileUtils.rm("/etc/apt/sources.list.d/chef-integration-test.list")
- rescue Errno::ENOENT
- puts("Attempted to remove integration test from /etc/apt/sources.list.d but it didn't exist")
- end
-
- def tcp_test_port(hostname, port)
- tcp_socket = TCPSocket.new(hostname, port)
- true
- rescue Errno::ETIMEDOUT
- false
- rescue Errno::ECONNREFUSED
- false
- ensure
- tcp_socket && tcp_socket.close
- end
-
- def apt_server
- @apt_server ||= WEBrick::HTTPServer.new(
- :Port => 9000,
- :DocumentRoot => apt_data_dir + "/var/www/apt",
- # Make WEBrick quiet, comment out for debug.
- :Logger => Logger.new(StringIO.new),
- :AccessLog => [ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ]
- )
- end
-
- def run_apt_server
- apt_server.start
- end
-
- def start_apt_server
- @apt_server_thread = Thread.new do
- run_apt_server
- end
- until tcp_test_port("localhost", 9000)
- if @apt_server_thread.alive?
- sleep 1
- else
- @apt_server_thread.join
- raise "apt server failed to start"
- end
- end
- end
-
- def stop_apt_server
- apt_server.shutdown
- @apt_server_thread.join
- end
-
- def apt_data_dir
- File.join(CHEF_SPEC_DATA, "apt")
- end
-end
-
-metadata = { :unix_only => true,
- :requires_root => true,
- :provider => { :package => Chef::Provider::Package::Apt },
- :arch => "x86_64" # test packages are 64bit
-}
-
-describe Chef::Resource::Package, metadata do
- include Chef::Mixin::ShellOut
-
- context "with a remote package source" do
-
- include AptServer
-
- before(:all) do
- # Disable mixlib-shellout live streams
- Chef::Log.level = :warn
- start_apt_server
- enable_testing_apt_source
- end
-
- after(:all) do
- stop_apt_server
- disable_testing_apt_source
- shell_out!("apt-get clean")
- end
-
- after do
- shell_out!("dpkg -r chef-integration-test")
- shell_out("dpkg --clear-avail")
- shell_out!("apt-get clean")
- end
-
- let(:node) do
- n = Chef::Node.new
- n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
- n
- end
-
- let(:events) do
- Chef::EventDispatch::Dispatcher.new
- end
-
- # TODO: lots of duplication from client.rb;
- # All of this must be setup for preseed files to get found
- let(:cookbook_collection) do
- cookbook_path = File.join(CHEF_SPEC_DATA, "cookbooks")
- cl = Chef::CookbookLoader.new(cookbook_path)
- cl.load_cookbooks
- Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_path)
- Chef::CookbookCollection.new(cl)
- end
-
- let(:run_context) do
- Chef::RunContext.new(node, cookbook_collection, events)
- end
-
- def base_resource
- r = Chef::Resource::Package.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")
- r
- end
-
- let(:package_resource) do
- base_resource
- end
-
- context "when the package is not yet installed" do
- it "installs the package with action :install" do
- package_resource.run_action(:install)
- shell_out!("dpkg -l chef-integration-test")
- expect(package_resource).to be_updated_by_last_action
- end
-
- it "installs the package for action :upgrade" do
- package_resource.run_action(:upgrade)
- shell_out!("dpkg -l chef-integration-test")
- expect(package_resource).to be_updated_by_last_action
- end
-
- it "does nothing for action :remove" do
- package_resource.run_action(:remove)
- shell_out!("dpkg -l chef-integration-test", :returns => [1])
- expect(package_resource).not_to be_updated_by_last_action
- end
-
- it "does nothing for action :purge" do
- package_resource.run_action(:purge)
- shell_out!("dpkg -l chef-integration-test", :returns => [1])
- expect(package_resource).not_to be_updated_by_last_action
- end
-
- context "and a not-available package version is specified" do
- let(:package_resource) do
- r = base_resource
- r.version("2.0")
- r
- end
-
- it "raises a reasonable error for action :install" do
- expect do
- package_resource.run_action(:install)
- end.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
- end
-
- end
-
- describe "when preseeding the install" do
-
- let(:file_cache_path) { Dir.mktmpdir }
-
- before do
- Chef::Config[:file_cache_path] = file_cache_path
- debconf_reset = 'chef-integration-test chef-integration-test/sample-var string "INVALID"'
- shell_out!("echo #{debconf_reset} |debconf-set-selections")
- end
-
- after do
- FileUtils.rm_rf(file_cache_path)
- end
-
- context "with a preseed file" do
-
- let(:package_resource) do
- r = base_resource
- r.cookbook_name = "preseed"
- r.response_file("preseed-file.seed")
- r
- end
-
- it "preseeds the package, then installs it" do
- package_resource.run_action(:install)
- cmd = shell_out!("debconf-show chef-integration-test")
- expect(cmd.stdout).to include('chef-integration-test/sample-var: "hello world"')
- expect(package_resource).to be_updated_by_last_action
- end
-
- context "and the preseed file exists and is up-to-date" do
-
- before do
- # Code here is duplicated from the implementation. Not great, but
- # it should at least fail if the code gets out of sync.
- source = File.join(CHEF_SPEC_DATA, "cookbooks/preseed/files/default/preseed-file.seed")
- file_cache_dir = Chef::FileCache.create_cache_path("preseed/preseed")
- dest = "#{file_cache_dir}/chef-integration-test-1.1-1.seed"
- FileUtils.cp(source, dest)
- end
-
- it "does not update the package configuration" do
- package_resource.run_action(:install)
- cmd = shell_out!("debconf-show chef-integration-test")
- expect(cmd.stdout).to include("chef-integration-test/sample-var: INVALID")
- expect(package_resource).to be_updated_by_last_action
- end
-
- end
-
- end
-
- context "with a preseed template" do
-
- # NOTE: in the fixtures, there is also a cookbook_file named
- # "preseed-template.seed". This implicitly tests that templates are
- # preferred over cookbook_files when both are present.
-
- let(:package_resource) do
- r = base_resource
- r.cookbook_name = "preseed"
- r.response_file("preseed-template.seed")
- r
- end
-
- before do
- node.normal[:preseed_value] = "FROM TEMPLATE"
- end
-
- it "preseeds the package, then installs it" do
- package_resource.run_action(:install)
- cmd = shell_out!("debconf-show chef-integration-test")
- expect(cmd.stdout).to include('chef-integration-test/sample-var: "FROM TEMPLATE"')
- expect(package_resource).to be_updated_by_last_action
- end
-
- context "with variables" do
- let(:package_resource) do
- r = base_resource
- r.cookbook_name = "preseed"
- r.response_file("preseed-template-variables.seed")
- r.response_file_variables({ :template_variable => "SUPPORTS VARIABLES" })
- r
- end
-
- it "preseeds the package, then installs it" do
- package_resource.run_action(:install)
- cmd = shell_out!("debconf-show chef-integration-test")
- expect(cmd.stdout).to include('chef-integration-test/sample-var: "SUPPORTS VARIABLES"')
- expect(package_resource).to be_updated_by_last_action
- end
- end
-
- end
- end # installing w/ preseed
- end # when package not installed
-
- context "and the desired version of the package is installed" do
-
- before do
- v_1_1_package = File.expand_path("apt/chef-integration-test_1.1-1_amd64.deb", CHEF_SPEC_DATA)
- shell_out!("dpkg -i #{v_1_1_package}")
- end
-
- it "does nothing for action :install" do
- package_resource.run_action(:install)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(package_resource).not_to be_updated_by_last_action
- end
-
- it "does nothing for action :upgrade" do
- package_resource.run_action(:upgrade)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(package_resource).not_to be_updated_by_last_action
- end
-
- # Verify that the package is removed by running `dpkg -l PACKAGE`
- # On Ubuntu 12.10 and newer, the command exits 1.
- #
- # On Ubuntu 12.04 and older, the `dpkg -l` command will exit 0 and
- # display a package status message like this:
- #
- # Desired=Unknown/Install/Remove/Purge/Hold
- # | Status=Not/Inst/Cfg-files/Unpacked/Failed-cfg/Half-inst/trig-aWait/Trig-pend
- # |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
- # ||/ Name Version Description
- # +++-=================================-=========================================-============================================
- # un chef-integration-test <none> (no description available)
- def pkg_should_be_removed
- # will raise if exit code != 0,1
- pkg_check = shell_out!("dpkg -l chef-integration-test", :returns => [0, 1])
-
- if pkg_check.exitstatus == 0
- expect(pkg_check.stdout).to match(/un[\s]+chef-integration-test/)
- end
- end
-
- it "removes the package for action :remove" do
- package_resource.run_action(:remove)
- pkg_should_be_removed
- expect(package_resource).to be_updated_by_last_action
- end
-
- it "removes the package for action :purge" do
- package_resource.run_action(:purge)
- pkg_should_be_removed
- expect(package_resource).to be_updated_by_last_action
- end
-
- end
-
- context "and an older version of the package is installed" do
- before do
- v_1_0_package = File.expand_path("apt/chef-integration-test_1.0-1_amd64.deb", CHEF_SPEC_DATA)
- shell_out!("dpkg -i #{v_1_0_package}")
- end
-
- it "does nothing for action :install" do
- package_resource.run_action(:install)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(package_resource).not_to be_updated_by_last_action
- end
-
- it "upgrades the package for action :upgrade" do
- package_resource.run_action(:upgrade)
- dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/)
- expect(package_resource).to be_updated_by_last_action
- end
-
- context "and the resource specifies the new version" do
- let(:package_resource) do
- r = base_resource
- r.version("1.1-1")
- r
- end
-
- it "upgrades the package for action :install" do
- package_resource.run_action(:install)
- dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/)
- expect(package_resource).to be_updated_by_last_action
- end
- end
-
- end
-
- end
-
-end
diff --git a/spec/functional/resource/powershell_package_source_spec.rb b/spec/functional/resource/powershell_package_source_spec.rb
new file mode 100644
index 0000000000..abc9dcabd1
--- /dev/null
+++ b/spec/functional/resource/powershell_package_source_spec.rb
@@ -0,0 +1,107 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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::Resource::PowershellPackageSource, :windows_gte_10 do
+ include Chef::Mixin::PowershellExec
+
+ let(:source_name) { "fake" }
+ let(:url) { "https://www.nuget.org/api/v2" }
+ let(:trusted) { true }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::PowershellPackageSource.new("test powershell package source", run_context)
+ new_resource.source_name source_name
+ new_resource.url url
+ new_resource.trusted trusted
+ new_resource.provider_name provider_name
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ shared_examples "package_source" do
+ context "register a package source" do
+ after { remove_package_source }
+
+ it "registers the package source" do
+ subject.run_action(:register)
+ expect(get_installed_package_source_name).to eq(source_name)
+ end
+
+ it "does not register the package source if already installed" do
+ subject.run_action(:register)
+ subject.run_action(:register)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates an existing package source if changed" do
+ subject.run_action(:register)
+ subject.trusted !trusted
+ subject.run_action(:register)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ context "unregister a package source" do
+ it "unregisters the package source" do
+ subject.run_action(:register)
+ subject.run_action(:unregister)
+ expect(get_installed_package_source_name).to be_empty
+ end
+
+ it "does not unregister the package source if not already installed" do
+ subject.run_action(:unregister)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "with NuGet provider" do
+ let(:provider_name) { "NuGet" }
+
+ before(:all) do
+ powershell_exec!("[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;Install-PackageProvider -Name NuGet -Force")
+ end
+
+ it_behaves_like "package_source"
+ end
+
+ context "with PowerShellGet provider" do
+ let(:provider_name) { "PowerShellGet" }
+
+ it_behaves_like "package_source"
+ end
+
+ def get_installed_package_source_name
+ powershell_exec!("(Get-PackageSource -Name #{source_name} -ErrorAction SilentlyContinue).Name").result
+ end
+
+ def remove_package_source
+ pkg_to_remove = Chef::Resource::PowershellPackageSource.new(source_name, run_context)
+ pkg_to_remove.run_action(:unregister)
+ end
+end \ No newline at end of file
diff --git a/spec/functional/resource/powershell_script_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index af345b0ea4..68fa94afe9 100644
--- a/spec/functional/resource/powershell_script_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,33 +23,31 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
include_context Chef::Resource::WindowsScript
- let (:architecture_command) { "echo $env:PROCESSOR_ARCHITECTURE" }
- let (:output_command) { " | out-file -encoding ASCII " }
+ let(:architecture_command) { "echo $env:PROCESSOR_ARCHITECTURE" }
+ let(:output_command) { " | out-file -encoding ASCII " }
it_behaves_like "a Windows script running on Windows"
- let(:successful_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
- let(:failed_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe /badargument" }
+ let(:successful_executable_script_content) { "#{ENV["SystemRoot"]}\\system32\\attrib.exe $env:systemroot" }
+ let(:failed_executable_script_content) { "#{ENV["SystemRoot"]}\\system32\\attrib.exe /badargument" }
let(:processor_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTURE" }
let(:native_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTUREW6432" }
let(:cmdlet_exit_code_not_found_content) { "get-item '.\\thisdoesnotexist'" }
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_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" }
let(:valid_powershell_interpreter_flag) { "-Sta" }
let!(:resource) do
- r = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
+ r = Chef::Resource::WindowsScript::PowershellScript.new("PowerShell resource functional test", @run_context)
r.code(successful_executable_script_content)
r
end
- describe "when the run action is invoked on Windows" do
+ shared_examples_for "a running powershell script" do
it "successfully executes a non-cmdlet Windows binary as the last command of the script" do
resource.code(successful_executable_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.returns(0)
@@ -57,8 +55,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the exit status 27 for a powershell script that exits with 27" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
file = Tempfile.new(["foo", ".ps1"])
begin
file.write "exit 27"
@@ -72,10 +68,9 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
end
- let (:negative_exit_status) { -27 }
- let (:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
+ let(:negative_exit_status) { -27 }
+ let(:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
it "returns the exit status -27 as a signed integer or an unsigned 16-bit 2's complement value of 65509 for a powershell script that exits with -27" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
# Versions of PowerShell prior to 4.0 return a 16-bit unsigned value --
# PowerShell 4.0 and later versions return a 32-bit signed value.
@@ -100,8 +95,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the process exit code" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code(arbitrary_nonzero_process_exit_code_content)
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -120,34 +113,24 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 if the last command was a cmdlet that failed" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code(cmdlet_exit_code_not_found_content)
resource.returns(1)
resource.run_action(:run)
end
it "returns 1 if the last command was a cmdlet that failed and was preceded by a successfully executed non-cmdlet Windows binary" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([windows_process_exit_code_success_content, cmdlet_exit_code_not_found_content].join(";"))
resource.returns(1)
expect { resource.run_action(:run) }.not_to raise_error
end
it "raises a Mixlib::ShellOut::ShellCommandFailed error if the script is not syntactically correct" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code("if({)")
resource.returns(0)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
it "raises an error if the script is not syntactically correct even if returns is set to 1 which is what powershell.exe returns for syntactically invalid scripts" do
- # This test fails because shell_out expects the exit status to be 1, but it is actually 0
- # The error is a false-positive.
- skip "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code("if({)")
resource.returns(1)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
@@ -156,38 +139,30 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
# This somewhat ambiguous case, two failures of different types,
# seems to violate the principle of returning the status of the
# last line executed -- in this case, we return the status of the
- # second to last line. This happens because Powershell gives no
+ # second to last line. This happens because PowerShell gives no
# way for us to determine whether the last operation was a cmdlet
# or Windows process. Because the latter gives more specific
# errors than 0 or 1, we return that instead, which is acceptable
# since callers can test for nonzero rather than testing for 1.
it "returns 1 if the last command was a cmdlet that failed and was preceded by an unsuccessfully executed non-cmdlet Windows binary" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([arbitrary_nonzero_process_exit_code_content, cmdlet_exit_code_not_found_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns 0 if the last command was a non-cmdlet Windows binary that succeeded and was preceded by a failed cmdlet" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that succeeded" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that failed" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_not_found_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -206,8 +181,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 for $false as the last line of the script when convert_boolean_return is true" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.code "$false"
resource.returns(1)
@@ -233,43 +206,79 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect(is_64_bit).to eq(detected_64_bit)
end
- it "returns 1 if an invalid flag is passed to the interpreter" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
+ it "returns 0 if a valid flag is passed to the interpreter" do
resource.code(cmdlet_exit_code_success_content)
- resource.flags(invalid_powershell_interpreter_flag)
- resource.returns(1)
+ resource.flags(valid_powershell_interpreter_flag)
+ resource.returns(0)
resource.run_action(:run)
end
- it "returns 0 if a valid flag is passed to the interpreter" do
+ it "returns 0 if no flag is passed to the interpreter" do
resource.code(cmdlet_exit_code_success_content)
- resource.flags(valid_powershell_interpreter_flag)
resource.returns(0)
resource.run_action(:run)
end
+ it "returns 1 if an invalid flag is passed to the interpreter" do
+ resource.code(cmdlet_exit_code_success_content)
+ resource.flags(invalid_powershell_interpreter_flag)
+ resource.returns(1)
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
it "raises an error when given a block and a guard_interpreter" do
resource.guard_interpreter :sh
resource.only_if { true }
expect { resource.should_skip?(:run) }.to raise_error(ArgumentError, /guard_interpreter does not support blocks/)
end
+ end
- context "when dsc is supported", :windows_powershell_dsc_only do
- it "can execute LCM configuration code" do
- resource.code <<-EOF
-configuration LCM
-{
- param ($thumbprint)
- localconfigurationmanager
- {
- RebootNodeIfNeeded = $false
- ConfigurationMode = 'ApplyOnly'
- }
-}
- EOF
- expect { resource.run_action(:run) }.not_to raise_error
- end
+ context "when using the powershell interpreter" do
+ before do
+ resource.interpreter "powershell"
+ end
+
+ it_behaves_like "a running powershell script"
+
+ it "runs Windows Powershell" do
+ resource.code("$PSVersionTable.PSVersion.Major | out-file -encoding ASCII #{script_output_path}")
+ resource.returns(0)
+ resource.run_action(:run)
+
+ expect(get_script_output.to_i).to be < 6
+ end
+ end
+
+ context "when using the pwsh interpreter", :pwsh_installed do
+ before do
+ resource.interpreter "pwsh"
+ end
+
+ it_behaves_like "a running powershell script"
+
+ it "runs a version of powershell greater than 6" do
+ resource.code("$PSVersionTable.PSVersion.Major | out-file -encoding ASCII #{script_output_path}")
+ resource.returns(0)
+ resource.run_action(:run)
+
+ expect(get_script_output.to_i).to be > 6
+ end
+ end
+
+ context "when dsc is supported", :windows_powershell_dsc_only do
+ it "can execute LCM configuration code" do
+ resource.code <<~EOF
+ configuration LCM
+ {
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ }
+ }
+ EOF
+ expect { resource.run_action(:run) }.not_to raise_error
end
end
@@ -296,10 +305,10 @@ configuration LCM
context "when running on a 32-bit version of Windows", :windows32_only do
it "raises an exception if :x86_64 process architecture is specified" do
- begin
- expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
- rescue Chef::Exceptions::Win32ArchitectureIncorrect
- end
+
+ expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
+ rescue Chef::Exceptions::Win32ArchitectureIncorrect
+
end
end
end
@@ -314,7 +323,7 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, "AMD64" )).to eq(true)
end
- it "executes a script with a 32-bit process if :i386 arch is specified", :not_supported_on_nano do
+ it "executes a script with a 32-bit process if :i386 arch is specified" do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.architecture(:i386)
resource.returns(0)
@@ -322,12 +331,6 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, "x86" )).to eq(true)
end
-
- it "raises an error when executing a script with a 32-bit process on Windows Nano Server", :windows_nano_only do
- resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
- expect { resource.architecture(:i386) }.to raise_error(Chef::Exceptions::Win32ArchitectureIncorrect,
- "cannot execute script with requested architecture 'i386' on Windows Nano Server")
- end
end
describe "when executing guards" do
@@ -376,13 +379,22 @@ configuration LCM
context "with powershell_script as the guard_interpreter" do
+ context "when pwsh is the interpreter", :pwsh_installed do
+ before do
+ resource.interpreter "pwsh"
+ end
+
+ it "uses powershell core to evaluate the guard" do
+ resource.not_if "$PSVersionTable.PSEdition -eq 'Core'"
+ expect(resource.should_skip?(:run)).to be_truthy
+ end
+ end
+
it "has a guard_interpreter attribute set to :powershell_script" do
expect(resource.guard_interpreter).to eq(:powershell_script)
end
it "evaluates a powershell $false for a not_if block as true" do
- pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -393,8 +405,6 @@ configuration LCM
end
it "evaluates a powershell $false for an only_if block as false" do
- pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -415,8 +425,6 @@ configuration LCM
end
it "evaluates a non-zero powershell exit status for not_if as true" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "exit 37"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -427,8 +435,6 @@ configuration LCM
end
it "evaluates a failed executable exit status for not_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -439,8 +445,6 @@ configuration LCM
end
it "evaluates a failed executable exit status for only_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -451,8 +455,6 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for not_if as true" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -463,8 +465,6 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for only_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -475,26 +475,26 @@ configuration LCM
end
it "evaluates a not_if block using the cwd guard parameter" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
- resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
+ resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", cwd: custom_cwd
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates an only_if block using the cwd guard parameter" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
- resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
+ resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", cwd: custom_cwd
expect(resource.should_skip?(:run)).to be_falsey
end
it "inherits cwd from the parent resource for only_if" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
resource.cwd custom_cwd
resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
expect(resource.should_skip?(:run)).to be_falsey
end
it "inherits cwd from the parent resource for not_if" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
resource.cwd custom_cwd
resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
expect(resource.should_skip?(:run)).to be_truthy
@@ -507,36 +507,30 @@ configuration LCM
end
it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean true as nonzero status code", :windows64_only do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.architecture :x86_64
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'AMD64')"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code" do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'X86')"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code" do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'X86')"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for only_if" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for not_if" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
@@ -554,40 +548,33 @@ configuration LCM
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
-
- it "raises an error when a 32-bit guard is used on Windows Nano Server", :windows_nano_only do
- resource.only_if "$true", :architecture => :i386
- expect { resource.run_action(:run) }.to raise_error(
- Chef::Exceptions::Win32ArchitectureIncorrect,
- /cannot execute script with requested architecture 'i386' on Windows Nano Server/)
- end
end
end
diff --git a/spec/functional/resource/reboot_spec.rb b/spec/functional/resource/reboot_spec.rb
index c264b122a7..5489dc1c72 100644
--- a/spec/functional/resource/reboot_spec.rb
+++ b/spec/functional/resource/reboot_spec.rb
@@ -22,9 +22,9 @@ describe Chef::Resource::Reboot do
let(:expected) do
{
- :delay_mins => 5,
- :requested_by => "reboot resource functional test",
- :reason => "reboot resource spec test",
+ delay_mins: 5,
+ requested_by: "reboot resource functional test",
+ reason: "reboot resource spec test",
}
end
@@ -45,7 +45,7 @@ describe Chef::Resource::Reboot do
shared_context "testing run context modification" do
def test_reboot_action(resource)
reboot_info = resource.run_context.reboot_info
- expect(reboot_info.keys.sort).to eq([:delay_mins, :reason, :requested_by, :timestamp])
+ expect(reboot_info.keys.sort).to eq(%i{delay_mins reason requested_by timestamp})
expect(reboot_info[:delay_mins]).to eq(expected[:delay_mins])
expect(reboot_info[:reason]).to eq(expected[:reason])
expect(reboot_info[:requested_by]).to eq(expected[:requested_by])
diff --git a/spec/functional/resource/registry_spec.rb b/spec/functional/resource/registry_spec.rb
index 8393e0a074..f3ad946f0e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,13 +34,13 @@ describe Chef::Resource::RegistryKey, :unix_only do
context "when load_current_resource is run on a non-windows node" do
it "throws an exception because you don't have a windows registry (derp)" do
@resource.key("HKCU\\Software\\Opscode")
- @resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @resource.values([{ name: "Color", type: :string, data: "Orange" }])
expect { @resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32NotWindows)
end
end
end
-describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
+describe Chef::Resource::RegistryKey, :windows_only, broken: true do
# parent and key must be single keys, not paths
let(:parent) { "Opscode" }
@@ -107,14 +107,14 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
reset_registry
end
- #Reporting setup
+ # Reporting setup
before 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)
@@ -123,104 +123,128 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@run_id = @resource_reporter.run_id
@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)
+ @cookbook_version = double("Cookbook::Version", version: "1.2.3")
+ @new_resource.cookbook_version(@cookbook_version)
end
- after (:all) do
+ after(:all) do
clean_registry
end
context "when action is create" do
- before (:all) do
+ before(:all) do
reset_registry
end
it "creates registry key, value if the key is missing" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ 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, type and data" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ 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.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)
+ 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" }])
+ @new_resource.values([{ name: "Mango", type: :string, data: "Yellow" }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Mango", :type => :string, :data => "Yellow" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Mango", type: :string, data: "Yellow" })).to eq(true)
end
it "modifies the data if the key and value exist and type matches" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Not just Orange - OpscodeOrange!" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Not just Orange - OpscodeOrange!" }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Not just Orange - OpscodeOrange!" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Not just Orange - OpscodeOrange!" })).to eq(true)
end
it "modifys the type if the key and value exist and the type does not match" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :multi_string, :data => ["Not just Orange - OpscodeOrange!"] }])
+ @new_resource.values([{ name: "Color", type: :multi_string, data: ["Not just Orange - OpscodeOrange!"] }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :multi_string, :data => ["Not just Orange - OpscodeOrange!"] })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :multi_string, data: ["Not just Orange - OpscodeOrange!"] })).to eq(true)
end
it "creates subkey if parent exists" do
@new_resource.key(reg_child + '\OpscodeTest')
- @new_resource.values([{ :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} }])
+ @new_resource.values([{ name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} }])
@new_resource.recursive(false)
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\OpscodeTest')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\OpscodeTest', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
+ 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.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" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(true)
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\Missing1\Missing2')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Missing1\Missing2', { :name => "OC", :type => :string, :data => "MissingData" })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\Missing1\Missing2', { name: "OC", type: :string, data: "MissingData" })).to eq(true)
end
it "creates key with multiple value as specified" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "one", :type => :string, :data => "1" }, { :name => "two", :type => :string, :data => "2" }, { :name => "three", :type => :string, :data => "3" }])
+ @new_resource.values([{ name: "one", type: :string, data: "1" }, { name: "two", type: :string, data: "2" }, { name: "three", type: :string, data: "3" }])
@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
@@ -235,12 +259,12 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
it "creates a key in a 32-bit registry that is not viewable in 64-bit" do
@new_resource.key(reg_child + '\Atraxi' )
- @new_resource.values([{ :name => "OC", :type => :string, :data => "Data" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "Data" }])
@new_resource.recursive(true)
@new_resource.architecture(:i386)
@new_resource.run_action(:create)
@registry.architecture = :i386
- expect(@registry.data_exists?(reg_child + '\Atraxi', { :name => "OC", :type => :string, :data => "Data" })).to eq(true)
+ expect(@registry.data_exists?(reg_child + '\Atraxi', { name: "OC", type: :string, data: "Data" })).to eq(true)
@registry.architecture = :x86_64
expect(@registry.key_exists?(reg_child + '\Atraxi')).to eq(false)
end
@@ -248,7 +272,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
it "prepares the reporting data for action :create" do
@new_resource.key(reg_child + '\Ood')
- @new_resource.values([{ :name => "ReportingVal1", :type => :string, :data => "report1" }, { :name => "ReportingVal2", :type => :string, :data => "report2" }])
+ @new_resource.values([{ name: "ReportingVal1", type: :string, data: "report1" }, { name: "ReportingVal2", type: :string, data: "report2" }])
@new_resource.recursive(true)
@new_resource.run_action(:create)
@report = @resource_reporter.prepare_run_data
@@ -257,8 +281,8 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_child + '\Ood')
- expect(@report["resources"][0]["after"][:values]).to eq([{ :name => "ReportingVal1", :type => :string, :data => "report1" },
- { :name => "ReportingVal2", :type => :string, :data => "report2" }])
+ expect(@report["resources"][0]["after"][:values]).to eq([{ name: "ReportingVal1", type: :string, data: "report1" },
+ { name: "ReportingVal2", type: :string, data: "report2" }])
expect(@report["resources"][0]["before"][:values]).to eq([])
expect(@report["resources"][0]["result"]).to eq("create")
expect(@report["status"]).to eq("success")
@@ -266,101 +290,154 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) 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.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create) # should not raise_error
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" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@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
context "when action is create_if_missing" do
- before (:all) do
+ before(:all) do
reset_registry
end
it "creates registry key, value if the key is missing" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_parent)).to eq(true)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ 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, type and data" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Orange" })).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" }])
+ @new_resource.values([{ name: "Mango", type: :string, data: "Yellow" }])
@new_resource.run_action(:create_if_missing)
- expect(@registry.data_exists?(reg_child, { :name => "Mango", :type => :string, :data => "Yellow" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Mango", type: :string, data: "Yellow" })).to eq(true)
end
it "creates subkey if parent exists" do
@new_resource.key(reg_child + '\Pyrovile')
- @new_resource.values([{ :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} }])
+ @new_resource.values([{ name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} }])
@new_resource.recursive(false)
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Pyrovile')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Pyrovile', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
+ 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.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" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Sontaran\Sontar')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Sontaran\Sontar', { :name => "OC", :type => :string, :data => "MissingData" })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\Sontaran\Sontar', { name: "OC", type: :string, data: "MissingData" })).to eq(true)
end
it "creates key with multiple value as specified" do
@new_resource.key(reg_child + '\Adipose')
- @new_resource.values([{ :name => "one", :type => :string, :data => "1" }, { :name => "two", :type => :string, :data => "2" }, { :name => "three", :type => :string, :data => "3" }])
+ @new_resource.values([{ name: "one", type: :string, data: "1" }, { name: "two", type: :string, data: "2" }, { name: "three", type: :string, data: "3" }])
@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
it "prepares the reporting data for :create_if_missing" do
@new_resource.key(reg_child + '\Judoon')
- @new_resource.values([{ :name => "ReportingVal3", :type => :string, :data => "report3" }])
+ @new_resource.values([{ name: "ReportingVal3", type: :string, data: "report3" }])
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
@report = @resource_reporter.prepare_run_data
@@ -369,7 +446,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_child + '\Judoon')
- expect(@report["resources"][0]["after"][:values]).to eq([{ :name => "ReportingVal3", :type => :string, :data => "report3" }])
+ expect(@report["resources"][0]["after"][:values]).to eq([{ name: "ReportingVal3", type: :string, data: "report3" }])
expect(@report["resources"][0]["before"][:values]).to eq([])
expect(@report["resources"][0]["result"]).to eq("create_if_missing")
expect(@report["status"]).to eq("success")
@@ -377,25 +454,54 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) 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.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create_if_missing) # should not raise_error
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" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@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
@@ -416,66 +522,66 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
it "takes no action if the key exists but the value does not" do
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "LooksLike", :type => :multi_string, :data => %w{SeattleGrey OCOrange} }])
+ @new_resource.values([{ name: "LooksLike", type: :multi_string, data: %w{SeattleGrey OCOrange} }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "deletes only specified values under a key path" do
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "Opscode", :type => :multi_string, :data => %w{Seattle Washington} }, { :name => "AKA", :type => :string, :data => "OC" }])
+ @new_resource.values([{ name: "Opscode", type: :multi_string, data: %w{Seattle Washington} }, { name: "AKA", type: :string, data: "OC" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "AKA", :type => :string, :data => "OC" })).to eq(false)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "Opscode", :type => :multi_string, :data => %w{Seattle Washington} })).to eq(false)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "AKA", type: :string, data: "OC" })).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "Opscode", type: :multi_string, data: %w{Seattle Washington} })).to eq(false)
end
it "it deletes the values with the same name irrespective of it type and data" do
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "Color", :type => :multi_string, :data => %w{Black Orange} }])
+ @new_resource.values([{ name: "Color", type: :multi_string, data: %w{Black Orange} }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(false)
end
it "prepares the reporting data for action :delete" do
@new_resource.key(reg_parent + '\ReportKey')
- @new_resource.values([{ :name => "ReportVal4", :type => :string, :data => "report4" }, { :name => "ReportVal5", :type => :string, :data => "report5" }])
+ @new_resource.values([{ name: "ReportVal4", type: :string, data: "report4" }, { name: "ReportVal5", type: :string, data: "report5" }])
@new_resource.recursive(true)
@new_resource.run_action(:delete)
@report = @resource_reporter.prepare_run_data
- expect(@registry.value_exists?(reg_parent + '\ReportKey', [{ :name => "ReportVal4", :type => :string, :data => "report4" }, { :name => "ReportVal5", :type => :string, :data => "report5" }])).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\ReportKey', [{ name: "ReportVal4", type: :string, data: "report4" }, { name: "ReportVal5", type: :string, data: "report5" }])).to eq(false)
expect(@report["action"]).to eq("end")
expect(@report["resources"].count).to eq(1)
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey')
- expect(@report["resources"][0]["before"][:values]).to eq([{ :name => "ReportVal4", :type => :string, :data => "report4" },
- { :name => "ReportVal5", :type => :string, :data => "report5" }])
- #Not testing for after values to match since after -> new_resource values.
+ expect(@report["resources"][0]["before"][:values]).to eq([{ name: "ReportVal4", type: :string, data: "report4" },
+ { name: "ReportVal5", type: :string, data: "report5" }])
+ # Not testing for after values to match since after -> new_resource values.
expect(@report["resources"][0]["result"]).to eq("delete")
expect(@report["status"]).to eq("success")
expect(@report["total_res_count"]).to eq("1")
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
it "does nothing if the action is delete" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
@@ -485,7 +591,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "when the action is delete_key" do
- before (:all) do
+ before(:all) do
reset_registry
create_deletable_keys
end
@@ -516,7 +622,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
it "ignores the values under a key" do
@new_resource.key(reg_parent + '\OpscodeIgnoredValues')
- #@new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}])
+ # @new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}])
@new_resource.recursive(true)
@new_resource.run_action(:delete_key)
end
@@ -539,27 +645,27 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey')
- #Not testing for before or after values to match since
- #after -> new_resource.values and
- #before -> current_resource.values
+ # Not testing for before or after values to match since
+ # after -> new_resource.values and
+ # before -> current_resource.values
expect(@report["resources"][0]["result"]).to eq("delete_key")
expect(@report["status"]).to eq("success")
expect(@report["total_res_count"]).to eq("1")
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
it "does not throw an exception if the key has subkeys but recursive is set to false" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete_key)
end
it "does nothing if the action is delete_key" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete_key)
diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb
index c25e51cf2a..2d9d1de351 100644
--- a/spec/functional/resource/remote_directory_spec.rb
+++ b/spec/functional/resource/remote_directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index 1f92a567f3..09e4fdccb4 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 (c) 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] = 1
+ Chef::Config[:http_retry_count] = 2
end
after(:each) do
@@ -55,11 +58,11 @@ describe Chef::Resource::RemoteFile do
let(:default_mode) { (0666 & ~File.umask).to_s(8) }
context "when fetching files over HTTP" do
- before(:each) do
- start_tiny_server
+ before(:all) do
+ start_tiny_server(RequestTimeout: 1)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -97,21 +100,22 @@ describe Chef::Resource::RemoteFile do
context "when fetching files over HTTPS" do
- before(:each) do
+ before(:all) do
cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA))
cert = OpenSSL::X509::Certificate.new(cert_text)
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)
+ start_tiny_server(**server_opts)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -123,12 +127,183 @@ 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).tr("/", "\\") }
+ 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.tr("/", '\\')}\" /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(:each) do
- start_tiny_server
+ before(:all) do
+ start_tiny_server(RequestTimeout: 1)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -185,7 +360,7 @@ describe Chef::Resource::RemoteFile do
it "should raise ContentLengthMismatch" do
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- #File.should_not exist(path) # XXX: CHEF-5081
+ # File.should_not exist(path) # XXX: CHEF-5081
end
end
@@ -198,7 +373,7 @@ describe Chef::Resource::RemoteFile do
it "should raise ContentLengthMismatch" do
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- #File.should_not exist(path) # XXX: CHEF-5081
+ # File.should_not exist(path) # XXX: CHEF-5081
end
end
@@ -234,13 +409,7 @@ describe Chef::Resource::RemoteFile do
it "should not create the file" do
# 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 { resource.run_action(:create) }.to raise_error(SystemCallError)
expect(File).not_to exist(path)
end
diff --git a/spec/functional/resource/rpm_spec.rb b/spec/functional/resource/rpm_spec.rb
index ce9332e4ed..15b6731357 100644
--- a/spec/functional/resource/rpm_spec.rb
+++ b/spec/functional/resource/rpm_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,52 +17,49 @@
#
require "spec_helper"
-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])
-describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test do
+exclude_test = !%w{aix rhel fedora suse amazon}.include?(ohai[:platform_family])
+describe Chef::Resource::RpmPackage, :requires_root, external: exclude_test do
include Chef::Mixin::ShellOut
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::RpmPackage.new(@pkg_name, run_context)
new_resource.source @pkg_path
new_resource
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
+ ::File.exist?("/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")
+ !::File.exist?("/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 32529fbb0c..4707fa8ee0 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Resource::Template do
def binread(file)
- File.open(file, "rb") { |f| f.read }
+ File.open(file, "rb", &:read)
end
include_context Chef::Resource::File
@@ -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
@@ -67,7 +68,7 @@ describe Chef::Resource::Template do
context "when the target file does not exist" do
it "creates the template with the rendered content using the variable attribute when the :create action is run" do
resource.source("openldap_variable_stuff.conf.erb")
- resource.variables(:secret => "nutella")
+ resource.variables(secret: "nutella")
resource.run_action(:create)
expect(IO.read(path)).to eq("super secret is nutella")
end
@@ -111,7 +112,7 @@ describe Chef::Resource::Template do
context "using single helper syntax referencing @node" do
before do
node.normal[:helper_test_attr] = "value from helper method"
- resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" }
+ resource.helper(:helper_method) { (@node[:helper_test_attr]).to_s }
end
it_behaves_like "a template with helpers"
@@ -202,11 +203,43 @@ describe Chef::Resource::Template do
it "output should contain platform's line endings" do
resource.run_action(:create)
binread(path).each_line do |line|
- expect(line).to end_with(Chef::Platform.windows? ? "\r\n" : "\n")
+ expect(line).to end_with(ChefUtils.windows? ? "\r\n" : "\n")
end
end
end
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/timezone_spec.rb b/spec/functional/resource/timezone_spec.rb
new file mode 100644
index 0000000000..d44d5b34a8
--- /dev/null
+++ b/spec/functional/resource/timezone_spec.rb
@@ -0,0 +1,41 @@
+#
+# Author:: Gary Bright (<digitalgaz@hotmail.com>)
+# Copyright:: Copyright (c) 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::Timezone, :windows_only do
+ let(:timezone) { "GMT Standard Time" }
+
+ def timezone_resource
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
+ Chef::Resource::Timezone.new(timezone, run_context)
+ end
+
+ describe "when a timezone is provided on windows" do
+ it "should set a timezone" do
+ timezone_resource.run_action(:set)
+ end
+ end
+
+ describe "when a timezone is not provided on windows" do
+ let(:timezone) { nil }
+ it "raises an exception" do
+ expect { timezone_resource.run_action(:set) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+end
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index bedb37838c..50da812b0f 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,20 +19,17 @@ require "spec_helper"
require "chef/mixin/shell_out"
metadata = {
- :mac_osx_only => true,
- :requires_root => true,
- :not_supported_on_mac_osx_106 => true,
+ macos_1013: true,
+ requires_root: true,
}
describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metadata do
include Chef::Mixin::ShellOut
def clean_user
- begin
- shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
- rescue Mixlib::ShellOut::ShellCommandFailed
- # Raised when the user is already cleaned
- end
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ # Raised when the user is already cleaned
end
def user_should_exist
@@ -124,13 +121,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
describe "when password is being set via shadow hash" do
let(:password) do
- if node[:platform_version].start_with?("10.7.")
- # On Mac 10.7 we only need to set the password
- "c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\
-d0f5e65c61558243e50b1a490088bd4824e3b35562d383ca02260398\
-ef1979b302212ec1c5383d1d05fc8d843"
- else
- "c734b6e4787c3727bb35e29fdd92b97c\
+ "c734b6e4787c3727bb35e29fdd92b97c\
1de12df509577a045728255ec7c6c5f5\
c18efa05ed02b682ffa7ebc05119900e\
b1d4880833aa7a190afc13e2bf0936b8\
@@ -138,7 +129,6 @@ b1d4880833aa7a190afc13e2bf0936b8\
9464a8c234f3919082400b4f939bb77b\
c5adbbac718b7eb99463a7b679571e0f\
1c9fef2ef08d0b9e9c2bcf644eed2ffc"
- end
end
let(:iterations) { 25000 }
diff --git a/spec/functional/resource/user/mac_user_spec.rb b/spec/functional/resource/user/mac_user_spec.rb
new file mode 100644
index 0000000000..dabc303afb
--- /dev/null
+++ b/spec/functional/resource/user/mac_user_spec.rb
@@ -0,0 +1,207 @@
+#
+# Copyright:: Copyright (c) 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/shell_out"
+
+metadata = {
+ macos_gte_1014: true,
+ requires_root: true,
+}
+
+describe "Chef::Resource::User with Chef::Provider::User::MacUser provider", metadata do
+ include Chef::Mixin::ShellOut
+
+ def clean_user
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ # Raised when the user is already cleaned
+ end
+
+ def ensure_file_cache_path_exists
+ path = Chef::Config["file_cache_path"]
+ FileUtils.mkdir_p(path) unless File.directory?(path)
+ end
+
+ def user_should_exist
+ expect(shell_out("/usr/bin/dscl . -read /Users/#{username}").error?).to be(false)
+ end
+
+ def check_password(pass)
+ # In order to test the password we use dscl passwd command since
+ # that's the only command that gets the user password from CLI.
+ expect(shell_out("dscl . -passwd /Users/greatchef #{pass} new_password").exitstatus).to eq(0)
+ # Now reset the password back
+ expect(shell_out("dscl . -passwd /Users/greatchef new_password #{pass}").exitstatus).to eq(0)
+ end
+
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ n
+ end
+
+ let(:events) do
+ Chef::EventDispatch::Dispatcher.new
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ let(:username) do
+ "greatchef"
+ end
+
+ let(:uid) { nil }
+ let(:gid) { 20 }
+ let(:home) { nil }
+ let(:manage_home) { false }
+ let(:password) { "XXXYYYZZZ" }
+ let(:comment) { "Great Chef" }
+ let(:shell) { "/bin/bash" }
+ let(:salt) { nil }
+ let(:iterations) { nil }
+
+ let(:user_resource) do
+ r = Chef::Resource::User::MacUser.new("TEST USER RESOURCE", run_context)
+ r.username(username)
+ r.uid(uid)
+ r.gid(gid)
+ r.home(home)
+ r.shell(shell)
+ r.comment(comment)
+ r.manage_home(manage_home)
+ r.password(password)
+ r.salt(salt)
+ r.iterations(iterations)
+ r
+ end
+
+ before do
+ clean_user
+ ensure_file_cache_path_exists
+ end
+
+ after(:each) do
+ clean_user
+ end
+
+ describe "action :create" do
+ it "should create the user" do
+ user_resource.run_action(:create)
+ user_should_exist
+ check_password(password)
+ end
+ end
+
+ describe "when user exists" do
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+ user_should_exist
+ end
+
+ describe "when password is updated" do
+ it "should update the password of the user" do
+ user_resource.password("mykitchen")
+ user_resource.run_action(:create)
+ check_password("mykitchen")
+ end
+ end
+ end
+
+ describe "when password is being set via shadow hash" do
+ let(:password) do
+ "c734b6e4787c3727bb35e29fdd92b97c\
+1de12df509577a045728255ec7c6c5f5\
+c18efa05ed02b682ffa7ebc05119900e\
+b1d4880833aa7a190afc13e2bf0936b8\
+20123e8c98f0f9bcac2a629d9163caac\
+9464a8c234f3919082400b4f939bb77b\
+c5adbbac718b7eb99463a7b679571e0f\
+1c9fef2ef08d0b9e9c2bcf644eed2ffc"
+ end
+
+ let(:iterations) { 25000 }
+ let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
+
+ it "action :create should create the user" do
+ user_resource.run_action(:create)
+ user_should_exist
+ check_password("soawesome")
+ end
+
+ describe "when user exists" do
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+ user_should_exist
+ end
+
+ describe "when password is updated" do
+ describe "without salt" do
+ let(:salt) { nil }
+
+ it "it sets the password" do
+ user_resource.password("mykitchen")
+ user_resource.run_action(:create)
+ check_password("mykitchen")
+ end
+ end
+
+ describe "with salt and plaintext password" do
+ it "raises Chef::Exceptions::User" do
+ expect do
+ user_resource.password("notasha512")
+ user_resource.run_action(:create)
+ end.to raise_error(Chef::Exceptions::User)
+ end
+ end
+ end
+ end
+ end
+
+ describe "when a user is member of some groups" do
+ let(:groups) { %w{staff operator} }
+
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+
+ groups.each do |group|
+ shell_out!("/usr/bin/dscl . -append '/Groups/#{group}' GroupMembership #{username}")
+ end
+ end
+
+ after do
+ groups.each do |group|
+ # Do not raise an error when user is correctly removed
+ shell_out("/usr/bin/dscl . -delete '/Groups/#{group}' GroupMembership #{username}")
+ end
+ end
+
+ it ":remove action removes the user from the groups and deletes the user" do
+ user_resource.run_action(:remove)
+ groups.each do |group|
+ # Do not raise an error when group is empty
+ expect(shell_out("dscl . read /Groups/staff GroupMembership").stdout).not_to include(group)
+ end
+ end
+ end
+
+end
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
deleted file mode 100644
index 79d62436f5..0000000000
--- a/spec/functional/resource/user/useradd_spec.rb
+++ /dev/null
@@ -1,698 +0,0 @@
-# encoding: UTF-8
-#
-# Author:: Daniel DeLeo (<dan@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 "spec_helper"
-require "functional/resource/base"
-require "chef/mixin/shell_out"
-
-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
- 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_filter },
-}
-
-describe Chef::Provider::User::Useradd, metadata do
-
- include Chef::Mixin::ShellOut
-
- # Utility code for /etc/passwd interaction, avoid any caching of user records:
- PwEntry = Struct.new(:name, :passwd, :uid, :gid, :gecos, :home, :shell)
-
- class UserNotFound < StandardError; end
-
- def pw_entry
- passwd_file = File.open("/etc/passwd", "rb") { |f| f.read }
- matcher = /^#{Regexp.escape(username)}.+$/
- if passwd_entry = passwd_file.scan(matcher).first
- PwEntry.new(*passwd_entry.split(":"))
- else
- raise UserNotFound, "no entry matching #{matcher.inspect} found in /etc/passwd"
- end
- end
-
- def etc_shadow
- case ohai[:platform]
- when "aix"
- File.open("/etc/security/passwd") { |f| f.read }
- else
- File.open("/etc/shadow") { |f| f.read }
- end
- end
-
- def self.quote_in_username_unsupported?
- if OHAI_SYSTEM["platform_family"] == "debian"
- false
- else
- "Only debian family systems support quotes in username"
- end
- end
-
- def password_should_be_set
- if ohai[:platform] == "aix"
- expect(pw_entry.passwd).to eq("!")
- else
- expect(pw_entry.passwd).to eq("x")
- end
- end
-
- def try_cleanup
- ["/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 = resource_for_platform("DELETE USER", run_context)
- r.manage_home true
- r.username("cf-test")
- r.run_action(:remove)
- end
- end
-
- before do
- # Silence shell_out live stream
- Chef::Log.level = :warn
- try_cleanup
- end
-
- after do
- max_retries = 3
- while max_retries > 0
- begin
- pw_entry # will raise if the user doesn't exist
- status = shell_out!("userdel", "-r", username, :returns => [0, 8, 12])
-
- # Error code 8 during userdel indicates that the user is logged in.
- # This occurs randomly because the accounts daemon holds a lock due to which userdel fails.
- # The work around is to retry userdel for 3 times.
- break if status.exitstatus != 8
-
- sleep 1
- max_retries = max_retries - 1
- rescue UserNotFound
- break
- end
- end
-
- status.error! if max_retries == 0
- end
-
- let(:node) do
- n = Chef::Node.new
- n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
- n
- end
-
- let(:events) do
- Chef::EventDispatch::Dispatcher.new
- end
-
- let(:run_context) do
- Chef::RunContext.new(node, {}, events)
- end
-
- let(:username) { "cf-test" }
- let(:uid) { nil }
- let(:home) { nil }
- let(:manage_home) { false }
- let(:password) { nil }
- let(:system) { false }
- let(:comment) { nil }
-
- let(:user_resource) do
- r = resource_for_platform("TEST USER RESOURCE", run_context)
- r.username(username)
- r.uid(uid)
- r.home(home)
- r.comment(comment)
- r.manage_home(manage_home)
- r.password(password)
- r.system(system)
- r
- end
-
- let(:expected_shadow) do
- if ohai[:platform] == "aix"
- expected_shadow = "cf-test" # For aix just check user entry in shadow file
- else
- expected_shadow = "cf-test:$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- describe "action :create" do
-
- context "when the user does not exist beforehand" do
- before do
- user_resource.run_action(:create)
- expect(user_resource).to be_updated_by_last_action
- end
-
- it "ensures the user exists" do
- expect(pw_entry.name).to eq(username)
- end
-
- # On Debian, the only constraints are that usernames must neither start
- # with a dash ('-') nor plus ('+') nor tilde ('~') nor contain a colon
- # (':'), a comma (','), or a whitespace (space: ' ', end of line: '\n',
- # tabulation: '\t', etc.). Note that using a slash ('/') may break the
- # default algorithm for the definition of the user's home directory.
-
- context "and the username contains a single quote", skip: quote_in_username_unsupported? do
-
- let(:username) { "t'bilisi" }
-
- it "ensures the user exists" do
- expect(pw_entry.name).to eq(username)
- end
- end
-
- context "when uid is set" do
- # Should verify uid not in use...
- let(:uid) { 1999 }
-
- it "ensures the user has the given uid" do
- expect(pw_entry.uid).to eq("1999")
- end
- end
-
- context "when comment is set" do
- let(:comment) { "hello this is dog" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq("hello this is dog")
- end
-
- context "in standard gecos format" do
- let(:comment) { "Bobo T. Clown,some building,555-555-5555,@boboclown" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq(comment)
- end
- end
-
- context "to a string containing multibyte characters" do
- let(:comment) { "(╯°□°)╯︵ ┻━┻" }
-
- it "ensures the comment is set" do
- actual = pw_entry.gecos
- actual.force_encoding(Encoding::UTF_8) if "".respond_to?(:force_encoding)
- expect(actual).to eq(comment)
- end
- end
-
- context "to a string containing an apostrophe `'`" do
- let(:comment) { "don't go" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq(comment)
- end
- end
- end
-
- context "when home is set" do
- let(:home) { "/home/#{username}" }
-
- it "ensures the user's home is set to the given path" do
- expect(pw_entry.home).to eq(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
- let(:manage_home) { true }
-
- it "ensures the user's home directory exists" 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
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "sets the user's shadow password" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
- end
-
- context "when a system user is specified", skip: aix? do
- let(:system) { true }
- let(:uid_min) do
- # from `man useradd`, login user means uid will be between
- # UID_SYS_MIN and UID_SYS_MAX defined in /etc/login.defs. On my
- # Ubuntu 13.04 system, these are commented out, so we'll look at
- # UID_MIN to find the lower limit of the non-system-user range, and
- # use that value in our assertions.
- login_defs = File.open("/etc/login.defs", "rb") { |f| f.read }
- uid_min_scan = /^UID_MIN\s+(\d+)/
- login_defs.match(uid_min_scan)[1]
- end
-
- it "ensures the user has the properties of a system user" do
- expect(pw_entry.uid.to_i).to be < uid_min.to_i
- end
- end
- end # when the user does not exist beforehand
-
- context "when the user already exists" do
-
- let(:expect_updated?) { true }
-
- let(:existing_uid) { nil }
- let(:existing_home) { nil }
- let(:existing_manage_home) { false }
- let(:existing_password) { nil }
- let(:existing_system) { false }
- let(:existing_comment) { nil }
-
- let(:existing_user) do
- 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)
- r.comment(existing_comment)
- r.manage_home(existing_manage_home)
- r.password(existing_password)
- r.system(existing_system)
- r
- end
-
- before do
- if reason = skip
- skip(reason)
- end
- existing_user.run_action(:create)
- expect(existing_user).to be_updated_by_last_action
- user_resource.run_action(:create)
- expect(user_resource.updated_by_last_action?).to eq(expect_updated?)
- end
-
- context "and all properties are in the desired state" do
- let(:uid) { 1999 }
- let(:home) { "/home/bobo" }
- let(:manage_home) { true }
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- let(:system) { false }
- let(:comment) { "hello this is dog" }
-
- let(:existing_uid) { uid }
- let(:existing_home) { home }
- let(:existing_manage_home) { manage_home }
- let(:existing_password) { password }
- let(:existing_system) { false }
- let(:existing_comment) { comment }
-
- let(:expect_updated?) { false }
-
- it "does not update the user" do
- expect(user_resource).not_to be_updated
- end
- end
-
- context "and the uid is updated" do
- let(:uid) { 1999 }
- let(:existing_uid) { 1998 }
-
- it "ensures the uid is set to the desired value" do
- expect(pw_entry.uid).to eq("1999")
- end
- end
-
- context "and the comment is updated" do
- let(:comment) { "hello this is dog" }
- let(:existing_comment) { "woof" }
-
- it "ensures the comment field is set to the desired value" do
- expect(pw_entry.gecos).to eq("hello this is dog")
- end
- end
-
- context "and home directory is updated" do
- let(:existing_home) { "/home/cheftestfoo" }
- let(:home) { "/home/cheftestbar" }
- it "ensures the home directory is set to the desired value" do
- expect(pw_entry.home).to eq("/home/cheftestbar")
- end
-
- context "and manage_home is enabled" do
- let(:existing_manage_home) { true }
- let(:manage_home) { true }
- it "moves the home directory to the new location" do
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- end
-
- context "and manage_home wasn't enabled but is now" do
- let(:existing_manage_home) { false }
- let(:manage_home) { true }
-
- if %w{rhel fedora}.include?(OHAI_SYSTEM["platform_family"])
- # Inconsistent behavior. See: CHEF-2205
- it "created the home dir b/c of CHEF-2205 so it still exists" do
- # This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- elsif ohai[:platform] == "aix"
- it "creates the home dir in the desired location" do
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- else
- it "does not create the home dir in the desired location (XXX)" do
- # This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).not_to exist("/home/cheftestbar")
- end
- end
- end
-
- context "and manage_home was enabled but is not now" do
- let(:existing_manage_home) { true }
- let(:manage_home) { false }
-
- it "leaves the old home directory around (XXX)" do
- # Would it be better to remove the old home?
- expect(File).to exist("/home/cheftestfoo")
- expect(File).not_to exist("/home/cheftestbar")
- end
- end
- end
-
- context "and a password is added" do
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "ensures the password is set" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
-
- end
-
- context "and the password is updated" do
- # openssl passwd -1 "OLDpassword"
- let(:existing_password) do
- case ohai[:platform]
- when "aix"
- "jkzG6MvUxjk2g"
- else
- "$1$1dVmwm4z$CftsFn8eBDjDRUytYKkXB."
- end
- end
-
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "ensures the password is set to the desired value" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
- end
-
- context "and the user is changed from not-system to system" do
- let(:existing_system) { false }
- let(:system) { true }
-
- let(:expect_updated?) { false }
-
- it "does not modify the user at all" do
- end
- end
-
- context "and the user is changed from system to not-system" do
- let(:existing_system) { true }
- let(:system) { false }
-
- let(:expect_updated?) { false }
-
- it "does not modify the user at all" do
- end
- end
-
- end # when the user already exists
- end # action :create
-
- shared_context "user exists for lock/unlock" do
- let(:user_locked_context?) { false }
-
- def shadow_entry
- etc_shadow.lines.find { |l| l.include?(username) }
- end
-
- def shadow_password
- shadow_entry.split(":")[1]
- end
-
- def aix_user_lock_status
- lock_info = shell_out!("lsuser -a account_locked #{username}")
- /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1]
- end
-
- def user_account_should_be_locked
- case ohai[:platform]
- when "aix"
- expect(aix_user_lock_status).to eq("true")
- else
- expect(shadow_password).to include("!")
- end
- end
-
- def user_account_should_be_unlocked
- case ohai[:platform]
- when "aix"
- expect(aix_user_lock_status).to eq("false")
- else
- expect(shadow_password).not_to include("!")
- end
- end
-
- def lock_user_account
- case ohai[:platform]
- when "aix"
- shell_out!("chuser account_locked=true #{username}")
- else
- shell_out!("usermod -L #{username}")
- end
- end
-
- before do
- # create user and setup locked/unlocked state
- user_resource.dup.run_action(:create)
-
- if user_locked_context?
- lock_user_account
- user_account_should_be_locked
- elsif password
- user_account_should_be_unlocked
- end
- end
- end
-
- describe "action :lock" do
- context "when the user does not exist" do
- it "raises a sensible error" do
- expect { user_resource.run_action(:lock) }.to raise_error(Chef::Exceptions::User)
- end
- end
-
- context "when the user exists" do
-
- include_context "user exists for lock/unlock"
-
- before do
- user_resource.run_action(:lock)
- end
-
- context "and the user is not locked" do
- # user will be locked if it has no password
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "locks the user's password" do
- user_account_should_be_locked
- end
- end
-
- context "and the user is locked" do
- # user will be locked if it has no password
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- let(:user_locked_context?) { true }
- it "does not update the user" do
- expect(user_resource).not_to be_updated_by_last_action
- end
- end
- end
- end # action :lock
-
- describe "action :unlock" do
- context "when the user does not exist" do
- it "raises a sensible error" do
- expect { user_resource.run_action(:unlock) }.to raise_error(Chef::Exceptions::User)
- end
- end
-
- context "when the user exists" do
-
- include_context "user exists for lock/unlock"
-
- before do
- begin
- user_resource.run_action(:unlock)
- @error = nil
- rescue Exception => e
- @error = e
- end
- end
-
- 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:
- 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
- else
-
- # borked on all other platforms:
- it "is marked as updated but doesn't modify the user (XXX)" do
- # This should be an error instead; note that usermod still exits 0
- # (which is probably why this case silently fails):
- #
- # DEBUG: ---- Begin output of usermod -U chef-functional-test ----
- # DEBUG: STDOUT:
- # DEBUG: STDERR: usermod: unlocking the user's password would result in a passwordless account.
- # You should set a password with usermod -p to unlock this user's password.
- # DEBUG: ---- End output of usermod -U chef-functional-test ----
- # DEBUG: Ran usermod -U chef-functional-test returned 0
- expect(@error).to be_nil
- if ohai[:platform] == "aix"
- expect(pw_entry.passwd).to eq("*")
- user_account_should_be_unlocked
- else
- expect(pw_entry.passwd).to eq("x")
- expect(shadow_password).to include("!")
- end
- end
- end
- end
-
- context "and has a password" do
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- context "and the user is not locked" do
- it "does not update the user" do
- expect(user_resource).not_to be_updated_by_last_action
- end
- end
-
- context "and the user is locked" do
- let(:user_locked_context?) { true }
-
- it "unlocks the user's password" do
- user_account_should_be_unlocked
- end
- end
- end
- end
- end # action :unlock
-
-end
diff --git a/spec/functional/resource/user/windows_spec.rb b/spec/functional/resource/user/windows_spec.rb
index f61a51c636..72db110266 100644
--- a/spec/functional/resource/user/windows_spec.rb
+++ b/spec/functional/resource/user/windows_spec.rb
@@ -1,5 +1,7 @@
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Author:: Stuart Preston (<stuart@chef.io>)
+#
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +24,7 @@ describe Chef::Provider::User::Windows, :windows_only do
include Chef::Mixin::ShellOut
let(:username) { "ChefFunctionalTest" }
- let(:password) { SecureRandom.uuid }
+ let(:password) { "DummyP2ssw0rd!" }
let(:node) do
n = Chef::Node.new
@@ -31,9 +33,10 @@ 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|
+ Chef::Resource::User::WindowsUser.new(username, run_context).tap do |r|
r.provider(Chef::Provider::User::Windows)
r.password(password)
end
@@ -43,33 +46,142 @@ describe Chef::Provider::User::Windows, :windows_only do
shell_out("net user #{u} /delete")
end
- before do
+ def backup_secedit_policy
+ backup_command = "secedit /export /cfg #{ENV["TEMP"]}\\secedit_restore.inf /areas SECURITYPOLICY"
+ shell_out(backup_command)
+ end
+
+ def restore_secedit_policy
+ security_database = "C:\\windows\\security\\database\\seceditnew.sdb"
+ restore_command = "secedit /configure /db #{security_database} /cfg #{ENV["TEMP"]}\\secedit_restore.inf /areas SECURITYPOLICY"
+ shell_out(restore_command)
+ end
+
+ def set_windows_minimum_password_length(minimum_password_length = 0)
+ require "tempfile"
+ temp_security_database = "C:\\windows\\security\\database\\seceditnew.sdb"
+ temp_security_template = Tempfile.new(["chefpolicy", ".inf"])
+ file_content = <<~EOF
+ [Unicode]
+ Unicode=yes
+ [System Access]
+ MinimumPasswordLength = #{minimum_password_length}
+ PasswordComplexity = 0
+ [Version]
+ signature="$CHICAGO$"
+ Revision=1
+ EOF
+ windows_template_path = temp_security_template.path.gsub("/") { "\\" }
+ security_command = "secedit /configure /db #{temp_security_database} /cfg #{windows_template_path} /areas SECURITYPOLICY"
+ temp_security_template.write(file_content)
+ temp_security_template.close
+ shell_out(security_command)
+ end
+
+ before(:all) do
+ backup_secedit_policy
+ end
+
+ before(:each) do
delete_user(username)
+ allow(run_context).to receive(:logger).and_return(logger)
+ end
+
+ after(:all) do
+ restore_secedit_policy
end
describe "action :create" do
- it "creates a user when a username and password are given" do
- new_resource.run_action(:create)
- expect(new_resource).to be_updated_by_last_action
- expect(shell_out("net user #{username}").exitstatus).to eq(0)
- end
+ context "on a Windows system with a policy that requires non-blank passwords and no complexity requirements" do
- it "reports no changes if there are no changes needed" do
- new_resource.run_action(:create)
- new_resource.run_action(:create)
- expect(new_resource).not_to be_updated_by_last_action
+ before(:all) do
+ set_windows_minimum_password_length(1)
+ end
+
+ context "when a username and non-empty password are given" do
+ it "creates a user" do
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password" do
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when a username and empty password are given" do
+ it "does not create the specified user" do
+ new_resource.password("")
+ expect { new_resource.run_action(:create) }.to raise_exception(Chef::Exceptions::Win32APIError, /The password does not meet the password policy requirements/)
+ end
+ end
end
- it "allows chaning the password" do
- new_resource.run_action(:create)
- new_resource.password(SecureRandom.uuid)
- new_resource.run_action(:create)
- expect(new_resource).to be_updated_by_last_action
+ context "on a Windows system with a policy that allows blank passwords" do
+
+ before(:all) do
+ set_windows_minimum_password_length(0)
+ end
+
+ context "when a username and non-empty password are given" do
+ it "creates a user" do
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password" do
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when a username and empty password are given" do
+ it "creates a user" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password from empty to a value" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
end
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_certificate_spec.rb b/spec/functional/resource/windows_certificate_spec.rb
new file mode 100644
index 0000000000..b5d0484e0c
--- /dev/null
+++ b/spec/functional/resource/windows_certificate_spec.rb
@@ -0,0 +1,316 @@
+# Author: Nimesh Patni (nimesh.patni@msystechnologies.com)
+# Copyright:: Copyright (c) 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"
+require "chef/resource/windows_certificate"
+
+describe Chef::Resource::WindowsCertificate, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ def create_store
+ powershell_exec <<~EOC
+ New-Item -Path Cert:\\LocalMachine\\#{store}
+ EOC
+ end
+
+ def delete_store
+ powershell_exec <<~EOC
+ Remove-Item -Path Cert:\\LocalMachine\\#{store} -Recurse
+ EOC
+ end
+
+ def certificate_count
+ powershell_exec(<<~EOC).result.to_i
+ (Get-ChildItem -Force -Path Cert:\\LocalMachine\\#{store} | measure).Count
+ EOC
+ end
+
+ let(:password) { "P@ssw0rd!" }
+ let(:store) { "Chef-Functional-Test" }
+ let(:cer_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.cer") }
+ let(:base64_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "base64_test.cer") }
+ let(:pem_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.pem") }
+ let(:p7b_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.p7b") }
+ let(:pfx_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.pfx") }
+ let(:tests_thumbprint) { "e45a4a7ff731e143cf20b8bfb9c7c4edd5238bb3" }
+ let(:other_cer_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "othertest.cer") }
+ let(:others_thumbprint) { "6eae1deefaf59daf1a97c9ceeff39c98b3da38cb" }
+ let(:p7b_thumbprint) { "f867e25b928061318ed2c36ca517681774b06260" }
+ let(:p7b_nested_thumbprint) { "dc395eae6be5b69951b8b6e1090cfc33df30d2cd" }
+
+ let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ Chef::Resource::WindowsCertificate.new("ChefFunctionalTest", run_context).tap do |r|
+ r.store_name = store
+ end
+ end
+
+ before do
+ # Bypass validation of the store name so we can use a fake test store.
+ allow_any_instance_of(Chef::Mixin::ParamsValidate)
+ .to receive(:_pv_equal_to)
+ .with({ store_name: store }, :store_name, anything)
+ .and_return(true)
+
+ create_store
+ end
+
+ after { delete_store }
+
+ describe "action: create" do
+ it "starts with no certificates" do
+ expect(certificate_count).to eq(0)
+ end
+
+ it "can add a certificate idempotently" do
+ resource.source = cer_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).to be_updated_by_last_action
+
+ # Adding the cert again should have no effect
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Adding the cert again with a different format should have no effect
+ resource.source = pem_path
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Adding another cert should work correctly
+ resource.source = other_cer_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(2)
+ expect(resource).to be_updated_by_last_action
+ end
+
+ it "can add a base64 encoded certificate idempotently" do
+ resource.source = base64_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "can add a PEM certificate idempotently" do
+ resource.source = pem_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "can add a P7B certificate idempotently" do
+ resource.source = p7b_path
+ resource.run_action(:create)
+
+ # A P7B cert includes nested certs
+ expect(certificate_count).to eq(3)
+
+ resource.run_action(:create)
+
+ expect(resource).not_to be_updated_by_last_action
+ expect(certificate_count).to eq(3)
+ end
+
+ it "can add a PFX certificate idempotently with a valid password" do
+ resource.source = pfx_path
+ resource.pfx_password = password
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "raises an error when adding a PFX certificate with an invalid password" do
+ resource.source = pfx_path
+ resource.pfx_password = "Invalid password"
+
+ expect { resource.run_action(:create) }.to raise_error(OpenSSL::PKCS12::PKCS12Error)
+ end
+ end
+
+ describe "action: verify" do
+ it "fails with no certificates in the store" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ context "with a certificate in the store" do
+ before do
+ resource.source = cer_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with a valid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+
+ context "with a nested certificate in the store" do
+ before do
+ resource.source = p7b_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with the main certificate's thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = p7b_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "succeeds with the nested certificate's thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = p7b_nested_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "action: fetch" do
+ it "does nothing with no certificates in the store" do
+ expect(Chef::Log).not_to receive(:info)
+
+ resource.source = tests_thumbprint
+ resource.run_action(:fetch)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ context "with a certificate in the store" do
+ before do
+ resource.source = cer_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with a valid thumbprint" do
+ resource.source = tests_thumbprint
+
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, "test.pem")
+ expect(Chef::Log).to receive(:info).with("Certificate export in #{path}")
+
+ resource.cert_path = path
+ resource.run_action(:fetch)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).not_to receive(:info)
+
+ resource.source = others_thumbprint
+
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, "test.pem")
+
+ resource.cert_path = path
+ resource.run_action(:fetch)
+
+ expect(File.exist?(path)).to be_falsy
+ end
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "action: delete" do
+ it "does nothing when attempting to delete a certificate that doesn't exist" do
+ expect(Chef::Log).to receive(:debug).with("Certificate not found")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:delete)
+ end
+
+ it "deletes an existing certificate while leaving other certificates alone" do
+ # Add two certs
+ resource.source = cer_path
+ resource.run_action(:create)
+
+ resource.source = other_cer_path
+ resource.run_action(:create)
+
+ # Delete the first cert added
+ resource.source = tests_thumbprint
+ resource.run_action(:delete)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).to be_updated_by_last_action
+
+ resource.run_action(:delete)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Verify second cert still exists
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+ end
+ end
+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..bbcbf393e2
--- /dev/null
+++ b/spec/functional/resource/windows_env_spec.rb
@@ -0,0 +1,285 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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 example 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).to_s)
+ 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_firewall_rule_spec.rb b/spec/functional/resource/windows_firewall_rule_spec.rb
new file mode 100644
index 0000000000..86220c1b71
--- /dev/null
+++ b/spec/functional/resource/windows_firewall_rule_spec.rb
@@ -0,0 +1,93 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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::Resource::WindowsFirewallRule, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ let(:rule_name) { "fake_rule" }
+ let(:remote_port) { "5555" }
+ let(:enabled) { false }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsFirewallRule.new("test firewall rule", run_context)
+ new_resource.rule_name rule_name
+ new_resource.remote_port remote_port
+ new_resource.enabled enabled
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ context "create a new rule" do
+ after { delete_rule }
+
+ it "creates the rule" do
+ subject.run_action(:create)
+ expect(get_installed_rule_name).to eq(rule_name)
+ expect(get_installed_rule_remote_port).to eq(remote_port)
+ end
+
+ it "does not create rule if it already exists" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the rule if it changed" do
+ subject.run_action(:create)
+ subject.remote_port = "7777"
+ subject.run_action(:create)
+ expect(get_installed_rule_remote_port).to eq("7777")
+ end
+ end
+
+ context "delete a rule" do
+ it "deletes an existing rule" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ expect(get_installed_rule_name).to be_empty
+ end
+
+ it "does not delete rule if it does not exist" do
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def get_installed_rule_name
+ powershell_exec!("(Get-NetFirewallRule -Name #{rule_name} -ErrorAction SilentlyContinue).Name").result
+ end
+
+ def get_installed_rule_remote_port
+ powershell_exec!("((Get-NetFirewallRule -Name #{rule_name} -ErrorAction SilentlyContinue) | Get-NetFirewallPortFilter).RemotePort").result
+ end
+
+ def delete_rule
+ rule_to_remove = Chef::Resource::WindowsFirewallRule.new(rule_name, run_context)
+ rule_to_remove.run_action(:delete)
+ end
+end
diff --git a/spec/functional/resource/windows_font_spec.rb b/spec/functional/resource/windows_font_spec.rb
new file mode 100644
index 0000000000..e46e4aca17
--- /dev/null
+++ b/spec/functional/resource/windows_font_spec.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Dheeraj Singh Dubey (<ddubey@chef.io>)
+# Copyright:: Copyright (c) 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, :windows_only do
+ let(:resource_name) { "Playmaker.ttf" }
+ let(:resource_source) { "https://www.wfonts.com/download/data/2020/05/06/playmaker/Playmaker.ttf" }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ subject do
+ resource = Chef::Resource::WindowsFont.new(resource_name, run_context)
+ resource.source resource_source
+ resource
+ end
+
+ it "installs font on first install" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "does not install font when already installed" do
+ subject.run_action(:install)
+ expect(subject).not_to be_updated_by_last_action
+ end
+end
diff --git a/spec/functional/resource/windows_package_spec.rb b/spec/functional/resource/windows_package_spec.rb
index bc508dc526..5fed41e9ae 100644
--- a/spec/functional/resource/windows_package_spec.rb
+++ b/spec/functional/resource/windows_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
let(:pkg_name) { nil }
@@ -26,6 +25,10 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
let(:pkg_version) { nil }
let(:pkg_type) { nil }
let(:pkg_options) { nil }
+ let(:remote_file_attributes) { nil }
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
subject do
new_resource = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
@@ -34,13 +37,14 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
new_resource.installer_type pkg_type
new_resource.options pkg_options
new_resource.checksum pkg_checksum
+ new_resource.remote_file_attributes
new_resource
end
describe "install package" do
let(:pkg_name) { "Microsoft Visual C++ 2005 Redistributable" }
- let(:pkg_checksum) { "d6832398e3bc9156a660745f427dc1c2392ce4e9a872e04f41f62d0c6bae07a8" }
- let(:pkg_path) { "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe" }
+ let(:pkg_checksum) { "4ee4da0fe62d5fa1b5e80c6e6d88a4a2f8b3b140c35da51053d0d7b72a381d29" }
+ let(:pkg_path) { "https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x86.EXE" }
let(:pkg_checksum) { nil }
let(:pkg_type) { :custom }
let(:pkg_options) { "/Q" }
@@ -56,9 +60,9 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
end
context "installing additional version" do
- let(:pkg_path) { "https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe" }
- let(:pkg_checksum) { "eb00f891919d4f894ab725b158459db8834470c382dc60cd3c3ee2c6de6da92c" }
- let(:pkg_version) { "8.0.56336" }
+ let(:pkg_path) { "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe" }
+ let(:pkg_checksum) { "d6832398e3bc9156a660745f427dc1c2392ce4e9a872e04f41f62d0c6bae07a8" }
+ let(:pkg_version) { "8.0.59193" }
it "installs older version" do
subject.run_action(:install)
@@ -70,14 +74,14 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
subject { Chef::Resource::WindowsPackage.new(pkg_name, run_context) }
context "multiple versions and a version given to remove" do
- before { subject.version("8.0.56336") }
+ before { subject.version("8.0.59193") }
it "removes specified version" do
subject.run_action(:remove)
expect(subject).to be_updated_by_last_action
prov = subject.provider_for_action(:remove)
prov.load_current_resource
- expect(prov.current_version_array).to eq([["8.0.59193"]])
+ expect(prov.current_version_array).to eq([["8.0.61001"]])
end
end
@@ -102,8 +106,8 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
install1.run_action(:install)
install2 = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
- install2.source "https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe"
- install2.version "8.0.56336"
+ install2.source "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe"
+ install2.version "8.0.59193"
install2.installer_type pkg_type
install2.options pkg_options
install2.run_action(:install)
@@ -136,7 +140,7 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
context "inno" do
let(:pkg_name) { "Mercurial 3.6.1 (64-bit)" }
- let(:pkg_path) { "http://mercurial.selenic.com/release/windows/Mercurial-3.6.1-x64.exe" }
+ let(:pkg_path) { "https://www.mercurial-scm.org/release/windows/Mercurial-3.6.1-x64.exe" }
let(:pkg_checksum) { "febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d" }
it "finds the correct installer type" do
@@ -165,4 +169,25 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
expect(subject).to be_updated_by_last_action
end
end
+
+ describe "install package with remote_file_attributes" do
+ let(:pkg_name) { "7zip" }
+ let(:pkg_path) { "http://www.7-zip.org/a/7z938-x64.msi" }
+ let(:remote_file_attributes) {
+ {
+ path: ::File.join(Chef::Config[:file_cache_path], "7zip.msi"),
+ checksum: "7c8e873991c82ad9cfcdbdf45254ea6101e9a645e12977dcd518979e50fdedf3",
+ }
+ }
+
+ it "installs the package" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "uninstalls the package" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ 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..b2a3e5f5a4
--- /dev/null
+++ b/spec/functional/resource/windows_path_spec.rb
@@ -0,0 +1,68 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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" }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ 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_security_policy_spec.rb b/spec/functional/resource/windows_security_policy_spec.rb
new file mode 100644
index 0000000000..76764e01b0
--- /dev/null
+++ b/spec/functional/resource/windows_security_policy_spec.rb
@@ -0,0 +1,86 @@
+#
+# Author:: Ashwini Nehate (<anehate@chef.io>)
+# Copyright:: Copyright (c) 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::WindowsSecurityPolicy, :windows_only do
+ let(:secoption) { "MaximumPasswordAge" }
+ let(:secvalue) { "30" }
+ let(:windows_test_run_context) do
+ node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ 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
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsSecurityPolicy.new(secoption, windows_test_run_context)
+ new_resource.secoption = secoption
+ new_resource.secvalue = secvalue
+ new_resource
+ end
+
+ describe "Set MaximumPasswordAge Policy" do
+ after {
+ subject.secvalue("60")
+ subject.run_action(:set)
+ }
+
+ it "should set MaximumPasswordAge to 30" do
+ subject.secvalue("30")
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ subject.secvalue("30")
+ subject.run_action(:set)
+ guardscript_and_script_time = subject.elapsed_time
+ subject.run_action(:set)
+ only_guardscript_time = subject.elapsed_time
+ expect(only_guardscript_time).to be < guardscript_and_script_time
+ end
+ end
+
+ describe "secoption and id: " do
+ it "accepts 'MinimumPasswordAge', 'MinimumPasswordAge', 'MaximumPasswordAge', 'MinimumPasswordLength', 'PasswordComplexity', 'PasswordHistorySize', 'LockoutBadCount', 'RequireLogonToChangePassword', 'ForceLogoffWhenHourExpire', 'NewAdministratorName', 'NewGuestName', 'ClearTextPassword', 'LSAAnonymousNameLookup', 'EnableAdminAccount', 'EnableGuestAccount' " do
+ expect { subject.secoption("MinimumPasswordAge") }.not_to raise_error
+ expect { subject.secoption("MaximumPasswordAge") }.not_to raise_error
+ expect { subject.secoption("MinimumPasswordLength") }.not_to raise_error
+ expect { subject.secoption("PasswordComplexity") }.not_to raise_error
+ expect { subject.secoption("PasswordHistorySize") }.not_to raise_error
+ expect { subject.secoption("LockoutBadCount") }.not_to raise_error
+ expect { subject.secoption("RequireLogonToChangePassword") }.not_to raise_error
+ expect { subject.secoption("ForceLogoffWhenHourExpire") }.not_to raise_error
+ expect { subject.secoption("NewAdministratorName") }.not_to raise_error
+ expect { subject.secoption("NewGuestName") }.not_to raise_error
+ expect { subject.secoption("ClearTextPassword") }.not_to raise_error
+ expect { subject.secoption("LSAAnonymousNameLookup") }.not_to raise_error
+ expect { subject.secoption("EnableAdminAccount") }.not_to raise_error
+ expect { subject.secoption("EnableGuestAccount") }.not_to raise_error
+ end
+
+ it "rejects any other option" do
+ expect { subject.secoption "XYZ" }.to raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
index 531f9e9250..4c0c3acb58 100644
--- a/spec/functional/resource/windows_service_spec.rb
+++ b/spec/functional/resource/windows_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Chris Doherty (<cdoherty@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,13 +18,12 @@
require "spec_helper"
-describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only, :appveyor_only, :broken => true do
- # Marking as broken. This test is causing appveyor tests to exit with 116.
+describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do
include_context "using Win32::Service"
let(:username) { "service_spec_user" }
- let(:qualified_username) { "#{ENV['COMPUTERNAME']}\\#{username}" }
+ let(:qualified_username) { "#{ENV["COMPUTERNAME"]}\\#{username}" }
let(:password) { "1a2b3c4X!&narf" }
let(:user_resource) do
@@ -36,7 +35,7 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
end
let(:global_service_file_path) do
- "#{ENV['WINDIR']}\\temp\\#{File.basename(test_service[:service_file_path])}"
+ "#{ENV["WINDIR"]}\\temp\\#{File.basename(test_service[:service_file_path])}"
end
let(:service_params) do
@@ -59,10 +58,14 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
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]) }
+ %i{run_as_user run_as_password}.each { |prop| r.send(prop, service_params[prop]) }
r
end
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
before do
user_resource.run_action(:create)
diff --git a/spec/functional/resource/windows_share_spec.rb b/spec/functional/resource/windows_share_spec.rb
new file mode 100644
index 0000000000..6fae7d45d3
--- /dev/null
+++ b/spec/functional/resource/windows_share_spec.rb
@@ -0,0 +1,103 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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::Resource::WindowsShare, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ let(:share_name) { "fake_share" }
+ let(:path) { ENV["temp"] }
+ let(:concurrent_user_limit) { 7 }
+ let(:full_users) { ["#{ENV["USERNAME"]}"] }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default["hostname"] = ENV["COMPUTERNAME"]
+ Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsShare.new("test windows share", run_context)
+ new_resource.share_name share_name
+ new_resource.path path
+ new_resource.concurrent_user_limit concurrent_user_limit
+ new_resource.full_users full_users
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ context "create a new share" do
+ after { delete_share }
+
+ it "creates the share" do
+ subject.run_action(:create)
+ share = get_installed_share
+ expect(share["Name"]).to eq(share_name)
+ expect(share["Path"]).to eq(path)
+ expect(get_installed_share_access["AccountName"]).to eq("#{ENV["COMPUTERNAME"]}\\#{full_users[0]}")
+ end
+
+ it "does not create share if it already exists" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the share if it changed" do
+ subject.run_action(:create)
+ subject.concurrent_user_limit 8
+ subject.full_users ["BUILTIN\\Administrators"]
+ subject.run_action(:create)
+ share = get_installed_share
+ expect(share["ConcurrentUserLimit"]).to eq(8)
+ expect(get_installed_share_access["AccountName"]).to eq("BUILTIN\\Administrators")
+ end
+
+ end
+
+ context "delete a share" do
+ it "deletes an existing share" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ expect(get_installed_share).to be_empty
+ end
+
+ it "does not delete share if it does not exist" do
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def get_installed_share
+ powershell_exec!("Get-SmbShare -Name #{share_name} -ErrorAction SilentlyContinue").result
+ end
+
+ def get_installed_share_access
+ powershell_exec!("Get-SmbShareAccess -Name #{share_name} -ErrorAction SilentlyContinue").result
+ end
+
+ def delete_share
+ rule_to_remove = Chef::Resource::WindowsShare.new(share_name, run_context)
+ rule_to_remove.run_action(:delete)
+ end
+end
diff --git a/spec/functional/resource/windows_task_spec.rb b/spec/functional/resource/windows_task_spec.rb
new file mode 100644
index 0000000000..3affa625fd
--- /dev/null
+++ b/spec/functional/resource/windows_task_spec.rb
@@ -0,0 +1,1969 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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-utils/dist"
+
+describe Chef::Resource::WindowsTask, :windows_only do
+ # resource.task.application_name will default to task_name unless resource.command is set
+ let(:task_name) { "chef-client-functional-test" }
+ let(:new_resource) { Chef::Resource::WindowsTask.new(task_name, run_context) }
+ let(:windows_task_provider) do
+ new_resource.provider_for_action(:create)
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ describe "action :create" do
+ after { delete_task }
+ context "when command is with arguments" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ # Make sure MM/DD/YYYY is accepted
+
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ context "With Arguments" do
+ it "creates scheduled task and sets command arguments" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ 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(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to eq("-W")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ subject.run_action(:create)
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments when arguments inclusive single quotes" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ 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(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to eq("-W -L 'C:\\chef\\chef-ad-join.log'")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ subject.run_action(:create)
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments with spaces in command" do
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq('"C:\\Program Files\\example\\program.exe"')
+ expect(current_resource.task.parameters).to eq("-arg1 --arg2")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ subject.run_action(:create)
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments with spaces in arguments" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to eq('-file "C:\\Program Files\\app\\script.ps1"')
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments" do
+ subject.command "ping http://www.google.com"
+ 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("ping")
+ expect(current_resource.task.parameters).to eq("http://www.google.com")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "ping http://www.google.com"
+ subject.run_action(:create)
+ subject.command "ping http://www.google.com"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "Without Arguments" do
+ it "creates scheduled task and sets command arguments" do
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ 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(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to be_empty
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ subject.run_action(:create)
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ it "creates scheduled task and Re-sets command arguments" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to eq('-file "C:\\Program Files\\app\\script.ps1"')
+
+ subject.command "powershell.exe"
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to be_empty
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ context "when description is passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource.command task_name
+ # Make sure MM/DD/YYYY is accepted
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ let(:some_description) { "this is test description" }
+
+ it "create the task and sets its description" do
+ subject.description some_description
+ 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.description).to eq(some_description)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.description some_description
+ subject.run_action(:create)
+ subject.description some_description
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates task with new description if task already exist" do
+ subject.description some_description
+ subject.run_action(:create)
+ subject.description "test description"
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ 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 accepts 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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 modifier 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 modifier 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(task_name)
+ 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 accepts 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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 accepts 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 separated 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
+ 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
+ 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 third week of month" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error
+ 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(task_name)
+ 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
+ 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(task_name)
+ 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 accepts 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
+ 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(task_name)
+ 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
+ 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(task_name)
+ 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
+ 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(task_name)
+ 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
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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(task_name)
+ 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 accepts 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(task_name)
+ 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 "when battery options are 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 accepts this
+ new_resource
+ end
+
+ it "sets the default if options are not provided" do
+ subject.frequency :minute
+ 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)
+ expect(current_resource.stop_if_going_on_batteries).to eql(false)
+ expect(current_resource.disallow_start_if_on_batteries).to eql(false)
+ end
+
+ it "sets disallow_start_if_on_batteries to true" do
+ subject.frequency :minute
+ subject.disallow_start_if_on_batteries true
+ 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)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(true)
+ end
+
+ it "sets disallow_start_if_on_batteries to false" do
+ subject.frequency :minute
+ subject.disallow_start_if_on_batteries false
+ 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)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(false)
+ end
+
+ it "sets stop_if_going_on_batteries to true" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ 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.settings[:stop_if_going_on_batteries]).to eql(true)
+ end
+
+ it "sets stop_if_going_on_batteries to false" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries false
+ 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)
+ expect(current_resource.task.settings[:stop_if_going_on_batteries]).to eql(false)
+ end
+
+ it "sets the default if options are nil" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries nil
+ subject.disallow_start_if_on_batteries nil
+ 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)
+ expect(current_resource.task.settings[:stop_if_going_on_batteries]).to eql(false)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(false)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ 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 accepts 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(task_name)
+ 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
+
+ context "when start_when_available 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 accepts this
+ new_resource
+ end
+
+ it "sets start_when_available to true" do
+ subject.frequency :minute
+ subject.start_when_available true
+ 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.settings[:start_when_available]).to eql(true)
+ end
+
+ it "sets start_when_available to false" do
+ subject.frequency :minute
+ subject.start_when_available false
+ 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)
+ expect(current_resource.task.settings[:start_when_available]).to eql(false)
+ end
+
+ it "sets the default if start_when_available is nil" do
+ subject.frequency :minute
+ subject.start_when_available nil
+ 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)
+ expect(current_resource.task.settings[:start_when_available]).to eql(false)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.start_when_available true
+ subject.run_action(:create)
+ subject.frequency :minute
+ subject.start_when_available true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "task_name with parent folder" do
+ describe "task_name with path '\\foo\\chef-client-functional-test' " do
+ let(:task_name) { "\\foo\\chef-client-functional-test" }
+ after { delete_task }
+ 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 accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task with task name 'chef-client-functional-test' inside path '\\foo'" do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(task_name)
+ 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 "task_name with path '\\foo\\bar\\chef-client-functional-test' " do
+ let(:task_name) { "\\foo\\bar\\chef-client-functional-test" }
+ after { delete_task }
+ 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 accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task with task with name 'chef-client-functional-test' inside path '\\foo\\bar' " do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(task_name)
+ 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 "priority" do
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "default sets to 7" do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_7")
+ end
+
+ it "0 sets priority level to critical" do
+ subject.priority = 0
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("critical")
+ end
+
+ it "2 sets priority level to highest" do
+ subject.priority = 1
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("highest")
+ end
+
+ it "2 sets priority level to above_normal" do
+ subject.priority = 2
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("above_normal_2")
+ end
+
+ it "3 sets priority level to above_normal" do
+ subject.priority = 3
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("above_normal_3")
+ end
+
+ it "4 sets priority level to normal" do
+ subject.priority = 4
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_4")
+ end
+
+ it "5 sets priority level to normal" do
+ subject.priority = 5
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_5")
+ end
+
+ it "6 sets priority level to normal" do
+ subject.priority = 6
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_6")
+ end
+
+ it "7 sets priority level to below_normal" do
+ subject.priority = 7
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_7")
+ end
+
+ it "8 sets priority level to below_normal" do
+ subject.priority = 8
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_8")
+ end
+
+ it "9 sets priority level to lowest" do
+ subject.priority = 9
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("lowest")
+ end
+
+ it "10 sets priority level to idle" do
+ subject.priority = 10
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("idle")
+ end
+
+ it "is idempotent" do
+ subject.priority 8
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ 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 accepts 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 accepts 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 accepts 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 accepts 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 accepts 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 accepts 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 accepts 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 accepts 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 accepts this
+ new_resource
+ end
+
+ context "when start_day is passed with frequency :onstart" do
+ it "does 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 "USER"
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error(%q{Please provide a password or check if this task needs to be interactive! Valid passwordless users are: 'SYSTEM', 'NT AUTHORITY\SYSTEM', 'LOCAL SERVICE', 'NT AUTHORITY\LOCAL SERVICE', 'NETWORK SERVICE', 'NT AUTHORITY\NETWORK SERVICE', 'ADMINISTRATORS', 'BUILTIN\ADMINISTRATORS', 'USERS', 'BUILTIN\USERS', 'GUESTS', 'BUILTIN\GUESTS'})
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "USER"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a system user is passed without password" do
+ it "does not raises error" do
+ subject.user "ADMINISTRATORS"
+ subject.frequency :onstart
+ expect { subject.after_created }.not_to raise_error
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "ADMINISTRATORS"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a non system user is passed with password" do
+ it "does not raises error" do
+ subject.user "USER"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ expect { subject.after_created }.not_to raise_error
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "USER"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a system user is passed with password" do
+ it "raises error" do
+ subject.user "ADMINISTRATORS"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error("Password is not required for system users.")
+ end
+ it "raises error when task is interactive" do
+ subject.user "ADMINISTRATORS"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.to raise_error("Password is not required for system users.")
+ 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 accepts 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
+ let(:task_name) { "chef-client-functional-test-enable" }
+ 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
+ let(:task_name) { "chef-client-functional-test-disable" }
+ 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/windows_user_privilege_spec.rb b/spec/functional/resource/windows_user_privilege_spec.rb
new file mode 100644
index 0000000000..f52bbbe23b
--- /dev/null
+++ b/spec/functional/resource/windows_user_privilege_spec.rb
@@ -0,0 +1,192 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@chef.io>)
+# Copyright:: Copyright (c) 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 "spec_helper"
+
+describe Chef::Resource::WindowsUserPrivilege, :windows_only do
+ let(:principal) { nil }
+ let(:privilege) { nil }
+ let(:users) { nil }
+ let(:sensitive) { true }
+
+ let(:windows_test_run_context) do
+ node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ 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
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsUserPrivilege.new(principal, windows_test_run_context)
+ new_resource.privilege = privilege
+ new_resource.principal = principal
+ new_resource.users = users
+ new_resource
+ end
+
+ describe "#add privilege" do
+ after { subject.run_action(:remove) }
+
+ context "when privilege is passed as string" do
+ let(:principal) { "Administrator" }
+ let(:privilege) { "SeCreateSymbolicLinkPrivilege" }
+
+ it "adds user to privilege" do
+ # Removing so that add update happens
+ subject.run_action(:remove)
+ subject.run_action(:add)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:add)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when privilege is passed as array" do
+ let(:principal) { "Administrator" }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege SeCreatePagefilePrivilege} }
+
+ it "adds user to privilege" do
+ subject.run_action(:add)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:add)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "#set privilege" do
+ after { remove_user_privilege("Administrator", subject.privilege) }
+
+ let(:principal) { "user_privilege" }
+ let(:users) { %w{Administrators Administrator} }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege} }
+
+ it "sets user to privilege" do
+ subject.action(:set)
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.action(:set)
+ subject.run_action(:set)
+ subject.run_action(:set)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "raise error if users not provided" do
+ subject.users = nil
+ subject.action(:set)
+ expect { subject.run_action(:set) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
+ describe "#remove privilege" do
+ let(:principal) { "Administrator" }
+ context "when privilege is passed as array" do
+ let(:privilege) { "SeCreateSymbolicLinkPrivilege" }
+ it "remove user from privilege" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ subject.run_action(:remove)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when privilege is passed as array" do
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege SeCreatePagefilePrivilege} }
+ it "remove user from privilege" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ subject.run_action(:remove)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "running with non admin user" do
+ include Chef::Mixin::UserContext
+
+ let(:user) { "security_user" }
+ let(:password) { "Security@123" }
+ let(:principal) { "user_privilege" }
+ let(:users) { ["Administrators", "#{domain}\\security_user"] }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege} }
+
+ let(:domain) do
+ ENV["COMPUTERNAME"]
+ 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
+ remove_user_privilege("#{domain}\\#{user}", subject.privilege)
+ delete_user = Mixlib::ShellOut.new("net user #{user} /delete")
+ delete_user.run_command
+ delete_user.error!
+ end
+
+ it "sets user to privilege" do
+ subject.action(:set)
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.action(:set)
+ subject.run_action(:set)
+ subject.run_action(:set)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def remove_user_privilege(user, privilege)
+ subject.action(:remove)
+ subject.principal = user
+ subject.privilege = privilege
+ subject.run_action(:remove)
+ 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..5f902cff17
--- /dev/null
+++ b/spec/functional/resource/yum_package_spec.rb
@@ -0,0 +1,981 @@
+#
+# Copyright:: Copyright (c) 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/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora amazon}.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(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ 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).to_s)
+ 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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 is ignored when allow_downgrade is false" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade false
+ yum_package.version "1.2-1"
+ 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 "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
+
+ it "is idempotent when the package is already installed and there is a version string" do
+ preinstall("chef_rpm-1.2-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")
+ 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 and there is a version string with arch" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1.#{pkg_arch}"
+ 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" 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 need 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" 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" 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" 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" 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" 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" 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" 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" 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/resource/zypper_package_spec.rb b/spec/functional/resource/zypper_package_spec.rb
new file mode 100644
index 0000000000..ce6a3bf33c
--- /dev/null
+++ b/spec/functional/resource/zypper_package_spec.rb
@@ -0,0 +1,247 @@
+#
+# Author:: Dheeraj Dubey (<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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/shell_out"
+
+describe Chef::Resource::ZypperPackage, :requires_root, :suse_only do
+ include Chef::Mixin::ShellOut
+
+ # NOTE: every single test here needs to explicitly call preinstall.
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/zypprepo/#{rpm}")
+ end
+ end
+
+ before(:each) do
+ File.open("/etc/zypp/repos.d/chef-zypp-localtesting.repo", "w+") do |f|
+ f.write <<~EOF
+ [chef-zypp-localtesting]
+ name=Chef zypper spec testing repo
+ baseurl=file://#{CHEF_SPEC_ASSETS}/zypprepo
+ enable=1
+ gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm | xargs -r rpm -e")
+ # next line is useful cleanup if you happen to have been testing zypper func tests on the same box and
+ # have some zypper garbage left around
+ # FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:zypper_package) do
+ r = Chef::Resource::ZypperPackage.new(package_name, run_context)
+ r.global_options("--no-gpg-checks")
+ r
+ end
+
+ def pkg_arch
+ ohai[:kernel][:machine]
+ end
+
+ context "installing a package" do
+ after { remove_package }
+ it "installs the latest version" do
+ zypper_package.run_action(:install)
+ expect(zypper_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")
+ zypper_package.run_action(:install)
+ expect(zypper_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
+ zypper_package.run_action(:install)
+ expect(zypper_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}$")
+ zypper_package.run_action(:install)
+ expect(zypper_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")
+ zypper_package.run_action(:install)
+ expect(zypper_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 "multipackage installs which result in nils from the superclass" do
+ # this looks weird, it tests an internal condition of the allow_nils behavior where the arrays passed to install_package will have
+ # nil values, and ensures that doesn't wind up creating weirdness in the resulting shell_out that causes it to fail
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.package_name(%w{chef_rpm chef_rpm})
+ zypper_package.version(["1.2", "1.10"])
+ zypper_package.run_action(:install)
+ expect(zypper_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 versions" do
+ it "works with a version" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.10")
+ zypper_package.run_action(:install)
+ expect(zypper_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
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2")
+ zypper_package.run_action(:install)
+ expect(zypper_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
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2-1")
+ zypper_package.run_action(:install)
+ expect(zypper_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 when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ zypper_package.allow_downgrade true
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2-1")
+ zypper_package.run_action(:install)
+ expect(zypper_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
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ zypper_package.run_action(:remove)
+ expect(zypper_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")
+ zypper_package.run_action(:remove)
+ expect(zypper_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")
+ zypper_package.run_action(:remove)
+ expect(zypper_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$")
+ zypper_package.run_action(:remove)
+ expect(zypper_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")
+ zypper_package.run_action(:remove)
+ expect(zypper_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 no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:remove)
+ expect(zypper_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(:each) do
+ shell_out("zypper removelock chef_rpm") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).to match("chef_rpm")
+ end
+
+ it "does not lock if its already locked" do
+ shell_out!("zypper addlock chef_rpm")
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).to match("chef_rpm")
+ end
+
+ it "unlocks an rpm" do
+ shell_out!("zypper addlock chef_rpm")
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).not_to match("chef_rpm")
+ end
+
+ it "does not unlock an already locked rpm" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).not_to match("chef_rpm")
+ end
+
+ it "check that we can lock based on provides" do
+ zypper_package.package_name("chef_rpm_provides")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm_provides").stdout.chomp).to match("chef_rpm_provides")
+ end
+
+ it "check that we can unlock based on provides" do
+ shell_out!("zypper addlock chef_rpm_provides")
+ zypper_package.package_name("chef_rpm_provides")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm_provides").stdout.chomp).not_to match("chef_rpm_provides")
+ end
+ end
+ def remove_package
+ pkg_to_remove = Chef::Resource::ZypperPackage.new(package_name, run_context)
+ pkg_to_remove.run_action(:remove)
+ end
+end
diff --git a/spec/functional/rest_spec.rb b/spec/functional/rest_spec.rb
deleted file mode 100644
index 14e76087c4..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(:each) do
- start_tiny_server
- end
-
- after(:each) 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 d270803698..a7440638d7 100644
--- a/spec/functional/run_lock_spec.rb
+++ b/spec/functional/run_lock_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,13 +15,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
require "chef/client"
describe Chef::RunLock do
# This behavior works on windows, but the tests use fork :(
- describe "when locking the chef-client run", :unix_only => true do
+ describe "when locking the chef-client run", unix_only: true do
##
# Lockfile location and helpers
@@ -61,17 +61,17 @@ describe Chef::RunLock do
let!(:p1) { ClientProcess.new(self, "p1") }
let!(:p2) { ClientProcess.new(self, "p2") }
after(:each) do |example|
- begin
- p1.stop
- p2.stop
- rescue
- example.exception = $!
- raise
- ensure
- if example.exception
- print_events
- end
+
+ p1.stop
+ p2.stop
+ rescue
+ example.exception = $!
+ raise
+ ensure
+ if example.exception
+ print_events
end
+
end
def print_events
@@ -150,7 +150,7 @@ describe Chef::RunLock do
end
end
- it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
+ it "sets FD_CLOEXEC on the lockfile", supports_cloexec: true do
run_lock = File.open(lockfile)
expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
end
@@ -197,7 +197,7 @@ describe Chef::RunLock do
end
end
- it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
+ it "sets FD_CLOEXEC on the lockfile", supports_cloexec: true do
run_lock = File.open(lockfile)
expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
end
@@ -334,6 +334,7 @@ describe Chef::RunLock do
loop do
line = readline_nonblock(read_from_process)
break if line.nil?
+
event, time = line.split("@")
example.log_event("#{name}.last_event got #{event}")
example.log_event("[#{name}] #{event}", time.strip)
@@ -346,7 +347,7 @@ describe Chef::RunLock do
example.log_event("#{name}.run_to(#{to_event.inspect})")
# Start the process if it's not started
- start if !pid
+ start unless pid
# Tell the process what to stop at (also means it can go)
write_to_process.print "#{to_event}\n"
@@ -370,7 +371,7 @@ describe Chef::RunLock do
def run_to_completion
example.log_event("#{name}.run_to_completion")
# Start the process if it's not started
- start if !pid
+ start unless pid
# Tell the process to stop at nothing (no blocking)
@write_to_process.print "nothing\n"
@@ -403,6 +404,12 @@ describe Chef::RunLock do
example.log_event("#{name}.stop finished (pid #{pid} wasn't running)")
end
end
+
+ # close the IO.pipes so we don't leak them as open filehandles
+ @read_from_process.close rescue nil
+ @write_to_tests.close rescue nil
+ @read_from_tests.close rescue nil
+ @write_to_process.close rescue nil
end
def fire_event(event)
@@ -428,6 +435,7 @@ describe Chef::RunLock do
class TestRunLock < Chef::RunLock
attr_accessor :client_process
+
def create_lock
super
client_process.fire_event("created lock")
@@ -437,21 +445,21 @@ describe Chef::RunLock do
def start
example.log_event("#{name}.start")
@pid = fork do
- begin
- Timeout.timeout(CLIENT_PROCESS_TIMEOUT) do
- run_lock = TestRunLock.new(example.lockfile)
- run_lock.client_process = self
- fire_event("started")
- run_lock.acquire
- fire_event("acquired lock")
- run_lock.save_pid
- fire_event("saved pid")
- exit!(0)
- end
- rescue
- fire_event($!.message.lines.join(" // "))
- raise
+
+ Timeout.timeout(CLIENT_PROCESS_TIMEOUT) do
+ run_lock = TestRunLock.new(example.lockfile)
+ run_lock.client_process = self
+ fire_event("started")
+ run_lock.acquire
+ fire_event("acquired lock")
+ run_lock.save_pid
+ fire_event("saved pid")
+ exit!(0)
end
+ rescue
+ fire_event($!.message.lines.join(" // "))
+ raise
+
end
example.log_event("#{name}.start forked (pid #{pid})")
end
@@ -461,7 +469,7 @@ describe Chef::RunLock do
buffer << fd.read_nonblock(1) while buffer[-1] != "\n"
buffer
- #rescue IO::EAGAINUnreadable
+ # rescue IO::EAGAINUnreadable
rescue IO::WaitReadable
unless buffer == ""
sleep 0.1
diff --git a/spec/functional/shell_spec.rb b/spec/functional/shell_spec.rb
index 636162fb16..2289df3ef1 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,18 +17,15 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/version"
require "chef/shell"
-require "chef/mixin/command/unix"
describe Shell do
# chef-shell's unit tests are by necessity very mock-heavy, and frequently 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
+ describe "smoke tests", unix_only: true do
TIMEOUT = 300
@@ -79,53 +76,34 @@ 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)
+ reader, writer, pid = PTY.spawn("bundle exec chef-shell --no-multiline --no-singleline --no-colorize -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
+ it "boots correctly with -lauto", :executables do
output, exitstatus = run_chef_shell_with("-lauto")
expect(output).to include("done")
expect(exitstatus).to eq(0)
end
- it "sets the log_level from the command line" do
+ it "sets the log_level from the command line", :executables do
output, exitstatus = run_chef_shell_with("-lfatal") do |out, keyboard|
show_log_level_code = %q[puts "===#{Chef::Log.level}==="]
keyboard.puts(show_log_level_code)
@@ -135,7 +113,25 @@ describe Shell do
expect(exitstatus).to eq(0)
end
- it "sets the override_runlist from the command line" do
+ context "on solo mode" do
+ it "starts correctly", :executables 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", :executables 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", :executables do
output, exitstatus = run_chef_shell_with("-o 'override::foo,override::bar'") do |out, keyboard|
show_recipes_code = %q[puts "#{node["recipes"].inspect}"]
keyboard.puts(show_recipes_code)
diff --git a/spec/functional/tiny_server_spec.rb b/spec/functional/tiny_server_spec.rb
index 1ec56bd490..00393808b3 100644
--- a/spec/functional/tiny_server_spec.rb
+++ b/spec/functional/tiny_server_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/util/path_helper_spec.rb b/spec/functional/util/path_helper_spec.rb
index fd484adff2..e16fc554dc 100644
--- a/spec/functional/util/path_helper_spec.rb
+++ b/spec/functional/util/path_helper_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/functional/util/powershell/cmdlet_spec.rb b/spec/functional/util/powershell/cmdlet_spec.rb
deleted file mode 100644
index 19f5e58a49..0000000000
--- a/spec/functional/util/powershell/cmdlet_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright 2014-2016, 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/json_compat"
-require File.expand_path("../../../../spec_helper", __FILE__)
-
-describe Chef::Util::Powershell::Cmdlet, :windows_powershell_dsc_only do
- before(:all) do
- @node = Chef::Node.new
- @node.consume_external_attrs(OHAI_SYSTEM.data, {})
- end
- let(:cmd_output_format) { :text }
- let(:simple_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "get-childitem", cmd_output_format, { :depth => 2 }) }
- let(:invalid_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "get-idontexist", cmd_output_format) }
- let(:cmdlet_get_item_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, "get-item", cmd_output_format, { :depth => 2 }) }
- let(:cmdlet_alias_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, "alias", cmd_output_format, { :depth => 2 }) }
- let(:etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" }
- let(:architecture_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "$env:PROCESSOR_ARCHITECTURE") }
-
- it "executes a simple process" do
- result = simple_cmdlet.run
- expect(result.succeeded?).to eq(true)
- end
-
- it "#run does not raise a PowershellCmdletException exception if the command cannot be executed" do
- expect { invalid_cmdlet.run }.not_to raise_error
- end
-
- it "#run! raises a PowershellCmdletException exception if the command cannot be executed" do
- expect { invalid_cmdlet.run! }.to raise_error(Chef::Exceptions::PowershellCmdletException)
- end
-
- it "executes a 64-bit command on a 64-bit OS, 32-bit otherwise" do
- os_arch = ENV["PROCESSOR_ARCHITEW6432"]
- if os_arch.nil?
- os_arch = ENV["PROCESSOR_ARCHITECTURE"]
- end
-
- result = architecture_cmdlet.run
- execution_arch = result.return_value
- execution_arch.strip!
- expect(execution_arch).to eq(os_arch)
- end
-
- it "passes command line switches to the command" do
- result = cmdlet_alias_requires_switch_or_argument.run({ :name => "ls" })
- expect(result.succeeded?).to eq(true)
- end
-
- it "passes command line arguments to the command" do
- result = cmdlet_alias_requires_switch_or_argument.run({}, {}, "ls")
- expect(result.succeeded?).to eq(true)
- end
-
- it "passes command line arguments and switches to the command" do
- result = cmdlet_get_item_requires_switch_or_argument.run({ :path => etc_directory }, {}, " | select-object -property fullname | format-table -hidetableheaders")
- expect(result.succeeded?).to eq(true)
- returned_directory = result.return_value
- returned_directory.strip!
- expect(returned_directory).to eq(etc_directory)
- end
-
- it "passes execution options to the command" do
- result = cmdlet_get_item_requires_switch_or_argument.run({}, { :cwd => etc_directory }, ". | select-object -property fullname | format-table -hidetableheaders")
- expect(result.succeeded?).to eq(true)
- returned_directory = result.return_value
- returned_directory.strip!
- expect(returned_directory).to eq(etc_directory)
- end
-
- context "when returning json" do
- let(:cmd_output_format) { :json }
- it "returns json format data" do
- result = cmdlet_alias_requires_switch_or_argument.run({}, {}, "ls")
- expect(result.succeeded?).to eq(true)
- expect(lambda { Chef::JSONCompat.parse(result.return_value) }).not_to raise_error
- end
- end
-
- context "when returning Ruby objects" do
- let(:cmd_output_format) { :object }
- it "returns object format data" do
- result = simple_cmdlet.run({}, { :cwd => etc_directory }, "hosts")
- expect(result.succeeded?).to eq(true)
- data = result.return_value
- expect(data["Name"]).to eq("hosts")
- end
- end
-
- context "when constructor is given invalid arguments" do
- let(:cmd_output_format) { :invalid }
- it "throws an exception if an invalid format is passed to the constructor" do
- expect(lambda { simple_cmdlet }).to raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/functional/version_spec.rb b/spec/functional/version_spec.rb
index a45c25ff8c..5d0f0fce43 100644
--- a/spec/functional/version_spec.rb
+++ b/spec/functional/version_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,20 +15,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
require "chef/mixin/shell_out"
require "chef/version"
require "ohai/version"
+require "chef-utils/dist"
-describe "Chef Versions" do
+describe "Chef Versions", :executables do
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..") }
+ let(:chef_dir) { File.join(__dir__, "..", "..") }
- binaries = [ "chef-client", "chef-shell", "chef-apply", "knife", "chef-solo" ]
+ binaries = [ ChefUtils::Dist::Infra::CLIENT, "chef-shell", "chef-apply", "knife", ChefUtils::Dist::Solo::EXEC ]
binaries.each do |binary|
it "#{binary} version should be sane" do
- expect(shell_out!("ruby #{File.join("bin", binary)} -v", :cwd => chef_dir).stdout.chomp).to include("Chef: #{Chef::VERSION}")
+ expect(shell_out!("bundle exec #{binary} -v", cwd: chef_dir).stdout.chomp).to match(/.*: #{Chef::VERSION}/)
end
end
diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb
index 145c9881b9..b0f2d3af5d 100644
--- a/spec/functional/win32/crypto_spec.rb
+++ b/spec/functional/win32/crypto_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala(<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.windows?
require "chef/win32/crypto"
end
@@ -32,20 +32,20 @@ describe "Chef::ReservedNames::Win32::Crypto", :windows_only do
@run_context = Chef::RunContext.new(new_node, {}, events)
end
- let (:plaintext) { "p@assword" }
+ let(:plaintext) { "p@assword" }
it "can be decrypted by powershell" do
encrypted = Chef::ReservedNames::Win32::Crypto.encrypt(plaintext)
- resource = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
- resource.code <<-EOF
-$encrypted = '#{encrypted}' | ConvertTo-SecureString
-$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted)
-$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
-if ($plaintext -ne '#{plaintext}') {
- Write-Error 'Got: ' $plaintext
- exit 1
-}
-exit 0
+ resource = Chef::Resource::WindowsScript::PowershellScript.new("PowerShell resource functional test", @run_context)
+ resource.code <<~EOF
+ $encrypted = '#{encrypted}' | ConvertTo-SecureString
+ $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted)
+ $plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
+ if ($plaintext -ne '#{plaintext}') {
+ Write-Error 'Got: ' $plaintext
+ exit 1
+ }
+ exit 0
EOF
resource.returns(0)
resource.run_action(:run)
diff --git a/spec/functional/win32/registry_spec.rb b/spec/functional/win32/registry_spec.rb
index bcfa0ffd48..2b371ee9ed 100644
--- a/spec/functional/win32/registry_spec.rb
+++ b/spec/functional/win32/registry_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,7 @@ require "chef/win32/registry"
describe "Chef::Win32::Registry", :windows_only do
before(:all) do
- #Create a registry item
+ # 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"
@@ -39,29 +39,23 @@ describe "Chef::Win32::Registry", :windows_only do
reg["Petals", Win32::Registry::REG_MULTI_SZ] = %w{Pink Delicate}
end
- #Create the node with ohai data
+ # Create the node with ohai data
events = Chef::EventDispatch::Dispatcher.new
@node = Chef::Node.new
@node.consume_external_attrs(OHAI_SYSTEM.data, {})
@run_context = Chef::RunContext.new(@node, {}, events)
- #Create a registry object that has access ot the node previously created
+ # Create a registry object that has access ot the node previously created
@registry = Chef::Win32::Registry.new(@run_context)
end
- #Delete what is left of the registry key-values previously created
+ # Delete what is left of the registry key-values previously created
after(:all) do
::Win32::Registry::HKEY_CURRENT_USER.open("Software") do |reg|
reg.delete_key("Root", true)
end
end
- # Server Versions
- # it "succeeds if server versiion is 2003R2, 2008, 2008R2, 2012" do
- # end
- # it "falis if the server versions are anything else" do
- # end
-
describe "hive_exists?" do
it "returns true if the hive exists" do
expect(@registry.hive_exists?("HKCU\\Software\\Root")).to eq(true)
@@ -102,85 +96,85 @@ describe "Chef::Win32::Registry", :windows_only do
describe "value_exists?" do
it "throws an exception if the hive does not exist" do
- expect { @registry.value_exists?("JKLM\\Software\\Branch\\Flower", { :name => "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.value_exists?("JKLM\\Software\\Branch\\Flower", { name: "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
it "throws an exception if the key does not exist" do
- expect { @registry.value_exists?("HKCU\\Software\\Branch\\Flower", { :name => "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.value_exists?("HKCU\\Software\\Branch\\Flower", { name: "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "returns true if the value exists" do
- expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals" })).to eq(true)
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals" })).to eq(true)
end
it "returns true if the value exists with a case mismatch on the value name" do
- expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "petals" })).to eq(true)
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "petals" })).to eq(true)
end
it "returns false if the value does not exist" do
- expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "FOOBAR" })).to eq(false)
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "FOOBAR" })).to eq(false)
end
end
describe "value_exists!" do
it "throws an exception if the hive does not exist" do
- expect { @registry.value_exists!("JKLM\\Software\\Branch\\Flower", { :name => "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.value_exists!("JKLM\\Software\\Branch\\Flower", { name: "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
it "throws an exception if the key does not exist" do
- expect { @registry.value_exists!("HKCU\\Software\\Branch\\Flower", { :name => "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.value_exists!("HKCU\\Software\\Branch\\Flower", { name: "Petals" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "returns true if the value exists" do
- expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals" })).to eq(true)
+ expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals" })).to eq(true)
end
it "returns true if the value exists with a case mismatch on the value name" do
- expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "petals" })).to eq(true)
+ expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "petals" })).to eq(true)
end
it "throws an exception if the value does not exist" do
- expect { @registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "FOOBAR" }) }.to raise_error(Chef::Exceptions::Win32RegValueMissing)
+ expect { @registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "FOOBAR" }) }.to raise_error(Chef::Exceptions::Win32RegValueMissing)
end
end
describe "data_exists?" do
it "throws an exception if the hive does not exist" do
- expect { @registry.data_exists?("JKLM\\Software\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.data_exists?("JKLM\\Software\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
it "throws an exception if the key does not exist" do
- expect { @registry.data_exists?("HKCU\\Software\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.data_exists?("HKCU\\Software\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "returns true if all the data matches" do
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} })).to eq(true)
end
it "returns true if all the data matches with a case mismatch on the data name" do
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "petals", :type => :multi_string, :data => %w{Pink Delicate} })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "petals", type: :multi_string, data: %w{Pink Delicate} })).to eq(true)
end
it "returns false if the name does not exist" do
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "slateP", :type => :multi_string, :data => %w{Pink Delicate} })).to eq(false)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "slateP", type: :multi_string, data: %w{Pink Delicate} })).to eq(false)
end
it "returns false if the types do not match" do
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :string, :data => "Pink" })).to eq(false)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :string, data: "Pink" })).to eq(false)
end
it "returns false if the data does not match" do
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Mauve Delicate} })).to eq(false)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Mauve Delicate} })).to eq(false)
end
end
describe "data_exists!" do
it "throws an exception if the hive does not exist" do
- expect { @registry.data_exists!("JKLM\\Software\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.data_exists!("JKLM\\Software\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
it "throws an exception if the key does not exist" do
- expect { @registry.data_exists!("HKCU\\Software\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.data_exists!("HKCU\\Software\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "returns true if all the data matches" do
- expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Pink Delicate} })).to eq(true)
+ expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Pink Delicate} })).to eq(true)
end
it "returns true if all the data matches with a case mismatch on the data name" do
- expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "petals", :type => :multi_string, :data => %w{Pink Delicate} })).to eq(true)
+ expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "petals", type: :multi_string, data: %w{Pink Delicate} })).to eq(true)
end
it "throws an exception if the name does not exist" do
- expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "slateP", :type => :multi_string, :data => %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
+ expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "slateP", type: :multi_string, data: %w{Pink Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
end
it "throws an exception if the types do not match" do
- expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :string, :data => "Pink" }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
+ expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :string, data: "Pink" }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
end
it "throws an exception if the data does not match" do
- expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => %w{Mauve Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
+ expect { @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: %w{Mauve Delicate} }) }.to raise_error(Chef::Exceptions::Win32RegDataMissing)
end
end
@@ -188,8 +182,8 @@ describe "Chef::Win32::Registry", :windows_only do
it "returns all values for a key if it exists" do
values = @registry.get_values("HKCU\\Software\\Root")
expect(values).to be_an_instance_of Array
- expect(values).to eq([{ :name => "RootType1", :type => :string, :data => "fibrous" },
- { :name => "Roots", :type => :multi_string, :data => ["strong roots", "healthy tree"] }])
+ expect(values).to eq([{ name: "RootType1", type: :string, data: "fibrous" },
+ { name: "Roots", type: :multi_string, data: ["strong roots", "healthy tree"] }])
end
it "throws an exception if the key does not exist" do
@@ -203,80 +197,80 @@ describe "Chef::Win32::Registry", :windows_only do
describe "set_value" do
it "updates a value if the key, value exist and type matches and value different" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(true)
end
it "updates a value if the type does match and the values are different" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :string, :data => "Yellow" })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :string, :data => "Yellow" })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(false)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :string, data: "Yellow" })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :string, data: "Yellow" })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(false)
end
it "creates a value if key exists and value does not" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Stamen", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Stamen", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "Stamen", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Stamen", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(true)
end
it "does nothing if data,type and name parameters for the value are same" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Stamen", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(false)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "Stamen", :type => :multi_string, :data => ["Yellow", "Changed Color"] })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "Stamen", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(false)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "Stamen", type: :multi_string, data: ["Yellow", "Changed Color"] })).to eq(true)
end
it "throws an exception if the key does not exist" do
- expect { @registry.set_value("HKCU\\Software\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => ["Yellow", "Changed Color"] }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.set_value("HKCU\\Software\\Branch\\Flower", { name: "Petals", type: :multi_string, data: ["Yellow", "Changed Color"] }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "throws an exception if the hive does not exist" do
- expect { @registry.set_value("JKLM\\Software\\Root\\Branch\\Flower", { :name => "Petals", :type => :multi_string, :data => ["Yellow", "Changed Color"] }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.set_value("JKLM\\Software\\Root\\Branch\\Flower", { name: "Petals", type: :multi_string, data: ["Yellow", "Changed Color"] }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
# we are validating that the data gets .to_i called on it when type is a :dword
it "casts an integer string given as a dword into an integer" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBe32767", :type => :dword, :data => "32767" })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBe32767", :type => :dword, :data => 32767 })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBe32767", type: :dword, data: "32767" })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBe32767", type: :dword, data: 32767 })).to eq(true)
end
it "casts a nonsense string given as a dword into zero" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBeZero", :type => :dword, :data => "whatdoesthisdo" })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBeZero", :type => :dword, :data => 0 })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBeZero", type: :dword, data: "whatdoesthisdo" })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBeZero", type: :dword, data: 0 })).to eq(true)
end
it "throws an exception when trying to cast an array to an int for a dword" do
- expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldThrow", :type => :dword, :data => %w{one two} }) }.to raise_error NoMethodError
+ expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldThrow", type: :dword, data: %w{one two} }) }.to raise_error NoMethodError
end
# we are validating that the data gets .to_s called on it when type is a :string
it "stores the string representation of an array into a string if you pass it an array" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBePainful", :type => :string, :data => %w{one two} })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBePainful", :type => :string, :data => '["one", "two"]' })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBePainful", type: :string, data: %w{one two} })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBePainful", type: :string, data: '["one", "two"]' })).to eq(true)
end
it "stores the string representation of a number into a string if you pass it an number" do
- expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBe65535", :type => :string, :data => 65535 })).to eq(true)
- expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBe65535", :type => :string, :data => "65535" })).to eq(true)
+ expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBe65535", type: :string, data: 65535 })).to eq(true)
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBe65535", type: :string, data: "65535" })).to eq(true)
end
# we are validating that the data gets .to_a called on it when type is a :multi_string
it "throws an exception when a multi-string is passed a number" do
- expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldThrow", :type => :multi_string, :data => 65535 }) }.to raise_error NoMethodError
+ expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldThrow", type: :multi_string, data: 65535 }) }.to raise_error NoMethodError
end
it "throws an exception when a multi-string is passed a string" do
- expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { :name => "ShouldBeWat", :type => :multi_string, :data => "foo" }) }.to raise_error NoMethodError
+ expect { @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", { name: "ShouldBeWat", type: :multi_string, data: "foo" }) }.to raise_error NoMethodError
end
end
describe "create_key" do
before(:all) do
::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root") do |reg|
- begin
- reg.delete_key("Trunk", true)
- rescue
- end
+
+ reg.delete_key("Trunk", true)
+ rescue
+
end
end
@@ -309,26 +303,26 @@ describe "Chef::Win32::Registry", :windows_only do
end
it "deletes values if the value exists" do
- expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" })).to eq(true)
- expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" })).to eq(false)
+ expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" })).to eq(true)
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" })).to eq(false)
end
it "does nothing if value does not exist" do
- expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" })).to eq(true)
- expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" })).to eq(false)
+ expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" })).to eq(true)
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" })).to eq(false)
end
it "throws an exception if the key does not exist?" do
- expect { @registry.delete_value("HKCU\\Software\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.delete_value("HKCU\\Software\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "throws an exception if the hive does not exist" do
- expect { @registry.delete_value("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", { :name => "Peter", :type => :string, :data => "Tiny" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ expect { @registry.delete_value("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", { name: "Peter", type: :string, data: "Tiny" }) }.to raise_error(Chef::Exceptions::Win32RegHiveMissing)
end
end
describe "delete_key" do
- before (:all) do
+ before(:all) do
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Fruit"
::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch\\Fruit', Win32::Registry::KEY_ALL_ACCESS) do |reg|
reg["Apple", Win32::Registry::REG_MULTI_SZ] = %w{Red Juicy}
@@ -368,10 +362,10 @@ describe "Chef::Win32::Registry", :windows_only do
before(:all) do
::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk"
::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root\\Trunk") do |reg|
- begin
- reg.delete_key("Red", true)
- rescue
- end
+
+ reg.delete_key("Red", true)
+ rescue
+
end
end
@@ -566,38 +560,38 @@ describe "Chef::Win32::Registry", :windows_only do
describe "value_exists?" do
it "does not find 64-bit values in the 32-bit registry" do
@registry.architecture = :i386
- expect { @registry.value_exists?("HKLM\\Software\\Root\\Mauve", { :name => "Alert" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.value_exists?("HKLM\\Software\\Root\\Mauve", { name: "Alert" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "finds 32-bit values in the 32-bit registry" do
@registry.architecture = :i386
- expect(@registry.value_exists?("HKLM\\Software\\Root\\Poosh", { :name => "Status" })).to eq(true)
+ expect(@registry.value_exists?("HKLM\\Software\\Root\\Poosh", { name: "Status" })).to eq(true)
end
it "does not find 32-bit values in the 64-bit registry" do
@registry.architecture = :x86_64
- expect(@registry.value_exists?("HKLM\\Software\\Root\\Mauve", { :name => "Alert" })).to eq(true)
+ expect(@registry.value_exists?("HKLM\\Software\\Root\\Mauve", { name: "Alert" })).to eq(true)
end
it "finds 64-bit values in the 64-bit registry" do
@registry.architecture = :x86_64
- expect { @registry.value_exists?("HKLM\\Software\\Root\\Poosh", { :name => "Status" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.value_exists?("HKLM\\Software\\Root\\Poosh", { name: "Status" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
end
describe "data_exists?" do
it "does not find 64-bit keys in the 32-bit registry" do
@registry.architecture = :i386
- expect { @registry.data_exists?("HKLM\\Software\\Root\\Mauve", { :name => "Alert", :type => :string, :data => "Universal" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.data_exists?("HKLM\\Software\\Root\\Mauve", { name: "Alert", type: :string, data: "Universal" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
it "finds 32-bit keys in the 32-bit registry" do
@registry.architecture = :i386
- expect(@registry.data_exists?("HKLM\\Software\\Root\\Poosh", { :name => "Status", :type => :string, :data => "Lost" })).to eq(true)
+ expect(@registry.data_exists?("HKLM\\Software\\Root\\Poosh", { name: "Status", type: :string, data: "Lost" })).to eq(true)
end
it "does not find 32-bit keys in the 64-bit registry" do
@registry.architecture = :x86_64
- expect(@registry.data_exists?("HKLM\\Software\\Root\\Mauve", { :name => "Alert", :type => :string, :data => "Universal" })).to eq(true)
+ expect(@registry.data_exists?("HKLM\\Software\\Root\\Mauve", { name: "Alert", type: :string, data: "Universal" })).to eq(true)
end
it "finds 64-bit keys in the 64-bit registry" do
@registry.architecture = :x86_64
- expect { @registry.data_exists?("HKLM\\Software\\Root\\Poosh", { :name => "Status", :type => :string, :data => "Lost" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ expect { @registry.data_exists?("HKLM\\Software\\Root\\Poosh", { name: "Status", type: :string, data: "Lost" }) }.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
end
end
diff --git a/spec/functional/win32/security_spec.rb b/spec/functional/win32/security_spec.rb
index 40ae99bfa4..ef31e8b906 100644
--- a/spec/functional/win32/security_spec.rb
+++ b/spec/functional/win32/security_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,9 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+require "mixlib/shellout"
+require "chef/mixin/user_context"
+if ChefUtils.windows?
require "chef/win32/security"
end
@@ -26,19 +28,41 @@ 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
+ ENV["COMPUTERNAME"]
+ 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
it "should return a security descriptor when called with a path that exists" do
security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security(
- "C:\\Program Files")
+ "C:\\Program Files"
+ )
# Make sure the security descriptor works
expect(security_descriptor.dacl_present?).to be true
end
@@ -47,7 +71,8 @@ describe "Chef::Win32::Security", :windows_only do
describe "access_check" do
let(:security_descriptor) do
Chef::ReservedNames::Win32::Security.get_file_security(
- "C:\\Program Files")
+ "C:\\Program Files"
+ )
end
let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
@@ -55,7 +80,8 @@ describe "Chef::Win32::Security", :windows_only do
let(:token) do
Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
- token_rights).duplicate_token(:SecurityImpersonation)
+ token_rights
+ ).duplicate_token(:SecurityImpersonation)
end
let(:mapping) do
@@ -71,7 +97,7 @@ describe "Chef::Win32::Security", :windows_only do
it "should check if the provided token has the desired access" do
expect(Chef::ReservedNames::Win32::Security.access_check(security_descriptor,
- token, desired_access, mapping)).to be true
+ token, desired_access, mapping)).to be true
end
end
@@ -79,7 +105,8 @@ describe "Chef::Win32::Security", :windows_only do
let(:token) do
Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
- token_rights)
+ token_rights
+ )
end
context "with all rights" do
let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
@@ -97,4 +124,110 @@ 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) { %i{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 ".remove_account_right" do
+ let(:username) { ENV["USERNAME"] }
+
+ context "when given a valid username" do
+ it "removes the 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")
+ Chef::ReservedNames::Win32::Security.remove_account_right(username, "SeBatchLogonRight")
+ expect(Chef::ReservedNames::Win32::Security.get_account_right(username)).not_to include("SeBatchLogonRight")
+ end
+ end
+
+ context "when given an invalid username" do
+ let(:username) { "noooooooooope" }
+
+ it "raises an exception" do
+ expect { Chef::ReservedNames::Win32::Security.remove_account_right(username, "SeBatchLogonRight") }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+ end
+
+ describe ".get_account_with_user_rights" do
+ let(:domain) { ENV["COMPUTERNAME"] }
+ let(:username) { ENV["USERNAME"] }
+
+ context "when given a valid user right" do
+ it "gets all accounts associated with given user right" do
+ Chef::ReservedNames::Win32::Security.add_account_right(username, "SeBatchLogonRight")
+ expect(Chef::ReservedNames::Win32::Security.get_account_with_user_rights("SeBatchLogonRight").flatten).to include("#{domain}\\#{username}")
+ Chef::ReservedNames::Win32::Security.remove_account_right(username, "SeBatchLogonRight")
+ expect(Chef::ReservedNames::Win32::Security.get_account_with_user_rights("SeBatchLogonRight").flatten).not_to include("#{domain}\\#{username}")
+ end
+ end
+
+ context "when given an invalid user right" do
+ let(:user_right) { "SeTest" }
+
+ it "returns empty array" do
+ expect(Chef::ReservedNames::Win32::Security.get_account_with_user_rights(user_right)).to be_empty
+ 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/service_manager_spec.rb b/spec/functional/win32/service_manager_spec.rb
index 8fff73396e..5746283b78 100644
--- a/spec/functional/win32/service_manager_spec.rb
+++ b/spec/functional/win32/service_manager_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.windows?
require "chef/application/windows_service_manager"
end
@@ -33,7 +33,7 @@ end
# directories.
#
-describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only, :appveyor_only do
+describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only do
include_context "using Win32::Service"
@@ -43,7 +43,7 @@ describe "Chef::Application::WindowsServiceManager", :windows_only, :system_wind
end
it "throws an error with required missing options" do
- [:service_name, :service_display_name, :service_description, :service_file_path].each do |key|
+ %i{service_name service_display_name service_description service_file_path}.each do |key|
service_def = test_service.dup
service_def.delete(key)
@@ -125,7 +125,7 @@ describe "Chef::Application::WindowsServiceManager", :windows_only, :system_wind
it "start should start the service", :volatile do
service_manager.run(["-a", "start"])
expect(test_service_state).to eq("running")
- expect(File.exists?(test_service_file)).to be_truthy
+ expect(File.exist?(test_service_file)).to be_truthy
end
it "stop should not affect the service" do
diff --git a/spec/functional/win32/sid_spec.rb b/spec/functional/win32/sid_spec.rb
index eac62d88b6..bdc570dfe6 100644
--- a/spec/functional/win32/sid_spec.rb
+++ b/spec/functional/win32/sid_spec.rb
@@ -17,12 +17,12 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.windows?
require "chef/win32/security"
end
describe "Chef::ReservedNames::Win32::SID", :windows_only do
- if Chef::Platform.windows?
+ if ChefUtils.windows?
SID ||= Chef::ReservedNames::Win32::Security::SID
end
diff --git a/spec/functional/win32/version_info_spec.rb b/spec/functional/win32/version_info_spec.rb
index b5570732a0..1172ae86c1 100644
--- a/spec/functional/win32/version_info_spec.rb
+++ b/spec/functional/win32/version_info_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.windows?
require "chef/win32/file/version_info"
end
@@ -32,12 +32,12 @@ describe "Chef::ReservedNames::Win32::File::VersionInfo", :windows_only do
subject { Chef::ReservedNames::Win32::File::VersionInfo.new(file_path) }
- it "file version has the same version as windows" do
- expect(subject.FileVersion).to start_with(os_version)
+ it "file version has the same major.minor version as windows" do
+ expect(subject.FileVersion).to start_with(os_version.rpartition(".").first)
end
- it "product version has the same version as windows" do
- expect(subject.ProductVersion).to start_with(os_version)
+ it "product version has the same major.minor version as windows" do
+ expect(subject.ProductVersion).to start_with(os_version.rpartition(".").first)
end
it "company is microsoft" do
diff --git a/spec/functional/win32/versions_spec.rb b/spec/functional/win32/versions_spec.rb
index d6e840ed7f..4b57886d5f 100644
--- a/spec/functional/win32/versions_spec.rb
+++ b/spec/functional/win32/versions_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Chirag Jog (<chirag@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,11 @@
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.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,18 +31,16 @@ 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+.
- # So this expectation sould continue to hold without modification
+ # So this expectation should continue to hold without modification
# as new versions of Windows are released.
@current_os_version = host["caption"]
end
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index da3a2b98e4..ebde310eaf 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -1,7 +1,9 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
require "tiny_server"
require "tmpdir"
+require "chef-utils/dist"
describe "chef-client" do
@@ -9,8 +11,8 @@ describe "chef-client" do
File.join(CHEF_SPEC_DATA, "recipes.tgz")
end
- def start_tiny_server(server_opts = {})
- @server = TinyServer::Manager.new(server_opts)
+ def start_tiny_server(**server_opts)
+ @server = TinyServer::Manager.new(**server_opts)
@server.start
@api = TinyServer::API.instance
@api.clear
@@ -20,9 +22,7 @@ describe "chef-client" do
# just a normal file
# (expected_content should be uncompressed)
@api.get("/recipes.tgz", 200) do
- File.open(recipes_filename, "rb") do |f|
- f.read
- end
+ File.open(recipes_filename, "rb", &:read)
end
end
@@ -34,7 +34,7 @@ describe "chef-client" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -45,67 +45,50 @@ describe "chef-client" do
# 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(: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(" ") }
+ let(:chef_client) { "bundle exec #{ChefUtils::Dist::Infra::CLIENT} --minimal-ohai" }
+ let(:chef_solo) { "bundle exec #{ChefUtils::Dist::Solo::EXEC} --legacy-mode --minimal-ohai" }
when_the_repository "has a cookbook with a no-op recipe" do
before { file "cookbooks/x/recipes/default.rb", "" }
it "should complete with success" do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
- shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
- end
-
- it "should complete successfully with no other environment variables", :skip => (Chef::Platform.windows?) do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-EOM
-
- begin
- result = shell_out("env -i #{critical_env_vars} #{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
- result.error!
- rescue
- Chef::Log.info "Bare invocation will have the following load-path."
- Chef::Log.info shell_out!("env -i #{critical_env_vars} ruby -e 'puts $:'").stdout
- raise
- end
+ shell_out!("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
end
it "should complete successfully with --no-listen" do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
- result = shell_out("#{chef_client} --no-listen -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ result = shell_out("#{chef_client} --no-listen -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
result.error!
end
it "should be able to node.save with bad utf8 characters in the node data" do
file "cookbooks/x/attributes/default.rb", 'default["badutf8"] = "Elan Ruusam\xE4e"'
- result = shell_out("#{chef_client} -z -r 'x::default' --disable-config", :cwd => path_to(""))
+ result = shell_out("#{chef_client} -z -r 'x::default' --disable-config", cwd: path_to(""))
result.error!
end
context "and no config file" do
it "should complete with success when cwd is just above cookbooks and paths are not specified" do
- result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => path_to(""))
+ result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", cwd: path_to(""))
result.error!
end
it "should complete with success when cwd is below cookbooks and paths are not specified" do
- result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => path_to("cookbooks/x"))
+ result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", cwd: path_to("cookbooks/x"))
result.error!
end
it "should fail when cwd is below high above and paths are not specified" do
- result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => File.expand_path("..", path_to("")))
+ result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", cwd: File.expand_path("..", path_to("")))
expect(result.exitstatus).to eq(1)
end
end
@@ -114,7 +97,8 @@ EOM
before { file ".chef/knife.rb", "xxx.xxx" }
it "should load .chef/knife.rb when -z is specified" do
- result = shell_out("#{chef_client} -z -o 'x::default'", :cwd => path_to(""))
+ # On Solaris shell_out will invoke /bin/sh which doesn't understand how to correctly update ENV['PWD']
+ result = shell_out("#{chef_client} -z -o 'x::default'", cwd: path_to(""), env: { "PWD" => nil })
# FATAL: Configuration error NoMethodError: undefined method `xxx' for nil:NilClass
expect(result.stdout).to include("xxx")
end
@@ -122,79 +106,79 @@ EOM
end
it "should complete with success" do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ 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'", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
result.error!
end
context "and a private key" do
before do
- file "mykey.pem", <<EOM
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
-0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
-NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
-0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
-AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
-HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
-8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
-zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
-k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
-i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
-G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
-ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
-awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
-7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
-g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
-Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
-HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
-V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
-fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
-lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
-c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
-fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
-YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
-syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
-+vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
------END RSA PRIVATE KEY-----
-EOM
+ file "mykey.pem", <<~EOM
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
+ 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
+ NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
+ 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
+ AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
+ HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
+ 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
+ zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
+ k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
+ i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
+ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
+ ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
+ awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
+ 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
+ g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
+ Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
+ HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
+ V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
+ fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
+ lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
+ c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
+ fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
+ YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
+ syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
+ +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
+ -----END RSA PRIVATE KEY-----
+ EOM
end
it "should complete with success even with a client key" do
- file "config/client.rb", <<EOM
-local_mode true
-client_key #{path_to('mykey.pem').inspect}
-cookbook_path #{path_to('cookbooks').inspect}
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
result.error!
end
it "should run recipes specified directly on the command line" do
- file "config/client.rb", <<EOM
-local_mode true
-client_key #{path_to('mykey.pem').inspect}
-cookbook_path #{path_to('cookbooks').inspect}
-EOM
-
- file "arbitrary.rb", <<EOM
-file #{path_to('tempfile.txt').inspect} do
- content '1'
-end
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ EOM
+
+ file "arbitrary.rb", <<~EOM
+ file #{path_to("tempfile.txt").inspect} do
+ content '1'
+ end
+ EOM
- file "arbitrary2.rb", <<EOM
-file #{path_to('tempfile2.txt').inspect} do
- content '2'
-end
-EOM
+ file "arbitrary2.rb", <<~EOM
+ file #{path_to("tempfile2.txt").inspect} do
+ content '2'
+ end
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" #{path_to('arbitrary.rb')} #{path_to('arbitrary2.rb')}", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" #{path_to("arbitrary.rb")} #{path_to("arbitrary2.rb")}", cwd: chef_dir)
result.error!
expect(IO.read(path_to("tempfile.txt"))).to eq("1")
@@ -202,43 +186,110 @@ EOM
end
it "should run recipes specified as relative paths directly on the command line" do
- file "config/client.rb", <<EOM
-local_mode true
-client_key #{path_to('mykey.pem').inspect}
-cookbook_path #{path_to('cookbooks').inspect}
-EOM
-
- file "arbitrary.rb", <<EOM
-file #{path_to('tempfile.txt').inspect} do
- content '1'
-end
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ EOM
+
+ file "arbitrary.rb", <<~EOM
+ file #{path_to("tempfile.txt").inspect} do
+ content '1'
+ end
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" arbitrary.rb", :cwd => path_to(""))
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" arbitrary.rb", cwd: path_to(""))
result.error!
expect(IO.read(path_to("tempfile.txt"))).to eq("1")
end
it "should run recipes specified directly on the command line AFTER recipes in the run list" do
- file "config/client.rb", <<EOM
-local_mode true
-client_key #{path_to('mykey.pem').inspect}
-cookbook_path #{path_to('cookbooks').inspect}
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ EOM
+
+ file "cookbooks/x/recipes/constant_definition.rb", <<~EOM
+ class ::Blah
+ THECONSTANT = '1'
+ end
+ EOM
- file "cookbooks/x/recipes/constant_definition.rb", <<EOM
-class ::Blah
- THECONSTANT = '1'
-end
-EOM
- file "arbitrary.rb", <<EOM
-file #{path_to('tempfile.txt').inspect} do
- content ::Blah::THECONSTANT
-end
-EOM
+ file "arbitrary.rb", <<~EOM
+ file #{path_to("tempfile.txt").inspect} do
+ content ::Blah::THECONSTANT
+ end
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o x::constant_definition arbitrary.rb", :cwd => path_to(""))
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o x::constant_definition arbitrary.rb", cwd: path_to(""))
+ result.error!
+
+ expect(IO.read(path_to("tempfile.txt"))).to eq("1")
+ end
+
+ it "should run recipes specified directly on the command line AFTER recipes in the run list (without an override_runlist this time)" do
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ EOM
+
+ file "config/dna.json", <<~EOM
+ {
+ "run_list": [ "recipe[x::constant_definition]" ]
+ }
+ EOM
+
+ file "cookbooks/x/recipes/constant_definition.rb", <<~EOM
+ class ::Blah
+ THECONSTANT = '1'
+ end
+ EOM
+
+ file "arbitrary.rb", <<~EOM
+ file #{path_to("tempfile.txt").inspect} do
+ content ::Blah::THECONSTANT
+ end
+ EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -j \"#{path_to("config/dna.json")}\" arbitrary.rb", cwd: path_to(""))
+ result.error!
+
+ expect(IO.read(path_to("tempfile.txt"))).to eq("1")
+ end
+
+ it "an override_runlist of an empty string should allow a recipe specified directly on the command line to be the only one run" do
+ file "config/client.rb", <<~EOM
+ local_mode true
+ client_key #{path_to("mykey.pem").inspect}
+ cookbook_path #{path_to("cookbooks").inspect}
+ class ::Blah
+ THECONSTANT = "1"
+ end
+ EOM
+
+ file "config/dna.json", <<~EOM
+ {
+ "run_list": [ "recipe[x::constant_definition]" ]
+ }
+ EOM
+
+ file "cookbooks/x/recipes/constant_definition.rb", <<~EOM
+ class ::Blah
+ THECONSTANT = "2"
+ end
+ EOM
+
+ file "arbitrary.rb", <<~EOM
+ raise "this test failed" unless ::Blah::THECONSTANT == "1"
+ file #{path_to("tempfile.txt").inspect} do
+ content ::Blah::THECONSTANT
+ end
+ EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -j \"#{path_to("config/dna.json")}\" -o \"\" arbitrary.rb", cwd: path_to(""))
result.error!
expect(IO.read(path_to("tempfile.txt"))).to eq("1")
@@ -247,93 +298,116 @@ EOM
end
it "should complete with success when passed the -z flag" do
- file "config/client.rb", <<EOM
-chef_server_url 'http://omg.com/blah'
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ chef_server_url 'http://omg.com/blah'
+ 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 = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' -z", cwd: chef_dir)
result.error!
end
it "should complete with success when passed the --local-mode flag" do
- file "config/client.rb", <<EOM
-chef_server_url 'http://omg.com/blah'
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ chef_server_url 'http://omg.com/blah'
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --local-mode", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --local-mode", cwd: chef_dir)
result.error!
end
it "should not print SSL warnings when running in local-mode" do
- file "config/client.rb", <<EOM
-chef_server_url 'http://omg.com/blah'
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ chef_server_url 'http://omg.com/blah'
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --local-mode", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --local-mode", cwd: chef_dir)
expect(result.stdout).not_to include("SSL validation of HTTPS requests is disabled.")
result.error!
end
it "should complete with success when passed -z and --chef-zero-port" do
- file "config/client.rb", <<EOM
-chef_server_url 'http://omg.com/blah'
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ chef_server_url 'http://omg.com/blah'
+ 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 = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' -z", cwd: chef_dir)
result.error!
end
it "should complete with success when setting the run list with -r" do
- file "config/client.rb", <<EOM
-chef_server_url 'http://omg.com/blah'
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "config/client.rb", <<~EOM
+ 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
- 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)
+ 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
it "doesn't produce a profile when --profile-ruby is not present" 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", :cwd => chef_dir)
+ 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", cwd: chef_dir)
+ result.error!
expect(File.exist?(path_to("config/local-mode-cache/cache/graph_profile.out"))).to be false
end
end
+ when_the_repository "has a cookbook that outputs some node attributes" do
+ before do
+ file "cookbooks/x/recipes/default.rb", <<~'EOM'
+ puts "COOKBOOKS: #{node[:cookbooks]}"
+ EOM
+ file "cookbooks/x/metadata.rb", <<~EOM
+ name 'x'
+ version '0.0.1'
+ EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
+ end
+
+ it "should have a cookbook attribute" do
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --no-fork", cwd: chef_dir)
+ result.error!
+ expect(result.stdout).to include('COOKBOOKS: {"x"=>{"version"=>"0.0.1"}}')
+ end
+ end
+
when_the_repository "has a cookbook that should fail chef_version checks" do
before do
file "cookbooks/x/recipes/default.rb", ""
- file "cookbooks/x/metadata.rb", <<EOM
-name 'x'
-version '0.0.1'
-chef_version '~> 999.99'
-EOM
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-EOM
+ file "cookbooks/x/metadata.rb", <<~EOM
+ name 'x'
+ version '0.0.1'
+ chef_version '~> 999.99'
+ EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
end
it "should fail the chef client run" do
- command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ 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(/Chef::Exceptions::CookbookChefVersionMismatch/)
end
@@ -354,51 +428,21 @@ EOM
EOM
file "config/client.rb", <<-EOM
local_mode true
- cookbook_path "#{path_to('cookbooks')}"
+ cookbook_path "#{path_to("cookbooks")}"
EOM
end
it "the cheffish DSL is loaded lazily" do
- command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
+ command = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --no-fork", cwd: chef_dir)
expect(command.exitstatus).to eql(0)
end
end
- when_the_repository "has a cookbook that uses chef-provisioning resources" do
- before do
- file "cookbooks/x/recipes/default.rb", <<-EOM
- with_driver 'blah'
- EOM
- file "config/client.rb", <<-EOM
- local_mode true
- cookbook_path "#{path_to('cookbooks')}"
- 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/)
- end
- end
-
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
@@ -413,17 +457,17 @@ EOM
end
it "should output each deprecation warning only once, at the end of the run" do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-# Mimick what happens when you are on the console
-formatters << :doc
-log_level :warn
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ # Mimick what happens when you are on the console
+ formatters << :doc
+ log_level :warn
+ EOM
ENV.delete("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
- result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ result = shell_out!("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
expect(result.error?).to be_falsey
# Search to the end of the client run in the output
@@ -431,51 +475,74 @@ 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
- when_the_repository "has a cookbook with only an audit recipe" do
-
+ when_the_repository "has a cookbook that deploys a file" do
before do
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-audit_mode :enabled
-EOM
+ 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
- it "should exit with a zero code when there is not an audit failure" do
- file "cookbooks/audit_test/recipes/succeed.rb", <<-RECIPE
-control_group "control group without top level control" do
- it "should succeed" do
- expect(2 - 2).to eq(0)
+ [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
-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
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::succeed'", :cwd => chef_dir)
- expect(result.error?).to be_falsey
- expect(result.stdout).to include("Successfully executed all `control_group` blocks and contained examples")
+ 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 exit with a non-zero code when there is an audit failure" do
- file "cookbooks/audit_test/recipes/fail.rb", <<-RECIPE
-control_group "control group without top level control" do
- it "should fail" do
- expect(2 - 2).to eq(1)
- end
-end
- RECIPE
+ 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!
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::fail'", :cwd => chef_dir)
- expect(result.error?).to be_truthy
- expect(result.stdout).to include("Failure/Error: expect(2 - 2).to eq(1)")
+ 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
+ context "when using recipe-url" do
before(:each) do
start_tiny_server
end
@@ -487,16 +554,311 @@ end
let(:tmp_dir) { Dir.mktmpdir("recipe-url") }
it "should complete with success when passed -z and --recipe-url" do
- file "config/client.rb", <<EOM
-chef_repo_path "#{tmp_dir}"
-EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --recipe-url=http://localhost:9000/recipes.tgz -o 'x::default' -z", :cwd => tmp_dir)
+ file "config/client.rb", <<~EOM
+ chef_repo_path "#{tmp_dir}"
+ EOM
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --recipe-url=http://localhost:9000/recipes.tgz -o 'x::default' -z", cwd: tmp_dir)
result.error!
end
it "should fail when passed --recipe-url and not passed -z" do
- result = shell_out("#{chef_client} --recipe-url=http://localhost:9000/recipes.tgz", :cwd => tmp_dir)
+ 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
+
+ when_the_repository "has an eager_load_libraries false cookbook" do
+ before do
+ file "cookbooks/x/libraries/require_me.rb", <<~'EOM'
+ class RequireMe
+ end
+ EOM
+ file "cookbooks/x/recipes/default.rb", <<~'EOM'
+ # shouldn't be required by default
+ raise "boom" if defined?(RequireMe)
+ require "require_me"
+ # should be in the LOAD_PATH
+ raise "boom" unless defined?(RequireMe)
+ EOM
+ file "cookbooks/x/metadata.rb", <<~EOM
+ name 'x'
+ version '0.0.1'
+ eager_load_libraries false
+ EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ EOM
+ end
+
+ it "should not eagerly load the library" do
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
+ result.error!
+ end
+ end
+
+ when_the_repository "has an eager_load_libraries cookbook with a default hook" do
+ before do
+ file "cookbooks/x/libraries/aa_require_me.rb", <<~'EOM'
+ class RequireMe
+ end
+ EOM
+ file "cookbooks/x/libraries/default.rb", <<~'EOM'
+ raise "boom" if defined?(RequireMe)
+ require "aa_require_me"
+ EOM
+ file "cookbooks/x/libraries/nope/default.rb", <<~'EOM'
+ raise "boom" # this should never be required
+ EOM
+ file "cookbooks/x/recipes/default.rb", <<~'EOM'
+ raise "boom" unless defined?(RequireMe)
+ EOM
+ file "cookbooks/x/metadata.rb", <<~EOM
+ name 'x'
+ version '0.0.1'
+ eager_load_libraries "default.rb"
+ EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ always_dump_stacktrace true
+ EOM
+ end
+
+ it "should properly load the library via the hook" do
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
+ result.error!
+ end
+ end
+
+ when_the_repository "has an eager_load_libraries false cookbook" do
+ before do
+ # this is loaded by default.rb
+ file "cookbooks/x/libraries/aa_require_me.rb", <<~'EOM'
+ class RequireMe
+ end
+ EOM
+ # this is loaded by eager_load_libraries
+ file "cookbooks/x/libraries/default.rb", <<~'EOM'
+ raise "boom" if defined?(RequireMe)
+ require "aa_require_me"
+ EOM
+ # this is loaded by the recipe using the LOAD_PATH
+ file "cookbooks/x/libraries/require_me.rb", <<~'EOM'
+ class RequireMe4
+ end
+ EOM
+ # these two are loaded by eager_load_libraries glob
+ file "cookbooks/x/libraries/loadme/foo/require_me.rb", <<~'EOM'
+ class RequireMe2
+ end
+ EOM
+ file "cookbooks/x/libraries/loadme/require_me.rb", <<~'EOM'
+ class RequireMe3
+ end
+ EOM
+ # this should nevrer be loaded
+ file "cookbooks/x/libraries/nope/require_me.rb", <<~'EOM'
+ raise "boom" # this should never be required
+ EOM
+ file "cookbooks/x/recipes/default.rb", <<~'EOM'
+ # all these are loaded by the eager_load_libraries
+ raise "boom" unless defined?(RequireMe)
+ raise "boom" unless defined?(RequireMe2)
+ raise "boom" unless defined?(RequireMe3)
+ raise "boom" if defined?(RequireMe4)
+ require "require_me"
+ raise "boom" unless defined?(RequireMe4)
+ EOM
+ file "cookbooks/x/metadata.rb", <<~EOM
+ name 'x'
+ version '0.0.1'
+ eager_load_libraries [ "default.rb", "loadme/**/*.rb" ]
+ EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ always_dump_stacktrace true
+ EOM
+ end
+
+ it "should not eagerly load the library" do
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default'", cwd: chef_dir)
+ result.error!
+ end
end
end
diff --git a/spec/integration/client/exit_code_spec.rb b/spec/integration/client/exit_code_spec.rb
index 30020f6a3f..53a8f1f895 100644
--- a/spec/integration/client/exit_code_spec.rb
+++ b/spec/integration/client/exit_code_spec.rb
@@ -1,16 +1,18 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
require "tiny_server"
require "tmpdir"
require "chef/platform"
+require "chef-utils/dist"
describe "chef-client" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -21,135 +23,23 @@ describe "chef-client" do
# 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(:chef_client) { "bundle exec #{ChefUtils::Dist::Infra::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 "does not have exit_status configured" do
+ 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(2 - 2).to eq(1)
- end
-end
-RECIPE
- end
-
- it "exits with GENERIC_FAILURE, 1" do
- setup_client_rb_with_audit_mode
- run_chef_client_and_expect_exit_code 1
- 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 specified exit code" do
- setup_client_rb
- run_chef_client_and_expect_exit_code 123
- 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 specified exit code" do
- setup_client_rb
- run_chef_client_and_expect_exit_code 231
- end
- 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 GENERIC_FAILURE, 1" do
- setup_client_rb
- run_chef_client_and_expect_exit_code 1
- end
- end
- end
- end
-
- when_the_repository "does has exit_status enabled" do
-
- def setup_client_rb
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-exit_status :enabled
-EOM
- end
-
- def setup_client_rb_with_audit_mode
- file "config/client.rb", <<EOM
-local_mode true
-cookbook_path "#{path_to('cookbooks')}"
-exit_status :enabled
-audit_mode :audit_only
-EOM
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ 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])
+ 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
@@ -167,25 +57,6 @@ EOM
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'" }
@@ -216,9 +87,9 @@ RECIPE
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
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ raise Chef::Exceptions::Reboot.new
+ EOM
end
it "exits with REBOOT_SCHEDULED, 35" do
@@ -229,9 +100,9 @@ EOM
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
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ raise Chef::Exceptions::RebootFailed.new
+ EOM
end
it "exits with REBOOT_FAILED, 41" do
diff --git a/spec/integration/client/ipv6_spec.rb b/spec/integration/client/ipv6_spec.rb
index 68c58bb8ea..816e1c40e6 100644
--- a/spec/integration/client/ipv6_spec.rb
+++ b/spec/integration/client/ipv6_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -22,38 +23,38 @@ describe "chef-client" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_zero_opts) { { :host => "::1" } }
+ let(:chef_zero_opts) { { host: "::1" } }
let(:validation_pem) do
- <<-END_VALIDATION_PEM
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
-0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
-NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
-0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
-AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
-HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
-8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
-zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
-k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
-i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
-G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
-ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
-awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
-7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
-g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
-Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
-HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
-V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
-fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
-lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
-c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
-fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
-YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
-syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
-+vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
------END RSA PRIVATE KEY-----
-END_VALIDATION_PEM
+ <<~END_VALIDATION_PEM
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
+ 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
+ NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
+ 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
+ AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
+ HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
+ 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
+ zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
+ k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
+ i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
+ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
+ ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
+ awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
+ 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
+ g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
+ Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
+ HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
+ V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
+ fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
+ lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
+ c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
+ fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
+ YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
+ syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
+ +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
+ -----END RSA PRIVATE KEY-----
+ END_VALIDATION_PEM
end
let(:cache_path) do
@@ -61,21 +62,21 @@ END_VALIDATION_PEM
end
let(:basic_config_file) do
- <<-END_CLIENT_RB
-chef_server_url "http://[::1]:8900"
-validation_key '#{path_to('config/validator.pem')}'
-cache_path '#{cache_path}'
-client_key '#{cache_path}/client.pem'
-END_CLIENT_RB
+ <<~END_CLIENT_RB
+ chef_server_url "http://[::1]:8900"
+ validation_key '#{path_to("config/validator.pem")}'
+ cache_path '#{cache_path}'
+ client_key '#{cache_path}/client.pem'
+ END_CLIENT_RB
end
let(:client_rb_content) do
basic_config_file
end
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
- let(:chef_client_cmd) { %Q{ruby '#{chef_dir}/chef-client' --minimal-ohai -c "#{path_to('config/client.rb')}" -lwarn} }
+ let(:chef_client_cmd) { %Q{bundle exec chef-client --minimal-ohai -c "#{path_to("config/client.rb")}" -lwarn} }
after do
FileUtils.rm_rf(cache_path)
@@ -83,7 +84,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
@@ -93,7 +94,7 @@ END_CLIENT_RB
end
it "should complete with success" do
- result = shell_out("#{chef_client_cmd} -o 'noop::default'", :cwd => chef_dir)
+ result = shell_out("#{chef_client_cmd} -o 'noop::default'", cwd: chef_dir)
result.error!
end
@@ -125,7 +126,7 @@ END_CLIENT_RB
end
it "should complete with success" do
- result = shell_out("#{chef_client_cmd} -o 'api-smoke-test::default'", :cwd => chef_dir)
+ result = shell_out("#{chef_client_cmd} -o 'api-smoke-test::default'", cwd: chef_dir)
result.error!
end
diff --git a/spec/integration/compliance/compliance_spec.rb b/spec/integration/compliance/compliance_spec.rb
new file mode 100644
index 0000000000..98776308de
--- /dev/null
+++ b/spec/integration/compliance/compliance_spec.rb
@@ -0,0 +1,81 @@
+require "spec_helper"
+
+require "support/shared/integration/integration_helper"
+require "chef/mixin/shell_out"
+require "chef-utils/dist"
+
+describe "chef-client with audit mode" do
+
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "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) { "bundle exec #{ChefUtils::Dist::Infra::CLIENT} --minimal-ohai" }
+
+ when_the_repository "has a custom profile" do
+ let(:report_file) { path_to("report_file.json") }
+
+ before do
+ directory "profiles/my-profile" do
+ file "inspec.yml", <<~FILE
+ ---
+ name: my-profile
+ FILE
+
+ directory "controls" do
+ file "my_control.rb", <<~FILE
+ control "my control" do
+ describe Dir.home do
+ it { should be_kind_of String }
+ end
+ end
+ FILE
+ end
+ end
+
+ file "attributes.json", <<~FILE
+ {
+ "audit": {
+ "json_file": {
+ "location": "#{report_file}"
+ },
+ "profiles": {
+ "my-profile": {
+ "path": "#{path_to("profiles/my-profile")}"
+ }
+ }
+ }
+ }
+ FILE
+ end
+
+ it "should complete with success" do
+ result = shell_out!("#{chef_client} --local-mode --json-attributes #{path_to("attributes.json")}", cwd: chef_dir)
+ result.error!
+
+ inspec_report = JSON.parse(File.read(report_file))
+ expect(inspec_report["profiles"].length).to eq(1)
+
+ profile = inspec_report["profiles"].first
+ expect(profile["name"]).to eq("my-profile")
+ expect(profile["controls"].length).to eq(1)
+
+ control = profile["controls"].first
+ expect(control["id"]).to eq("my control")
+ expect(control["results"].length).to eq(1)
+
+ result = control["results"].first
+ expect(result["status"]).to eq("passed")
+ end
+ end
+end
diff --git a/spec/integration/knife/chef_fs_data_store_spec.rb b/spec/integration/knife/chef_fs_data_store_spec.rb
index 02508b799d..6e04684c33 100644
--- a/spec/integration/knife/chef_fs_data_store_spec.rb
+++ b/spec/integration/knife/chef_fs_data_store_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/list"
require "chef/knife/delete"
@@ -54,60 +55,60 @@ describe "ChefFSDataStore tests", :workstation do
context "GET /TYPE" do
it "knife list -z -R returns everything" do
- knife("list -z -Rfp /").should_succeed <<EOM
-/acls/
-/acls/clients/
-/acls/clients/x.json
-/acls/containers/
-/acls/containers/x.json
-/acls/cookbook_artifacts/
-/acls/cookbook_artifacts/x.json
-/acls/cookbooks/
-/acls/cookbooks/x.json
-/acls/data_bags/
-/acls/data_bags/x.json
-/acls/environments/
-/acls/environments/x.json
-/acls/groups/
-/acls/groups/x.json
-/acls/nodes/
-/acls/nodes/x.json
-/acls/organization.json
-/acls/policies/
-/acls/policies/x.json
-/acls/policy_groups/
-/acls/policy_groups/x.json
-/acls/roles/
-/acls/roles/x.json
-/clients/
-/clients/x.json
-/containers/
-/containers/x.json
-/cookbook_artifacts/
-/cookbook_artifacts/x-111/
-/cookbook_artifacts/x-111/metadata.rb
-/cookbooks/
-/cookbooks/x/
-/cookbooks/x/metadata.rb
-/data_bags/
-/data_bags/x/
-/data_bags/x/y.json
-/environments/
-/environments/x.json
-/groups/
-/groups/x.json
-/invitations.json
-/members.json
-/nodes/
-/nodes/x.json
-/org.json
-/policies/
-/policies/x-111.json
-/policy_groups/
-/policy_groups/x.json
-/roles/
-/roles/x.json
-EOM
+ knife("list -z -Rfp /").should_succeed <<~EOM
+ /acls/
+ /acls/clients/
+ /acls/clients/x.json
+ /acls/containers/
+ /acls/containers/x.json
+ /acls/cookbook_artifacts/
+ /acls/cookbook_artifacts/x.json
+ /acls/cookbooks/
+ /acls/cookbooks/x.json
+ /acls/data_bags/
+ /acls/data_bags/x.json
+ /acls/environments/
+ /acls/environments/x.json
+ /acls/groups/
+ /acls/groups/x.json
+ /acls/nodes/
+ /acls/nodes/x.json
+ /acls/organization.json
+ /acls/policies/
+ /acls/policies/x.json
+ /acls/policy_groups/
+ /acls/policy_groups/x.json
+ /acls/roles/
+ /acls/roles/x.json
+ /clients/
+ /clients/x.json
+ /containers/
+ /containers/x.json
+ /cookbook_artifacts/
+ /cookbook_artifacts/x-111/
+ /cookbook_artifacts/x-111/metadata.rb
+ /cookbooks/
+ /cookbooks/x/
+ /cookbooks/x/metadata.rb
+ /data_bags/
+ /data_bags/x/
+ /data_bags/x/y.json
+ /environments/
+ /environments/x.json
+ /groups/
+ /groups/x.json
+ /invitations.json
+ /members.json
+ /nodes/
+ /nodes/x.json
+ /org.json
+ /policies/
+ /policies/x-111.json
+ /policy_groups/
+ /policy_groups/x.json
+ /roles/
+ /roles/x.json
+ EOM
end
end
@@ -185,47 +186,47 @@ EOM
end
it "knife raw -z -i empty.json -m PUT /clients/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_succeed( /"x"/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /clients/x").should_succeed( /"x"/ )
knife("list --local /clients").should_succeed "/clients/x.json\n"
end
it "knife cookbook upload works" do
- knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed :stderr => <<EOM
-Uploading x [1.0.0]
-Uploaded 1 cookbook.
-EOM
- knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/x/\n/cookbooks/x/metadata.rb\n"
+ knife("cookbook upload -z --cookbook-path #{path_to("cookbooks_to_upload")} x").should_succeed stderr: <<~EOM
+ Uploading x [1.0.0]
+ Uploaded 1 cookbook.
+ EOM
+ knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/x/\n/cookbooks/x/metadata.json\n/cookbooks/x/metadata.rb\n"
end
it "knife raw -z -i empty.json -m PUT /data/x/y" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_succeed( /"y"/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /data/x/y").should_succeed( /"y"/ )
knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/y.json\n"
end
it "knife raw -z -i empty.json -m PUT /environments/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_succeed( /"x"/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /environments/x").should_succeed( /"x"/ )
knife("list --local /environments").should_succeed "/environments/x.json\n"
end
it "knife raw -z -i dummynode.json -m PUT /nodes/x" do
- knife("raw -z -i #{path_to('dummynode.json')} -m PUT /nodes/x").should_succeed( /"x"/ )
+ knife("raw -z -i #{path_to("dummynode.json")} -m PUT /nodes/x").should_succeed( /"x"/ )
knife("list --local /nodes").should_succeed "/nodes/x.json\n"
knife("show -z /nodes/x.json --verbose").should_succeed(/"bar"/)
end
it "knife raw -z -i empty.json -m PUT /roles/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_succeed( /"x"/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /roles/x").should_succeed( /"x"/ )
knife("list --local /roles").should_succeed "/roles/x.json\n"
end
- it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ )
- expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
-{
- "name": "x",
- "description": "hi there"
-}
-EOM
+ it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty" do
+ knife("raw -z -i #{path_to("rolestuff.json")} -m PUT /roles/x").should_succeed( /"x"/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<~EOM.strip
+ {
+ "name": "x",
+ "description": "hi there"
+ }
+ EOM
end
end
end
@@ -242,86 +243,86 @@ EOM
end
it "knife raw -z -i empty.json -m POST /clients" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /clients").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty.json")} -m POST /clients").should_succeed( /uri/ )
knife("list --local /clients").should_succeed "/clients/z.json\n"
end
it "knife cookbook upload works" do
- knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed :stderr => <<EOM
-Uploading z [1.0.0]
-Uploaded 1 cookbook.
-EOM
- knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/z/\n/cookbooks/z/metadata.rb\n"
+ knife("cookbook upload -z --cookbook-path #{path_to("cookbooks_to_upload")} z").should_succeed stderr: <<~EOM
+ Uploading z [1.0.0]
+ Uploaded 1 cookbook.
+ EOM
+ knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/z/\n/cookbooks/z/metadata.json\n/cookbooks/z/metadata.rb\n"
end
it "knife raw -z -i empty.json -m POST /data" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /data").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty.json")} -m POST /data").should_succeed( /uri/ )
knife("list --local -Rfp /data_bags").should_succeed "/data_bags/z/\n"
end
it "knife raw -z -i empty.json -m POST /data/x" do
- knife("raw -z -i #{path_to('empty_x.json')} -m POST /data").should_succeed( /uri/ )
- knife("raw -z -i #{path_to('empty_id.json')} -m POST /data/x").should_succeed( /"z"/ )
+ knife("raw -z -i #{path_to("empty_x.json")} -m POST /data").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty_id.json")} -m POST /data/x").should_succeed( /"z"/ )
knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/z.json\n"
end
it "knife raw -z -i empty.json -m POST /environments" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /environments").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty.json")} -m POST /environments").should_succeed( /uri/ )
knife("list --local /environments").should_succeed "/environments/z.json\n"
end
it "knife raw -z -i dummynode.json -m POST /nodes" do
- knife("raw -z -i #{path_to('dummynode.json')} -m POST /nodes").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("dummynode.json")} -m POST /nodes").should_succeed( /uri/ )
knife("list --local /nodes").should_succeed "/nodes/z.json\n"
knife("show -z /nodes/z.json").should_succeed(/"bar"/)
end
it "knife raw -z -i empty.json -m POST /roles" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /roles").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty.json")} -m POST /roles").should_succeed( /uri/ )
knife("list --local /roles").should_succeed "/roles/z.json\n"
end
- it "After knife raw -z -i rolestuff.json -m POST /roles, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -z -i #{path_to('rolestuff.json')} -m POST /roles").should_succeed( /uri/ )
- expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
-{
- "name": "x",
- "description": "hi there"
-}
-EOM
+ it "After knife raw -z -i rolestuff.json -m POST /roles, the output is pretty" do
+ knife("raw -z -i #{path_to("rolestuff.json")} -m POST /roles").should_succeed( /uri/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<~EOM.strip
+ {
+ "name": "x",
+ "description": "hi there"
+ }
+ EOM
end
end
it "knife list -z -R returns nothing" do
- knife("list -z -Rfp /").should_succeed <<EOM
-/acls/
-/acls/clients/
-/acls/containers/
-/acls/cookbook_artifacts/
-/acls/cookbooks/
-/acls/data_bags/
-/acls/environments/
-/acls/groups/
-/acls/nodes/
-/acls/organization.json
-/acls/policies/
-/acls/policy_groups/
-/acls/roles/
-/clients/
-/containers/
-/cookbook_artifacts/
-/cookbooks/
-/data_bags/
-/environments/
-/groups/
-/invitations.json
-/members.json
-/nodes/
-/org.json
-/policies/
-/policy_groups/
-/roles/
-EOM
+ knife("list -z -Rfp /").should_succeed <<~EOM
+ /acls/
+ /acls/clients/
+ /acls/containers/
+ /acls/cookbook_artifacts/
+ /acls/cookbooks/
+ /acls/data_bags/
+ /acls/environments/
+ /acls/groups/
+ /acls/nodes/
+ /acls/organization.json
+ /acls/policies/
+ /acls/policy_groups/
+ /acls/roles/
+ /clients/
+ /containers/
+ /cookbook_artifacts/
+ /cookbooks/
+ /data_bags/
+ /environments/
+ /groups/
+ /invitations.json
+ /members.json
+ /nodes/
+ /org.json
+ /policies/
+ /policy_groups/
+ /roles/
+ EOM
end
context "DELETE /TYPE/NAME" do
@@ -388,23 +389,23 @@ EOM
end
it "knife raw -z -i empty.json -m PUT /clients/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /clients/x").should_fail( /404/ )
end
it "knife raw -z -i empty.json -m PUT /data/x/y fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /data/x/y").should_fail( /404/ )
end
it "knife raw -z -i empty.json -m PUT /environments/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /environments/x").should_fail( /404/ )
end
it "knife raw -z -i empty.json -m PUT /nodes/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /nodes/x").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /nodes/x").should_fail( /404/ )
end
it "knife raw -z -i empty.json -m PUT /roles/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /roles/x").should_fail( /404/ )
end
end
@@ -442,24 +443,24 @@ EOM
context "GET /TYPE" do
it "knife list -z -R returns everything" do
- knife("list -z -Rfp /").should_succeed <<EOM
-/clients/
-/clients/x.json
-/cookbooks/
-/cookbooks/x/
-/cookbooks/x/metadata.rb
-/data_bags/
-/data_bags/x/
-/data_bags/x/y.json
-/environments/
-/environments/x.json
-/nodes/
-/nodes/x.json
-/roles/
-/roles/x.json
-/users/
-/users/x.json
-EOM
+ knife("list -z -Rfp /").should_succeed <<~EOM
+ /clients/
+ /clients/x.json
+ /cookbooks/
+ /cookbooks/x/
+ /cookbooks/x/metadata.rb
+ /data_bags/
+ /data_bags/x/
+ /data_bags/x/y.json
+ /environments/
+ /environments/x.json
+ /nodes/
+ /nodes/x.json
+ /roles/
+ /roles/x.json
+ /users/
+ /users/x.json
+ EOM
end
end
@@ -485,18 +486,18 @@ EOM
end
it "knife raw -z -i empty.json -m PUT /users/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_succeed( /"x"/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /users/x").should_succeed( /"x"/ )
knife("list --local /users").should_succeed "/users/x.json\n"
end
- it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ )
- expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
-{
- "name": "x",
- "description": "hi there"
-}
-EOM
+ it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty" do
+ knife("raw -z -i #{path_to("rolestuff.json")} -m PUT /roles/x").should_succeed( /"x"/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<~EOM.strip
+ {
+ "name": "x",
+ "description": "hi there"
+ }
+ EOM
end
end
end
@@ -513,21 +514,21 @@ EOM
end
it "knife raw -z -i empty.json -m POST /users" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /users").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to("empty.json")} -m POST /users").should_succeed( /uri/ )
knife("list --local /users").should_succeed "/users/z.json\n"
end
end
it "knife list -z -R returns nothing" do
- knife("list -z -Rfp /").should_succeed <<EOM
-/clients/
-/cookbooks/
-/data_bags/
-/environments/
-/nodes/
-/roles/
-/users/
-EOM
+ knife("list -z -Rfp /").should_succeed <<~EOM
+ /clients/
+ /cookbooks/
+ /data_bags/
+ /environments/
+ /nodes/
+ /roles/
+ /users/
+ EOM
end
context "DELETE /TYPE/NAME" do
@@ -548,7 +549,7 @@ EOM
end
it "knife raw -z -i empty.json -m PUT /users/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_fail( /404/ )
+ knife("raw -z -i #{path_to("empty.json")} -m PUT /users/x").should_fail( /404/ )
end
end
end
diff --git a/spec/integration/knife/chef_repo_path_spec.rb b/spec/integration/knife/chef_repo_path_spec.rb
index 1388aa8716..ac7dae15f0 100644
--- a/spec/integration/knife/chef_repo_path_spec.rb
+++ b/spec/integration/knife/chef_repo_path_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/list"
@@ -59,93 +60,95 @@ describe "chef_repo_path tests", :workstation do
it "knife list --local -Rfp --chef-repo-path chef_repo2 / grabs chef_repo2 stuff" do
Chef::Config.delete(:chef_repo_path)
- knife("list --local -Rfp --chef-repo-path #{path_to('chef_repo2')} /").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
+ knife("list --local -Rfp --chef-repo-path #{path_to("chef_repo2")} /").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
+ # "Skipping for BK... As Windows 2019 has 8dot3name disabled by default"
+ it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only, :skip_buildkite 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
+ 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
+ # "Skipping for BK... As Windows 2019 has 8dot3name disabled by default"
+ it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only, :skip_buildkite 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
+ 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
@@ -157,24 +160,24 @@ EOM
end
it "knife list --local -Rfp --chef-repo-path chef_repo2 / grabs chef_repo2 stuff" do
- knife("list --local -Rfp --chef-repo-path #{path_to('chef_repo2')} /").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
+ knife("list --local -Rfp --chef-repo-path #{path_to("chef_repo2")} /").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
context "when cwd is at the top level" do
@@ -194,34 +197,34 @@ EOM
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client2.json
-cookbooks/
-cookbooks/cookbook2/
-cookbooks/cookbook2/metadata.rb
-data_bags/
-data_bags/bag2/
-data_bags/bag2/item2.json
-environments/
-environments/env2.json
-nodes/
-nodes/node2.json
-roles/
-roles/role2.json
-users/
-users/user2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client2.json
+ cookbooks/
+ cookbooks/cookbook2/
+ cookbooks/cookbook2/metadata.rb
+ data_bags/
+ data_bags/bag2/
+ data_bags/bag2/item2.json
+ environments/
+ environments/env2.json
+ nodes/
+ nodes/node2.json
+ roles/
+ roles/role2.json
+ users/
+ users/user2.json
+ EOM
end
end
context "when cwd is inside data_bags2" do
before { cwd "data_bags2" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag2/
-bag2/item2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag2/
+ bag2/item2.json
+ EOM
end
it "knife list --local -Rfp ../roles lists roles" do
knife("list --local -Rfp ../roles").should_succeed "/roles/role2.json\n"
@@ -239,24 +242,24 @@ EOM
context "when cwd is at the top level" do
before { cwd "." }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client2.json
-cookbooks/
-cookbooks/cookbook2/
-cookbooks/cookbook2/metadata.rb
-data_bags/
-data_bags/bag2/
-data_bags/bag2/item2.json
-environments/
-environments/env2.json
-nodes/
-nodes/node2.json
-roles/
-roles/role2.json
-users/
-users/user2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client2.json
+ cookbooks/
+ cookbooks/cookbook2/
+ cookbooks/cookbook2/metadata.rb
+ data_bags/
+ data_bags/bag2/
+ data_bags/bag2/item2.json
+ environments/
+ environments/env2.json
+ nodes/
+ nodes/node2.json
+ roles/
+ roles/role2.json
+ users/
+ users/user2.json
+ EOM
end
end
@@ -277,10 +280,10 @@ EOM
context "when cwd is inside data_bags2" do
before { cwd "data_bags2" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag2/
-bag2/item2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag2/
+ bag2/item2.json
+ EOM
end
end
end
@@ -310,34 +313,34 @@ EOM
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").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
+ knife("list --local -Rfp").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
end
context "when cwd is inside chef_repo2/data_bags" do
before { cwd "chef_repo2/data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag3/
+ bag3/item3.json
+ EOM
end
end
end
@@ -359,12 +362,12 @@ EOM
file "clients2/blah.json", {}
end
it "knife show /clients/blah.json succeeds" do
- knife("show --local /clients/blah.json").should_succeed <<EOM
-/clients/blah.json:
-{
+ knife("show --local /clients/blah.json").should_succeed <<~EOM
+ /clients/blah.json:
+ {
-}
-EOM
+ }
+ EOM
end
end
@@ -374,14 +377,14 @@ EOM
file "cookbooks2/blah/metadata.rb", ""
end
it "knife list -Rfp cookbooks shows files in blah" do
- knife("list --local -Rfp /cookbooks").should_succeed <<EOM
-/cookbooks/blah/
-/cookbooks/blah/metadata.rb
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-EOM
+ knife("list --local -Rfp /cookbooks").should_succeed <<~EOM
+ /cookbooks/blah/
+ /cookbooks/blah/metadata.rb
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ EOM
end
end
@@ -391,14 +394,14 @@ EOM
file "cookbooks2/blah/metadata.rb", ""
end
it "knife list -Rfp cookbooks shows files in blah" do
- knife("list --local -Rfp /cookbooks").should_succeed(<<EOM, :stderr => "WARN: Cookbook 'blah' is empty or entirely chefignored at #{Chef::Config.cookbook_path[0]}/blah\n")
-/cookbooks/blah/
-/cookbooks/blah/metadata.rb
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-EOM
+ knife("list --local -Rfp /cookbooks").should_succeed(<<~EOM, stderr: "WARN: Cookbook 'blah' is empty or entirely chefignored at #{Chef::Config.cookbook_path[0]}/blah\n")
+ /cookbooks/blah/
+ /cookbooks/blah/metadata.rb
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ EOM
end
end
@@ -408,14 +411,14 @@ EOM
file "cookbooks2/blah/metadata.rb", ""
end
it "knife list -Rfp cookbooks shows files in the first cookbook and not the second" do
- knife("list --local -Rfp /cookbooks").should_succeed(<<EOM, :stderr => "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.cookbook_path[0]}/blah and #{Chef::Config.cookbook_path[1]}/blah\n")
-/cookbooks/blah/
-/cookbooks/blah/metadata.json
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-EOM
+ knife("list --local -Rfp /cookbooks").should_succeed(<<~EOM, stderr: "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.cookbook_path[0]}/blah and #{Chef::Config.cookbook_path[1]}/blah\n")
+ /cookbooks/blah/
+ /cookbooks/blah/metadata.json
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ EOM
end
end
@@ -425,14 +428,14 @@ EOM
file "data_bags2/blah/item.json", ""
end
it "knife list -Rfp data_bags shows files in blah" do
- knife("list --local -Rfp /data_bags").should_succeed <<EOM
-/data_bags/bag/
-/data_bags/bag/item.json
-/data_bags/bag2/
-/data_bags/bag2/item2.json
-/data_bags/blah/
-/data_bags/blah/item.json
-EOM
+ knife("list --local -Rfp /data_bags").should_succeed <<~EOM
+ /data_bags/bag/
+ /data_bags/bag/item.json
+ /data_bags/bag2/
+ /data_bags/bag2/item2.json
+ /data_bags/blah/
+ /data_bags/blah/item.json
+ EOM
end
end
@@ -442,14 +445,14 @@ EOM
file "data_bags2/blah/item2.json", ""
end
it "knife list -Rfp data_bags shows only items in data_bags1" do
- knife("list --local -Rfp /data_bags").should_succeed(<<EOM, :stderr => "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.data_bag_path[0]}/blah and #{Chef::Config.data_bag_path[1]}/blah\n")
-/data_bags/bag/
-/data_bags/bag/item.json
-/data_bags/bag2/
-/data_bags/bag2/item2.json
-/data_bags/blah/
-/data_bags/blah/item1.json
-EOM
+ knife("list --local -Rfp /data_bags").should_succeed(<<~EOM, stderr: "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.data_bag_path[0]}/blah and #{Chef::Config.data_bag_path[1]}/blah\n")
+ /data_bags/bag/
+ /data_bags/bag/item.json
+ /data_bags/bag2/
+ /data_bags/bag2/item2.json
+ /data_bags/blah/
+ /data_bags/blah/item1.json
+ EOM
end
end
@@ -459,12 +462,12 @@ EOM
file "environments2/blah.json", {}
end
it "knife show /environments/blah.json succeeds" do
- knife("show --local /environments/blah.json").should_succeed <<EOM
-/environments/blah.json:
-{
+ knife("show --local /environments/blah.json").should_succeed <<~EOM
+ /environments/blah.json:
+ {
-}
-EOM
+ }
+ EOM
end
end
@@ -474,12 +477,12 @@ EOM
file "nodes2/blah.json", {}
end
it "knife show /nodes/blah.json succeeds" do
- knife("show --local /nodes/blah.json").should_succeed <<EOM
-/nodes/blah.json:
-{
+ knife("show --local /nodes/blah.json").should_succeed <<~EOM
+ /nodes/blah.json:
+ {
-}
-EOM
+ }
+ EOM
end
end
@@ -489,12 +492,12 @@ EOM
file "roles2/blah.json", {}
end
it "knife show /roles/blah.json succeeds" do
- knife("show --local /roles/blah.json").should_succeed <<EOM
-/roles/blah.json:
-{
+ knife("show --local /roles/blah.json").should_succeed <<~EOM
+ /roles/blah.json:
+ {
-}
-EOM
+ }
+ EOM
end
end
@@ -504,12 +507,12 @@ EOM
file "users2/blah.json", {}
end
it "knife show /users/blah.json succeeds" do
- knife("show --local /users/blah.json").should_succeed <<EOM
-/users/blah.json:
-{
+ knife("show --local /users/blah.json").should_succeed <<~EOM
+ /users/blah.json:
+ {
-}
-EOM
+ }
+ EOM
end
end
@@ -523,57 +526,57 @@ EOM
context "when cwd is inside the data_bags directory" do
before { cwd "data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag2/
-bag2/item2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag2/
+ bag2/item2.json
+ EOM
end
end
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client1.json
-clients/client2.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook2/
-cookbooks/cookbook2/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-data_bags/bag2/
-data_bags/bag2/item2.json
-environments/
-environments/env1.json
-environments/env2.json
-nodes/
-nodes/node1.json
-nodes/node2.json
-roles/
-roles/role1.json
-roles/role2.json
-users/
-users/user1.json
-users/user2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client1.json
+ clients/client2.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook2/
+ cookbooks/cookbook2/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ data_bags/bag2/
+ data_bags/bag2/item2.json
+ environments/
+ environments/env1.json
+ environments/env2.json
+ nodes/
+ nodes/node1.json
+ nodes/node2.json
+ roles/
+ roles/role1.json
+ roles/role2.json
+ users/
+ users/user1.json
+ users/user2.json
+ EOM
end
end
context "when cwd is inside data_bags2" do
before { cwd "data_bags2" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag2/
-bag2/item2.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag2/
+ bag2/item2.json
+ EOM
end
end
end
@@ -592,90 +595,90 @@ EOM
context "when cwd is at the top level" do
before { cwd "." }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client1.json
-clients/client3.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook3/
-cookbooks/cookbook3/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-data_bags/bag3/
-data_bags/bag3/item3.json
-environments/
-environments/env1.json
-environments/env3.json
-nodes/
-nodes/node1.json
-nodes/node3.json
-roles/
-roles/role1.json
-roles/role3.json
-users/
-users/user1.json
-users/user3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client1.json
+ clients/client3.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook3/
+ cookbooks/cookbook3/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ data_bags/bag3/
+ data_bags/bag3/item3.json
+ environments/
+ environments/env1.json
+ environments/env3.json
+ nodes/
+ nodes/node1.json
+ nodes/node3.json
+ roles/
+ roles/role1.json
+ roles/role3.json
+ users/
+ users/user1.json
+ users/user3.json
+ EOM
end
end
context "when cwd is inside the data_bags directory" do
before { cwd "data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag3/
+ bag3/item3.json
+ EOM
end
end
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client1.json
-clients/client3.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook3/
-cookbooks/cookbook3/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-data_bags/bag3/
-data_bags/bag3/item3.json
-environments/
-environments/env1.json
-environments/env3.json
-nodes/
-nodes/node1.json
-nodes/node3.json
-roles/
-roles/role1.json
-roles/role3.json
-users/
-users/user1.json
-users/user3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client1.json
+ clients/client3.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook3/
+ cookbooks/cookbook3/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ data_bags/bag3/
+ data_bags/bag3/item3.json
+ environments/
+ environments/env1.json
+ environments/env3.json
+ nodes/
+ nodes/node1.json
+ nodes/node3.json
+ roles/
+ roles/role1.json
+ roles/role3.json
+ users/
+ users/user1.json
+ users/user3.json
+ EOM
end
end
context "when cwd is inside chef_repo2/data_bags" do
before { cwd "chef_repo2/data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag3/
+ bag3/item3.json
+ EOM
end
end
end
@@ -706,34 +709,34 @@ EOM
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").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
+ knife("list --local -Rfp").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
end
context "when cwd is inside chef_repo2/data_bags" do
before { cwd "chef_repo2/data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag3/
+ bag3/item3.json
+ EOM
end
end
end
@@ -753,97 +756,97 @@ EOM
context "when cwd is at the top level" do
before { cwd "." }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client1.json
-clients/client3.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook3/
-cookbooks/cookbook3/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-data_bags/bag3/
-data_bags/bag3/item3.json
-environments/
-environments/env1.json
-environments/env3.json
-nodes/
-nodes/node1.json
-nodes/node3.json
-roles/
-roles/role1.json
-roles/role3.json
-users/
-users/user1.json
-users/user3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client1.json
+ clients/client3.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook3/
+ cookbooks/cookbook3/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ data_bags/bag3/
+ data_bags/bag3/item3.json
+ environments/
+ environments/env1.json
+ environments/env3.json
+ nodes/
+ nodes/node1.json
+ nodes/node3.json
+ roles/
+ roles/role1.json
+ roles/role3.json
+ users/
+ users/user1.json
+ users/user3.json
+ EOM
end
end
context "when cwd is inside the data_bags directory" do
before { cwd "data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag3/
+ bag3/item3.json
+ EOM
end
end
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client1.json
-clients/client3.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook3/
-cookbooks/cookbook3/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-data_bags/bag3/
-data_bags/bag3/item3.json
-environments/
-environments/env1.json
-environments/env3.json
-nodes/
-nodes/node1.json
-nodes/node3.json
-roles/
-roles/role1.json
-roles/role3.json
-users/
-users/user1.json
-users/user3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client1.json
+ clients/client3.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook3/
+ cookbooks/cookbook3/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ data_bags/bag3/
+ data_bags/bag3/item3.json
+ environments/
+ environments/env1.json
+ environments/env3.json
+ nodes/
+ nodes/node1.json
+ nodes/node3.json
+ roles/
+ roles/role1.json
+ roles/role3.json
+ users/
+ users/user1.json
+ users/user3.json
+ EOM
end
end
context "when cwd is inside chef_repo2/data_bags" do
before { cwd "chef_repo2/data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-bag3/
-bag3/item3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ bag3/
+ bag3/item3.json
+ EOM
end
end
end
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")
@@ -860,34 +863,34 @@ EOM
context "when cwd is inside the data_bags directory" do
before { cwd "data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ EOM
end
end
context "when cwd is inside chef_repo2" do
before { cwd "chef_repo2" }
it "knife list --local -Rfp lists everything" do
- knife("list --local -Rfp").should_succeed <<EOM
-clients/
-clients/client3.json
-cookbooks/
-cookbooks/cookbook3/
-cookbooks/cookbook3/metadata.rb
-data_bags/
-data_bags/bag/
-data_bags/bag/item.json
-environments/
-environments/env3.json
-nodes/
-nodes/node3.json
-roles/
-roles/role3.json
-users/
-users/user3.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ clients/
+ clients/client3.json
+ cookbooks/
+ cookbooks/cookbook3/
+ cookbooks/cookbook3/metadata.rb
+ data_bags/
+ data_bags/bag/
+ data_bags/bag/item.json
+ environments/
+ environments/env3.json
+ nodes/
+ nodes/node3.json
+ roles/
+ roles/role3.json
+ users/
+ users/user3.json
+ EOM
end
end
@@ -903,7 +906,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)
@@ -911,27 +914,27 @@ EOM
end
it "knife list --local -Rfp / lists data bags" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/data_bags/
-/data_bags/bag/
-/data_bags/bag/item.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /data_bags/
+ /data_bags/bag/
+ /data_bags/bag/item.json
+ EOM
end
it "knife list --local -Rfp /data_bags lists data bags" do
- knife("list --local -Rfp /data_bags").should_succeed <<EOM
-/data_bags/bag/
-/data_bags/bag/item.json
-EOM
+ knife("list --local -Rfp /data_bags").should_succeed <<~EOM
+ /data_bags/bag/
+ /data_bags/bag/item.json
+ EOM
end
context "when cwd is inside the data_bags directory" do
before { cwd "data_bags" }
it "knife list --local -Rfp lists data bags" do
- knife("list --local -Rfp").should_succeed <<EOM
-bag/
-bag/item.json
-EOM
+ knife("list --local -Rfp").should_succeed <<~EOM
+ bag/
+ bag/item.json
+ EOM
end
end
end
diff --git a/spec/integration/knife/chef_repository_file_system_spec.rb b/spec/integration/knife/chef_repository_file_system_spec.rb
index cc538c98c0..295efc0c3a 100644
--- a/spec/integration/knife/chef_repository_file_system_spec.rb
+++ b/spec/integration/knife/chef_repository_file_system_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/list"
require "chef/knife/show"
@@ -32,12 +33,12 @@ describe "General chef_repo file system checks", :workstation do
end
it "knife list --local -Rfp / returns them" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/data_bags/
-/data_bags/bag1/
-/environments/
-/roles/
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /data_bags/
+ /data_bags/bag1/
+ /environments/
+ /roles/
+ EOM
end
end
@@ -53,9 +54,9 @@ EOM
before { directory "cookbooks/cookbook1" }
it "knife list --local -Rfp / does not return it" do
- knife("list --local -Rfp /").should_succeed(<<EOM, :stderr => "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
-/cookbooks/
-EOM
+ knife("list --local -Rfp /").should_succeed(<<~EOM, stderr: "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
+ /cookbooks/
+ EOM
end
end
@@ -63,9 +64,9 @@ EOM
before { directory "cookbooks/cookbook1/recipes" }
it "knife list --local -Rfp / does not return it" do
- knife("list --local -Rfp /").should_succeed(<<EOM, :stderr => "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
-/cookbooks/
-EOM
+ knife("list --local -Rfp /").should_succeed(<<~EOM, stderr: "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
+ /cookbooks/
+ EOM
end
end
@@ -76,13 +77,13 @@ EOM
end
it "knife list --local -Rfp / does not return the empty ones" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/templates/
-/cookbooks/cookbook1/templates/default/
-/cookbooks/cookbook1/templates/default/x.txt
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/templates/
+ /cookbooks/cookbook1/templates/default/
+ /cookbooks/cookbook1/templates/default/x.txt
+ EOM
end
end
@@ -90,9 +91,9 @@ EOM
before { directory "cookbooks/cookbook1/templates/default" }
it "knife list --local -Rfp / does not return it" do
- knife("list --local -Rfp /").should_succeed(<<EOM, :stderr => "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
-/cookbooks/
-EOM
+ knife("list --local -Rfp /").should_succeed(<<~EOM, stderr: "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
+ /cookbooks/
+ EOM
end
end
@@ -104,13 +105,13 @@ EOM
end
it "knife list --local -Rfp / does not return the empty ones" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/templates/
-/cookbooks/cookbook1/templates/default/
-/cookbooks/cookbook1/templates/default/x.txt
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/templates/
+ /cookbooks/cookbook1/templates/default/
+ /cookbooks/cookbook1/templates/default/x.txt
+ EOM
end
end
@@ -146,130 +147,33 @@ EOM
end
it "knife list --local -Rfp / should NOT return them" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/environments/
-/environments/environment1.json
-/roles/
-/roles/role1.json
-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
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /environments/
+ /environments/environment1.json
+ /roles/
+ /roles/role1.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
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ EOM
end
end
when_the_repository "has a file in data_bags/" do
before { file "data_bags/file", "" }
it "does not show up in list -Rfp" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/data_bags/
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /data_bags/
+ EOM
end
end
end
diff --git a/spec/integration/knife/chefignore_spec.rb b/spec/integration/knife/chefignore_spec.rb
index aa5a3979cc..eccd38d928 100644
--- a/spec/integration/knife/chefignore_spec.rb
+++ b/spec/integration/knife/chefignore_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/list"
require "chef/knife/show"
@@ -45,18 +46,18 @@ describe "chefignore tests", :workstation do
it "matching files and directories get ignored" do
# NOTE: many of the "chefignore" files should probably not show up
# themselves, but we have other tests that talk about that
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/chefignore
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/x.json
-/environments/
-/environments/x.json
-/roles/
-/roles/x.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/chefignore
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/x.json
+ /environments/
+ /environments/x.json
+ /roles/
+ /roles/x.json
+ EOM
end
end
end
@@ -69,9 +70,9 @@ EOM
end
it "the cookbook is not listed" do
- knife("list --local -Rfp /").should_succeed(<<EOM, :stderr => "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
-/cookbooks/
-EOM
+ knife("list --local -Rfp /").should_succeed(<<~EOM, stderr: "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n")
+ /cookbooks/
+ EOM
end
end
@@ -87,13 +88,13 @@ EOM
before { file "cookbooks/chefignore", "x.json\n" }
it "matching files and directories get ignored in all cookbooks" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -104,13 +105,13 @@ EOM
end
it "matching files and directories get ignored in all cookbooks" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -122,17 +123,17 @@ EOM
end
it "matching directories get ignored" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/x.json
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/recipes/
-/cookbooks/cookbook2/recipes/y.rb
-/cookbooks/cookbook2/x.json
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/x.json
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/recipes/
+ /cookbooks/cookbook2/recipes/y.rb
+ /cookbooks/cookbook2/x.json
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -143,17 +144,17 @@ EOM
end
it "matching directories do NOT get ignored" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/recipes/
-/cookbooks/cookbook1/recipes/y.rb
-/cookbooks/cookbook1/x.json
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/x.json
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/recipes/
+ /cookbooks/cookbook1/recipes/y.rb
+ /cookbooks/cookbook1/x.json
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/x.json
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -165,15 +166,15 @@ EOM
end
it "ignores the subdirectory entirely" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/x.json
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/x.json
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/x.json
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/x.json
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -183,15 +184,15 @@ EOM
end
it "nothing is ignored" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/x.json
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/x.json
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/x.json
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/x.json
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
@@ -201,13 +202,13 @@ EOM
end
it "matching files and directories get ignored in all cookbooks" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/y.json
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/y.json
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/y.json
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/y.json
+ EOM
end
end
end
@@ -233,13 +234,13 @@ EOM
file "cookbooks2/chefignore", "x.json\n"
end
it "chefignores apply only to the directories they are in" do
- knife("list --local -Rfp /").should_succeed <<EOM
-/cookbooks/
-/cookbooks/mycookbook/
-/cookbooks/mycookbook/x.json
-/cookbooks/yourcookbook/
-/cookbooks/yourcookbook/metadata.rb
-EOM
+ knife("list --local -Rfp /").should_succeed <<~EOM
+ /cookbooks/
+ /cookbooks/mycookbook/
+ /cookbooks/mycookbook/x.json
+ /cookbooks/yourcookbook/
+ /cookbooks/yourcookbook/metadata.rb
+ EOM
end
context "and conflicting cookbooks" do
@@ -251,14 +252,14 @@ EOM
end
it "chefignores apply only to the winning cookbook" do
- knife("list --local -Rfp /").should_succeed(<<EOM, :stderr => "WARN: Child with name 'yourcookbook' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks1/yourcookbook and #{Chef::Config.chef_repo_path}/cookbooks2/yourcookbook\n")
-/cookbooks/
-/cookbooks/mycookbook/
-/cookbooks/mycookbook/x.json
-/cookbooks/yourcookbook/
-/cookbooks/yourcookbook/onlyincookbooks1.rb
-/cookbooks/yourcookbook/x.json
-EOM
+ knife("list --local -Rfp /").should_succeed(<<~EOM, stderr: "WARN: Child with name 'yourcookbook' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks1/yourcookbook and #{Chef::Config.chef_repo_path}/cookbooks2/yourcookbook\n")
+ /cookbooks/
+ /cookbooks/mycookbook/
+ /cookbooks/mycookbook/x.json
+ /cookbooks/yourcookbook/
+ /cookbooks/yourcookbook/onlyincookbooks1.rb
+ /cookbooks/yourcookbook/x.json
+ EOM
end
end
end
@@ -269,10 +270,10 @@ EOM
file "cookbooks/chefignore/metadata.rb", {}
end
it "knife list -Rfp /cookbooks shows it" do
- knife("list --local -Rfp /cookbooks").should_succeed <<EOM
-/cookbooks/chefignore/
-/cookbooks/chefignore/metadata.rb
-EOM
+ knife("list --local -Rfp /cookbooks").should_succeed <<~EOM
+ /cookbooks/chefignore/
+ /cookbooks/chefignore/metadata.rb
+ EOM
end
end
@@ -289,12 +290,12 @@ EOM
]
end
it "knife list -Rfp /cookbooks shows the chefignore cookbook" do
- knife("list --local -Rfp /cookbooks").should_succeed <<EOM
-/cookbooks/blah/
-/cookbooks/blah/metadata.rb
-/cookbooks/chefignore/
-/cookbooks/chefignore/metadata.rb
-EOM
+ knife("list --local -Rfp /cookbooks").should_succeed <<~EOM
+ /cookbooks/blah/
+ /cookbooks/blah/metadata.rb
+ /cookbooks/chefignore/
+ /cookbooks/chefignore/metadata.rb
+ EOM
end
end
end
diff --git a/spec/integration/knife/client_bulk_delete_spec.rb b/spec/integration/knife/client_bulk_delete_spec.rb
index a422401af6..5c0ff94867 100644
--- a/spec/integration/knife/client_bulk_delete_spec.rb
+++ b/spec/integration/knife/client_bulk_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -33,41 +34,41 @@ describe "knife client bulk delete", :workstation do
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
+ 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
+ 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
@@ -81,50 +82,50 @@ EOM
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:
+ knife("client bulk delete ^ca.*", input: "Y").should_succeed <<~EOM
+ The following clients are validators and will not be deleted:
-car-validator
+ car-validator
-You must specify --delete-validators to delete the validator clients
-The following clients will be deleted:
+ You must specify --delete-validators to delete the validator clients
+ The following clients will be deleted:
-car cat
+ car cat
-Are you sure you want to delete these clients? (Y/N) Deleted client car
-Deleted client cat
-EOM
+ 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
+ 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:
+ knife("client bulk delete ^ca.* -D", input: "Y\nY").should_succeed <<~EOM
+ The following validators will be deleted:
-car-validator
+ car-validator
-Are you sure you want to delete these validators? (Y/N) Deleted client car-validator
-The following clients will be deleted:
+ Are you sure you want to delete these validators? (Y/N) Deleted client car-validator
+ The following clients will be deleted:
-car cat
+ car cat
-Are you sure you want to delete these clients? (Y/N) Deleted client car
-Deleted client cat
-EOM
+ 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
+ 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
index 10172833c8..2e48cde7ab 100644
--- a/spec/integration/knife/client_create_spec.rb
+++ b/spec/integration/knife/client_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "openssl"
@@ -33,18 +34,18 @@ describe "knife client create", :workstation do
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
+ 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)
+ expect { knife("client create -k bah") }.to raise_error(Net::HTTPClientException)
end
it "saves the private key to a file" do
diff --git a/spec/integration/knife/client_delete_spec.rb b/spec/integration/knife/client_delete_spec.rb
index d135dd0a5b..76a3b9a686 100644
--- a/spec/integration/knife/client_delete_spec.rb
+++ b/spec/integration/knife/client_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -33,30 +34,30 @@ describe "knife client delete", :workstation do
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
+ 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
+ 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
+ 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
diff --git a/spec/integration/knife/client_key_create_spec.rb b/spec/integration/knife/client_key_create_spec.rb
index b588afbe50..b9838d6718 100644
--- a/spec/integration/knife/client_key_create_spec.rb
+++ b/spec/integration/knife/client_key_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "openssl"
@@ -38,12 +39,12 @@ describe "knife client key create", :workstation do
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}/
+ 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)
+ expect { knife("client key create -k new bah") }.to raise_error(Net::HTTPClientException)
end
it "saves the private key to a file" do
diff --git a/spec/integration/knife/client_key_delete_spec.rb b/spec/integration/knife/client_key_delete_spec.rb
index d5827aa545..2730ee8cae 100644
--- a/spec/integration/knife/client_key_delete_spec.rb
+++ b/spec/integration/knife/client_key_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -31,9 +32,9 @@ describe "knife client key delete", :workstation do
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 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
diff --git a/spec/integration/knife/client_key_list_spec.rb b/spec/integration/knife/client_key_list_spec.rb
index de9894622e..773445eca9 100644
--- a/spec/integration/knife/client_key_list_spec.rb
+++ b/spec/integration/knife/client_key_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "date"
@@ -41,11 +42,11 @@ describe "knife client key list", :workstation do
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
+ 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
diff --git a/spec/integration/knife/client_key_show_spec.rb b/spec/integration/knife/client_key_show_spec.rb
index e96ff3b6fe..ee17fc3e5a 100644
--- a/spec/integration/knife/client_key_show_spec.rb
+++ b/spec/integration/knife/client_key_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "date"
diff --git a/spec/integration/knife/client_list_spec.rb b/spec/integration/knife/client_list_spec.rb
index 4159df73f1..f7875b44af 100644
--- a/spec/integration/knife/client_list_spec.rb
+++ b/spec/integration/knife/client_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -33,15 +34,15 @@ describe "knife client list", :workstation do
end
it "lists the clients" do
- knife("client list").should_succeed <<EOM
-car
-car-validator
-cat
-cdr
-chef-validator
-chef-webui
-cons
-EOM
+ knife("client list").should_succeed <<~EOM
+ car
+ car-validator
+ cat
+ cdr
+ chef-validator
+ chef-webui
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/client_show_spec.rb b/spec/integration/knife/client_show_spec.rb
index 23ac204d77..1520575e48 100644
--- a/spec/integration/knife/client_show_spec.rb
+++ b/spec/integration/knife/client_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
diff --git a/spec/integration/knife/common_options_spec.rb b/spec/integration/knife/common_options_spec.rb
index c941dcc1ee..468b7af8be 100644
--- a/spec/integration/knife/common_options_spec.rb
+++ b/spec/integration/knife/common_options_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/raw"
@@ -22,6 +23,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.*$/im }
+
when_the_repository "has a node" do
before { file "nodes/x.json", {} }
@@ -30,15 +39,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,107 +62,112 @@ 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
context "and there is a private key" do
before do
- file "mykey.pem", <<EOM
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
-0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
-NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
-0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
-AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
-HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
-8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
-zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
-k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
-i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
-G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
-ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
-awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
-7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
-g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
-Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
-HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
-V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
-fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
-lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
-c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
-fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
-YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
-syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
-+vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
------END RSA PRIVATE KEY-----
-EOM
+ file "mykey.pem", <<~EOM
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
+ 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
+ NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
+ 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
+ AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
+ HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
+ 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
+ zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
+ k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
+ i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
+ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
+ ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
+ awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
+ 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
+ g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
+ Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
+ HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
+ V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
+ fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
+ lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
+ c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
+ fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
+ YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
+ syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
+ +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
+ -----END RSA PRIVATE KEY-----
+ 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
context "when the default port (8889) is already bound" do
before :each do
- begin
- @server = ChefZero::Server.new(:host => "localhost", :port => 8889)
- @server.start_background
- rescue Errno::EADDRINUSE
- # OK. Don't care who has it in use, as long as *someone* does.
- end
+
+ @server = ChefZero::Server.new(host: "localhost", port: 8889)
+ @server.start_background
+ rescue Errno::EADDRINUSE
+ # OK. Don't care who has it in use, as long as *someone* does.
+
end
after :each do
@server.stop if @server
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
context "when port 9999 is already bound" do
before :each do
- begin
- @server = ChefZero::Server.new(:host => "localhost", :port => 9999)
- @server.start_background
- rescue Errno::EADDRINUSE
- # OK. Don't care who has it in use, as long as *someone* does.
- end
+
+ @server = ChefZero::Server.new(host: "localhost", port: 9999)
+ @server.start_background
+ rescue Errno::EADDRINUSE
+ # OK. Don't care who has it in use, as long as *someone* does.
+
end
after :each do
@server.stop if @server
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/config_list_spec.rb b/spec/integration/knife/config_list_spec.rb
new file mode 100644
index 0000000000..b05350ed87
--- /dev/null
+++ b/spec/integration/knife/config_list_spec.rb
@@ -0,0 +1,220 @@
+#
+# Copyright 2018, Noah Kantrowitz
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife config list", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ when_the_repository("has a custom env") do
+ let(:cmd_args) { [] }
+ let(:knife_list) do
+ knife("config", "list", *cmd_args, instance_filter: lambda { |instance|
+ # Fake the failsafe check because this command doesn't actually process knife.rb.
+ $__KNIFE_INTEGRATION_FAILSAFE_CHECK << " ole"
+ allow(File).to receive(:file?).and_call_original
+ })
+ end
+ subject { knife_list.stdout }
+
+ around do |ex|
+ # Store and reset the value of some env vars.
+ old_home = ENV["HOME"]
+ old_wd = Dir.pwd
+ # Clear these out because they are cached permanently.
+ ChefConfig::PathHelper.class_exec { remove_class_variable(:@@home_dir) }
+ Chef::Knife::ConfigList.reset_config_loader!
+ begin
+ ex.run
+ ensure
+ ENV["HOME"] = old_home
+ Dir.chdir(old_wd)
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ end
+ end
+
+ before do
+ # Always run from the temp folder. This can't be in the `around` block above
+ # because it has to run after the before set in the "with a chef repo" shared context.
+ directory("repo")
+ Dir.chdir(path_to("repo"))
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ ENV["HOME"] = path_to(".")
+ allow(TTY::Screen).to receive(:width).and_return(200)
+ end
+
+ # NOTE: The funky formatting with # at the end of the line of some of the
+ # output examples are because of how the format strings are built, there is
+ # substantial trailing whitespace in most cases which many editors "helpfully" remove.
+
+ context "with no credentials file" do
+ subject { knife_list.stderr }
+ it { is_expected.to eq "FATAL: No profiles found, #{path_to(".chef/credentials")} does not exist or is empty\n" }
+ end
+
+ context "with an empty credentials file" do
+ before { file(".chef/credentials", "") }
+ subject { knife_list.stderr }
+ it { is_expected.to eq "FATAL: No profiles found, #{path_to(".chef/credentials")} does not exist or is empty\n" }
+ end
+
+ context "with a simple default profile" do
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to eq <<~EOH.delete("#") }
+ Profile Client Key Server #
+ --------------------------------------------------------------------------------#
+ *default testuser ~/.chef/testkey.pem https://example.com/organizations/testorg #
+ EOH
+ end
+
+ context "with multiple profiles" do
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+
+ [prod]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/prod"
+
+ [qa]
+ client_name = "qauser"
+ client_key = "~/src/qauser.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to eq <<~EOH.delete("#") }
+ Profile Client Key Server #
+ --------------------------------------------------------------------------------#
+ *default testuser ~/.chef/testkey.pem https://example.com/organizations/testorg #
+ prod testuser ~/.chef/testkey.pem https://example.com/organizations/prod #
+ qa qauser ~/src/qauser.pem https://example.com/organizations/testorg #
+ EOH
+ end
+
+ context "with a non-default active profile" do
+ let(:cmd_args) { %w{--profile prod} }
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+
+ [prod]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/prod"
+
+ [qa]
+ client_name = "qauser"
+ client_key = "~/src/qauser.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to eq <<~EOH.delete("#") }
+ Profile Client Key Server #
+ --------------------------------------------------------------------------------#
+ default testuser ~/.chef/testkey.pem https://example.com/organizations/testorg #
+ *prod testuser ~/.chef/testkey.pem https://example.com/organizations/prod #
+ qa qauser ~/src/qauser.pem https://example.com/organizations/testorg #
+ EOH
+ end
+
+ context "with a bad profile as an active profile" do
+ let(:cmd_args) { %w{--profile production} }
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+
+ [prod]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/prod"
+
+ [qa]
+ client_name = "qauser"
+ client_key = "~/src/qauser.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to eq <<~EOH.delete("#") }
+ Profile Client Key Server #
+ --------------------------------------------------------------------------------#
+ default testuser ~/.chef/testkey.pem https://example.com/organizations/testorg #
+ prod testuser ~/.chef/testkey.pem https://example.com/organizations/prod #
+ qa qauser ~/src/qauser.pem https://example.com/organizations/testorg #
+ EOH
+ end
+
+ context "with a minimal profile" do
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to match %r{^*default .*? https://example.com/organizations/testorg} }
+ end
+
+ context "with -i" do
+ let(:cmd_args) { %w{-i} }
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it { is_expected.to eq <<~EOH.delete("#") }
+ Profile Client Key Server #
+ --------------------------------------------------------------#
+ *default https://example.com/organizations/testorg #
+ EOH
+ end
+
+ context "with --format=json" do
+ let(:cmd_args) { %w{--format=json node_name} }
+ before { file(".chef/credentials", <<~EOH) }
+ [default]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+
+ [prod]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/prod"
+
+ [qa]
+ client_name = "qauser"
+ client_key = "~/src/qauser.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it {
+ expect(JSON.parse(subject)).to eq [
+ { "profile" => "default", "active" => true, "client_name" => "testuser", "client_key" => path_to(".chef/testkey.pem"), "server_url" => "https://example.com/organizations/testorg" },
+ { "profile" => "prod", "active" => false, "client_name" => "testuser", "client_key" => path_to(".chef/testkey.pem"), "server_url" => "https://example.com/organizations/prod" },
+ { "profile" => "qa", "active" => false, "client_name" => "qauser", "client_key" => path_to("src/qauser.pem"), "server_url" => "https://example.com/organizations/testorg" },
+ ]
+ }
+ end
+ end
+end
diff --git a/spec/integration/knife/config_show_spec.rb b/spec/integration/knife/config_show_spec.rb
new file mode 100644
index 0000000000..9e6ff73aa1
--- /dev/null
+++ b/spec/integration/knife/config_show_spec.rb
@@ -0,0 +1,192 @@
+#
+# Copyright 2018, Noah Kantrowitz
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife config show", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:cmd_args) { [] }
+
+ when_the_repository("has a custom env") do
+ subject do
+ cmd = knife("config", "show", *cmd_args, instance_filter: lambda { |instance|
+ # Clear the stub set up in KnifeSupport.
+ allow(File).to receive(:file?).and_call_original
+ # Lies, damn lies, and config files. We need to allow normal config loading
+ # behavior to be able to test stuff.
+ instance.config.delete(:config_file)
+ $__KNIFE_INTEGRATION_FAILSAFE_CHECK << " ole"
+ })
+ cmd.stdout
+ end
+
+ around do |ex|
+ # Store and reset the value of some env vars.
+ old_chef_home = ENV["CHEF_HOME"]
+ old_knife_home = ENV["KNIFE_HOME"]
+ old_home = ENV["HOME"]
+ old_wd = Dir.pwd
+ ChefConfig::PathHelper.per_tool_home_environment = "KNIFE_HOME"
+ # Clear these out because they are cached permanently.
+ ChefConfig::PathHelper.class_exec { remove_class_variable(:@@home_dir) }
+ Chef::Knife::ConfigShow.reset_config_loader!
+ begin
+ ex.run
+ ensure
+ ENV["CHEF_HOME"] = old_chef_home
+ ENV["KNIFE_HOME"] = old_knife_home
+ ENV["HOME"] = old_home
+ Dir.chdir(old_wd)
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ ChefConfig::PathHelper.per_tool_home_environment = nil
+ end
+ end
+
+ before do
+ # Always run from the temp folder. This can't be in the `around` block above
+ # because it has to run after the before set in the "with a chef repo" shared context.
+ directory("repo")
+ Dir.chdir(path_to("repo"))
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ ENV["HOME"] = path_to(".")
+ end
+
+ context "with a global knife.rb" do
+ before { file(".chef/knife.rb", "node_name 'one'\n") }
+
+ it { is_expected.to match(%r{^Loading from configuration file .*/#{File.basename(path_to("."))}/.chef/knife.rb$}) }
+ it { is_expected.to match(/^node_name:\s+one$/) }
+ end
+
+ context "with a repo knife.rb" do
+ before { file("repo/.chef/knife.rb", "node_name 'two'\n") }
+
+ it { is_expected.to match(%r{^Loading from configuration file .*/#{File.basename(path_to("."))}/repo/.chef/knife.rb$}) }
+ it { is_expected.to match(/^node_name:\s+two$/) }
+ end
+
+ context "with both knife.rb" do
+ before do
+ file(".chef/knife.rb", "node_name 'one'\n")
+ file("repo/.chef/knife.rb", "node_name 'two'\n")
+ end
+
+ it { is_expected.to match(%r{^Loading from configuration file .*/#{File.basename(path_to("."))}/repo/.chef/knife.rb$}) }
+ it { is_expected.to match(/^node_name:\s+two$/) }
+ end
+
+ context "with a credentials file" do
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { is_expected.to match(%r{^Loading from credentials file .*/#{File.basename(path_to("."))}/.chef/credentials$}) }
+ it { is_expected.to match(/^node_name:\s+three$/) }
+ end
+
+ context "with a credentials file and knife.rb" do
+ before do
+ file(".chef/knife.rb", "node_name 'one'\n")
+ file(".chef/credentials", "[default]\nclient_name = \"three\"\n")
+ end
+
+ it { is_expected.to match(%r{^Loading from configuration file .*/#{File.basename(path_to("."))}/.chef/knife.rb$}) }
+ it { is_expected.to match(%r{^Loading from credentials file .*/#{File.basename(path_to("."))}/.chef/credentials$}) }
+ it { is_expected.to match(/^node_name:\s+one$/) }
+ end
+
+ context "with a config dot d files" do
+ before { file(".chef/config.d/abc.rb", "node_name 'one'\n") }
+
+ it { is_expected.to match(%r{^Loading from .d/ configuration file .*/#{File.basename(path_to("."))}/.chef/config.d/abc.rb$}) }
+ it { is_expected.to match(/^node_name:\s+one$/) }
+ end
+
+ context "with a credentials file and CHEF_HOME" do
+ before do
+ file(".chef/credentials", "[default]\nclient_name = \"three\"\n")
+ file("foo/.chef/credentials", "[default]\nclient_name = \"four\"\n")
+ ENV["CHEF_HOME"] = path_to("foo")
+ end
+
+ it { is_expected.to match(%r{^Loading from credentials file .*/#{File.basename(path_to("."))}/foo/.chef/credentials$}) }
+ it { is_expected.to match(/^node_name:\s+four$/) }
+ end
+
+ context "with a credentials file and KNIFE_HOME" do
+ before do
+ file(".chef/credentials", "[default]\nclient_name = \"three\"\n")
+ file("bar/.chef/credentials", "[default]\nclient_name = \"four\"\n")
+ ENV["KNIFE_HOME"] = path_to("bar")
+ end
+
+ it { is_expected.to match(%r{^Loading from credentials file .*/#{File.basename(path_to("."))}/bar/.chef/credentials$}) }
+ it { is_expected.to match(/^node_name:\s+four$/) }
+ end
+
+ context "with single argument" do
+ let(:cmd_args) { %w{node_name} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { is_expected.to match(/^node_name:\s+three\Z/) }
+ end
+
+ context "with two arguments" do
+ let(:cmd_args) { %w{node_name client_key} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\nclient_key = \"three.pem\"") }
+
+ it { is_expected.to match(%r{^client_key:\s+\S*/.chef/three.pem\nnode_name:\s+three\Z}) }
+ end
+
+ context "with a dotted argument" do
+ let(:cmd_args) { %w{knife.ssh_user} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n[default.knife]\nssh_user = \"foo\"\n") }
+
+ it { is_expected.to match(/^knife.ssh_user:\s+foo\Z/) }
+ end
+
+ context "with regex argument" do
+ let(:cmd_args) { %w{/name/} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { is_expected.to match(/^node_name:\s+three\Z/) }
+ end
+
+ context "with --all" do
+ let(:cmd_args) { %w{-a /key_contents/} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { is_expected.to match(/^client_key_contents:\s+\nvalidation_key_contents:\s+\Z/) }
+ end
+
+ context "with --raw" do
+ let(:cmd_args) { %w{-r node_name} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { is_expected.to eq("three\n") }
+ end
+
+ context "with --format=json" do
+ let(:cmd_args) { %w{--format=json node_name} }
+ before { file(".chef/credentials", "[default]\nclient_name = \"three\"\n") }
+
+ it { expect(JSON.parse(subject)).to eq({ "node_name" => "three" }) }
+ end
+ end
+end
diff --git a/spec/integration/knife/config_use_spec.rb b/spec/integration/knife/config_use_spec.rb
new file mode 100644
index 0000000000..0431729b25
--- /dev/null
+++ b/spec/integration/knife/config_use_spec.rb
@@ -0,0 +1,198 @@
+#
+# Copyright 2018, Noah Kantrowitz
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife config use", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:cmd_args) { [] }
+
+ when_the_repository("has a custom env") do
+ let(:knife_use) do
+ knife("config", "use", *cmd_args, instance_filter: lambda { |instance|
+ # Fake the failsafe check because this command doesn't actually process knife.rb.
+ $__KNIFE_INTEGRATION_FAILSAFE_CHECK << " ole"
+ allow(File).to receive(:file?).and_call_original
+ })
+ end
+
+ subject { knife_use.stdout }
+
+ around do |ex|
+ # Store and reset the value of some env vars.
+ old_chef_home = ENV["CHEF_HOME"]
+ old_knife_home = ENV["KNIFE_HOME"]
+ old_home = ENV["HOME"]
+ old_wd = Dir.pwd
+ ChefConfig::PathHelper.per_tool_home_environment = "KNIFE_HOME"
+ # Clear these out because they are cached permanently.
+ ChefConfig::PathHelper.class_exec { remove_class_variable(:@@home_dir) }
+ Chef::Knife::ConfigUse.reset_config_loader!
+ begin
+ ex.run
+ ensure
+ ENV["CHEF_HOME"] = old_chef_home
+ ENV["KNIFE_HOME"] = old_knife_home
+ ENV["HOME"] = old_home
+ Dir.chdir(old_wd)
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ ChefConfig::PathHelper.per_tool_home_environment = nil
+ end
+ end
+
+ before do
+ # Always run from the temp folder. This can't be in the `around` block above
+ # because it has to run after the before set in the "with a chef repo" shared context.
+ directory("repo")
+ Dir.chdir(path_to("repo"))
+ ENV[ChefUtils.windows? ? "CD" : "PWD"] = Dir.pwd
+ ENV["HOME"] = path_to(".")
+ end
+
+ context "with no argument" do
+ context "with no configuration" do
+ it { is_expected.to eq "default\n" }
+ end
+
+ context "with --profile" do
+ let(:cmd_args) { %w{--profile production} }
+ it { is_expected.to eq "production\n" }
+ end
+
+ context "with an environment variable" do
+ around do |ex|
+ old_chef_profile = ENV["CHEF_PROFILE"]
+ begin
+ ENV["CHEF_PROFILE"] = "staging"
+ ex.run
+ ensure
+ ENV["CHEF_PROFILE"] = old_chef_profile
+ end
+ end
+
+ it { is_expected.to eq "staging\n" }
+ end
+
+ context "with a context file" do
+ before { file(".chef/context", "development\n") }
+ it { is_expected.to eq "development\n" }
+ end
+
+ context "with a context file under $CHEF_HOME" do
+ before do
+ file("chefhome/.chef/context", "other\n")
+ ENV["CHEF_HOME"] = path_to("chefhome")
+ end
+
+ it { is_expected.to eq "other\n" }
+ end
+
+ context "with a context file under $KNIFE_HOME" do
+ before do
+ file("knifehome/.chef/context", "other\n")
+ ENV["KNIFE_HOME"] = path_to("knifehome")
+ end
+
+ it { is_expected.to eq "other\n" }
+ end
+ end
+
+ context "with an argument" do
+ let(:cmd_args) { %w{production} }
+ before { file(".chef/credentials", <<~EOH) }
+ [production]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ it do
+ is_expected.to eq "Set default profile to production\n"
+ expect(File.read(path_to(".chef/context"))).to eq "production\n"
+ end
+ end
+
+ context "with no credentials file" do
+ let(:cmd_args) { %w{production} }
+ subject { knife_use.stderr }
+ it { is_expected.to eq "FATAL: No profiles found, #{path_to(".chef/credentials")} does not exist or is empty\n" }
+ end
+
+ context "with an empty credentials file" do
+ let(:cmd_args) { %w{production} }
+ before { file(".chef/credentials", "") }
+ subject { knife_use.stderr }
+ it { is_expected.to eq "FATAL: No profiles found, #{path_to(".chef/credentials")} does not exist or is empty\n" }
+ end
+
+ context "with an wrong argument" do
+ let(:cmd_args) { %w{staging} }
+ before { file(".chef/credentials", <<~EOH) }
+ [production]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ subject { knife_use }
+ it { expect { subject }.to raise_error ChefConfig::ConfigurationError, "Profile staging doesn't exist. Please add it to #{path_to(".chef/credentials")} and if it is profile with DNS name check that you are not missing single quotes around it as per docs https://docs.chef.io/workstation/knife_setup/#knife-profiles." }
+ end
+
+ context "with $CHEF_HOME" do
+ let(:cmd_args) { %w{staging} }
+ before do
+ ENV["CHEF_HOME"] = path_to("chefhome"); file("chefhome/tmp", "")
+ file("chefhome/.chef/credentials", <<~EOH
+ [staging]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ )
+ end
+
+ it do
+ is_expected.to eq "Set default profile to staging\n"
+ expect(File.read(path_to("chefhome/.chef/context"))).to eq "staging\n"
+ expect(File.exist?(path_to(".chef/context"))).to be_falsey
+ end
+ end
+
+ context "with $KNIFE_HOME" do
+ let(:cmd_args) { %w{development} }
+
+ before do
+ ENV["KNIFE_HOME"] = path_to("knifehome"); file("knifehome/tmp", "")
+ file("knifehome/.chef/credentials", <<~EOH
+ [development]
+ client_name = "testuser"
+ client_key = "testkey.pem"
+ chef_server_url = "https://example.com/organizations/testorg"
+ EOH
+ )
+ end
+
+ it do
+ is_expected.to eq "Set default profile to development\n"
+ expect(File.read(path_to("knifehome/.chef/context"))).to eq "development\n"
+ expect(File.exist?(path_to(".chef/context"))).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/integration/knife/cookbook_api_ipv6_spec.rb b/spec/integration/knife/cookbook_api_ipv6_spec.rb
index 0a4a6a6e94..b65cdc697b 100644
--- a/spec/integration/knife/cookbook_api_ipv6_spec.rb
+++ b/spec/integration/knife/cookbook_api_ipv6_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -23,45 +24,45 @@ describe "Knife cookbook API integration with IPv6", :workstation, :not_supporte
include Chef::Mixin::ShellOut
when_the_chef_server "is bound to IPv6" do
- let(:chef_zero_opts) { { :host => "::1" } }
+ let(:chef_zero_opts) { { host: "::1" } }
let(:client_key) do
- <<-END_VALIDATION_PEM
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
-0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
-NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
-0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
-AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
-HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
-8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
-zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
-k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
-i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
-G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
-ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
-awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
-7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
-g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
-Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
-HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
-V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
-fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
-lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
-c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
-fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
-YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
-syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
-+vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
------END RSA PRIVATE KEY-----
-END_VALIDATION_PEM
+ <<~END_VALIDATION_PEM
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf
+ 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk
+ NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn
+ 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O
+ AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP
+ HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom
+ 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB
+ zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx
+ k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb
+ i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ
+ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV
+ ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL
+ awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK
+ 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns
+ g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr
+ Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy
+ HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2
+ V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO
+ fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN
+ lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr
+ c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo
+ fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV
+ YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL
+ syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T
+ +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA=
+ -----END RSA PRIVATE KEY-----
+ END_VALIDATION_PEM
end
let(:cache_path) do
Dir.mktmpdir
end
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
let(:knife) { "ruby '#{chef_dir}/knife'" }
let(:knife_config_flag) { "-c '#{path_to("config/knife.rb")}'" }
@@ -74,13 +75,13 @@ END_VALIDATION_PEM
when_the_repository "has the cookbook to be uploaded" do
let(:knife_rb_content) do
- <<-END_CLIENT_RB
-chef_server_url "http://[::1]:8900"
-syntax_check_cache_path '#{cache_path}'
-client_key '#{path_to('config/knifeuser.pem')}'
-node_name 'whoisthisis'
-cookbook_path '#{CHEF_SPEC_DATA}/cookbooks'
-END_CLIENT_RB
+ <<~END_CLIENT_RB
+ chef_server_url "http://[::1]:8900"
+ syntax_check_cache_path '#{cache_path}'
+ client_key '#{path_to("config/knifeuser.pem")}'
+ node_name 'whoisthisis'
+ cookbook_path '#{CHEF_SPEC_DATA}/cookbooks'
+ END_CLIENT_RB
end
before do
@@ -89,7 +90,7 @@ END_CLIENT_RB
end
it "successfully uploads a cookbook" do
- shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", :cwd => chef_dir)
+ shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", cwd: chef_dir)
versions_list_json = Chef::HTTP::Simple.new("http://[::1]:8900").get("/cookbooks/apache2", "accept" => "application/json")
versions_list = Chef::JSONCompat.from_json(versions_list_json)
expect(versions_list["apache2"]["versions"]).not_to be_empty
@@ -97,11 +98,11 @@ END_CLIENT_RB
context "and the cookbook has been uploaded to the server" do
before do
- shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", :cwd => chef_dir)
+ shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", cwd: chef_dir)
end
it "downloads the cookbook" do
- shell_out!("knife cookbook download apache2 #{knife_config_flag} -d #{cache_path}", :cwd => chef_dir)
+ shell_out!("knife cookbook download apache2 #{knife_config_flag} -d #{cache_path}", cwd: chef_dir)
expect(Dir["#{cache_path}/*"].map { |entry| File.basename(entry) }).to include("apache2-0.0.1")
end
end
diff --git a/spec/integration/knife/cookbook_bulk_delete_spec.rb b/spec/integration/knife/cookbook_bulk_delete_spec.rb
index 4740813ce1..677a6aaa31 100644
--- a/spec/integration/knife/cookbook_bulk_delete_spec.rb
+++ b/spec/integration/knife/cookbook_bulk_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/cookbook_bulk_delete"
@@ -34,31 +35,31 @@ describe "knife cookbook bulk delete", :workstation do
cookbook "zfa", "0.6.5"
end
- # rubocop:disable Style/TrailingWhitespace
+ # 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:
+ stdout = <<~EOM
+ All versions of the following cookbooks will be deleted:
+
+ foo fox
+
+ Do you really want to delete these cookbooks? (Y/N)
+ EOM
-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
+ 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
+ knife("cookbook list -a").should_succeed <<~EOM
+ fax 0.6.0
+ zfa 0.6.5
+ EOM
end
- # rubocop:enable Style/TrailingWhitespace
+ # rubocop:enable Layout/TrailingWhitespace
end
end
diff --git a/spec/integration/knife/cookbook_download_spec.rb b/spec/integration/knife/cookbook_download_spec.rb
index 2fbffb9dea..1cc05c909a 100644
--- a/spec/integration/knife/cookbook_download_spec.rb
+++ b/spec/integration/knife/cookbook_download_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/cookbook_download"
@@ -33,39 +34,23 @@ describe "knife cookbook download", :workstation do
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 resources
-Downloading providers
-Downloading recipes
-Downloading definitions
-Downloading libraries
-Downloading attributes
-Downloading files
-Downloading templates
-Downloading root_files
-Cookbook downloaded to #{tmpdir}/x-1.0.1
-EOM
+ 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 resources
-Downloading providers
-Downloading recipes
-Downloading definitions
-Downloading libraries
-Downloading attributes
-Downloading files
-Downloading templates
-Downloading root_files
-Cookbook downloaded to #{tmpdir}/x-1.0.1
-EOM
+ 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)
+ expect { knife("cookbook download -d #{tmpdir} x 1.0.0") }.to raise_error(Net::HTTPClientException)
end
end
@@ -76,20 +61,12 @@ EOM
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 resources
-Downloading providers
-Downloading recipes
-Downloading definitions
-Downloading libraries
-Downloading attributes
-Downloading files
-Downloading templates
-Downloading root_files
-Cookbook downloaded to #{tmpdir}/x-1.0.1
-EOM
-)
+ 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
index 65578696f2..c94df52272 100644
--- a/spec/integration/knife/cookbook_list_spec.rb
+++ b/spec/integration/knife/cookbook_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/cookbook_list"
@@ -35,19 +36,19 @@ describe "knife cookbook list", :workstation do
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
+ 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
+ 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
diff --git a/spec/integration/knife/cookbook_show_spec.rb b/spec/integration/knife/cookbook_show_spec.rb
index c001d66b97..57701d4426 100644
--- a/spec/integration/knife/cookbook_show_spec.rb
+++ b/spec/integration/knife/cookbook_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/cookbook_show"
@@ -34,111 +35,100 @@ describe "knife cookbook show", :workstation do
knife("cookbook show x").should_succeed "x 1.0.0 0.6.5\n"
end
- # rubocop:disable Style/TrailingWhitespace
+ # 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
-attributes:
-chef_type: cookbook_version
-cookbook_name: x
-definitions:
-files:
-frozen?: false
-json_class: Chef::CookbookVersion
-libraries:
-metadata:
- attributes:
- chef_versions:
- conflicting:
- dependencies:
- description:
- gems:
- groupings:
- issues_url:
- license: All rights reserved
- long_description:
- maintainer:
- maintainer_email:
- name: x
- ohai_versions:
- platforms:
- privacy: false
- providing:
- recipes:
- recommendations:
- replacing:
- source_url:
- suggestions:
- version: 1.0.0
-name: x-1.0.0
-providers:
-recipes:
- checksum: 4631b34cf58de10c5ef1304889941b2e
- name: default.rb
- path: recipes/default.rb
- specificity: default
- url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
-
- checksum: d41d8cd98f00b204e9800998ecf8427e
- name: x.rb
- path: recipes/x.rb
- specificity: default
- url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
-resources:
-root_files:
- checksum: 8226671f751ba102dea6a6b6bd32fa8d
- name: metadata.rb
- path: metadata.rb
- specificity: default
- url: http://127.0.0.1:8900/file_store/checksums/8226671f751ba102dea6a6b6bd32fa8d
-templates:
-version: 1.0.0
-EOM
+ knife("cookbook show x 1.0.0").should_succeed <<~EOM
+ cookbook_name: x
+ frozen?: false
+ metadata:
+ chef_versions:
+ dependencies:
+ description:
+ eager_load_libraries: true
+ 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:
-conflicting:
-dependencies:
-description:
-gems:
-groupings:
-issues_url:
-license: All rights reserved
-long_description:
-maintainer:
-maintainer_email:
-name: x
-ohai_versions:
-platforms:
-privacy: false
-providing:
-recipes:
-recommendations:
-replacing:
-source_url:
-suggestions:
-version: 1.0.0
-EOM
+ knife("cookbook show x 1.0.0 metadata").should_succeed <<~EOM
+ chef_versions:
+ dependencies:
+ description:
+ eager_load_libraries: true
+ 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: default.rb
-path: recipes/default.rb
-specificity: default
-url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
+ 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: x.rb
-path: recipes/x.rb
-specificity: default
-url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
-EOM
+ 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 Style/TrailingWhitespace
+ # 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"
@@ -149,11 +139,11 @@ EOM
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)
+ expect { knife("cookbook show x 1.0.1") }.to raise_error(Net::HTTPClientException)
end
it "knife cookbook show with a non-existent cookbook displays an error" do
- expect { knife("cookbook show y") }.to raise_error(Net::HTTPServerException)
+ expect { knife("cookbook show y") }.to raise_error(Net::HTTPClientException)
end
end
end
diff --git a/spec/integration/knife/cookbook_upload_spec.rb b/spec/integration/knife/cookbook_upload_spec.rb
index a0de725603..7139f0accd 100644
--- a/spec/integration/knife/cookbook_upload_spec.rb
+++ b/spec/integration/knife/cookbook_upload_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/cookbook_upload"
@@ -24,7 +25,7 @@ describe "knife cookbook upload", :workstation do
include_context "default config options"
- let (:cb_dir) { "#{@repository_dir}/cookbooks" }
+ let(:cb_dir) { "#{@repository_dir}/cookbooks" }
when_the_chef_server "is empty" do
when_the_repository "has a cookbook" do
@@ -33,25 +34,25 @@ describe "knife cookbook upload", :workstation do
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
+ 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
+ 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
+ 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
@@ -62,29 +63,66 @@ EOM
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
+ 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
+ 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
+ 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
+
+ when_the_repository "has cookbook metadata without name attribute in metadata file" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata(nil, "1.0.0")
+ end
+
+ it "knife cookbook upload x " do
+ expect { knife("cookbook upload x -o #{cb_dir}") }.to raise_error(Chef::Exceptions::MetadataNotValid)
+ end
+ end
+
+ when_the_repository "has cookbooks at multiple paths" do
+
+ let(:cb_dir_first) do
+ File.join(@repository_dir, "cookbooks")
+ .gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+ end
+
+ let(:cb_dir_second) do
+ File.join(@repository_dir, "test_cookbooks")
+ .gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+ end
+
+ before(:each) do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "test_cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0")
+ end
+
+ it "knife cookbook upload with -o or --cookbook-path" do
+ knife("cookbook upload x y -o #{cb_dir_first}#{File::PATH_SEPARATOR}#{cb_dir_second}").should_succeed stderr: <<~EOM
+ Uploading x [1.0.0]
+ Uploading y [1.0.0]
+ Uploaded 2 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
index 0a07792dbc..ca01a2d8ab 100644
--- a/spec/integration/knife/data_bag_create_spec.rb
+++ b/spec/integration/knife/data_bag_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/data_bag_create"
@@ -27,32 +28,98 @@ describe "knife data bag create", :workstation do
let(:err) { "Created data_bag[foo]\n" }
let(:out) { "Created data_bag_item[bar]\n" }
let(:exists) { "Data bag foo already exists\n" }
+ let(:secret) { "abc" }
when_the_chef_server "is empty" do
- it "creates a new data bag" do
- knife("data bag create foo").should_succeed stderr: err
+ context "with encryption key" do
+ it "creates a new data bag and item" do
+ pretty_json = Chef::JSONCompat.to_json_pretty({ id: "bar", test: "pass" })
+ allow(Chef::JSONCompat).to receive(:to_json_pretty).and_return(pretty_json)
+ knife("data bag create foo bar --secret #{secret}").should_succeed stdout: out, stderr: err
+ expect(knife("data bag show foo bar --secret #{secret}").stderr).to eq("Encrypted data bag detected, decrypting with provided secret.\n")
+ expect(knife("data bag show foo bar --secret #{secret}").stdout).to eq("id: bar\ntest: pass\n")
+ end
+
+ it "creates a new data bag and an empty item" do
+ knife("data bag create foo bar --secret #{secret}").should_succeed stdout: out, stderr: err
+ expect(knife("data bag show foo bar --secret #{secret}").stderr).to eq("WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n")
+ expect(knife("data bag show foo bar --secret #{secret}").stdout).to eq("id: bar\n")
+ end
end
- it "creates a new data bag and item" do
- pending "Deprecation warning must get fixed"
- knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+ context "without encryption key" do
+ it "creates a new data bag" do
+ knife("data bag create foo").should_succeed stderr: err
+ expect(knife("data bag show foo").stderr).to eq("")
+ end
+
+ it "creates a new data bag and item" do
+ knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+ expect(knife("data bag show foo").stdout).to eq("bar\n")
+ end
end
+ end
- it "adds a new item to an existing bag" do
- pending "Deprecation warning must get fixed"
- knife("data bag create foo").should_succeed stderr: err
- knife("data bag create foo bar").should_succeed stdout: out, stderr: exists
+ when_the_chef_server "has some data bags" do
+ before do
+ data_bag "foo", {}
+ data_bag "bag", { "box" => {} }
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
+ context "with encryption key" do
+ it "creates a new data bag and item" do
+ pretty_json = Chef::JSONCompat.to_json_pretty({ id: "bar", test: "pass" })
+ allow(Chef::JSONCompat).to receive(:to_json_pretty).and_return(pretty_json)
+ knife("data bag create rocket bar --secret #{secret}").should_succeed stdout: out, stderr: <<~EOM
+ Created data_bag[rocket]
+ EOM
+ expect(knife("data bag show rocket bar --secret #{secret}").stderr).to eq("Encrypted data bag detected, decrypting with provided secret.\n")
+ expect(knife("data bag show rocket bar --secret #{secret}").stdout).to eq("id: bar\ntest: pass\n")
+ end
+
+ it "creates a new data bag and an empty item" do
+ knife("data bag create rocket bar --secret #{secret}").should_succeed stdout: out, stderr: <<~EOM
+ Created data_bag[rocket]
+ EOM
+ expect(knife("data bag show rocket bar --secret #{secret}").stderr).to eq("WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n")
+ expect(knife("data bag show rocket bar --secret #{secret}").stdout).to eq("id: bar\n")
+ end
+
+ it "adds a new item to an existing bag" do
+ knife("data bag create foo bar --secret #{secret}").should_succeed stdout: out, stderr: exists
+ expect(knife("data bag show foo bar --secret #{secret}").stderr).to eq("WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n")
+ expect(knife("data bag show foo bar --secret #{secret}").stdout).to eq("id: bar\n")
+ end
+
+ it "fails to add an existing item" do
+ expect { knife("data bag create bag box --secret #{secret}") }.to raise_error(Net::HTTPClientException)
+ end
end
- it "fails to add an existing item" do
- pending "Deprecation warning must get fixed"
- knife("data bag create foo bar").should_succeed stdout: out, stderr: err
- expect { knife("data bag create foo bar") }.to raise_error(Net::HTTPServerException)
+ context "without encryption key" do
+ it "creates a new data bag" do
+ knife("data bag create rocket").should_succeed stderr: <<~EOM
+ Created data_bag[rocket]
+ EOM
+ end
+
+ it "creates a new data bag and item" do
+ knife("data bag create rocket bar").should_succeed stdout: out, stderr: <<~EOM
+ Created data_bag[rocket]
+ EOM
+ end
+
+ it "adds a new item to an existing bag" do
+ knife("data bag create foo bar").should_succeed stdout: out, stderr: exists
+ end
+
+ it "refuses to create an existing data bag" do
+ knife("data bag create foo").should_succeed stderr: exists
+ end
+
+ it "fails to add an existing item" do
+ expect { knife("data bag create bag box") }.to raise_error(Net::HTTPClientException)
+ end
end
end
end
diff --git a/spec/integration/knife/data_bag_delete_spec.rb b/spec/integration/knife/data_bag_delete_spec.rb
index 96345b0d2b..c0a17779b9 100644
--- a/spec/integration/knife/data_bag_delete_spec.rb
+++ b/spec/integration/knife/data_bag_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/data_bag_delete"
@@ -32,27 +33,27 @@ describe "knife data bag delete", :workstation do
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
+ 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
+ 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
+ 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
+ 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_edit_spec.rb b/spec/integration/knife/data_bag_edit_spec.rb
new file mode 100644
index 0000000000..1063b5d14f
--- /dev/null
+++ b/spec/integration/knife/data_bag_edit_spec.rb
@@ -0,0 +1,105 @@
+#
+# Copyright:: Copyright (c) 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/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_edit"
+
+describe "knife data bag edit", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ include_context "default config options"
+
+ let(:out) { "Saved data_bag_item[box]\n" }
+ let(:err) { "Saving data bag unencrypted. To encrypt it, provide an appropriate secret.\n" }
+ let(:secret) { "abc" }
+ let(:encrypt) { "Encrypted data bag detected, decrypting with provided secret.\n" }
+
+ when_the_chef_server "is empty" do
+ context "with encryption key" do
+ it "fails to edit an item" do
+ expect { knife("data bag edit bag box --secret #{secret}") }.to raise_error(Net::HTTPClientException)
+ end
+ end
+
+ context "without encryption key" do
+ it "fails to edit an item" do
+ expect { knife("data bag edit bag box") }.to raise_error(Net::HTTPClientException)
+ end
+ end
+ end
+
+ when_the_chef_server "has some data bags" do
+ before do
+ data_bag "foo", {}
+ data_bag "bag", { "box" => {} }
+ data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+ data_bag "encrypt", { "box" => { id: "box", foo: { "encrypted_data": "J8N0pJ+LFDQF3XvhzWgkSBOuZZn8Og==\n", "iv": "4S1sb4zLnMt71SXV\n", "auth_tag": "4ChINhxz4WmqOizvZNoPPg==\n", "version": 3, "cipher": "aes-256-gcm" } } }
+ end
+
+ context "with encryption key" do
+ it "fails to edit a non-existing item" do
+ expect { knife("data bag edit foo box --secret #{secret}") }.to raise_error(Net::HTTPClientException)
+ end
+
+ it "edits an encrypted data bag item" do
+ pretty_json = Chef::JSONCompat.to_json_pretty({ id: "box", foo: "bar" })
+ allow(Chef::JSONCompat).to receive(:to_json_pretty).and_return(pretty_json)
+ knife("data bag edit encrypt box --secret #{secret}")
+ knife("data bag show encrypt box --secret #{secret}").should_succeed stderr: encrypt, stdout: <<~EOM
+ foo: bar
+ id: box
+ EOM
+ end
+
+ it "encrypts an unencrypted data bag item" do
+ knife("data bag edit rocket falcon9 --secret #{secret}")
+ knife("data bag show rocket falcon9 --secret #{secret}").should_succeed stderr: encrypt, stdout: <<~EOM
+ heavy: true
+ id: falcon9
+ EOM
+ end
+ end
+
+ context "without encryption key" do
+ it "fails to edit a non-existing item" do
+ expect { knife("data bag edit foo box") }.to raise_error(Net::HTTPClientException)
+ end
+ it "edits an empty data bag item" do
+ pretty_json = Chef::JSONCompat.to_json_pretty({ id: "box", ab: "abc" })
+ allow(Chef::JSONCompat).to receive(:to_json_pretty).and_return(pretty_json)
+ knife("data bag edit bag box").should_succeed stderr: err, stdout: out
+ knife("data bag show bag box").should_succeed <<~EOM
+ ab: abc
+ id: box
+ EOM
+ end
+ it "edits a non-empty data bag item" do
+ pretty_json = Chef::JSONCompat.to_json_pretty({ id: "falcon9", heavy: false })
+ allow(Chef::JSONCompat).to receive(:to_json_pretty).and_return(pretty_json)
+ knife("data bag edit rocket falcon9").should_succeed stderr: err, stdout: <<~EOM
+ Saved data_bag_item[falcon9]
+ EOM
+ knife("data bag show rocket falcon9").should_succeed <<~EOM
+ heavy: false
+ id: falcon9
+ EOM
+ end
+ 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
index ca8f743487..93801226d0 100644
--- a/spec/integration/knife/data_bag_from_file_spec.rb
+++ b/spec/integration/knife/data_bag_from_file_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -23,7 +24,7 @@ describe "knife data bag from file", :workstation do
include_context "default config options"
- let (:db_dir) { "#{@repository_dir}/data_bags" }
+ let(:db_dir) { "#{@repository_dir}/data_bags" }
when_the_chef_server "has an empty data bag" do
before do
@@ -37,77 +38,77 @@ describe "knife data bag from file", :workstation do
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
+ 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
+ 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
+ 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
+ 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
+ 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
+ knife("data bag show foo").should_succeed <<~EOM
+ bar
+ bzr
+ cat
+ dog
+ encrypted
+ EOM
end
end
diff --git a/spec/integration/knife/data_bag_list_spec.rb b/spec/integration/knife/data_bag_list_spec.rb
index 7db9638660..0216b90c5d 100644
--- a/spec/integration/knife/data_bag_list_spec.rb
+++ b/spec/integration/knife/data_bag_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/data_bag_list"
@@ -32,11 +33,11 @@ describe "knife data bag list", :workstation do
end
it "knife data bag list shows all the cookbooks" do
- knife("data bag list").should_succeed <<EOM
-canteloupe
-rocket
-x
-EOM
+ knife("data bag list").should_succeed <<~EOM
+ canteloupe
+ rocket
+ x
+ EOM
end
end
diff --git a/spec/integration/knife/data_bag_show_spec.rb b/spec/integration/knife/data_bag_show_spec.rb
index 22381adb9e..b332b1b114 100644
--- a/spec/integration/knife/data_bag_show_spec.rb
+++ b/spec/integration/knife/data_bag_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/data_bag_show"
@@ -24,30 +25,71 @@ describe "knife data bag show", :workstation do
include_context "default config options"
- when_the_chef_server "has some data bags" do
+ when_the_chef_server "is empty" do
+ it "raises error if try to retrieve it" do
+ expect { knife("data bag show bag") }.to raise_error(Net::HTTPClientException)
+ end
+ end
+
+ when_the_chef_server "contains data bags" do
+ let(:right_secret) { "abc" }
+ let(:wrong_secret) { "ab" }
+ let(:err) { "Encrypted data bag detected, decrypting with provided secret.\n" }
before do
data_bag "x", {}
data_bag "canteloupe", {}
data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+ data_bag "encrypt", { "box" => { id: "box", foo: { "encrypted_data": "J8N0pJ+LFDQF3XvhzWgkSBOuZZn8Og==\n", "iv": "4S1sb4zLnMt71SXV\n", "auth_tag": "4ChINhxz4WmqOizvZNoPPg==\n", "version": 3, "cipher": "aes-256-gcm" } } }
end
- it "with an empty data bag" do
- knife("data bag show canteloupe").should_succeed "\n"
- end
+ context "with encrypted data" do
+ context "provided secret key" do
+ it "shows data if secret key is correct" do
+ knife("data bag show encrypt box --secret #{right_secret}").should_succeed stderr: err, stdout: <<~EOM
+ foo: bar
+ id: box
+ EOM
+ end
- it "with a bag with some items" do
- knife("data bag show rocket").should_succeed <<EOM
-ariane
-atlas
-falcon9
-EOM
+ it "raises error if secret key is incorrect" do
+ expect { knife("data bag show encrypt box --secret #{wrong_secret}") }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
+ end
+ end
+
+ context "not provided secret key" do
+ it "shows encrypted data with a warning" do
+ expect(knife("data bag show encrypt box").stderr).to eq("WARNING: Encrypted data bag detected, but no secret provided for decoding. Displaying encrypted data.\n")
+ end
+ end
end
- it "with a single item" do
- knife("data bag show rocket falcon9").should_succeed <<EOM, stderr: "WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n"
-heavy: true
-id: falcon9
-EOM
+ context "with unencrypted data" do
+ context "provided secret key" do
+ it "shows unencrypted data with a warning" do
+ expect(knife("data bag show rocket falcon9 --secret #{right_secret}").stderr).to eq("WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n")
+ end
+ end
+
+ context "not provided secret key" do
+ it "shows null with an empty data bag" do
+ knife("data bag show canteloupe").should_succeed "\n"
+ end
+
+ it "show list of items in a bag" do
+ knife("data bag show rocket").should_succeed <<~EOM
+ ariane
+ atlas
+ falcon9
+ EOM
+ end
+
+ it "show data of the item" do
+ knife("data bag show rocket falcon9").should_succeed <<~EOM
+ heavy: true
+ id: falcon9
+ EOM
+ end
+ end
end
end
end
diff --git a/spec/integration/knife/delete_spec.rb b/spec/integration/knife/delete_spec.rb
index 1c69fbf9c9..851c492a66 100644
--- a/spec/integration/knife/delete_spec.rb
+++ b/spec/integration/knife/delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/delete"
require "chef/knife/list"
@@ -25,77 +26,77 @@ describe "knife delete", :workstation do
include KnifeSupport
let :everything do
- <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
let :server_everything do
- <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
+ <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
end
let :server_nothing do
- <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/cookbooks
-/data_bags
-/environments
-/environments/_default.json
-/nodes
-/roles
-/users
-/users/admin.json
-EOM
+ <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /cookbooks
+ /data_bags
+ /environments
+ /environments/_default.json
+ /nodes
+ /roles
+ /users
+ /users/admin.json
+ EOM
end
let :nothing do
- <<EOM
-/clients
-/cookbooks
-/data_bags
-/environments
-/nodes
-/roles
-/users
-EOM
+ <<~EOM
+ /clients
+ /cookbooks
+ /data_bags
+ /environments
+ /nodes
+ /roles
+ /users
+ EOM
end
when_the_chef_server "has one of each thing" do
@@ -122,99 +123,99 @@ EOM
end
it "knife delete --both /cookbooks/x fails" do
- knife("delete --both /cookbooks/x").should_fail <<EOM
-ERROR: /cookbooks/x (remote) must be deleted recursively! Pass -r to knife delete.
-ERROR: /cookbooks/x (local) must be deleted recursively! Pass -r to knife delete.
-EOM
+ knife("delete --both /cookbooks/x").should_fail <<~EOM
+ ERROR: /cookbooks/x (remote) must be deleted recursively! Pass -r to knife delete.
+ ERROR: /cookbooks/x (local) must be deleted recursively! Pass -r to knife delete.
+ EOM
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed everything
end
it "knife delete --both -r /cookbooks/x deletes x" do
knife("delete --both -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete -r --local /cookbooks/x deletes x locally but not remotely" do
knife("delete -r --local /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
knife("list -Rf /").should_succeed server_everything
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete -r /cookbooks/x deletes x remotely but not locally" do
knife("delete -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
knife("list -Rf --local /").should_succeed everything
end
@@ -226,213 +227,213 @@ EOM
end
it "knife delete --both /data_bags/empty fails but deletes local version" do
- knife("delete --both /data_bags/empty").should_fail <<EOM
-ERROR: /data_bags/empty (remote) must be deleted recursively! Pass -r to knife delete.
-ERROR: /data_bags/empty (local) must be deleted recursively! Pass -r to knife delete.
-EOM
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/empty
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/empty
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("delete --both /data_bags/empty").should_fail <<~EOM
+ ERROR: /data_bags/empty (remote) must be deleted recursively! Pass -r to knife delete.
+ ERROR: /data_bags/empty (local) must be deleted recursively! Pass -r to knife delete.
+ EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/empty
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/empty
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
end
it "knife delete --both /data_bags/x fails" do
- knife("delete --both /data_bags/x").should_fail <<EOM
-ERROR: /data_bags/x (remote) must be deleted recursively! Pass -r to knife delete.
-ERROR: /data_bags/x (local) must be deleted recursively! Pass -r to knife delete.
-EOM
+ knife("delete --both /data_bags/x").should_fail <<~EOM
+ ERROR: /data_bags/x (remote) must be deleted recursively! Pass -r to knife delete.
+ ERROR: /data_bags/x (local) must be deleted recursively! Pass -r to knife delete.
+ EOM
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed everything
end
it "knife delete --both -r /data_bags/x deletes x" do
knife("delete --both -r /data_bags/x").should_succeed "Deleted /data_bags/x\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /environments/x.json deletes x" do
knife("delete --both /environments/x.json").should_succeed "Deleted /environments/x.json\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /roles/x.json deletes x" do
knife("delete --both /roles/x.json").should_succeed "Deleted /roles/x.json\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/users
-/users/admin.json
-/users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/users
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /environments/_default.json fails but still deletes the local copy" do
- knife("delete --both /environments/_default.json").should_fail :stderr => "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", :stdout => "Deleted /environments/_default.json\n"
+ knife("delete --both /environments/_default.json").should_fail stderr: "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", stdout: "Deleted /environments/_default.json\n"
knife("list -Rf /").should_succeed server_everything
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /environments/nonexistent.json fails" do
@@ -442,33 +443,33 @@ EOM
end
it "knife delete --both / fails" do
- knife("delete --both /").should_fail <<EOM
-ERROR: / (remote) cannot be deleted.
-ERROR: / (local) cannot be deleted.
-EOM
+ knife("delete --both /").should_fail <<~EOM
+ ERROR: / (remote) cannot be deleted.
+ ERROR: / (local) cannot be deleted.
+ EOM
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed everything
end
it "knife delete --both -r /* fails" do
- knife("delete --both -r /*").should_fail <<EOM
-ERROR: / (remote) cannot be deleted.
-ERROR: / (local) cannot be deleted.
-ERROR: /clients (remote) cannot be deleted.
-ERROR: /clients (local) cannot be deleted.
-ERROR: /cookbooks (remote) cannot be deleted.
-ERROR: /cookbooks (local) cannot be deleted.
-ERROR: /data_bags (remote) cannot be deleted.
-ERROR: /data_bags (local) cannot be deleted.
-ERROR: /environments (remote) cannot be deleted.
-ERROR: /environments (local) cannot be deleted.
-ERROR: /nodes (remote) cannot be deleted.
-ERROR: /nodes (local) cannot be deleted.
-ERROR: /roles (remote) cannot be deleted.
-ERROR: /roles (local) cannot be deleted.
-ERROR: /users (remote) cannot be deleted.
-ERROR: /users (local) cannot be deleted.
-EOM
+ knife("delete --both -r /*").should_fail <<~EOM
+ ERROR: / (remote) cannot be deleted.
+ ERROR: / (local) cannot be deleted.
+ ERROR: /clients (remote) cannot be deleted.
+ ERROR: /clients (local) cannot be deleted.
+ ERROR: /cookbooks (remote) cannot be deleted.
+ ERROR: /cookbooks (local) cannot be deleted.
+ ERROR: /data_bags (remote) cannot be deleted.
+ ERROR: /data_bags (local) cannot be deleted.
+ ERROR: /environments (remote) cannot be deleted.
+ ERROR: /environments (local) cannot be deleted.
+ ERROR: /nodes (remote) cannot be deleted.
+ ERROR: /nodes (local) cannot be deleted.
+ ERROR: /roles (remote) cannot be deleted.
+ ERROR: /roles (local) cannot be deleted.
+ ERROR: /users (remote) cannot be deleted.
+ ERROR: /users (local) cannot be deleted.
+ EOM
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed everything
end
@@ -493,26 +494,26 @@ EOM
it "knife delete --both -r /cookbooks/x deletes x" do
knife("delete --both -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
knife("list -Rf --local /").should_succeed nothing
end
@@ -524,83 +525,83 @@ EOM
it "knife delete --both -r /data_bags/x deletes x" do
knife("delete --both -r /data_bags/x").should_succeed "Deleted /data_bags/x\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
knife("list -Rf --local /").should_succeed nothing
end
it "knife delete --both /environments/x.json deletes x" do
knife("delete --both /environments/x.json").should_succeed "Deleted /environments/x.json\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/admin.json
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
knife("list -Rf --local /").should_succeed nothing
end
it "knife delete --both /roles/x.json deletes x" do
knife("delete --both /roles/x.json").should_succeed "Deleted /roles/x.json\n"
- knife("list -Rf /").should_succeed <<EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/users
-/users/admin.json
-/users/x.json
-EOM
+ knife("list -Rf /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /users
+ /users/admin.json
+ /users/x.json
+ EOM
knife("list -Rf --local /").should_succeed nothing
end
it "knife delete --both /environments/_default.json fails" do
- knife("delete --both /environments/_default.json").should_fail "", :stderr => "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n"
+ knife("delete --both /environments/_default.json").should_fail "", stderr: "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n"
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed nothing
end
@@ -612,24 +613,24 @@ EOM
end
it "knife delete --both -r /* fails" do
- knife("delete --both -r /*").should_fail <<EOM
-ERROR: / (remote) cannot be deleted.
-ERROR: / (local) cannot be deleted.
-ERROR: /clients (remote) cannot be deleted.
-ERROR: /clients (local) cannot be deleted.
-ERROR: /cookbooks (remote) cannot be deleted.
-ERROR: /cookbooks (local) cannot be deleted.
-ERROR: /data_bags (remote) cannot be deleted.
-ERROR: /data_bags (local) cannot be deleted.
-ERROR: /environments (remote) cannot be deleted.
-ERROR: /environments (local) cannot be deleted.
-ERROR: /nodes (remote) cannot be deleted.
-ERROR: /nodes (local) cannot be deleted.
-ERROR: /roles (remote) cannot be deleted.
-ERROR: /roles (local) cannot be deleted.
-ERROR: /users (remote) cannot be deleted.
-ERROR: /users (local) cannot be deleted.
-EOM
+ knife("delete --both -r /*").should_fail <<~EOM
+ ERROR: / (remote) cannot be deleted.
+ ERROR: / (local) cannot be deleted.
+ ERROR: /clients (remote) cannot be deleted.
+ ERROR: /clients (local) cannot be deleted.
+ ERROR: /cookbooks (remote) cannot be deleted.
+ ERROR: /cookbooks (local) cannot be deleted.
+ ERROR: /data_bags (remote) cannot be deleted.
+ ERROR: /data_bags (local) cannot be deleted.
+ ERROR: /environments (remote) cannot be deleted.
+ ERROR: /environments (local) cannot be deleted.
+ ERROR: /nodes (remote) cannot be deleted.
+ ERROR: /nodes (local) cannot be deleted.
+ ERROR: /roles (remote) cannot be deleted.
+ ERROR: /roles (local) cannot be deleted.
+ ERROR: /users (remote) cannot be deleted.
+ ERROR: /users (local) cannot be deleted.
+ EOM
knife("list -Rf /").should_succeed server_everything
knife("list -Rf --local /").should_succeed nothing
end
@@ -643,38 +644,38 @@ EOM
context "and cwd is at the top level" do
before { cwd "." }
it "knife delete fails" do
- knife("delete").should_fail "FATAL: You must specify at least one argument. If you want to delete everything in this directory, run \"knife delete --recurse .\"\n", :stdout => /USAGE/
- knife("list -Rf /").should_succeed <<EOM
-clients
-clients/chef-validator.json
-clients/chef-webui.json
-clients/x.json
-cookbooks
-cookbooks/x
-cookbooks/x/metadata.rb
-data_bags
-data_bags/x
-data_bags/x/y.json
-environments
-environments/_default.json
-environments/x.json
-nodes
-nodes/x.json
-roles
-roles/x.json
-users
-users/admin.json
-users/x.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-clients
-cookbooks
-data_bags
-environments
-nodes
-roles
-users
-EOM
+ knife("delete").should_fail "FATAL: You must specify at least one argument. If you want to delete everything in this directory, run \"knife delete --recurse .\"\n", stdout: /USAGE/
+ knife("list -Rf /").should_succeed <<~EOM
+ clients
+ clients/chef-validator.json
+ clients/chef-webui.json
+ clients/x.json
+ cookbooks
+ cookbooks/x
+ cookbooks/x/metadata.rb
+ data_bags
+ data_bags/x
+ data_bags/x/y.json
+ environments
+ environments/_default.json
+ environments/x.json
+ nodes
+ nodes/x.json
+ roles
+ roles/x.json
+ users
+ users/admin.json
+ users/x.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ clients
+ cookbooks
+ data_bags
+ environments
+ nodes
+ roles
+ users
+ EOM
end
end
end
@@ -702,23 +703,23 @@ EOM
it "knife delete --both -r /cookbooks/x deletes x" do
knife("delete --both -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
knife("list -Rf /").should_succeed server_nothing
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /data_bags/x fails" do
@@ -730,92 +731,92 @@ EOM
it "knife delete --both -r /data_bags/x deletes x" do
knife("delete --both -r /data_bags/x").should_succeed "Deleted /data_bags/x\n"
knife("list -Rf /").should_succeed server_nothing
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /environments/x.json deletes x" do
knife("delete --both /environments/x.json").should_succeed "Deleted /environments/x.json\n"
knife("list -Rf /").should_succeed server_nothing
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /roles/x.json deletes x" do
knife("delete --both /roles/x.json").should_succeed "Deleted /roles/x.json\n"
knife("list -Rf /").should_succeed server_nothing
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/_default.json
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/_default.json
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both /environments/_default.json fails but still deletes the local copy" do
- knife("delete --both /environments/_default.json").should_fail :stderr => "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", :stdout => "Deleted /environments/_default.json\n"
+ knife("delete --both /environments/_default.json").should_fail stderr: "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", stdout: "Deleted /environments/_default.json\n"
knife("list -Rf /").should_succeed server_nothing
- knife("list -Rf --local /").should_succeed <<EOM
-/clients
-/clients/x.json
-/cookbooks
-/cookbooks/x
-/cookbooks/x/metadata.rb
-/data_bags
-/data_bags/x
-/data_bags/x/y.json
-/environments
-/environments/x.json
-/nodes
-/nodes/x.json
-/roles
-/roles/x.json
-/users
-/users/x.json
-EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ /clients
+ /clients/x.json
+ /cookbooks
+ /cookbooks/x
+ /cookbooks/x/metadata.rb
+ /data_bags
+ /data_bags/x
+ /data_bags/x/y.json
+ /environments
+ /environments/x.json
+ /nodes
+ /nodes/x.json
+ /roles
+ /roles/x.json
+ /users
+ /users/x.json
+ EOM
end
it "knife delete --both / fails" do
@@ -825,24 +826,24 @@ EOM
end
it "knife delete --both -r /* fails" do
- knife("delete --both -r /*").should_fail <<EOM
-ERROR: / (remote) cannot be deleted.
-ERROR: / (local) cannot be deleted.
-ERROR: /clients (remote) cannot be deleted.
-ERROR: /clients (local) cannot be deleted.
-ERROR: /cookbooks (remote) cannot be deleted.
-ERROR: /cookbooks (local) cannot be deleted.
-ERROR: /data_bags (remote) cannot be deleted.
-ERROR: /data_bags (local) cannot be deleted.
-ERROR: /environments (remote) cannot be deleted.
-ERROR: /environments (local) cannot be deleted.
-ERROR: /nodes (remote) cannot be deleted.
-ERROR: /nodes (local) cannot be deleted.
-ERROR: /roles (remote) cannot be deleted.
-ERROR: /roles (local) cannot be deleted.
-ERROR: /users (remote) cannot be deleted.
-ERROR: /users (local) cannot be deleted.
-EOM
+ knife("delete --both -r /*").should_fail <<~EOM
+ ERROR: / (remote) cannot be deleted.
+ ERROR: / (local) cannot be deleted.
+ ERROR: /clients (remote) cannot be deleted.
+ ERROR: /clients (local) cannot be deleted.
+ ERROR: /cookbooks (remote) cannot be deleted.
+ ERROR: /cookbooks (local) cannot be deleted.
+ ERROR: /data_bags (remote) cannot be deleted.
+ ERROR: /data_bags (local) cannot be deleted.
+ ERROR: /environments (remote) cannot be deleted.
+ ERROR: /environments (local) cannot be deleted.
+ ERROR: /nodes (remote) cannot be deleted.
+ ERROR: /nodes (local) cannot be deleted.
+ ERROR: /roles (remote) cannot be deleted.
+ ERROR: /roles (local) cannot be deleted.
+ ERROR: /users (remote) cannot be deleted.
+ ERROR: /users (local) cannot be deleted.
+ EOM
knife("list -Rf /").should_succeed server_nothing
knife("list -Rf --local /").should_succeed everything
end
@@ -856,39 +857,39 @@ EOM
context "and cwd is at the top level" do
before { cwd "." }
it "knife delete fails" do
- knife("delete").should_fail "FATAL: You must specify at least one argument. If you want to delete everything in this directory, run \"knife delete --recurse .\"\n", :stdout => /USAGE/
- knife("list -Rf /").should_succeed <<EOM
-clients
-clients/chef-validator.json
-clients/chef-webui.json
-cookbooks
-data_bags
-environments
-environments/_default.json
-nodes
-roles
-users
-users/admin.json
-EOM
- knife("list -Rf --local /").should_succeed <<EOM
-clients
-clients/x.json
-cookbooks
-cookbooks/x
-cookbooks/x/metadata.rb
-data_bags
-data_bags/x
-data_bags/x/y.json
-environments
-environments/_default.json
-environments/x.json
-nodes
-nodes/x.json
-roles
-roles/x.json
-users
-users/x.json
-EOM
+ knife("delete").should_fail "FATAL: You must specify at least one argument. If you want to delete everything in this directory, run \"knife delete --recurse .\"\n", stdout: /USAGE/
+ knife("list -Rf /").should_succeed <<~EOM
+ clients
+ clients/chef-validator.json
+ clients/chef-webui.json
+ cookbooks
+ data_bags
+ environments
+ environments/_default.json
+ nodes
+ roles
+ users
+ users/admin.json
+ EOM
+ knife("list -Rf --local /").should_succeed <<~EOM
+ clients
+ clients/x.json
+ cookbooks
+ cookbooks/x
+ cookbooks/x/metadata.rb
+ data_bags
+ data_bags/x
+ data_bags/x/y.json
+ environments
+ environments/_default.json
+ environments/x.json
+ nodes
+ nodes/x.json
+ roles
+ roles/x.json
+ users
+ users/x.json
+ EOM
end
end
end
@@ -962,7 +963,7 @@ EOM
end
end
- when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do
+ when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
before do
organization "foo" do
container "x", {}
diff --git a/spec/integration/knife/deps_spec.rb b/spec/integration/knife/deps_spec.rb
index 292bce6002..77505e6332 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/deps"
@@ -41,12 +42,12 @@ describe "knife deps", :workstation do
file "cookbooks/soup/recipes/chicken.rb", ""
end
it "knife deps reports all dependencies" do
- knife("deps /roles/starring.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-EOM
+ knife("deps /roles/starring.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ EOM
end
end
@@ -60,12 +61,12 @@ EOM
file "cookbooks/soup/recipes/chicken.rb", ""
end
it "knife deps reports all dependencies" do
- knife("deps /roles/starring.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-EOM
+ knife("deps /roles/starring.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ EOM
end
end
@@ -94,12 +95,12 @@ EOM
file "nodes/mort.json", { "run_list" => %w{role[minor] recipe[quiche] recipe[soup::chicken]} }
end
it "knife deps reports just the node" do
- knife("deps /nodes/mort.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/nodes/mort.json
-EOM
+ knife("deps /nodes/mort.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /nodes/mort.json
+ EOM
end
end
when_the_repository "has a cookbook with no dependencies" do
@@ -148,57 +149,57 @@ depends "kettle"'
end
it "knife deps reports all dependencies" do
- knife("deps /nodes/mort.json").should_succeed <<EOM
-/environments/desert.json
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps /nodes/mort.json").should_succeed <<~EOM
+ /environments/desert.json
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps * reports all dependencies of all things" do
- knife("deps /nodes/*").should_succeed <<EOM
-/roles/minor.json
-/nodes/bart.json
-/environments/desert.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps /nodes/*").should_succeed <<~EOM
+ /roles/minor.json
+ /nodes/bart.json
+ /environments/desert.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps a b reports all dependencies of a and b" do
- knife("deps /nodes/bart.json /nodes/mort.json").should_succeed <<EOM
-/roles/minor.json
-/nodes/bart.json
-/environments/desert.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps /nodes/bart.json /nodes/mort.json").should_succeed <<~EOM
+ /roles/minor.json
+ /nodes/bart.json
+ /environments/desert.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps --tree /* shows dependencies in a tree" do
- knife("deps --tree /nodes/*").should_succeed <<EOM
-/nodes/bart.json
- /roles/minor.json
-/nodes/mort.json
- /environments/desert.json
- /roles/starring.json
- /roles/minor.json
- /cookbooks/quiche
- /cookbooks/soup
-EOM
+ knife("deps --tree /nodes/*").should_succeed <<~EOM
+ /nodes/bart.json
+ /roles/minor.json
+ /nodes/mort.json
+ /environments/desert.json
+ /roles/starring.json
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ EOM
end
it "knife deps --tree --no-recurse shows only the first level of dependencies" do
- knife("deps --tree --no-recurse /nodes/*").should_succeed <<EOM
-/nodes/bart.json
- /roles/minor.json
-/nodes/mort.json
- /environments/desert.json
- /roles/starring.json
-EOM
+ knife("deps --tree --no-recurse /nodes/*").should_succeed <<~EOM
+ /nodes/bart.json
+ /roles/minor.json
+ /nodes/mort.json
+ /environments/desert.json
+ /roles/starring.json
+ EOM
end
end
@@ -211,20 +212,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
@@ -236,17 +233,17 @@ depends "self"'
file "roles/self.json", { "run_list" => [ "role[self]" ] }
end
it "knife deps prints each once" do
- knife("deps /roles/foo.json /roles/self.json").should_succeed <<EOM
-/roles/baz.json
-/roles/bar.json
-/roles/foo.json
-/roles/self.json
-EOM
+ knife("deps /roles/foo.json /roles/self.json").should_succeed <<~EOM
+ /roles/baz.json
+ /roles/bar.json
+ /roles/foo.json
+ /roles/self.json
+ EOM
end
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/ for details.\n")
end
end
end
@@ -256,44 +253,44 @@ EOM
when_the_repository "is empty" do
it "knife deps /blah reports an error" do
knife("deps /blah").should_fail(
- :exit_code => 2,
- :stdout => "/blah\n",
- :stderr => "ERROR: /blah: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/blah\n",
+ stderr: "ERROR: /blah: No such file or directory\n"
)
end
it "knife deps /roles/x.json reports an error" do
knife("deps /roles/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/roles/x.json\n",
- :stderr => "ERROR: /roles/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/roles/x.json\n",
+ stderr: "ERROR: /roles/x.json: No such file or directory\n"
)
end
it "knife deps /nodes/x.json reports an error" do
knife("deps /nodes/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/nodes/x.json\n",
- :stderr => "ERROR: /nodes/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/nodes/x.json\n",
+ stderr: "ERROR: /nodes/x.json: No such file or directory\n"
)
end
it "knife deps /environments/x.json reports an error" do
knife("deps /environments/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/environments/x.json\n",
- :stderr => "ERROR: /environments/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/environments/x.json\n",
+ stderr: "ERROR: /environments/x.json: No such file or directory\n"
)
end
it "knife deps /cookbooks/x reports an error" do
knife("deps /cookbooks/x").should_fail(
- :exit_code => 2,
- :stdout => "/cookbooks/x\n",
- :stderr => "ERROR: /cookbooks/x: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/cookbooks/x\n",
+ stderr: "ERROR: /cookbooks/x: No such file or directory\n"
)
end
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.json\n",
- :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/data_bags/bag/item.json\n",
+ stderr: "ERROR: /data_bags/bag/item.json: No such file or directory\n"
)
end
end
@@ -303,9 +300,9 @@ EOM
end
it "knife deps reports the cookbook, along with an error" do
knife("deps /roles/starring.json").should_fail(
- :exit_code => 2,
- :stdout => "/cookbooks/quiche\n/roles/starring.json\n",
- :stderr => "ERROR: /cookbooks/quiche: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/cookbooks/quiche\n/roles/starring.json\n",
+ stderr: "ERROR: /cookbooks/quiche: No such file or directory\n"
)
end
end
@@ -315,9 +312,9 @@ EOM
end
it "knife deps reports the environment, along with an error" do
knife("deps /nodes/mort.json").should_fail(
- :exit_code => 2,
- :stdout => "/environments/desert.json\n/nodes/mort.json\n",
- :stderr => "ERROR: /environments/desert.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/environments/desert.json\n/nodes/mort.json\n",
+ stderr: "ERROR: /environments/desert.json: No such file or directory\n"
)
end
end
@@ -327,9 +324,9 @@ EOM
end
it "knife deps reports the role, along with an error" do
knife("deps /roles/starring.json").should_fail(
- :exit_code => 2,
- :stdout => "/roles/minor.json\n/roles/starring.json\n",
- :stderr => "ERROR: /roles/minor.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/roles/minor.json\n/roles/starring.json\n",
+ stderr: "ERROR: /roles/minor.json: No such file or directory\n"
)
end
end
@@ -341,9 +338,9 @@ EOM
end
it "knife deps /roles reports an error" do
knife("deps /roles").should_fail(
- :exit_code => 2,
- :stderr => "ERROR: /roles: No such file or directory\n",
- :stdout => "/roles\n"
+ exit_code: 2,
+ stderr: "ERROR: /roles: No such file or directory\n",
+ stdout: "/roles\n"
)
end
end
@@ -382,12 +379,12 @@ EOM
cookbook "soup", "1.0.0", { "metadata.rb" => %Q{name "soup"\nversion "1.0.0"\n}, "recipes" => { "chicken.rb" => "" } }
end
it "knife deps reports all dependencies" do
- knife("deps --remote /roles/starring.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-EOM
+ knife("deps --remote /roles/starring.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ EOM
end
end
@@ -399,12 +396,12 @@ EOM
cookbook "soup", "1.0.0", { "metadata.rb" => %Q{name "soup"\nversion "1.0.0"\n}, "recipes" => { "chicken.rb" => "" } }
end
it "knife deps reports all dependencies" do
- knife("deps --remote /roles/starring.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-EOM
+ knife("deps --remote /roles/starring.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ EOM
end
end
@@ -431,12 +428,12 @@ EOM
node "mort", { "run_list" => %w{role[minor] recipe[quiche] recipe[soup::chicken]} }
end
it "knife deps reports just the node" do
- knife("deps --remote /nodes/mort.json").should_succeed <<EOM
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/nodes/mort.json
-EOM
+ knife("deps --remote /nodes/mort.json").should_succeed <<~EOM
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /nodes/mort.json
+ EOM
end
end
when_the_chef_server "has a cookbook with no dependencies" do
@@ -481,89 +478,89 @@ depends "kettle"', "recipes" => { "default.rb" => "" } }
end
it "knife deps reports all dependencies" do
- knife("deps --remote /nodes/mort.json").should_succeed <<EOM
-/environments/desert.json
-/roles/minor.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps --remote /nodes/mort.json").should_succeed <<~EOM
+ /environments/desert.json
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps * reports all dependencies of all things" do
- knife("deps --remote /nodes/*").should_succeed <<EOM
-/roles/minor.json
-/nodes/bart.json
-/environments/desert.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps --remote /nodes/*").should_succeed <<~EOM
+ /roles/minor.json
+ /nodes/bart.json
+ /environments/desert.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps a b reports all dependencies of a and b" do
- knife("deps --remote /nodes/bart.json /nodes/mort.json").should_succeed <<EOM
-/roles/minor.json
-/nodes/bart.json
-/environments/desert.json
-/cookbooks/quiche
-/cookbooks/soup
-/roles/starring.json
-/nodes/mort.json
-EOM
+ knife("deps --remote /nodes/bart.json /nodes/mort.json").should_succeed <<~EOM
+ /roles/minor.json
+ /nodes/bart.json
+ /environments/desert.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ /roles/starring.json
+ /nodes/mort.json
+ EOM
end
it "knife deps --tree /* shows dependencies in a tree" do
- knife("deps --remote --tree /nodes/*").should_succeed <<EOM
-/nodes/bart.json
- /roles/minor.json
-/nodes/mort.json
- /environments/desert.json
- /roles/starring.json
- /roles/minor.json
- /cookbooks/quiche
- /cookbooks/soup
-EOM
+ knife("deps --remote --tree /nodes/*").should_succeed <<~EOM
+ /nodes/bart.json
+ /roles/minor.json
+ /nodes/mort.json
+ /environments/desert.json
+ /roles/starring.json
+ /roles/minor.json
+ /cookbooks/quiche
+ /cookbooks/soup
+ EOM
end
it "knife deps --tree --no-recurse shows only the first level of dependencies" do
- knife("deps --remote --tree --no-recurse /nodes/*").should_succeed <<EOM
-/nodes/bart.json
- /roles/minor.json
-/nodes/mort.json
- /environments/desert.json
- /roles/starring.json
-EOM
+ knife("deps --remote --tree --no-recurse /nodes/*").should_succeed <<~EOM
+ /nodes/bart.json
+ /roles/minor.json
+ /nodes/mort.json
+ /environments/desert.json
+ /roles/starring.json
+ EOM
end
end
context "circular dependencies" do
when_the_chef_server "has cookbooks with circular dependencies" do
before do
- cookbook "foo", "1.0.0", { "metadata.rb" => 'name "foo"
+ cookbook "foo", "1.0.0", { "metadata.rb" => 'name "foo"
depends "bar"' }
- cookbook "bar", "1.0.0", { "metadata.rb" => 'name "bar"
+ cookbook "bar", "1.0.0", { "metadata.rb" => 'name "bar"
depends "baz"' }
- cookbook "baz", "1.0.0", { "metadata.rb" => 'name "baz"
+ cookbook "baz", "1.0.0", { "metadata.rb" => 'name "baz"
depends "foo"' }
cookbook "self", "1.0.0", { "metadata.rb" => 'name "self"
depends "self"' }
end
it "knife deps prints each once" do
- knife("deps --remote /cookbooks/foo /cookbooks/self").should_succeed <<EOM
-/cookbooks/baz
-/cookbooks/bar
-/cookbooks/foo
-/cookbooks/self
-EOM
+ knife("deps --remote /cookbooks/foo /cookbooks/self").should_succeed <<~EOM
+ /cookbooks/baz
+ /cookbooks/bar
+ /cookbooks/foo
+ /cookbooks/self
+ EOM
end
it "knife deps --tree prints each once" do
- knife("deps --remote --tree /cookbooks/foo /cookbooks/self").should_succeed <<EOM
-/cookbooks/foo
- /cookbooks/bar
- /cookbooks/baz
- /cookbooks/foo
-/cookbooks/self
- /cookbooks/self
-EOM
+ knife("deps --remote --tree /cookbooks/foo /cookbooks/self").should_succeed <<~EOM
+ /cookbooks/foo
+ /cookbooks/bar
+ /cookbooks/baz
+ /cookbooks/foo
+ /cookbooks/self
+ /cookbooks/self
+ EOM
end
end
when_the_chef_server "has roles with circular dependencies" do
@@ -574,17 +571,17 @@ EOM
role "self", { "run_list" => [ "role[self]" ] }
end
it "knife deps prints each once" do
- knife("deps --remote /roles/foo.json /roles/self.json").should_succeed <<EOM
-/roles/baz.json
-/roles/bar.json
-/roles/foo.json
-/roles/self.json
-EOM
+ knife("deps --remote /roles/foo.json /roles/self.json").should_succeed <<~EOM
+ /roles/baz.json
+ /roles/bar.json
+ /roles/foo.json
+ /roles/self.json
+ EOM
end
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/ for details.\n")
end
end
end
@@ -594,44 +591,44 @@ EOM
when_the_chef_server "is empty" do
it "knife deps /blah reports an error" do
knife("deps --remote /blah").should_fail(
- :exit_code => 2,
- :stdout => "/blah\n",
- :stderr => "ERROR: /blah: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/blah\n",
+ stderr: "ERROR: /blah: No such file or directory\n"
)
end
it "knife deps /roles/x.json reports an error" do
knife("deps --remote /roles/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/roles/x.json\n",
- :stderr => "ERROR: /roles/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/roles/x.json\n",
+ stderr: "ERROR: /roles/x.json: No such file or directory\n"
)
end
it "knife deps /nodes/x.json reports an error" do
knife("deps --remote /nodes/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/nodes/x.json\n",
- :stderr => "ERROR: /nodes/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/nodes/x.json\n",
+ stderr: "ERROR: /nodes/x.json: No such file or directory\n"
)
end
it "knife deps /environments/x.json reports an error" do
knife("deps --remote /environments/x.json").should_fail(
- :exit_code => 2,
- :stdout => "/environments/x.json\n",
- :stderr => "ERROR: /environments/x.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/environments/x.json\n",
+ stderr: "ERROR: /environments/x.json: No such file or directory\n"
)
end
it "knife deps /cookbooks/x reports an error" do
knife("deps --remote /cookbooks/x").should_fail(
- :exit_code => 2,
- :stdout => "/cookbooks/x\n",
- :stderr => "ERROR: /cookbooks/x: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/cookbooks/x\n",
+ stderr: "ERROR: /cookbooks/x: No such file or directory\n"
)
end
it "knife deps /data_bags/bag/item reports an error" do
knife("deps --remote /data_bags/bag/item.json").should_fail(
- :exit_code => 2,
- :stdout => "/data_bags/bag/item.json\n",
- :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/data_bags/bag/item.json\n",
+ stderr: "ERROR: /data_bags/bag/item.json: No such file or directory\n"
)
end
end
@@ -641,9 +638,9 @@ EOM
end
it "knife deps reports the cookbook, along with an error" do
knife("deps --remote /roles/starring.json").should_fail(
- :exit_code => 2,
- :stdout => "/cookbooks/quiche\n/roles/starring.json\n",
- :stderr => "ERROR: /cookbooks/quiche: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/cookbooks/quiche\n/roles/starring.json\n",
+ stderr: "ERROR: /cookbooks/quiche: No such file or directory\n"
)
end
end
@@ -653,9 +650,9 @@ EOM
end
it "knife deps reports the environment, along with an error" do
knife("deps --remote /nodes/mort.json").should_fail(
- :exit_code => 2,
- :stdout => "/environments/desert.json\n/nodes/mort.json\n",
- :stderr => "ERROR: /environments/desert.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/environments/desert.json\n/nodes/mort.json\n",
+ stderr: "ERROR: /environments/desert.json: No such file or directory\n"
)
end
end
@@ -665,9 +662,9 @@ EOM
end
it "knife deps reports the role, along with an error" do
knife("deps --remote /roles/starring.json").should_fail(
- :exit_code => 2,
- :stdout => "/roles/minor.json\n/roles/starring.json\n",
- :stderr => "ERROR: /roles/minor.json: No such file or directory\n"
+ exit_code: 2,
+ stdout: "/roles/minor.json\n/roles/starring.json\n",
+ stderr: "ERROR: /roles/minor.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 b3bd23f48e..41ae5ea519 100644
--- a/spec/integration/knife/diff_spec.rb
+++ b/spec/integration/knife/diff_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/diff"
@@ -46,19 +47,19 @@ describe "knife diff", :workstation do
end
it "knife diff reports everything as deleted" do
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients/chef-validator.json
-D\t/clients/chef-webui.json
-D\t/clients/x.json
-D\t/cookbooks/x
-D\t/data_bags/x
-D\t/environments/_default.json
-D\t/environments/x.json
-D\t/nodes/x.json
-D\t/roles/x.json
-D\t/users/admin.json
-D\t/users/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
end
end
@@ -92,17 +93,17 @@ EOM
context "except the role file" do
before do
- file "roles/x.json", <<EOM
-{
- "foo": "bar"
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "foo": "bar"
+ }
+ EOM
end
it "knife diff reports the role as different" do
- knife("diff --name-status /").should_succeed <<EOM
-M\t/roles/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ M\t/roles/x.json
+ EOM
end
end
@@ -120,32 +121,32 @@ EOM
end
it "knife diff reports the new files as added" do
- knife("diff --name-status /").should_succeed <<EOM
-A\t/clients/y.json
-A\t/cookbooks/x/blah.rb
-A\t/cookbooks/y
-A\t/data_bags/x/z.json
-A\t/data_bags/y
-A\t/environments/y.json
-A\t/nodes/y.json
-A\t/roles/y.json
-A\t/users/y.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ A\t/clients/y.json
+ A\t/cookbooks/x/blah.rb
+ A\t/cookbooks/y
+ A\t/data_bags/x/z.json
+ A\t/data_bags/y
+ A\t/environments/y.json
+ A\t/nodes/y.json
+ A\t/roles/y.json
+ A\t/users/y.json
+ EOM
end
context "when cwd is the data_bags directory" do
before { cwd "data_bags" }
it "knife diff reports different data bags" do
- knife("diff --name-status").should_succeed <<EOM
-A\tx/z.json
-A\ty
-EOM
+ knife("diff --name-status").should_succeed <<~EOM
+ A\tx/z.json
+ A\ty
+ EOM
end
it "knife diff * reports different data bags" do
- knife("diff --name-status *").should_succeed <<EOM
-A\tx/z.json
-A\ty
-EOM
+ knife("diff --name-status *").should_succeed <<~EOM
+ A\tx/z.json
+ A\ty
+ EOM
end
end
end
@@ -153,15 +154,15 @@ EOM
when_the_repository "is empty" do
it "knife diff reports everything as deleted" do
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
end
end
@@ -179,18 +180,18 @@ EOM
end
it "knife diff /cookbooks/x shows differences" do
- knife("diff --name-status /cookbooks/x").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("diff --name-status /cookbooks/x").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
it "knife diff --diff-filter=MAT does not show deleted files" do
- knife("diff --diff-filter=MAT --name-status /cookbooks/x").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("diff --diff-filter=MAT --name-status /cookbooks/x").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
end
@@ -210,11 +211,11 @@ EOM
end
it "knife diff /cookbooks/x shows the differences" do
- knife("diff --name-status /cookbooks/x").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("diff --name-status /cookbooks/x").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
end
@@ -224,11 +225,11 @@ EOM
end
it "knife diff /cookbooks/x shows the differences" do
- knife("diff --name-status /cookbooks/x").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin0.9.9.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("diff --name-status /cookbooks/x").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin0.9.9.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
end
end
@@ -247,7 +248,7 @@ EOM
end
when_the_chef_server "has an environment with a different value" do
before { environment "x", { "description" => "hi" } }
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
- "name": "x",
@@ -277,7 +278,7 @@ EOM
environment "x", {}
end
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
- "name": "x"
@@ -291,7 +292,7 @@ EOM
before do
environment "x", { "description" => "lo" }
end
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
"name": "x",
@@ -309,15 +310,17 @@ EOM
when_the_repository "has an environment with bad JSON" do
before { file "environments/x.json", "{" }
it "knife diff reports an error and does a textual diff" do
- error_text = "WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF"
+ error_text = "WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF"
error_match = Regexp.new(Regexp.escape(error_text))
- knife("diff /environments/x.json").should_succeed(/- "name": "x"/, :stderr => error_match)
+ knife("diff /environments/x.json").should_succeed(/- "name": "x"/, stderr: error_match)
end
end
end
end # without versioned cookbooks
- with_versioned_cookbooks do
+ context "with versioned cookbooks" do
+ before { Chef::Config[:versioned_cookbooks] = true }
+
when_the_chef_server "has one of each thing" do
before do
client "x", "{}"
@@ -341,19 +344,19 @@ EOM
end
it "knife diff reports everything as deleted" do
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients/chef-validator.json
-D\t/clients/chef-webui.json
-D\t/clients/x.json
-D\t/cookbooks/x-1.0.0
-D\t/data_bags/x
-D\t/environments/_default.json
-D\t/environments/x.json
-D\t/nodes/x.json
-D\t/roles/x.json
-D\t/users/admin.json
-D\t/users/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x-1.0.0
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
end
end
@@ -386,17 +389,17 @@ EOM
context "except the role file" do
before do
- file "roles/x.json", <<EOM
-{
- "foo": "bar"
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "foo": "bar"
+ }
+ EOM
end
it "knife diff reports the role as different" do
- knife("diff --name-status /").should_succeed <<EOM
-M\t/roles/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ M\t/roles/x.json
+ EOM
end
end
@@ -415,33 +418,33 @@ EOM
end
it "knife diff reports the new files as added" do
- knife("diff --name-status /").should_succeed <<EOM
-A\t/clients/y.json
-A\t/cookbooks/x-1.0.0/blah.rb
-A\t/cookbooks/x-2.0.0
-A\t/cookbooks/y-1.0.0
-A\t/data_bags/x/z.json
-A\t/data_bags/y
-A\t/environments/y.json
-A\t/nodes/y.json
-A\t/roles/y.json
-A\t/users/y.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ A\t/clients/y.json
+ A\t/cookbooks/x-1.0.0/blah.rb
+ A\t/cookbooks/x-2.0.0
+ A\t/cookbooks/y-1.0.0
+ A\t/data_bags/x/z.json
+ A\t/data_bags/y
+ A\t/environments/y.json
+ A\t/nodes/y.json
+ A\t/roles/y.json
+ A\t/users/y.json
+ EOM
end
context "when cwd is the data_bags directory" do
before { cwd "data_bags" }
it "knife diff reports different data bags" do
- knife("diff --name-status").should_succeed <<EOM
-A\tx/z.json
-A\ty
-EOM
+ knife("diff --name-status").should_succeed <<~EOM
+ A\tx/z.json
+ A\ty
+ EOM
end
it "knife diff * reports different data bags" do
- knife("diff --name-status *").should_succeed <<EOM
-A\tx/z.json
-A\ty
-EOM
+ knife("diff --name-status *").should_succeed <<~EOM
+ A\tx/z.json
+ A\ty
+ EOM
end
end
end
@@ -449,15 +452,15 @@ EOM
when_the_repository "is empty" do
it "knife diff reports everything as deleted" do
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
end
end
@@ -475,9 +478,9 @@ EOM
end
it "knife diff /cookbooks shows differences" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x-1.0.1
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-1.0.1
+ EOM
end
it "knife diff --diff-filter=MAT does not show deleted files" do
@@ -501,10 +504,10 @@ EOM
end
it "knife diff /cookbooks shows the differences" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x-1.0.1
-A\t/cookbooks/x-1.0.0
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-1.0.1
+ A\t/cookbooks/x-1.0.0
+ EOM
end
end
@@ -514,10 +517,10 @@ EOM
end
it "knife diff /cookbooks shows the differences" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x-0.9.9
-A\t/cookbooks/x-1.0.0
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-0.9.9
+ A\t/cookbooks/x-1.0.0
+ EOM
end
end
end
@@ -533,7 +536,7 @@ EOM
end
when_the_chef_server "has an environment with a different value" do
before { environment "x", { "description" => "hi" } }
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
- "name": "x",
@@ -560,7 +563,7 @@ EOM
end
when_the_chef_server "has an environment with no value" do
before { environment "x", {} }
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
- "name": "x"
@@ -574,7 +577,7 @@ EOM
before do
environment "x", { "description" => "lo" }
end
- it "knife diff reports the difference", :skip => (RUBY_VERSION < "1.9") do
+ it "knife diff reports the difference" do
knife("diff /environments/x.json").should_succeed(/
{
"name": "x",
@@ -592,9 +595,9 @@ EOM
when_the_repository "has an environment with bad JSON" do
before { file "environments/x.json", "{" }
it "knife diff reports an error and does a textual diff" do
- error_text = "WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF"
+ error_text = "WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF"
error_match = Regexp.new(Regexp.escape(error_text))
- knife("diff /environments/x.json").should_succeed(/- "name": "x"/, :stderr => error_match)
+ knife("diff /environments/x.json").should_succeed(/- "name": "x"/, stderr: error_match)
end
end
end
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index be0fc9d708..7bdec7b356 100644
--- a/spec/integration/knife/download_spec.rb
+++ b/spec/integration/knife/download_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/download"
require "chef/knife/diff"
@@ -48,21 +49,21 @@ describe "knife download", :workstation do
end
it "knife download downloads everything" do
- knife("download /").should_succeed <<EOM
-Created /clients/chef-validator.json
-Created /clients/chef-webui.json
-Created /clients/x.json
-Created /cookbooks/x
-Created /cookbooks/x/metadata.rb
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments/_default.json
-Created /environments/x.json
-Created /nodes/x.json
-Created /roles/x.json
-Created /users/admin.json
-Created /users/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /clients/chef-validator.json
+ Created /clients/chef-webui.json
+ Created /clients/x.json
+ Created /cookbooks/x
+ Created /cookbooks/x/metadata.rb
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments/_default.json
+ Created /environments/x.json
+ Created /nodes/x.json
+ Created /roles/x.json
+ Created /users/admin.json
+ Created /users/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
@@ -94,23 +95,23 @@ EOM
context "except the role file" do
before do
- file "roles/x.json", <<EOM
-{
- "chef_type": "role",
- "default_attributes": {
- },
- "description": "blarghle",
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "override_attributes": {
- },
- "run_list": [
-
- ]
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "description": "blarghle",
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
end
it "knife download changes the role" do
@@ -126,23 +127,23 @@ EOM
context "except the role file is textually different, but not ACTUALLY different" do
before do
- file "roles/x.json", <<EOM
-{
- "chef_type": "role",
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
end
it "knife download / does not change anything" do
@@ -166,31 +167,31 @@ EOM
it "knife download does nothing" do
knife("download /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-A\t/clients/y.json
-A\t/cookbooks/x/blah.rb
-A\t/cookbooks/y
-A\t/data_bags/x/z.json
-A\t/data_bags/y
-A\t/environments/y.json
-A\t/nodes/y.json
-A\t/roles/y.json
-A\t/users/y.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ A\t/clients/y.json
+ A\t/cookbooks/x/blah.rb
+ A\t/cookbooks/y
+ A\t/data_bags/x/z.json
+ A\t/data_bags/y
+ A\t/environments/y.json
+ A\t/nodes/y.json
+ A\t/roles/y.json
+ A\t/users/y.json
+ EOM
end
it "knife download --purge deletes the extra files" do
- knife("download --purge /").should_succeed <<EOM
-Deleted extra entry /clients/y.json (purge is on)
-Deleted extra entry /cookbooks/x/blah.rb (purge is on)
-Deleted extra entry /cookbooks/y (purge is on)
-Deleted extra entry /data_bags/x/z.json (purge is on)
-Deleted extra entry /data_bags/y (purge is on)
-Deleted extra entry /environments/y.json (purge is on)
-Deleted extra entry /nodes/y.json (purge is on)
-Deleted extra entry /roles/y.json (purge is on)
-Deleted extra entry /users/y.json (purge is on)
-EOM
+ knife("download --purge /").should_succeed <<~EOM
+ Deleted extra entry /clients/y.json (purge is on)
+ Deleted extra entry /cookbooks/x/blah.rb (purge is on)
+ Deleted extra entry /cookbooks/y (purge is on)
+ Deleted extra entry /data_bags/x/z.json (purge is on)
+ Deleted extra entry /data_bags/y (purge is on)
+ Deleted extra entry /environments/y.json (purge is on)
+ Deleted extra entry /nodes/y.json (purge is on)
+ Deleted extra entry /roles/y.json (purge is on)
+ Deleted extra entry /users/y.json (purge is on)
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
@@ -198,54 +199,54 @@ EOM
when_the_repository "is empty" do
it "knife download creates the extra files" do
- knife("download /").should_succeed <<EOM
-Created /clients
-Created /clients/chef-validator.json
-Created /clients/chef-webui.json
-Created /clients/x.json
-Created /cookbooks
-Created /cookbooks/x
-Created /cookbooks/x/metadata.rb
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments
-Created /environments/_default.json
-Created /environments/x.json
-Created /nodes
-Created /nodes/x.json
-Created /roles
-Created /roles/x.json
-Created /users
-Created /users/admin.json
-Created /users/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /clients
+ Created /clients/chef-validator.json
+ Created /clients/chef-webui.json
+ Created /clients/x.json
+ Created /cookbooks
+ Created /cookbooks/x
+ Created /cookbooks/x/metadata.rb
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments
+ Created /environments/_default.json
+ Created /environments/x.json
+ Created /nodes
+ Created /nodes/x.json
+ Created /roles
+ Created /roles/x.json
+ Created /users
+ Created /users/admin.json
+ Created /users/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
it "knife download --no-diff creates the extra files" do
- knife("download --no-diff /").should_succeed <<EOM
-Created /clients
-Created /clients/chef-validator.json
-Created /clients/chef-webui.json
-Created /clients/x.json
-Created /cookbooks
-Created /cookbooks/x
-Created /cookbooks/x/metadata.rb
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments
-Created /environments/_default.json
-Created /environments/x.json
-Created /nodes
-Created /nodes/x.json
-Created /roles
-Created /roles/x.json
-Created /users
-Created /users/admin.json
-Created /users/x.json
-EOM
+ knife("download --no-diff /").should_succeed <<~EOM
+ Created /clients
+ Created /clients/chef-validator.json
+ Created /clients/chef-webui.json
+ Created /clients/x.json
+ Created /cookbooks
+ Created /cookbooks/x
+ Created /cookbooks/x/metadata.rb
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments
+ Created /environments/_default.json
+ Created /environments/x.json
+ Created /nodes
+ Created /nodes/x.json
+ Created /roles
+ Created /roles/x.json
+ Created /users
+ Created /users/admin.json
+ Created /users/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
@@ -255,7 +256,7 @@ EOM
end
it "knife download with no parameters reports an error" do
- knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", :stdout => /USAGE/
+ knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", stdout: /USAGE/
end
end
end
@@ -269,44 +270,44 @@ EOM
end
it "knife download of one data bag item itself succeeds" do
- knife("download /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/z.json
-EOM
+ knife("download /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/z.json
+ EOM
end
it "knife download /data_bags/x /data_bags/x/y.json downloads x once" do
- knife("download /data_bags/x /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /data_bags/x/z.json
-EOM
+ knife("download /data_bags/x /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /data_bags/x/z.json
+ EOM
end
end
end
when_the_repository "has three data bag items" do
before do
- file "data_bags/x/deleted.json", <<EOM
-{
- "id": "deleted"
-}
-EOM
- file "data_bags/x/modified.json", <<EOM
-{
- "id": "modified"
-}
-EOM
- file "data_bags/x/unmodified.json", <<EOM
-{
- "id": "unmodified"
-}
-EOM
+ file "data_bags/x/deleted.json", <<~EOM
+ {
+ "id": "deleted"
+ }
+ EOM
+ file "data_bags/x/modified.json", <<~EOM
+ {
+ "id": "modified"
+ }
+ EOM
+ file "data_bags/x/unmodified.json", <<~EOM
+ {
+ "id": "unmodified"
+ }
+ EOM
end
when_the_chef_server "has a modified, unmodified, added and deleted data bag item" do
@@ -319,63 +320,63 @@ EOM
end
it "knife download of the modified file succeeds" do
- knife("download /data_bags/x/modified.json").should_succeed <<EOM
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the unmodified file does nothing" do
knife("download /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the added file succeeds" do
- knife("download /data_bags/x/added.json").should_succeed <<EOM
-Created /data_bags/x/added.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the deleted file does nothing" do
knife("download /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download --purge of the deleted file deletes it" do
- knife("download --purge /data_bags/x/deleted.json").should_succeed <<EOM
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-EOM
+ knife("download --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ EOM
end
it "knife download of the entire data bag downloads everything" do
- knife("download /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download --purge of the entire data bag downloads everything" do
- knife("download --purge /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
+ knife("download --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
context "when cwd is the /data_bags directory" do
@@ -383,22 +384,22 @@ EOM
cwd "data_bags"
end
it "knife download fails" do
- knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", :stdout => /USAGE/
+ knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", stdout: /USAGE/
end
it "knife download --purge . downloads everything" do
- knife("download --purge .").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("download --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
it "knife download --purge * downloads everything" do
- knife("download --purge *").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("download --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
end
@@ -418,48 +419,48 @@ EOM
it "knife download of a modified file succeeds" do
knife("download /cookbooks/x/metadata.rb").should_succeed "Updated /cookbooks/x/metadata.rb\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x/y.rb
-A\t/cookbooks/x/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/y.rb
+ A\t/cookbooks/x/z.rb
+ EOM
end
it "knife download of a deleted file does nothing" do
knife("download /cookbooks/x/z.rb").should_succeed ""
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/y.rb
-A\t/cookbooks/x/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/y.rb
+ A\t/cookbooks/x/z.rb
+ EOM
end
it "knife download --purge of a deleted file succeeds" do
knife("download --purge /cookbooks/x/z.rb").should_succeed "Deleted extra entry /cookbooks/x/z.rb (purge is on)\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/y.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/y.rb
+ EOM
end
it "knife download of an added file succeeds" do
knife("download /cookbooks/x/y.rb").should_succeed "Created /cookbooks/x/y.rb\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-A\t/cookbooks/x/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ A\t/cookbooks/x/z.rb
+ EOM
end
it "knife download of the cookbook itself succeeds" do
- knife("download /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/metadata.rb
-Created /cookbooks/x/y.rb
-EOM
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-A\t/cookbooks/x/z.rb
-EOM
+ knife("download /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/metadata.rb
+ Created /cookbooks/x/y.rb
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ A\t/cookbooks/x/z.rb
+ EOM
end
it "knife download --purge of the cookbook itself succeeds" do
- knife("download --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/metadata.rb
-Created /cookbooks/x/y.rb
-Deleted extra entry /cookbooks/x/z.rb (purge is on)
-EOM
+ knife("download --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/metadata.rb
+ Created /cookbooks/x/y.rb
+ Deleted extra entry /cookbooks/x/z.rb (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -478,11 +479,11 @@ EOM
end
it "knife download /cookbooks/x downloads the latest version" do
- knife("download --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/metadata.rb
-Created /cookbooks/x/onlyin1.0.1.rb
-Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
-EOM
+ knife("download --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/metadata.rb
+ Created /cookbooks/x/onlyin1.0.1.rb
+ Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -494,9 +495,9 @@ EOM
end
it "knife download /cookbooks/x downloads the updated file" do
- knife("download --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("download --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/onlyin1.0.0.rb
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -507,11 +508,11 @@ EOM
end
it "knife download /cookbooks/x downloads the latest version" do
- knife("download --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/metadata.rb
-Created /cookbooks/x/onlyin1.0.1.rb
-Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
-EOM
+ knife("download --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/metadata.rb
+ Created /cookbooks/x/onlyin1.0.1.rb
+ Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -522,11 +523,11 @@ EOM
end
it "knife download /cookbooks/x downloads the old version" do
- knife("download --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x/metadata.rb
-Created /cookbooks/x/onlyin0.9.9.rb
-Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
-EOM
+ knife("download --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x/metadata.rb
+ Created /cookbooks/x/onlyin0.9.9.rb
+ Deleted extra entry /cookbooks/x/onlyin1.0.0.rb (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -538,14 +539,14 @@ EOM
end
when_the_repository "has the role in ruby" do
before do
- file "roles/x.rb", <<EOM
-name "x"
-description "x"
-EOM
+ 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("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
@@ -560,13 +561,13 @@ EOM
file "environments/x.json", "{"
end
it "knife download succeeds" do
- warning = <<-EOH
-WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF
- {
- (right here) ------^
+ warning = <<~EOH
+ WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
+ {
+ (right here) ------^
-EOH
- knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n", :stderr => warning
+ EOH
+ knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n", stderr: warning
knife("diff --name-status /environments/x.json").should_succeed ""
end
end
@@ -593,7 +594,9 @@ EOH
end
end # without versioned cookbooks
- with_versioned_cookbooks do
+ context "with versioned cookbooks" do
+ before { Chef::Config[:versioned_cookbooks] = true }
+
when_the_chef_server "has one of each thing" do
before do
client "x", {}
@@ -617,21 +620,21 @@ EOH
end
it "knife download downloads everything" do
- knife("download /").should_succeed <<EOM
-Created /clients/chef-validator.json
-Created /clients/chef-webui.json
-Created /clients/x.json
-Created /cookbooks/x-1.0.0
-Created /cookbooks/x-1.0.0/metadata.rb
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments/_default.json
-Created /environments/x.json
-Created /nodes/x.json
-Created /roles/x.json
-Created /users/admin.json
-Created /users/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /clients/chef-validator.json
+ Created /clients/chef-webui.json
+ Created /clients/x.json
+ Created /cookbooks/x-1.0.0
+ Created /cookbooks/x-1.0.0/metadata.rb
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments/_default.json
+ Created /environments/x.json
+ Created /nodes/x.json
+ Created /roles/x.json
+ Created /users/admin.json
+ Created /users/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
@@ -674,23 +677,23 @@ EOM
context "except the role file is textually different, but not ACTUALLY different" do
before do
- file "roles/x.json", <<EOM
-{
- "chef_type": "role" ,
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role" ,
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
end
it "knife download / does not change anything" do
@@ -715,33 +718,33 @@ EOM
it "knife download does nothing" do
knife("download /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-A\t/clients/y.json
-A\t/cookbooks/x-1.0.0/blah.rb
-A\t/cookbooks/x-2.0.0
-A\t/cookbooks/y-1.0.0
-A\t/data_bags/x/z.json
-A\t/data_bags/y
-A\t/environments/y.json
-A\t/nodes/y.json
-A\t/roles/y.json
-A\t/users/y.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ A\t/clients/y.json
+ A\t/cookbooks/x-1.0.0/blah.rb
+ A\t/cookbooks/x-2.0.0
+ A\t/cookbooks/y-1.0.0
+ A\t/data_bags/x/z.json
+ A\t/data_bags/y
+ A\t/environments/y.json
+ A\t/nodes/y.json
+ A\t/roles/y.json
+ A\t/users/y.json
+ EOM
end
it "knife download --purge deletes the extra files" do
- knife("download --purge /").should_succeed <<EOM
-Deleted extra entry /clients/y.json (purge is on)
-Deleted extra entry /cookbooks/x-1.0.0/blah.rb (purge is on)
-Deleted extra entry /cookbooks/x-2.0.0 (purge is on)
-Deleted extra entry /cookbooks/y-1.0.0 (purge is on)
-Deleted extra entry /data_bags/x/z.json (purge is on)
-Deleted extra entry /data_bags/y (purge is on)
-Deleted extra entry /environments/y.json (purge is on)
-Deleted extra entry /nodes/y.json (purge is on)
-Deleted extra entry /roles/y.json (purge is on)
-Deleted extra entry /users/y.json (purge is on)
-EOM
+ knife("download --purge /").should_succeed <<~EOM
+ Deleted extra entry /clients/y.json (purge is on)
+ Deleted extra entry /cookbooks/x-1.0.0/blah.rb (purge is on)
+ Deleted extra entry /cookbooks/x-2.0.0 (purge is on)
+ Deleted extra entry /cookbooks/y-1.0.0 (purge is on)
+ Deleted extra entry /data_bags/x/z.json (purge is on)
+ Deleted extra entry /data_bags/y (purge is on)
+ Deleted extra entry /environments/y.json (purge is on)
+ Deleted extra entry /nodes/y.json (purge is on)
+ Deleted extra entry /roles/y.json (purge is on)
+ Deleted extra entry /users/y.json (purge is on)
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
@@ -749,28 +752,28 @@ EOM
when_the_repository "is empty" do
it "knife download creates the extra files" do
- knife("download /").should_succeed <<EOM
-Created /clients
-Created /clients/chef-validator.json
-Created /clients/chef-webui.json
-Created /clients/x.json
-Created /cookbooks
-Created /cookbooks/x-1.0.0
-Created /cookbooks/x-1.0.0/metadata.rb
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments
-Created /environments/_default.json
-Created /environments/x.json
-Created /nodes
-Created /nodes/x.json
-Created /roles
-Created /roles/x.json
-Created /users
-Created /users/admin.json
-Created /users/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /clients
+ Created /clients/chef-validator.json
+ Created /clients/chef-webui.json
+ Created /clients/x.json
+ Created /cookbooks
+ Created /cookbooks/x-1.0.0
+ Created /cookbooks/x-1.0.0/metadata.rb
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments
+ Created /environments/_default.json
+ Created /environments/x.json
+ Created /nodes
+ Created /nodes/x.json
+ Created /roles
+ Created /roles/x.json
+ Created /users
+ Created /users/admin.json
+ Created /users/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
@@ -779,7 +782,7 @@ EOM
cwd "."
end
it "knife download with no parameters reports an error" do
- knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", :stdout => /USAGE/
+ knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", stdout: /USAGE/
end
end
end
@@ -793,35 +796,35 @@ EOM
end
it "knife download of one data bag item itself succeeds" do
- knife("download /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/z.json
-EOM
+ knife("download /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/z.json
+ EOM
end
end
end
when_the_repository "has three data bag items" do
before do
- file "data_bags/x/deleted.json", <<EOM
-{
- "id": "deleted"
-}
-EOM
- file "data_bags/x/modified.json", <<EOM
-{
- "id": "modified"
-}
-EOM
- file "data_bags/x/unmodified.json", <<EOM
-{
- "id": "unmodified"
-}
-EOM
+ file "data_bags/x/deleted.json", <<~EOM
+ {
+ "id": "deleted"
+ }
+ EOM
+ file "data_bags/x/modified.json", <<~EOM
+ {
+ "id": "modified"
+ }
+ EOM
+ file "data_bags/x/unmodified.json", <<~EOM
+ {
+ "id": "unmodified"
+ }
+ EOM
end
when_the_chef_server "has a modified, unmodified, added and deleted data bag item" do
@@ -834,63 +837,63 @@ EOM
end
it "knife download of the modified file succeeds" do
- knife("download /data_bags/x/modified.json").should_succeed <<EOM
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the unmodified file does nothing" do
knife("download /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the added file succeeds" do
- knife("download /data_bags/x/added.json").should_succeed <<EOM
-Created /data_bags/x/added.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download of the deleted file does nothing" do
knife("download /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/deleted.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download --purge of the deleted file deletes it" do
- knife("download --purge /data_bags/x/deleted.json").should_succeed <<EOM
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/added.json
-M\t/data_bags/x/modified.json
-EOM
+ knife("download --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/added.json
+ M\t/data_bags/x/modified.json
+ EOM
end
it "knife download of the entire data bag downloads everything" do
- knife("download /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-A\t/data_bags/x/deleted.json
-EOM
+ knife("download /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/deleted.json
+ EOM
end
it "knife download --purge of the entire data bag downloads everything" do
- knife("download --purge /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
+ knife("download --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
context "when cwd is the /data_bags directory" do
@@ -898,22 +901,22 @@ EOM
cwd "data_bags"
end
it "knife download fails" do
- knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", :stdout => /USAGE/
+ knife("download").should_fail "FATAL: You must specify at least one argument. If you want to download everything in this directory, run \"knife download .\"\n", stdout: /USAGE/
end
it "knife download --purge . downloads everything" do
- knife("download --purge .").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("download --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
it "knife download --purge * downloads everything" do
- knife("download --purge *").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("download --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
end
@@ -933,48 +936,48 @@ EOM
it "knife download of a modified file succeeds" do
knife("download /cookbooks/x-1.0.0/metadata.rb").should_succeed "Updated /cookbooks/x-1.0.0/metadata.rb\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x-1.0.0/y.rb
-A\t/cookbooks/x-1.0.0/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-1.0.0/y.rb
+ A\t/cookbooks/x-1.0.0/z.rb
+ EOM
end
it "knife download of a deleted file does nothing" do
knife("download /cookbooks/x-1.0.0/z.rb").should_succeed ""
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x-1.0.0/metadata.rb
-D\t/cookbooks/x-1.0.0/y.rb
-A\t/cookbooks/x-1.0.0/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x-1.0.0/metadata.rb
+ D\t/cookbooks/x-1.0.0/y.rb
+ A\t/cookbooks/x-1.0.0/z.rb
+ EOM
end
it "knife download --purge of a deleted file succeeds" do
knife("download --purge /cookbooks/x-1.0.0/z.rb").should_succeed "Deleted extra entry /cookbooks/x-1.0.0/z.rb (purge is on)\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x-1.0.0/metadata.rb
-D\t/cookbooks/x-1.0.0/y.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x-1.0.0/metadata.rb
+ D\t/cookbooks/x-1.0.0/y.rb
+ EOM
end
it "knife download of an added file succeeds" do
knife("download /cookbooks/x-1.0.0/y.rb").should_succeed "Created /cookbooks/x-1.0.0/y.rb\n"
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x-1.0.0/metadata.rb
-A\t/cookbooks/x-1.0.0/z.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x-1.0.0/metadata.rb
+ A\t/cookbooks/x-1.0.0/z.rb
+ EOM
end
it "knife download of the cookbook itself succeeds" do
- knife("download /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0/metadata.rb
-Created /cookbooks/x-1.0.0/y.rb
-EOM
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-A\t/cookbooks/x-1.0.0/z.rb
-EOM
+ knife("download /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0/metadata.rb
+ Created /cookbooks/x-1.0.0/y.rb
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ A\t/cookbooks/x-1.0.0/z.rb
+ EOM
end
it "knife download --purge of the cookbook itself succeeds" do
- knife("download --purge /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0/metadata.rb
-Created /cookbooks/x-1.0.0/y.rb
-Deleted extra entry /cookbooks/x-1.0.0/z.rb (purge is on)
-EOM
+ knife("download --purge /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0/metadata.rb
+ Created /cookbooks/x-1.0.0/y.rb
+ Deleted extra entry /cookbooks/x-1.0.0/z.rb (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -993,12 +996,12 @@ EOM
end
it "knife download /cookbooks/x downloads the latest version" do
- knife("download --purge /cookbooks").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0/onlyin1.0.0.rb
-Created /cookbooks/x-1.0.1
-Created /cookbooks/x-1.0.1/metadata.rb
-Created /cookbooks/x-1.0.1/onlyin1.0.1.rb
-EOM
+ knife("download --purge /cookbooks").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0/onlyin1.0.0.rb
+ Created /cookbooks/x-1.0.1
+ Created /cookbooks/x-1.0.1/metadata.rb
+ Created /cookbooks/x-1.0.1/onlyin1.0.1.rb
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1010,12 +1013,12 @@ EOM
end
it "knife download /cookbooks downloads the updated file" do
- knife("download --purge /cookbooks").should_succeed <<EOM
-Created /cookbooks/x-0.9.9
-Created /cookbooks/x-0.9.9/metadata.rb
-Created /cookbooks/x-0.9.9/onlyin0.9.9.rb
-Updated /cookbooks/x-1.0.0/onlyin1.0.0.rb
-EOM
+ knife("download --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-0.9.9
+ Created /cookbooks/x-0.9.9/metadata.rb
+ Created /cookbooks/x-0.9.9/onlyin0.9.9.rb
+ Updated /cookbooks/x-1.0.0/onlyin1.0.0.rb
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1026,12 +1029,12 @@ EOM
end
it "knife download /cookbooks/x downloads the latest version" do
- knife("download --purge /cookbooks").should_succeed <<EOM
-Created /cookbooks/x-1.0.1
-Created /cookbooks/x-1.0.1/metadata.rb
-Created /cookbooks/x-1.0.1/onlyin1.0.1.rb
-Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
-EOM
+ knife("download --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.1
+ Created /cookbooks/x-1.0.1/metadata.rb
+ Created /cookbooks/x-1.0.1/onlyin1.0.1.rb
+ Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1042,12 +1045,12 @@ EOM
end
it "knife download --purge /cookbooks downloads the old version and deletes the new version" do
- knife("download --purge /cookbooks").should_succeed <<EOM
-Created /cookbooks/x-0.9.9
-Created /cookbooks/x-0.9.9/metadata.rb
-Created /cookbooks/x-0.9.9/onlyin0.9.9.rb
-Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
-EOM
+ knife("download --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-0.9.9
+ Created /cookbooks/x-0.9.9/metadata.rb
+ Created /cookbooks/x-0.9.9/onlyin0.9.9.rb
+ Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1102,16 +1105,16 @@ EOM
new_result
}.at_least(:once)
- knife("download /cookbooks/x").should_succeed <<EOM
-Created /cookbooks
-Created /cookbooks/x
-Created /cookbooks/x/metadata.rb
-EOM
+ knife("download /cookbooks/x").should_succeed <<~EOM
+ Created /cookbooks
+ Created /cookbooks/x
+ Created /cookbooks/x/metadata.rb
+ EOM
end
end
end
- when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do
+ when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
before do
user "foo", {}
user "bar", {}
@@ -1125,71 +1128,71 @@ EOM
when_the_repository "has all the default stuff" do
before do
- knife("download /").should_succeed <<EOM
-Created /acls
-Created /acls/clients
-Created /acls/clients/foo-validator.json
-Created /acls/containers
-Created /acls/containers/clients.json
-Created /acls/containers/containers.json
-Created /acls/containers/cookbook_artifacts.json
-Created /acls/containers/cookbooks.json
-Created /acls/containers/data.json
-Created /acls/containers/environments.json
-Created /acls/containers/groups.json
-Created /acls/containers/nodes.json
-Created /acls/containers/policies.json
-Created /acls/containers/policy_groups.json
-Created /acls/containers/roles.json
-Created /acls/containers/sandboxes.json
-Created /acls/cookbook_artifacts
-Created /acls/cookbooks
-Created /acls/data_bags
-Created /acls/environments
-Created /acls/environments/_default.json
-Created /acls/groups
-Created /acls/groups/admins.json
-Created /acls/groups/billing-admins.json
-Created /acls/groups/clients.json
-Created /acls/groups/users.json
-Created /acls/nodes
-Created /acls/policies
-Created /acls/policy_groups
-Created /acls/roles
-Created /acls/organization.json
-Created /clients
-Created /clients/foo-validator.json
-Created /containers
-Created /containers/clients.json
-Created /containers/containers.json
-Created /containers/cookbook_artifacts.json
-Created /containers/cookbooks.json
-Created /containers/data.json
-Created /containers/environments.json
-Created /containers/groups.json
-Created /containers/nodes.json
-Created /containers/policies.json
-Created /containers/policy_groups.json
-Created /containers/roles.json
-Created /containers/sandboxes.json
-Created /cookbook_artifacts
-Created /cookbooks
-Created /data_bags
-Created /environments
-Created /environments/_default.json
-Created /groups
-Created /groups/admins.json
-Created /groups/billing-admins.json
-Created /groups/clients.json
-Created /groups/users.json
-Created /invitations.json
-Created /members.json
-Created /nodes
-Created /org.json
-Created /policies
-Created /policy_groups
-Created /roles
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /acls
+ Created /acls/clients
+ Created /acls/clients/foo-validator.json
+ Created /acls/containers
+ Created /acls/containers/clients.json
+ Created /acls/containers/containers.json
+ Created /acls/containers/cookbook_artifacts.json
+ Created /acls/containers/cookbooks.json
+ Created /acls/containers/data.json
+ Created /acls/containers/environments.json
+ Created /acls/containers/groups.json
+ Created /acls/containers/nodes.json
+ Created /acls/containers/policies.json
+ Created /acls/containers/policy_groups.json
+ Created /acls/containers/roles.json
+ Created /acls/containers/sandboxes.json
+ Created /acls/cookbook_artifacts
+ Created /acls/cookbooks
+ Created /acls/data_bags
+ Created /acls/environments
+ Created /acls/environments/_default.json
+ Created /acls/groups
+ Created /acls/groups/admins.json
+ Created /acls/groups/billing-admins.json
+ Created /acls/groups/clients.json
+ Created /acls/groups/users.json
+ Created /acls/nodes
+ Created /acls/policies
+ Created /acls/policy_groups
+ Created /acls/roles
+ Created /acls/organization.json
+ Created /clients
+ Created /clients/foo-validator.json
+ Created /containers
+ Created /containers/clients.json
+ Created /containers/containers.json
+ Created /containers/cookbook_artifacts.json
+ Created /containers/cookbooks.json
+ Created /containers/data.json
+ Created /containers/environments.json
+ Created /containers/groups.json
+ Created /containers/nodes.json
+ Created /containers/policies.json
+ Created /containers/policy_groups.json
+ Created /containers/roles.json
+ Created /containers/sandboxes.json
+ Created /cookbook_artifacts
+ Created /cookbooks
+ Created /data_bags
+ Created /environments
+ Created /environments/_default.json
+ Created /groups
+ Created /groups/admins.json
+ Created /groups/billing-admins.json
+ Created /groups/clients.json
+ Created /groups/users.json
+ Created /invitations.json
+ Created /members.json
+ Created /nodes
+ Created /org.json
+ Created /policies
+ Created /policy_groups
+ Created /roles
+ EOM
end
context "and the server has one of each thing" do
@@ -1217,44 +1220,44 @@ EOM
end
before do
- knife("download /acls /groups/clients.json /groups/users.json").should_succeed <<-EOM
-Created /acls/clients/x.json
-Created /acls/containers/x.json
-Created /acls/cookbook_artifacts/x.json
-Created /acls/cookbooks/x.json
-Created /acls/data_bags/x.json
-Created /acls/environments/x.json
-Created /acls/groups/x.json
-Created /acls/nodes/x.json
-Created /acls/policies/blah.json
-Created /acls/policies/x.json
-Created /acls/policy_groups/x.json
-Created /acls/roles/x.json
-Updated /groups/clients.json
-Updated /groups/users.json
-EOM
+ knife("download /acls /groups/clients.json /groups/users.json").should_succeed <<~EOM
+ Created /acls/clients/x.json
+ Created /acls/containers/x.json
+ Created /acls/cookbook_artifacts/x.json
+ Created /acls/cookbooks/x.json
+ Created /acls/data_bags/x.json
+ Created /acls/environments/x.json
+ Created /acls/groups/x.json
+ Created /acls/nodes/x.json
+ Created /acls/policies/blah.json
+ Created /acls/policies/x.json
+ Created /acls/policy_groups/x.json
+ Created /acls/roles/x.json
+ Updated /groups/clients.json
+ Updated /groups/users.json
+ EOM
end
it "knife download / downloads everything" do
- knife("download /").should_succeed <<EOM
-Created /clients/x.json
-Created /containers/x.json
-Created /cookbook_artifacts/x-1x1
-Created /cookbook_artifacts/x-1x1/metadata.rb
-Created /cookbooks/x
-Created /cookbooks/x/metadata.rb
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments/x.json
-Created /groups/x.json
-Updated /invitations.json
-Updated /members.json
-Created /nodes/x.json
-Created /policies/blah-1.0.0.json
-Created /policies/x-1.0.0.json
-Created /policy_groups/x.json
-Created /roles/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Created /clients/x.json
+ Created /containers/x.json
+ Created /cookbook_artifacts/x-1x1
+ Created /cookbook_artifacts/x-1x1/metadata.rb
+ Created /cookbooks/x
+ Created /cookbooks/x/metadata.rb
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments/x.json
+ Created /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Created /nodes/x.json
+ Created /policies/blah-1.0.0.json
+ Created /policies/x-1.0.0.json
+ Created /policy_groups/x.json
+ Created /roles/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
@@ -1308,22 +1311,22 @@ EOM
end
it "knife download updates everything" do
- knife("download /").should_succeed <<EOM
-Updated /clients/x.json
-Updated /cookbook_artifacts/x-1x1/metadata.rb
-Updated /cookbooks/x/metadata.rb
-Updated /data_bags/x/y.json
-Updated /environments/x.json
-Updated /groups/x.json
-Updated /invitations.json
-Updated /members.json
-Updated /nodes/x.json
-Updated /org.json
-Created /policies/blah-1.0.0.json
-Updated /policies/x-1.0.0.json
-Updated /policy_groups/x.json
-Updated /roles/x.json
-EOM
+ knife("download /").should_succeed <<~EOM
+ Updated /clients/x.json
+ Updated /cookbook_artifacts/x-1x1/metadata.rb
+ Updated /cookbooks/x/metadata.rb
+ Updated /data_bags/x/y.json
+ Updated /environments/x.json
+ Updated /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Updated /nodes/x.json
+ Updated /org.json
+ Created /policies/blah-1.0.0.json
+ Updated /policies/x-1.0.0.json
+ Updated /policy_groups/x.json
+ Updated /roles/x.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
diff --git a/spec/integration/knife/environment_compare_spec.rb b/spec/integration/knife/environment_compare_spec.rb
index 3259b27d1b..7a623adf4c 100644
--- a/spec/integration/knife/environment_compare_spec.rb
+++ b/spec/integration/knife/environment_compare_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -42,33 +43,33 @@ describe "knife environment compare", :workstation do
}
end
- # rubocop:disable Style/TrailingWhitespace
+ # 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
+ 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
+ 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
+ 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 Style/TrailingWhitespace
+ # rubocop:enable Layout/TrailingWhitespace
end
end
diff --git a/spec/integration/knife/environment_create_spec.rb b/spec/integration/knife/environment_create_spec.rb
index 03fd4e63f7..66ba9ed6e6 100644
--- a/spec/integration/knife/environment_create_spec.rb
+++ b/spec/integration/knife/environment_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -33,7 +34,7 @@ describe "knife environment create", :workstation do
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)
+ expect { knife("environment create bah") }.to raise_error(Net::HTTPClientException)
end
end
diff --git a/spec/integration/knife/environment_delete_spec.rb b/spec/integration/knife/environment_delete_spec.rb
index 0f1fe5c4a8..f55a1c96bd 100644
--- a/spec/integration/knife/environment_delete_spec.rb
+++ b/spec/integration/knife/environment_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
diff --git a/spec/integration/knife/environment_from_file_spec.rb b/spec/integration/knife/environment_from_file_spec.rb
index 67d4373939..f9d35f4d47 100644
--- a/spec/integration/knife/environment_from_file_spec.rb
+++ b/spec/integration/knife/environment_from_file_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -23,91 +24,91 @@ describe "knife environment from file", :workstation do
# include_context "default config options"
- let (:env_dir) { "#{@repository_dir}/environments" }
+ 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
+ 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
+ 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
+ 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
+ knife("environment list").should_succeed <<~EOM
+ _default
+ car
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/environment_list_spec.rb b/spec/integration/knife/environment_list_spec.rb
index 5e74453d1f..dba685a82e 100644
--- a/spec/integration/knife/environment_list_spec.rb
+++ b/spec/integration/knife/environment_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -30,11 +31,11 @@ describe "knife environment list", :workstation do
end
it "lists all the environments" do
- knife("environment list").should_succeed <<EOM
-_default
-b
-y
-EOM
+ knife("environment list").should_succeed <<~EOM
+ _default
+ b
+ y
+ EOM
end
end
diff --git a/spec/integration/knife/environment_show_spec.rb b/spec/integration/knife/environment_show_spec.rb
index e74bf6d05d..de6ad1efd4 100644
--- a/spec/integration/knife/environment_show_spec.rb
+++ b/spec/integration/knife/environment_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -26,31 +27,51 @@ describe "knife environment show", :workstation do
when_the_chef_server "has some environments" do
before do
environment "b", {
- "default_attributes" => { "foo" => "bar" },
+ "default_attributes" => { "foo" => "bar", "baz" => { "raz.my" => "mataz" } },
}
end
- # rubocop:disable Style/TrailingWhitespace
+ # rubocop:disable Layout/TrailingWhitespace
it "shows an environment" do
- knife("environment show b").should_succeed <<EOM
-chef_type: environment
-cookbook_versions:
-default_attributes:
- foo: bar
-description:
-json_class: Chef::Environment
-name: b
-override_attributes:
-EOM
+ 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 Style/TrailingWhitespace
+ # rubocop:enable Layout/TrailingWhitespace
it "shows the requested attribute of an environment" do
- pending "KnifeSupport doesn't appear to pass this through correctly"
- knife("environment show b -a foo").should_succeed <<EOM
-b:
- foo: bar
-EOM
+ 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 4aa74f3f0e..4c711f3306 100644
--- a/spec/integration/knife/list_spec.rb
+++ b/spec/integration/knife/list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/list"
@@ -27,46 +28,46 @@ describe "knife list", :workstation do
when_the_chef_server "is empty" do
it "knife list / returns all top level directories" do
- knife("list /").should_succeed <<-EOM
-/clients
-/cookbooks
-/data_bags
-/environments
-/nodes
-/roles
-/users
-EOM
+ knife("list /").should_succeed <<~EOM
+ /clients
+ /cookbooks
+ /data_bags
+ /environments
+ /nodes
+ /roles
+ /users
+ EOM
end
it "knife list -R / returns everything" do
- knife("list -R /").should_succeed <<-EOM
-/:
-clients
-cookbooks
-data_bags
-environments
-nodes
-roles
-users
+ knife("list -R /").should_succeed <<~EOM
+ /:
+ clients
+ cookbooks
+ data_bags
+ environments
+ nodes
+ roles
+ users
-/clients:
-chef-validator.json
-chef-webui.json
+ /clients:
+ chef-validator.json
+ chef-webui.json
-/cookbooks:
+ /cookbooks:
-/data_bags:
+ /data_bags:
-/environments:
-_default.json
+ /environments:
+ _default.json
-/nodes:
+ /nodes:
-/roles:
+ /roles:
-/users:
-admin.json
-EOM
+ /users:
+ admin.json
+ EOM
end
end
@@ -92,161 +93,161 @@ EOM
end
it "knife list / returns all top level directories" do
- knife("list /").should_succeed <<-EOM
-/clients
-/cookbooks
-/data_bags
-/environments
-/nodes
-/roles
-/users
-EOM
+ knife("list /").should_succeed <<~EOM
+ /clients
+ /cookbooks
+ /data_bags
+ /environments
+ /nodes
+ /roles
+ /users
+ EOM
end
it "knife list -R / returns everything" do
- knife("list -R /").should_succeed <<-EOM
-/:
-clients
-cookbooks
-data_bags
-environments
-nodes
-roles
-users
-
-/clients:
-chef-validator.json
-chef-webui.json
-client1.json
-client2.json
-
-/cookbooks:
-cookbook1
-cookbook2
-
-/cookbooks/cookbook1:
-metadata.rb
-
-/cookbooks/cookbook2:
-metadata.rb
-recipes
-
-/cookbooks/cookbook2/recipes:
-default.rb
-
-/data_bags:
-bag1
-bag2
-
-/data_bags/bag1:
-item1.json
-item2.json
-
-/data_bags/bag2:
-item1.json
-item2.json
-
-/environments:
-_default.json
-environment1.json
-environment2.json
-
-/nodes:
-node1.json
-node2.json
-
-/roles:
-role1.json
-role2.json
-
-/users:
-admin.json
-user1.json
-user2.json
-EOM
+ knife("list -R /").should_succeed <<~EOM
+ /:
+ clients
+ cookbooks
+ data_bags
+ environments
+ nodes
+ roles
+ users
+
+ /clients:
+ chef-validator.json
+ chef-webui.json
+ client1.json
+ client2.json
+
+ /cookbooks:
+ cookbook1
+ cookbook2
+
+ /cookbooks/cookbook1:
+ metadata.rb
+
+ /cookbooks/cookbook2:
+ metadata.rb
+ recipes
+
+ /cookbooks/cookbook2/recipes:
+ default.rb
+
+ /data_bags:
+ bag1
+ bag2
+
+ /data_bags/bag1:
+ item1.json
+ item2.json
+
+ /data_bags/bag2:
+ item1.json
+ item2.json
+
+ /environments:
+ _default.json
+ environment1.json
+ environment2.json
+
+ /nodes:
+ node1.json
+ node2.json
+
+ /roles:
+ role1.json
+ role2.json
+
+ /users:
+ admin.json
+ user1.json
+ user2.json
+ EOM
end
it "knife list -R --flat / returns everything" do
- knife("list -R --flat /").should_succeed <<-EOM
-/clients
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/client1.json
-/clients/client2.json
-/cookbooks
-/cookbooks/cookbook1
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes
-/cookbooks/cookbook2/recipes/default.rb
-/data_bags
-/data_bags/bag1
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/nodes
-/nodes/node1.json
-/nodes/node2.json
-/roles
-/roles/role1.json
-/roles/role2.json
-/users
-/users/admin.json
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list -R --flat /").should_succeed <<~EOM
+ /clients
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/client1.json
+ /clients/client2.json
+ /cookbooks
+ /cookbooks/cookbook1
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes
+ /cookbooks/cookbook2/recipes/default.rb
+ /data_bags
+ /data_bags/bag1
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles
+ /roles/role1.json
+ /roles/role2.json
+ /users
+ /users/admin.json
+ /users/user1.json
+ /users/user2.json
+ EOM
end
it "knife list -Rfp / returns everything" do
- knife("list -Rfp /").should_succeed <<-EOM
-/clients/
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/client1.json
-/clients/client2.json
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes/
-/cookbooks/cookbook2/recipes/default.rb
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/nodes/
-/nodes/node1.json
-/nodes/node2.json
-/roles/
-/roles/role1.json
-/roles/role2.json
-/users/
-/users/admin.json
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list -Rfp /").should_succeed <<~EOM
+ /clients/
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/client1.json
+ /clients/client2.json
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes/
+ /cookbooks/cookbook2/recipes/default.rb
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes/
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles/
+ /roles/role1.json
+ /roles/role2.json
+ /users/
+ /users/admin.json
+ /users/user1.json
+ /users/user2.json
+ EOM
end
it "knife list /cookbooks returns the list of cookbooks" do
- knife("list /cookbooks").should_succeed <<-EOM
-/cookbooks/cookbook1
-/cookbooks/cookbook2
-EOM
+ knife("list /cookbooks").should_succeed <<~EOM
+ /cookbooks/cookbook1
+ /cookbooks/cookbook2
+ EOM
end
it "knife list /cookbooks/*2/*/*.rb returns the one file" do
@@ -254,51 +255,51 @@ EOM
end
it "knife list /**.rb returns all ruby files" do
- knife("list /**.rb").should_succeed <<-EOM
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes/default.rb
-EOM
+ knife("list /**.rb").should_succeed <<~EOM
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes/default.rb
+ EOM
end
it "knife list /cookbooks/**.rb returns all ruby files" do
- knife("list /cookbooks/**.rb").should_succeed <<-EOM
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes/default.rb
-EOM
+ knife("list /cookbooks/**.rb").should_succeed <<~EOM
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes/default.rb
+ EOM
end
it "knife list /**.json returns all json files" do
- knife("list /**.json").should_succeed <<-EOM
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/client1.json
-/clients/client2.json
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/nodes/node1.json
-/nodes/node2.json
-/roles/role1.json
-/roles/role2.json
-/users/admin.json
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list /**.json").should_succeed <<~EOM
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/client1.json
+ /clients/client2.json
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles/role1.json
+ /roles/role2.json
+ /users/admin.json
+ /users/user1.json
+ /users/user2.json
+ EOM
end
it "knife list /data**.json returns all data bag json files" do
- knife("list /data**.json").should_succeed <<-EOM
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-EOM
+ knife("list /data**.json").should_succeed <<~EOM
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ EOM
end
it "knife list /environments/missing_file.json reports missing file" do
@@ -317,41 +318,41 @@ EOM
before { cwd "." }
it "knife list -Rfp returns everything" do
- knife("list -Rfp").should_succeed <<-EOM
-clients/
-clients/chef-validator.json
-clients/chef-webui.json
-clients/client1.json
-clients/client2.json
-cookbooks/
-cookbooks/cookbook1/
-cookbooks/cookbook1/metadata.rb
-cookbooks/cookbook2/
-cookbooks/cookbook2/metadata.rb
-cookbooks/cookbook2/recipes/
-cookbooks/cookbook2/recipes/default.rb
-data_bags/
-data_bags/bag1/
-data_bags/bag1/item1.json
-data_bags/bag1/item2.json
-data_bags/bag2/
-data_bags/bag2/item1.json
-data_bags/bag2/item2.json
-environments/
-environments/_default.json
-environments/environment1.json
-environments/environment2.json
-nodes/
-nodes/node1.json
-nodes/node2.json
-roles/
-roles/role1.json
-roles/role2.json
-users/
-users/admin.json
-users/user1.json
-users/user2.json
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ clients/
+ clients/chef-validator.json
+ clients/chef-webui.json
+ clients/client1.json
+ clients/client2.json
+ cookbooks/
+ cookbooks/cookbook1/
+ cookbooks/cookbook1/metadata.rb
+ cookbooks/cookbook2/
+ cookbooks/cookbook2/metadata.rb
+ cookbooks/cookbook2/recipes/
+ cookbooks/cookbook2/recipes/default.rb
+ data_bags/
+ data_bags/bag1/
+ data_bags/bag1/item1.json
+ data_bags/bag1/item2.json
+ data_bags/bag2/
+ data_bags/bag2/item1.json
+ data_bags/bag2/item2.json
+ environments/
+ environments/_default.json
+ environments/environment1.json
+ environments/environment2.json
+ nodes/
+ nodes/node1.json
+ nodes/node2.json
+ roles/
+ roles/role1.json
+ roles/role2.json
+ users/
+ users/admin.json
+ users/user1.json
+ users/user2.json
+ EOM
end
end
end
@@ -362,90 +363,90 @@ EOM
before { cwd "cookbooks" }
it "knife list -Rfp / returns everything" do
- knife("list -Rfp /").should_succeed <<-EOM
-/clients/
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/client1.json
-/clients/client2.json
-./
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/nodes/
-/nodes/node1.json
-/nodes/node2.json
-/roles/
-/roles/role1.json
-/roles/role2.json
-/users/
-/users/admin.json
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list -Rfp /").should_succeed <<~EOM
+ /clients/
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/client1.json
+ /clients/client2.json
+ ./
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes/
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles/
+ /roles/role1.json
+ /roles/role2.json
+ /users/
+ /users/admin.json
+ /users/user1.json
+ /users/user2.json
+ EOM
end
it "knife list -Rfp .. returns everything" do
- knife("list -Rfp ..").should_succeed <<-EOM
-/clients/
-/clients/chef-validator.json
-/clients/chef-webui.json
-/clients/client1.json
-/clients/client2.json
-./
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/nodes/
-/nodes/node1.json
-/nodes/node2.json
-/roles/
-/roles/role1.json
-/roles/role2.json
-/users/
-/users/admin.json
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list -Rfp ..").should_succeed <<~EOM
+ /clients/
+ /clients/chef-validator.json
+ /clients/chef-webui.json
+ /clients/client1.json
+ /clients/client2.json
+ ./
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes/
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles/
+ /roles/role1.json
+ /roles/role2.json
+ /users/
+ /users/admin.json
+ /users/user1.json
+ /users/user2.json
+ EOM
end
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ EOM
end
end
end
@@ -457,16 +458,16 @@ EOM
before { cwd "cookbooks/cookbook2" }
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-metadata.rb
-recipes/
-recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ metadata.rb
+ recipes/
+ recipes/default.rb
+ EOM
end
end
end
- when_the_repository "has a cookbooks directory and a symlinked cookbooks directory", :skip => (Chef::Platform.windows?) do
+ when_the_repository "has a cookbooks directory and a symlinked cookbooks directory", skip: (ChefUtils.windows?) do
before do
directory "cookbooks"
symlink "symlinked", "cookbooks"
@@ -476,14 +477,14 @@ EOM
before { cwd "cookbooks" }
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ EOM
end
end
@@ -491,19 +492,19 @@ EOM
before { cwd "symlinked" }
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ EOM
end
end
end
- when_the_repository "has a real_cookbooks directory and a cookbooks symlink to it", :skip => (Chef::Platform.windows?) do
+ when_the_repository "has a real_cookbooks directory and a cookbooks symlink to it", skip: (ChefUtils.windows?) do
before do
directory "real_cookbooks"
symlink "cookbooks", "real_cookbooks"
@@ -513,14 +514,14 @@ EOM
before { cwd "real_cookbooks" }
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ EOM
end
end
@@ -528,14 +529,14 @@ EOM
before { cwd "cookbooks" }
it "knife list -Rfp returns cookbooks" do
- knife("list -Rfp").should_succeed <<-EOM
-cookbook1/
-cookbook1/metadata.rb
-cookbook2/
-cookbook2/metadata.rb
-cookbook2/recipes/
-cookbook2/recipes/default.rb
-EOM
+ knife("list -Rfp").should_succeed <<~EOM
+ cookbook1/
+ cookbook1/metadata.rb
+ cookbook2/
+ cookbook2/metadata.rb
+ cookbook2/recipes/
+ cookbook2/recipes/default.rb
+ EOM
end
end
end
@@ -589,37 +590,37 @@ EOM
end
it "knife list -Rfp / returns everything" do
- knife("list -Rp --local --flat /").should_succeed <<-EOM
-/clients/
-/clients/client1.json
-/clients/client2.json
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes/
-/cookbooks/cookbook2/recipes/default.rb
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/
-/environments/environment1.json
-/environments/environment2.json
-/nodes/
-/nodes/node1.json
-/nodes/node2.json
-/roles/
-/roles/role1.json
-/roles/role2.json
-/users/
-/users/user1.json
-/users/user2.json
-EOM
+ knife("list -Rp --local --flat /").should_succeed <<~EOM
+ /clients/
+ /clients/client1.json
+ /clients/client2.json
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes/
+ /cookbooks/cookbook2/recipes/default.rb
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/
+ /environments/environment1.json
+ /environments/environment2.json
+ /nodes/
+ /nodes/node1.json
+ /nodes/node2.json
+ /roles/
+ /roles/role1.json
+ /roles/role2.json
+ /users/
+ /users/user1.json
+ /users/user2.json
+ EOM
end
context "missing file/directory tests" do
@@ -638,7 +639,7 @@ EOM
end
end
- when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do
+ when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
before do
organization "foo"
end
@@ -649,254 +650,254 @@ EOM
context "and is empty" do
it "knife list / returns all top level directories" do
- knife("list /").should_succeed <<-EOM
-/acls
-/clients
-/containers
-/cookbook_artifacts
-/cookbooks
-/data_bags
-/environments
-/groups
-/invitations.json
-/members.json
-/nodes
-/org.json
-/policies
-/policy_groups
-/roles
-EOM
+ knife("list /").should_succeed <<~EOM
+ /acls
+ /clients
+ /containers
+ /cookbook_artifacts
+ /cookbooks
+ /data_bags
+ /environments
+ /groups
+ /invitations.json
+ /members.json
+ /nodes
+ /org.json
+ /policies
+ /policy_groups
+ /roles
+ EOM
end
it "knife list -R / returns everything" do
- knife("list -R /").should_succeed <<-EOM
-/:
-acls
-clients
-containers
-cookbook_artifacts
-cookbooks
-data_bags
-environments
-groups
-invitations.json
-members.json
-nodes
-org.json
-policies
-policy_groups
-roles
-
-/acls:
-clients
-containers
-cookbook_artifacts
-cookbooks
-data_bags
-environments
-groups
-nodes
-organization.json
-policies
-policy_groups
-roles
-
-/acls/clients:
-foo-validator.json
-
-/acls/containers:
-clients.json
-containers.json
-cookbook_artifacts.json
-cookbooks.json
-data.json
-environments.json
-groups.json
-nodes.json
-policies.json
-policy_groups.json
-roles.json
-sandboxes.json
-
-/acls/cookbook_artifacts:
-
-/acls/cookbooks:
-
-/acls/data_bags:
-
-/acls/environments:
-_default.json
-
-/acls/groups:
-admins.json
-billing-admins.json
-clients.json
-users.json
-
-/acls/nodes:
-
-/acls/policies:
-
-/acls/policy_groups:
-
-/acls/roles:
-
-/clients:
-foo-validator.json
-
-/containers:
-clients.json
-containers.json
-cookbook_artifacts.json
-cookbooks.json
-data.json
-environments.json
-groups.json
-nodes.json
-policies.json
-policy_groups.json
-roles.json
-sandboxes.json
-
-/cookbook_artifacts:
-
-/cookbooks:
-
-/data_bags:
-
-/environments:
-_default.json
-
-/groups:
-admins.json
-billing-admins.json
-clients.json
-users.json
-
-/nodes:
-
-/policies:
-
-/policy_groups:
-
-/roles:
-EOM
+ knife("list -R /").should_succeed <<~EOM
+ /:
+ acls
+ clients
+ containers
+ cookbook_artifacts
+ cookbooks
+ data_bags
+ environments
+ groups
+ invitations.json
+ members.json
+ nodes
+ org.json
+ policies
+ policy_groups
+ roles
+
+ /acls:
+ clients
+ containers
+ cookbook_artifacts
+ cookbooks
+ data_bags
+ environments
+ groups
+ nodes
+ organization.json
+ policies
+ policy_groups
+ roles
+
+ /acls/clients:
+ foo-validator.json
+
+ /acls/containers:
+ clients.json
+ containers.json
+ cookbook_artifacts.json
+ cookbooks.json
+ data.json
+ environments.json
+ groups.json
+ nodes.json
+ policies.json
+ policy_groups.json
+ roles.json
+ sandboxes.json
+
+ /acls/cookbook_artifacts:
+
+ /acls/cookbooks:
+
+ /acls/data_bags:
+
+ /acls/environments:
+ _default.json
+
+ /acls/groups:
+ admins.json
+ billing-admins.json
+ clients.json
+ users.json
+
+ /acls/nodes:
+
+ /acls/policies:
+
+ /acls/policy_groups:
+
+ /acls/roles:
+
+ /clients:
+ foo-validator.json
+
+ /containers:
+ clients.json
+ containers.json
+ cookbook_artifacts.json
+ cookbooks.json
+ data.json
+ environments.json
+ groups.json
+ nodes.json
+ policies.json
+ policy_groups.json
+ roles.json
+ sandboxes.json
+
+ /cookbook_artifacts:
+
+ /cookbooks:
+
+ /data_bags:
+
+ /environments:
+ _default.json
+
+ /groups:
+ admins.json
+ billing-admins.json
+ clients.json
+ users.json
+
+ /nodes:
+
+ /policies:
+
+ /policy_groups:
+
+ /roles:
+ EOM
end
end
it "knife list -R / returns everything" do
- knife("list -R /").should_succeed <<-EOM
-/:
-acls
-clients
-containers
-cookbook_artifacts
-cookbooks
-data_bags
-environments
-groups
-invitations.json
-members.json
-nodes
-org.json
-policies
-policy_groups
-roles
-
-/acls:
-clients
-containers
-cookbook_artifacts
-cookbooks
-data_bags
-environments
-groups
-nodes
-organization.json
-policies
-policy_groups
-roles
-
-/acls/clients:
-foo-validator.json
-
-/acls/containers:
-clients.json
-containers.json
-cookbook_artifacts.json
-cookbooks.json
-data.json
-environments.json
-groups.json
-nodes.json
-policies.json
-policy_groups.json
-roles.json
-sandboxes.json
-
-/acls/cookbook_artifacts:
-
-/acls/cookbooks:
-
-/acls/data_bags:
-
-/acls/environments:
-_default.json
-
-/acls/groups:
-admins.json
-billing-admins.json
-clients.json
-users.json
-
-/acls/nodes:
-
-/acls/policies:
-
-/acls/policy_groups:
-
-/acls/roles:
-
-/clients:
-foo-validator.json
-
-/containers:
-clients.json
-containers.json
-cookbook_artifacts.json
-cookbooks.json
-data.json
-environments.json
-groups.json
-nodes.json
-policies.json
-policy_groups.json
-roles.json
-sandboxes.json
-
-/cookbook_artifacts:
-
-/cookbooks:
-
-/data_bags:
-
-/environments:
-_default.json
-
-/groups:
-admins.json
-billing-admins.json
-clients.json
-users.json
-
-/nodes:
-
-/policies:
-
-/policy_groups:
-
-/roles:
-EOM
+ knife("list -R /").should_succeed <<~EOM
+ /:
+ acls
+ clients
+ containers
+ cookbook_artifacts
+ cookbooks
+ data_bags
+ environments
+ groups
+ invitations.json
+ members.json
+ nodes
+ org.json
+ policies
+ policy_groups
+ roles
+
+ /acls:
+ clients
+ containers
+ cookbook_artifacts
+ cookbooks
+ data_bags
+ environments
+ groups
+ nodes
+ organization.json
+ policies
+ policy_groups
+ roles
+
+ /acls/clients:
+ foo-validator.json
+
+ /acls/containers:
+ clients.json
+ containers.json
+ cookbook_artifacts.json
+ cookbooks.json
+ data.json
+ environments.json
+ groups.json
+ nodes.json
+ policies.json
+ policy_groups.json
+ roles.json
+ sandboxes.json
+
+ /acls/cookbook_artifacts:
+
+ /acls/cookbooks:
+
+ /acls/data_bags:
+
+ /acls/environments:
+ _default.json
+
+ /acls/groups:
+ admins.json
+ billing-admins.json
+ clients.json
+ users.json
+
+ /acls/nodes:
+
+ /acls/policies:
+
+ /acls/policy_groups:
+
+ /acls/roles:
+
+ /clients:
+ foo-validator.json
+
+ /containers:
+ clients.json
+ containers.json
+ cookbook_artifacts.json
+ cookbooks.json
+ data.json
+ environments.json
+ groups.json
+ nodes.json
+ policies.json
+ policy_groups.json
+ roles.json
+ sandboxes.json
+
+ /cookbook_artifacts:
+
+ /cookbooks:
+
+ /data_bags:
+
+ /environments:
+ _default.json
+
+ /groups:
+ admins.json
+ billing-admins.json
+ clients.json
+ users.json
+
+ /nodes:
+
+ /policies:
+
+ /policy_groups:
+
+ /roles:
+ EOM
end
context "has plenty of stuff in it" do
@@ -931,128 +932,128 @@ EOM
end
it "knife list -Rfp / returns everything" do
- knife("list -Rfp /").should_succeed <<-EOM
-/acls/
-/acls/clients/
-/acls/clients/client1.json
-/acls/clients/client2.json
-/acls/clients/foo-validator.json
-/acls/containers/
-/acls/containers/clients.json
-/acls/containers/container1.json
-/acls/containers/container2.json
-/acls/containers/containers.json
-/acls/containers/cookbook_artifacts.json
-/acls/containers/cookbooks.json
-/acls/containers/data.json
-/acls/containers/environments.json
-/acls/containers/groups.json
-/acls/containers/nodes.json
-/acls/containers/policies.json
-/acls/containers/policy_groups.json
-/acls/containers/roles.json
-/acls/containers/sandboxes.json
-/acls/cookbook_artifacts/
-/acls/cookbook_artifacts/cookbook_artifact1.json
-/acls/cookbook_artifacts/cookbook_artifact2.json
-/acls/cookbooks/
-/acls/cookbooks/cookbook1.json
-/acls/cookbooks/cookbook2.json
-/acls/data_bags/
-/acls/data_bags/bag1.json
-/acls/data_bags/bag2.json
-/acls/environments/
-/acls/environments/_default.json
-/acls/environments/environment1.json
-/acls/environments/environment2.json
-/acls/groups/
-/acls/groups/admins.json
-/acls/groups/billing-admins.json
-/acls/groups/clients.json
-/acls/groups/group1.json
-/acls/groups/group2.json
-/acls/groups/users.json
-/acls/nodes/
-/acls/nodes/node1.json
-/acls/nodes/node2.json
-/acls/organization.json
-/acls/policies/
-/acls/policies/policy1.json
-/acls/policies/policy2.json
-/acls/policy_groups/
-/acls/policy_groups/policy_group1.json
-/acls/policy_groups/policy_group2.json
-/acls/roles/
-/acls/roles/role1.json
-/acls/roles/role2.json
-/clients/
-/clients/client1.json
-/clients/client2.json
-/clients/foo-validator.json
-/containers/
-/containers/clients.json
-/containers/container1.json
-/containers/container2.json
-/containers/containers.json
-/containers/cookbook_artifacts.json
-/containers/cookbooks.json
-/containers/data.json
-/containers/environments.json
-/containers/groups.json
-/containers/nodes.json
-/containers/policies.json
-/containers/policy_groups.json
-/containers/roles.json
-/containers/sandboxes.json
-/cookbook_artifacts/
-/cookbook_artifacts/cookbook_artifact1-1x1/
-/cookbook_artifacts/cookbook_artifact1-1x1/metadata.rb
-/cookbook_artifacts/cookbook_artifact2-2x2/
-/cookbook_artifacts/cookbook_artifact2-2x2/metadata.rb
-/cookbook_artifacts/cookbook_artifact2-2x2/recipes/
-/cookbook_artifacts/cookbook_artifact2-2x2/recipes/default.rb
-/cookbooks/
-/cookbooks/cookbook1/
-/cookbooks/cookbook1/metadata.rb
-/cookbooks/cookbook2/
-/cookbooks/cookbook2/metadata.rb
-/cookbooks/cookbook2/recipes/
-/cookbooks/cookbook2/recipes/default.rb
-/data_bags/
-/data_bags/bag1/
-/data_bags/bag1/item1.json
-/data_bags/bag1/item2.json
-/data_bags/bag2/
-/data_bags/bag2/item1.json
-/data_bags/bag2/item2.json
-/environments/
-/environments/_default.json
-/environments/environment1.json
-/environments/environment2.json
-/groups/
-/groups/admins.json
-/groups/billing-admins.json
-/groups/clients.json
-/groups/group1.json
-/groups/group2.json
-/groups/users.json
-/invitations.json
-/members.json
-/nodes/
-/nodes/node1.json
-/nodes/node2.json
-/org.json
-/policies/
-/policies/policy1-1.2.3.json
-/policies/policy2-1.2.3.json
-/policies/policy2-1.3.5.json
-/policy_groups/
-/policy_groups/policy_group1.json
-/policy_groups/policy_group2.json
-/roles/
-/roles/role1.json
-/roles/role2.json
-EOM
+ knife("list -Rfp /").should_succeed <<~EOM
+ /acls/
+ /acls/clients/
+ /acls/clients/client1.json
+ /acls/clients/client2.json
+ /acls/clients/foo-validator.json
+ /acls/containers/
+ /acls/containers/clients.json
+ /acls/containers/container1.json
+ /acls/containers/container2.json
+ /acls/containers/containers.json
+ /acls/containers/cookbook_artifacts.json
+ /acls/containers/cookbooks.json
+ /acls/containers/data.json
+ /acls/containers/environments.json
+ /acls/containers/groups.json
+ /acls/containers/nodes.json
+ /acls/containers/policies.json
+ /acls/containers/policy_groups.json
+ /acls/containers/roles.json
+ /acls/containers/sandboxes.json
+ /acls/cookbook_artifacts/
+ /acls/cookbook_artifacts/cookbook_artifact1.json
+ /acls/cookbook_artifacts/cookbook_artifact2.json
+ /acls/cookbooks/
+ /acls/cookbooks/cookbook1.json
+ /acls/cookbooks/cookbook2.json
+ /acls/data_bags/
+ /acls/data_bags/bag1.json
+ /acls/data_bags/bag2.json
+ /acls/environments/
+ /acls/environments/_default.json
+ /acls/environments/environment1.json
+ /acls/environments/environment2.json
+ /acls/groups/
+ /acls/groups/admins.json
+ /acls/groups/billing-admins.json
+ /acls/groups/clients.json
+ /acls/groups/group1.json
+ /acls/groups/group2.json
+ /acls/groups/users.json
+ /acls/nodes/
+ /acls/nodes/node1.json
+ /acls/nodes/node2.json
+ /acls/organization.json
+ /acls/policies/
+ /acls/policies/policy1.json
+ /acls/policies/policy2.json
+ /acls/policy_groups/
+ /acls/policy_groups/policy_group1.json
+ /acls/policy_groups/policy_group2.json
+ /acls/roles/
+ /acls/roles/role1.json
+ /acls/roles/role2.json
+ /clients/
+ /clients/client1.json
+ /clients/client2.json
+ /clients/foo-validator.json
+ /containers/
+ /containers/clients.json
+ /containers/container1.json
+ /containers/container2.json
+ /containers/containers.json
+ /containers/cookbook_artifacts.json
+ /containers/cookbooks.json
+ /containers/data.json
+ /containers/environments.json
+ /containers/groups.json
+ /containers/nodes.json
+ /containers/policies.json
+ /containers/policy_groups.json
+ /containers/roles.json
+ /containers/sandboxes.json
+ /cookbook_artifacts/
+ /cookbook_artifacts/cookbook_artifact1-1x1/
+ /cookbook_artifacts/cookbook_artifact1-1x1/metadata.rb
+ /cookbook_artifacts/cookbook_artifact2-2x2/
+ /cookbook_artifacts/cookbook_artifact2-2x2/metadata.rb
+ /cookbook_artifacts/cookbook_artifact2-2x2/recipes/
+ /cookbook_artifacts/cookbook_artifact2-2x2/recipes/default.rb
+ /cookbooks/
+ /cookbooks/cookbook1/
+ /cookbooks/cookbook1/metadata.rb
+ /cookbooks/cookbook2/
+ /cookbooks/cookbook2/metadata.rb
+ /cookbooks/cookbook2/recipes/
+ /cookbooks/cookbook2/recipes/default.rb
+ /data_bags/
+ /data_bags/bag1/
+ /data_bags/bag1/item1.json
+ /data_bags/bag1/item2.json
+ /data_bags/bag2/
+ /data_bags/bag2/item1.json
+ /data_bags/bag2/item2.json
+ /environments/
+ /environments/_default.json
+ /environments/environment1.json
+ /environments/environment2.json
+ /groups/
+ /groups/admins.json
+ /groups/billing-admins.json
+ /groups/clients.json
+ /groups/group1.json
+ /groups/group2.json
+ /groups/users.json
+ /invitations.json
+ /members.json
+ /nodes/
+ /nodes/node1.json
+ /nodes/node2.json
+ /org.json
+ /policies/
+ /policies/policy1-1.2.3.json
+ /policies/policy2-1.2.3.json
+ /policies/policy2-1.3.5.json
+ /policy_groups/
+ /policy_groups/policy_group1.json
+ /policy_groups/policy_group2.json
+ /roles/
+ /roles/role1.json
+ /roles/role2.json
+ EOM
end
end
end
diff --git a/spec/integration/knife/node_bulk_delete_spec.rb b/spec/integration/knife/node_bulk_delete_spec.rb
index fa706cbd2b..dcaa71ef58 100644
--- a/spec/integration/knife/node_bulk_delete_spec.rb
+++ b/spec/integration/knife/node_bulk_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,19 +33,19 @@ describe "knife node bulk delete", :workstation do
end
it "deletes all matching nodes" do
- knife("node bulk delete ^ca.*", input: "Y").should_succeed <<EOM
-The following nodes will be deleted:
+ knife("node bulk delete ^ca.*", input: "Y").should_succeed <<~EOM
+ The following nodes will be deleted:
-car cat
+ car cat
-Are you sure you want to delete these nodes? (Y/N) Deleted node car
-Deleted node cat
-EOM
+ 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
+ knife("node list").should_succeed <<~EOM
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/node_create_spec.rb b/spec/integration/knife/node_create_spec.rb
index 93a2f9ce6f..e8f6d71694 100644
--- a/spec/integration/knife/node_create_spec.rb
+++ b/spec/integration/knife/node_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "openssl"
@@ -33,13 +34,13 @@ describe "knife node create", :workstation do
it "creates a new validator node" do
knife("node create bah").should_succeed out
- knife("node show bah").should_succeed /Node Name: bah/
+ 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)
+ expect { knife("node create bah") }.to raise_error(Net::HTTPClientException)
end
end
diff --git a/spec/integration/knife/node_delete_spec.rb b/spec/integration/knife/node_delete_spec.rb
index 5d88af6d4f..c743d6e03f 100644
--- a/spec/integration/knife/node_delete_spec.rb
+++ b/spec/integration/knife/node_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,15 +33,15 @@ describe "knife node delete", :workstation do
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 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
+ knife("node list").should_succeed <<~EOM
+ cat
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/node_environment_set_spec.rb b/spec/integration/knife/node_environment_set_spec.rb
index 6dadecf76a..16a86dbc30 100644
--- a/spec/integration/knife/node_environment_set_spec.rb
+++ b/spec/integration/knife/node_environment_set_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -30,8 +31,11 @@ describe "knife node environment set", :workstation do
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 /Environment:.*lisp/
+ 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
diff --git a/spec/integration/knife/node_from_file_spec.rb b/spec/integration/knife/node_from_file_spec.rb
index 3430967a21..6f7e0780f0 100644
--- a/spec/integration/knife/node_from_file_spec.rb
+++ b/spec/integration/knife/node_from_file_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -23,34 +24,34 @@ describe "knife node from file", :workstation do
# include_context "default config options"
- let (:node_dir) { "#{@repository_dir}/nodes" }
+ 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
+ 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
+ knife("node from file #{node_dir}/cons.json").should_succeed stderr: <<~EOM
+ Updated Node cons
+ EOM
end
end
diff --git a/spec/integration/knife/node_list_spec.rb b/spec/integration/knife/node_list_spec.rb
index 76f5861e03..8d3bc29a5a 100644
--- a/spec/integration/knife/node_list_spec.rb
+++ b/spec/integration/knife/node_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,12 +33,12 @@ describe "knife node list", :workstation do
end
it "lists all cookbooks" do
- knife("node list").should_succeed <<EOM
-car
-cat
-cdr
-cons
-EOM
+ knife("node list").should_succeed <<~EOM
+ car
+ cat
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/node_run_list_add_spec.rb b/spec/integration/knife/node_run_list_add_spec.rb
index 87d08e1975..f13e584526 100644
--- a/spec/integration/knife/node_run_list_add_spec.rb
+++ b/spec/integration/knife/node_run_list_add_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -29,7 +30,7 @@ describe "knife node run list add", :workstation do
end
it "sets the run list" do
- knife("node run list add cons recipe[foo]").should_succeed /run_list:\s*recipe\[foo\]\n/
+ knife("node run list add cons recipe[foo]").should_succeed(/run_list:\s*recipe\[foo\]\n/)
end
end
@@ -39,15 +40,15 @@ describe "knife node run list add", :workstation do
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
+ 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
+ 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
+ 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
index e85e3ed8e8..55f224b5ac 100644
--- a/spec/integration/knife/node_run_list_remove_spec.rb
+++ b/spec/integration/knife/node_run_list_remove_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -29,7 +30,7 @@ describe "knife node run list remove", :workstation do
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
+ 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
index ec6b08fb97..e642afc1ce 100644
--- a/spec/integration/knife/node_run_list_set_spec.rb
+++ b/spec/integration/knife/node_run_list_set_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -29,7 +30,7 @@ describe "knife node run list set", :workstation do
end
it "sets the run list" do
- knife("node run list set cons recipe[bar]").should_succeed /run_list:\s*recipe\[bar\]\n/m
+ 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
diff --git a/spec/integration/knife/node_show_spec.rb b/spec/integration/knife/node_show_spec.rb
index dd890aed59..cf3f166699 100644
--- a/spec/integration/knife/node_show_spec.rb
+++ b/spec/integration/knife/node_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -29,7 +30,7 @@ describe "knife node show", :workstation do
end
it "shows the node" do
- knife("node show cons").should_succeed /Run List:\s*recipe\[bar\], recipe\[foo\]\n/m
+ 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 5e0d3a3d11..ba26def473 100644
--- a/spec/integration/knife/raw_spec.rb
+++ b/spec/integration/knife/raw_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,15 +15,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/raw"
require "chef/knife/show"
+require "tiny_server"
describe "knife raw", :workstation do
include IntegrationSupport
include KnifeSupport
- include AppServerSupport
include_context "default config options"
@@ -38,206 +39,258 @@ describe "knife raw", :workstation do
user "x", "{}"
end
- it "knife raw /nodes/x returns the node", :skip => (RUBY_VERSION < "1.9") do
- knife("raw /nodes/x").should_succeed <<EOM
-{
- "name": "x",
- "json_class": "Chef::Node",
- "chef_type": "node",
- "chef_environment": "_default",
- "override": {
+ it "knife raw /nodes/x returns the node" do
+ knife("raw /nodes/x").should_succeed <<~EOM
+ {
+ "name": "x",
+ "json_class": "Chef::Node",
+ "chef_type": "node",
+ "chef_environment": "_default",
+ "override": {
- },
- "normal": {
- "tags": [
+ },
+ "normal": {
+ "tags": [
- ]
- },
- "default": {
+ ]
+ },
+ "default": {
- },
- "automatic": {
+ },
+ "automatic": {
- },
- "run_list": [
+ },
+ "run_list": [
- ]
-}
-EOM
+ ]
+ }
+ EOM
end
it "knife raw /blarghle returns 404" do
knife("raw /blarghle").should_fail(/ERROR: Server responded with error 404 "Not Found\s*"/)
end
- it "knife raw -m DELETE /roles/x succeeds", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -m DELETE /roles/x").should_succeed <<EOM
-{
- "name": "x",
- "description": "",
- "json_class": "Chef::Role",
- "chef_type": "role",
- "default_attributes": {
+ it "knife raw -m DELETE /roles/x succeeds" do
+ knife("raw -m DELETE /roles/x").should_succeed <<~EOM
+ {
+ "name": "x",
+ "description": "",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
- },
- "override_attributes": {
+ },
+ "override_attributes": {
- },
- "run_list": [
+ },
+ "run_list": [
- ],
- "env_run_lists": {
+ ],
+ "env_run_lists": {
- }
-}
-EOM
+ }
+ }
+ EOM
knife("show /roles/x.json").should_fail "ERROR: /roles/x.json: No such file or directory\n"
end
- it "knife raw -m PUT -i blah.txt /roles/x succeeds", :skip => (RUBY_VERSION < "1.9") do
+ it "knife raw -m PUT -i blah.txt /roles/x succeeds" do
Tempfile.open("raw_put_input") do |file|
- file.write <<EOM
-{
- "name": "x",
- "description": "eek",
- "json_class": "Chef::Role",
- "chef_type": "role",
- "default_attributes": {
-
- },
- "override_attributes": {
-
- },
- "run_list": [
-
- ],
- "env_run_lists": {
-
- }
-}
-EOM
+ file.write <<~EOM
+ {
+ "name": "x",
+ "description": "eek",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+
+ }
+ }
+ EOM
file.close
- knife("raw -m PUT -i #{file.path} /roles/x").should_succeed <<EOM
-{
- "name": "x",
- "description": "eek",
- "json_class": "Chef::Role",
- "chef_type": "role",
- "default_attributes": {
-
- },
- "override_attributes": {
-
- },
- "run_list": [
-
- ],
- "env_run_lists": {
-
- }
-}
-EOM
- knife("show /roles/x.json").should_succeed <<EOM
-/roles/x.json:
-{
- "name": "x",
- "description": "eek"
-}
-EOM
+ knife("raw -m PUT -i #{file.path} /roles/x").should_succeed <<~EOM
+ {
+ "name": "x",
+ "description": "eek",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+
+ }
+ }
+ EOM
+ knife("show /roles/x.json").should_succeed <<~EOM
+ /roles/x.json:
+ {
+ "name": "x",
+ "description": "eek",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+
+ }
+ }
+ EOM
end
end
- it "knife raw -m POST -i blah.txt /roles succeeds", :skip => (RUBY_VERSION < "1.9") do
+ it "knife raw -m POST -i blah.txt /roles succeeds" do
Tempfile.open("raw_put_input") do |file|
- file.write <<EOM
-{
- "name": "y",
- "description": "eek",
- "json_class": "Chef::Role",
- "chef_type": "role",
- "default_attributes": {
- },
- "override_attributes": {
- },
- "run_list": [
-
- ],
- "env_run_lists": {
- }
-}
-EOM
+ file.write <<~EOM
+ {
+ "name": "y",
+ "description": "eek",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+ }
+ }
+ EOM
file.close
- knife("raw -m POST -i #{file.path} /roles").should_succeed <<EOM
-{
- "uri": "#{Chef::Config.chef_server_url}/roles/y"
-}
-EOM
- knife("show /roles/y.json").should_succeed <<EOM
-/roles/y.json:
-{
- "name": "y",
- "description": "eek"
-}
-EOM
+ knife("raw -m POST -i #{file.path} /roles").should_succeed <<~EOM
+ {
+ "uri": "#{Chef::Config.chef_server_url}/roles/y"
+ }
+ EOM
+ knife("show /roles/y.json").should_succeed <<~EOM
+ /roles/y.json:
+ {
+ "name": "y",
+ "description": "eek",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+
+ }
+ }
+ EOM
end
end
context "When a server returns raw json" do
- before :each do
- Chef::Config.chef_server_url = "http://localhost:9018"
- app = lambda do |env|
- [200, { "Content-Type" => "application/json" }, ['{ "x": "y", "a": "b" }'] ]
+ def start_tiny_server(**server_opts)
+ @server = TinyServer::Manager.new(**server_opts)
+ @server.start
+ @api = TinyServer::API.instance
+ @api.clear
+
+ @api.get("/blah", 200, nil, { "Content-Type" => "application/json" }) do
+ '{ "x": "y", "a": "b" }'
end
- @raw_server, @raw_server_thread = start_app_server(app, 9018)
+ end
+
+ def stop_tiny_server
+ @server.stop
+ @server = @api = nil
+ end
+
+ before :each do
+ Chef::Config.chef_server_url = "http://localhost:9000"
+ start_tiny_server
end
after :each do
- @raw_server.shutdown if @raw_server
- @raw_server_thread.kill if @raw_server_thread
+ stop_tiny_server
end
- it "knife raw /blah returns the prettified json", :skip => (RUBY_VERSION < "1.9") do
- knife("raw /blah").should_succeed <<EOM
-{
- "x": "y",
- "a": "b"
-}
-EOM
+ it "knife raw /blah returns the prettified json" do
+ knife("raw /blah").should_succeed <<~EOM
+ {
+ "x": "y",
+ "a": "b"
+ }
+ EOM
end
it "knife raw --no-pretty /blah returns the raw json" do
- knife("raw --no-pretty /blah").should_succeed <<EOM
-{ "x": "y", "a": "b" }
-EOM
+ knife("raw --no-pretty /blah").should_succeed <<~EOM
+ { "x": "y", "a": "b" }
+ EOM
end
end
context "When a server returns text" do
- before :each do
- Chef::Config.chef_server_url = "http://localhost:9018"
- app = lambda do |env|
- [200, { "Content-Type" => "text" }, ['{ "x": "y", "a": "b" }'] ]
+ def start_tiny_server(**server_opts)
+ @server = TinyServer::Manager.new(**server_opts)
+ @server.start
+ @api = TinyServer::API.instance
+ @api.clear
+
+ @api.get("/blah", 200, nil, { "Content-Type" => "text" }) do
+ '{ "x": "y", "a": "b" }'
end
- @raw_server, @raw_server_thread = start_app_server(app, 9018)
+ end
+
+ def stop_tiny_server
+ @server.stop
+ @server = @api = nil
+ end
+
+ before :each do
+ Chef::Config.chef_server_url = "http://localhost:9000"
+ start_tiny_server
end
after :each do
- @raw_server.shutdown if @raw_server
- @raw_server_thread.kill if @raw_server_thread
+ stop_tiny_server
end
it "knife raw /blah returns the raw text" do
- knife("raw /blah").should_succeed(<<EOM)
-{ "x": "y", "a": "b" }
-EOM
+ knife("raw /blah").should_succeed(<<~EOM)
+ { "x": "y", "a": "b" }
+ EOM
end
it "knife raw --no-pretty /blah returns the raw text" do
- knife("raw --no-pretty /blah").should_succeed(<<EOM)
-{ "x": "y", "a": "b" }
-EOM
+ knife("raw --no-pretty /blah").should_succeed(<<~EOM)
+ { "x": "y", "a": "b" }
+ EOM
end
end
end
diff --git a/spec/integration/knife/redirection_spec.rb b/spec/integration/knife/redirection_spec.rb
index 29c1ee6ffb..34d5fe6efc 100644
--- a/spec/integration/knife/redirection_spec.rb
+++ b/spec/integration/knife/redirection_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
+require "tiny_server"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/list"
@@ -22,7 +24,21 @@ require "chef/knife/list"
describe "redirection", :workstation do
include IntegrationSupport
include KnifeSupport
- include AppServerSupport
+
+ def start_tiny_server(real_chef_server_url, **server_opts)
+ @server = TinyServer::Manager.new(**server_opts)
+ @server.start
+ @api = TinyServer::API.instance
+ @api.clear
+
+ @api.get("/roles", 302, nil, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}/roles" }) do
+ end
+ end
+
+ def stop_tiny_server
+ @server.stop
+ @server = @api = nil
+ end
include_context "default config options"
@@ -30,18 +46,14 @@ describe "redirection", :workstation do
before { role "x", {} }
context "and another server redirects to it with 302" do
- before :each do
+ before(:each) do
real_chef_server_url = Chef::Config.chef_server_url
- Chef::Config.chef_server_url = "http://localhost:9018"
- app = lambda do |env|
- [302, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}#{env['PATH_INFO']}" }, ["302 found"] ]
- end
- @redirector_server, @redirector_server_thread = start_app_server(app, 9018)
+ Chef::Config.chef_server_url = "http://localhost:9000"
+ start_tiny_server(real_chef_server_url)
end
- after :each do
- @redirector_server.shutdown if @redirector_server
- @redirector_thread.kill if @redirector_thread
+ after(:each) do
+ stop_tiny_server
end
it "knife list /roles returns the role" do
diff --git a/spec/integration/knife/role_bulk_delete_spec.rb b/spec/integration/knife/role_bulk_delete_spec.rb
index 0e7ff941e2..6810cebc91 100644
--- a/spec/integration/knife/role_bulk_delete_spec.rb
+++ b/spec/integration/knife/role_bulk_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,19 +33,19 @@ describe "knife role bulk delete", :workstation do
end
it "deletes all matching roles" do
- knife("role bulk delete ^ca.*", input: "Y").should_succeed <<EOM
-The following roles will be deleted:
+ knife("role bulk delete ^ca.*", input: "Y").should_succeed <<~EOM
+ The following roles will be deleted:
-car cat
+ car cat
-Are you sure you want to delete these roles? (Y/N) Deleted role car
-Deleted role cat
-EOM
+ 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
+ knife("role list").should_succeed <<~EOM
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/role_create_spec.rb b/spec/integration/knife/role_create_spec.rb
index 941eaf5cb6..80ef1d9a9f 100644
--- a/spec/integration/knife/role_create_spec.rb
+++ b/spec/integration/knife/role_create_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -33,7 +34,7 @@ describe "knife role create", :workstation do
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)
+ expect { knife("role create bah") }.to raise_error(Net::HTTPClientException)
end
end
diff --git a/spec/integration/knife/role_delete_spec.rb b/spec/integration/knife/role_delete_spec.rb
index 9fbd3758b9..c4c6498c51 100644
--- a/spec/integration/knife/role_delete_spec.rb
+++ b/spec/integration/knife/role_delete_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,15 +33,15 @@ describe "knife role delete", :workstation do
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 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
+ knife("role list").should_succeed <<~EOM
+ cat
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/role_from_file_spec.rb b/spec/integration/knife/role_from_file_spec.rb
index 60caa3fa88..4a2912935c 100644
--- a/spec/integration/knife/role_from_file_spec.rb
+++ b/spec/integration/knife/role_from_file_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -23,71 +24,71 @@ describe "knife role from file", :workstation do
# include_context "default config options"
- let (:role_dir) { "#{@repository_dir}/roles" }
+ 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": {
+ file "roles/cons.json", <<~EOM
+ {
+ "name": "cons",
+ "description": "An role",
+ "json_class": "Chef::role",
+ "chef_type": "role",
+ "default_attributes": {
+ "hola": "Amigos!"
+ },
+ "override_attributes": {
- }
-}
-EOM
+ }
+ }
+ 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": {
+ 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
+ }
+ }
+ 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": {
+ 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
+ }
+ }
+ EOM
end
it "uploads a single file" do
- knife("role from file #{role_dir}/cons.json").should_succeed stderr: <<EOM
-Updated Role cons
-EOM
+ 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
+ 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
diff --git a/spec/integration/knife/role_list_spec.rb b/spec/integration/knife/role_list_spec.rb
index 36dc76be4c..9e4b983698 100644
--- a/spec/integration/knife/role_list_spec.rb
+++ b/spec/integration/knife/role_list_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -32,12 +33,12 @@ describe "knife role list", :workstation do
end
it "lists all cookbooks" do
- knife("role list").should_succeed <<EOM
-car
-cat
-cdr
-cons
-EOM
+ knife("role list").should_succeed <<~EOM
+ car
+ cat
+ cdr
+ cons
+ EOM
end
end
diff --git a/spec/integration/knife/role_show_spec.rb b/spec/integration/knife/role_show_spec.rb
index df2572447c..dfa989bf69 100644
--- a/spec/integration/knife/role_show_spec.rb
+++ b/spec/integration/knife/role_show_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
@@ -31,20 +32,20 @@ describe "knife role show", :workstation do
role "cat", {}
end
- # rubocop:disable Style/TrailingWhitespace
+ # 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
+ 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 Style/TrailingWhitespace
+ # 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..8eaa30f7fa
--- /dev/null
+++ b/spec/integration/knife/search_node_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright (c) 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/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..fa9b1dc47c 100644
--- a/spec/integration/knife/serve_spec.rb
+++ b/spec/integration/knife/serve_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,35 +22,70 @@ require "chef/server_api"
describe "knife serve", :workstation do
include IntegrationSupport
include KnifeSupport
- include AppServerSupport
+
+ def with_knife_serve
+ exception = nil
+ t = Thread.new do
+
+ knife("serve --chef-zero-port=8890")
+ rescue
+ exception = $!
+
+ 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" } }
-
- it "knife serve serves up /nodes/x" do
- exception = nil
- t = Thread.new do
- begin
- knife("serve --chef-zero-port=8890")
- rescue
- exception = $!
+ 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
+
+ %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/show_spec.rb b/spec/integration/knife/show_spec.rb
index ed4802fef9..4bee492e7b 100644
--- a/spec/integration/knife/show_spec.rb
+++ b/spec/integration/knife/show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "support/shared/context/config"
require "chef/knife/show"
@@ -49,64 +50,91 @@ describe "knife show", :workstation do
end
it "knife show /cookbooks/x/metadata.rb shows the remote version" do
- knife("show /cookbooks/x/metadata.rb").should_succeed <<EOM
-/cookbooks/x/metadata.rb:
-name "x"; version "1.0.0"
-EOM
+ knife("show /cookbooks/x/metadata.rb").should_succeed <<~EOM
+ /cookbooks/x/metadata.rb:
+ name "x"; version "1.0.0"
+ EOM
end
it "knife show --local /cookbooks/x/metadata.rb shows the local version" do
- knife("show --local /cookbooks/x/metadata.rb").should_succeed <<EOM
-/cookbooks/x/metadata.rb:
-name "x"; version "1.0.0"
-EOM
+ knife("show --local /cookbooks/x/metadata.rb").should_succeed <<~EOM
+ /cookbooks/x/metadata.rb:
+ name "x"; version "1.0.0"
+ EOM
end
it "knife show /data_bags/x/y.json shows the remote version" do
- knife("show /data_bags/x/y.json").should_succeed <<EOM
-/data_bags/x/y.json:
-{
- "id": "y"
-}
-EOM
+ knife("show /data_bags/x/y.json").should_succeed <<~EOM
+ /data_bags/x/y.json:
+ {
+ "id": "y"
+ }
+ EOM
end
it "knife show --local /data_bags/x/y.json shows the local version" do
- knife("show --local /data_bags/x/y.json").should_succeed <<EOM
-/data_bags/x/y.json:
-{
- "foo": "bar"
-}
-EOM
+ knife("show --local /data_bags/x/y.json").should_succeed <<~EOM
+ /data_bags/x/y.json:
+ {
+ "foo": "bar"
+ }
+ EOM
end
- it "knife show /environments/x.json shows the remote version", :skip => (RUBY_VERSION < "1.9") do
- knife("show /environments/x.json").should_succeed <<EOM
-/environments/x.json:
-{
- "name": "x"
-}
-EOM
+ it "knife show /environments/x.json shows the remote version" do
+ knife("show /environments/x.json").should_succeed <<~EOM
+ /environments/x.json:
+ {
+ "name": "x",
+ "description": "",
+ "cookbook_versions": {
+
+ },
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment"
+ }
+ EOM
end
it "knife show --local /environments/x.json shows the local version" do
- knife("show --local /environments/x.json").should_succeed <<EOM
-/environments/x.json:
-{
- "foo": "bar"
-}
-EOM
+ knife("show --local /environments/x.json").should_succeed <<~EOM
+ /environments/x.json:
+ {
+ "foo": "bar"
+ }
+ EOM
end
- it "knife show /roles/x.json shows the remote version", :skip => (RUBY_VERSION < "1.9") do
- knife("show /roles/x.json").should_succeed <<EOM
-/roles/x.json:
-{
- "name": "x"
-}
-EOM
+ it "knife show /roles/x.json shows the remote version" do
+ knife("show /roles/x.json").should_succeed <<~EOM
+ /roles/x.json:
+ {
+ "name": "x",
+ "description": "",
+ "json_class": "Chef::Role",
+ "chef_type": "role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "run_list": [
+
+ ],
+ "env_run_lists": {
+
+ }
+ }
+ EOM
end
it "knife show --local /roles/x.json shows the local version" do
- knife("show --local /roles/x.json").should_succeed <<EOM
-/roles/x.json:
-{
- "foo": "bar"
-}
-EOM
+ knife("show --local /roles/x.json").should_succeed <<~EOM
+ /roles/x.json:
+ {
+ "foo": "bar"
+ }
+ EOM
end
# show directory
it "knife show /data_bags/x fails" do
@@ -135,33 +163,35 @@ EOM
"name" => "x",
}
end
- it "knife show shows the attributes in a predetermined order", :skip => (RUBY_VERSION < "1.9") do
- knife("show /environments/x.json").should_succeed <<EOM
-/environments/x.json:
-{
- "name": "x",
- "description": "woo",
- "cookbook_versions": {
- "blah": "= 1.0.0"
- },
- "default_attributes": {
- "foo": "bar"
- },
- "override_attributes": {
- "x": "y"
- }
-}
-EOM
+ it "knife show shows the attributes in a predetermined order" do
+ knife("show /environments/x.json").should_succeed <<~EOM
+ /environments/x.json:
+ {
+ "name": "x",
+ "description": "woo",
+ "cookbook_versions": {
+ "blah": "= 1.0.0"
+ },
+ "default_attributes": {
+ "foo": "bar"
+ },
+ "override_attributes": {
+ "x": "y"
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment"
+ }
+ EOM
end
end
when_the_repository "has an environment with bad JSON" do
before { file "environments/x.json", "{" }
it "knife show succeeds" do
- knife("show --local /environments/x.json").should_succeed <<EOM
-/environments/x.json:
-{
-EOM
+ knife("show --local /environments/x.json").should_succeed <<~EOM
+ /environments/x.json:
+ {
+ EOM
end
end
end
diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb
index d372a83a35..37cfcefa32 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/knife/upload"
require "chef/knife/diff"
@@ -52,37 +53,37 @@ describe "knife upload", :workstation do
it "knife upload does nothing" do
knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients/chef-validator.json
-D\t/clients/chef-webui.json
-D\t/clients/x.json
-D\t/cookbooks/x
-D\t/data_bags/x
-D\t/environments/_default.json
-D\t/environments/x.json
-D\t/nodes/x.json
-D\t/roles/x.json
-D\t/users/admin.json
-D\t/users/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
end
it "knife upload --purge deletes everything" do
- knife("upload --purge /").should_succeed(<<EOM, :stderr => "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
-Deleted extra entry /clients/chef-validator.json (purge is on)
-Deleted extra entry /clients/chef-webui.json (purge is on)
-Deleted extra entry /clients/x.json (purge is on)
-Deleted extra entry /cookbooks/x (purge is on)
-Deleted extra entry /data_bags/x (purge is on)
-Deleted extra entry /environments/x.json (purge is on)
-Deleted extra entry /nodes/x.json (purge is on)
-Deleted extra entry /roles/x.json (purge is on)
-Deleted extra entry /users/admin.json (purge is on)
-Deleted extra entry /users/x.json (purge is on)
-EOM
- knife("diff --name-status /").should_succeed <<EOM
-D\t/environments/_default.json
-EOM
+ knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
+ Deleted extra entry /clients/chef-validator.json (purge is on)
+ Deleted extra entry /clients/chef-webui.json (purge is on)
+ Deleted extra entry /clients/x.json (purge is on)
+ Deleted extra entry /cookbooks/x (purge is on)
+ Deleted extra entry /data_bags/x (purge is on)
+ Deleted extra entry /environments/x.json (purge is on)
+ Deleted extra entry /nodes/x.json (purge is on)
+ Deleted extra entry /roles/x.json (purge is on)
+ Deleted extra entry /users/admin.json (purge is on)
+ Deleted extra entry /users/x.json (purge is on)
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/environments/_default.json
+ EOM
end
end
@@ -129,23 +130,23 @@ EOM
context "except the role file is textually different, but not ACTUALLY different" do
before do
- file "roles/x.json", <<EOM
-{
- "chef_type": "role",
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
end
it "knife upload / does not change anything" do
@@ -156,10 +157,10 @@ EOM
context "the role is in ruby" do
before do
- file "roles/x.rb", <<EOM
-name "x"
-description "blargle"
-EOM
+ file "roles/x.rb", <<~EOM
+ name "x"
+ description "blargle"
+ EOM
end
it "knife upload changes the role" do
@@ -178,16 +179,8 @@ EOM
file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'"
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"
- )
- 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 "fails with RuntimeError" do
+ expect { knife("upload /cookbooks") }.to raise_error RuntimeError, /Cookbook depends on itself/
end
end
@@ -205,35 +198,41 @@ EOM
end
it "knife upload adds the new files" do
- knife("upload /").should_succeed <<EOM
-Created /clients/y.json
-Updated /cookbooks/x
-Created /cookbooks/y
-Created /data_bags/x/z.json
-Created /data_bags/y
-Created /data_bags/y/zz.json
-Created /environments/y.json
-Created /nodes/y.json
-Created /roles/y.json
-Created /users/y.json
-EOM
- knife("diff /").should_succeed ""
+ knife("upload /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x
+ Created /cookbooks/y
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ D\t/cookbooks/y/metadata.json
+ EOM
end
it "knife upload --no-diff adds the new files" do
- knife("upload --no-diff /").should_succeed <<EOM
-Created /clients/y.json
-Updated /cookbooks/x
-Created /cookbooks/y
-Created /data_bags/x/z.json
-Created /data_bags/y
-Created /data_bags/y/zz.json
-Created /environments/y.json
-Created /nodes/y.json
-Created /roles/y.json
-Created /users/y.json
-EOM
- knife("diff --name-status /").should_succeed ""
+ knife("upload --no-diff /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x
+ Created /cookbooks/y
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ D\t/cookbooks/y/metadata.json
+ EOM
end
end
end
@@ -241,36 +240,36 @@ EOM
when_the_repository "is empty" do
it "knife upload does nothing" do
knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
it "knife upload --purge deletes nothing" do
- knife("upload --purge /").should_fail <<EOM
-ERROR: /clients cannot be deleted.
-ERROR: /cookbooks cannot be deleted.
-ERROR: /data_bags cannot be deleted.
-ERROR: /environments cannot be deleted.
-ERROR: /nodes cannot be deleted.
-ERROR: /roles cannot be deleted.
-ERROR: /users cannot be deleted.
-EOM
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("upload --purge /").should_fail <<~EOM
+ ERROR: /clients cannot be deleted.
+ ERROR: /cookbooks cannot be deleted.
+ ERROR: /data_bags cannot be deleted.
+ ERROR: /environments cannot be deleted.
+ ERROR: /nodes cannot be deleted.
+ ERROR: /roles cannot be deleted.
+ ERROR: /users cannot be deleted.
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
context "when current directory is top level" do
@@ -279,7 +278,7 @@ EOM
end
it "knife upload with no parameters reports an error" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", :stdout => /USAGE/
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
end
end
end
@@ -293,20 +292,20 @@ EOM
end
it "knife upload of the data bag uploads only the values in the data bag item and no other" do
- knife("upload /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-EOM
- expect(Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, :create_additions => false).keys.sort).to eq(%w{foo id})
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ EOM
+ expect(Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false).keys.sort).to eq(%w{foo id})
end
it "knife upload /data_bags/x /data_bags/x/y.json uploads x once" do
- knife("upload /data_bags/x /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
+ knife("upload /data_bags/x /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
end
end
@@ -317,12 +316,12 @@ EOM
end
it "upload preserves chef_type and data_bag" do
- knife("upload /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
- result = Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, :create_additions => false)
+ result = Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false)
expect(result.keys.sort).to eq(%w{chef_type data_bag id})
expect(result["chef_type"]).to eq("aaa")
expect(result["data_bag"]).to eq("bbb")
@@ -336,13 +335,13 @@ EOM
file "data_bags/x/z.json", {}
end
it "knife upload of one data bag item itself succeeds" do
- knife("upload /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-A\t/data_bags/x/z.json
-EOM
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/z.json
+ EOM
end
end
end
@@ -361,63 +360,63 @@ EOM
end
it "knife upload of the modified file succeeds" do
- knife("upload /data_bags/x/modified.json").should_succeed <<EOM
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-A\t/data_bags/x/added.json
-EOM
+ knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the unmodified file does nothing" do
knife("upload /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the added file succeeds" do
- knife("upload /data_bags/x/added.json").should_succeed <<EOM
-Created /data_bags/x/added.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-EOM
+ knife("upload /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ EOM
end
it "knife upload of the deleted file does nothing" do
knife("upload /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload --purge of the deleted file deletes it" do
- knife("upload --purge /data_bags/x/deleted.json").should_succeed <<EOM
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the entire data bag uploads everything" do
- knife("upload /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-EOM
+ knife("upload /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ EOM
end
it "knife upload --purge of the entire data bag uploads everything" do
- knife("upload --purge /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
+ knife("upload --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
context "when cwd is the /data_bags directory" do
@@ -427,23 +426,23 @@ EOM
end
it "knife upload fails" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", :stdout => /USAGE/
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
end
it "knife upload --purge . uploads everything" do
- knife("upload --purge .").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("upload --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
it "knife upload --purge * uploads everything" do
- knife("upload --purge *").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("upload --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
end
@@ -454,11 +453,30 @@ EOM
# upload of a file is designed not to work at present. Make sure that is the
# case.
when_the_chef_server "has a cookbook" do
-
before do
cookbook "x", "1.0.0", { "z.rb" => "" }
end
+ when_the_repository "does not have metadata file" do
+ before do
+ file "cookbooks/x/y.rb", "hi"
+ end
+
+ it "raises MetadataNotFound exception" do
+ expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotFound)
+ end
+ end
+
+ when_the_repository "does not have valid metadata" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata(nil, "1.0.0")
+ end
+
+ it "raises exception for invalid metadata" do
+ expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotValid)
+ end
+ end
+
when_the_repository "has a modified, extra and missing file for the cookbook" do
before do
file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
@@ -470,20 +488,26 @@ EOM
knife("upload /cookbooks/x/y.rb").should_fail "ERROR: /cookbooks/x cannot have a child created under it.\n"
knife("upload --purge /cookbooks/x/z.rb").should_fail "ERROR: /cookbooks/x/z.rb cannot be deleted.\n"
end
+
# TODO this is a bit of an inconsistency: if we didn't specify --purge,
# technically we shouldn't have deleted missing files. But ... cookbooks
# are a special case.
it "knife upload of the cookbook itself succeeds" do
- knife("upload /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
+
it "knife upload --purge of the cookbook itself succeeds" do
- knife("upload /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
when_the_repository "has a missing file for the cookbook" do
@@ -493,10 +517,12 @@ EOM
end
it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
when_the_repository "has an extra file for the cookbook" do
@@ -508,10 +534,12 @@ EOM
end
it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
@@ -521,9 +549,9 @@ EOM
end
it "knife upload --freeze freezes the cookbook" do
- knife("upload --freeze /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
+ knife("upload --freeze /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
# Modify a file and attempt to upload
file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different'
knife("upload /cookbooks/x").should_fail "ERROR: /cookbooks failed to write: Cookbook x is frozen\n"
@@ -533,7 +561,7 @@ EOM
when_the_chef_server "has a frozen cookbook" do
before do
- cookbook "frozencook", "1.0.0", {}, :frozen => true
+ cookbook "frozencook", "1.0.0", {}, frozen: true
end
when_the_repository "has an update to said cookbook" do
@@ -546,9 +574,9 @@ EOM
knife("upload /cookbooks/frozencook").should_fail "ERROR: /cookbooks failed to write: Cookbook frozencook is frozen\n"
end
it "knife upload --force uploads the frozen cookbook" do
- knife("upload --force /cookbooks/frozencook").should_succeed <<EOM
-Updated /cookbooks/frozencook
-EOM
+ knife("upload --force /cookbooks/frozencook").should_succeed <<~EOM
+ Updated /cookbooks/frozencook
+ EOM
end
end
end
@@ -556,6 +584,7 @@ EOM
when_the_repository "has a cookbook" do
before do
file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x/metadata.json", { name: "x", version: "1.0.0" }
file "cookbooks/x/onlyin1.0.0.rb", "old_text"
end
@@ -566,19 +595,51 @@ EOM
end
it "knife upload /cookbooks/x uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
- knife("upload --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/metadata.json
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/metadata.json
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ end
+ end
+ end
+
+ when_the_repository "has a cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x/onlyin1.0.0.rb", "old_text"
+ end
+
+ when_the_chef_server "has a later version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version and generates metadata.json from metadata.rb and uploads it." do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
end
@@ -588,11 +649,13 @@ EOM
cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
end
- it "knife upload /cookbooks/x uploads the local version" do
- knife("upload --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ it "knife upload /cookbooks/x uploads the local version generates metadata.json and uploads it." do
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
@@ -601,33 +664,35 @@ EOM
cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
end
- it "knife upload /cookbooks/x uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
- knife("upload --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x/metadata.rb
-D\t/cookbooks/x/onlyin1.0.1.rb
-A\t/cookbooks/x/onlyin1.0.0.rb
-EOM
+ it "knife upload /cookbooks/x uploads the local version and generates metadata.json before upload and uploads it." do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
end
end
when_the_chef_server "has an earlier version for the cookbook, and no current version" do
before do
- cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
+ cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
end
it "knife upload /cookbooks/x uploads the new version" do
- knife("upload --purge /cookbooks/x").should_succeed <<EOM
-Updated /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
end
@@ -643,24 +708,24 @@ EOM
end
it "knife upload tries and fails" do
- error1 = <<-EOH
-WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF
- {
- (right here) ------^
-
-ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF
- {
- (right here) ------^
+ error1 = <<~EOH
+ WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
+ {
+ (right here) ------^
+
+ ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF
+ {
+ (right here) ------^
EOH
- warn = <<-EOH
-WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF
- {
- (right here) ------^
+ warn = <<~EOH
+ WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
+ {
+ (right here) ------^
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)
+ knife("diff --name-status /environments/x.json").should_succeed("M\t/environments/x.json\n", stderr: warn)
end
end
@@ -724,16 +789,20 @@ WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error
file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
end
it "knife upload succeeds" do
- knife("upload /cookbooks/x").should_succeed <<EOM
-Created /cookbooks/x
-EOM
- knife("diff --name-status /cookbooks").should_succeed ""
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Created /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
end
end
end
end # without versioned cookbooks
- with_versioned_cookbooks do
+ context "with versioned cookbooks" do
+ before { Chef::Config[:versioned_cookbooks] = true }
+
when_the_chef_server "has one of each thing" do
before do
@@ -759,37 +828,37 @@ EOM
it "knife upload does nothing" do
knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients/chef-validator.json
-D\t/clients/chef-webui.json
-D\t/clients/x.json
-D\t/cookbooks/x-1.0.0
-D\t/data_bags/x
-D\t/environments/_default.json
-D\t/environments/x.json
-D\t/nodes/x.json
-D\t/roles/x.json
-D\t/users/admin.json
-D\t/users/x.json
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x-1.0.0
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
end
it "knife upload --purge deletes everything" do
- knife("upload --purge /").should_succeed(<<EOM, :stderr => "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
-Deleted extra entry /clients/chef-validator.json (purge is on)
-Deleted extra entry /clients/chef-webui.json (purge is on)
-Deleted extra entry /clients/x.json (purge is on)
-Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
-Deleted extra entry /data_bags/x (purge is on)
-Deleted extra entry /environments/x.json (purge is on)
-Deleted extra entry /nodes/x.json (purge is on)
-Deleted extra entry /roles/x.json (purge is on)
-Deleted extra entry /users/admin.json (purge is on)
-Deleted extra entry /users/x.json (purge is on)
-EOM
- knife("diff --name-status /").should_succeed <<EOM
-D\t/environments/_default.json
-EOM
+ knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
+ Deleted extra entry /clients/chef-validator.json (purge is on)
+ Deleted extra entry /clients/chef-webui.json (purge is on)
+ Deleted extra entry /clients/x.json (purge is on)
+ Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
+ Deleted extra entry /data_bags/x (purge is on)
+ Deleted extra entry /environments/x.json (purge is on)
+ Deleted extra entry /nodes/x.json (purge is on)
+ Deleted extra entry /roles/x.json (purge is on)
+ Deleted extra entry /users/admin.json (purge is on)
+ Deleted extra entry /users/x.json (purge is on)
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/environments/_default.json
+ EOM
end
end
@@ -832,23 +901,23 @@ EOM
context "except the role file is textually different, but not ACTUALLY different" do
before do
- file "roles/x.json", <<EOM
-{
- "chef_type": "role",
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
-}
-EOM
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
end
it "knife upload / does not change anything" do
@@ -872,19 +941,19 @@ EOM
end
it "knife upload adds the new files" do
- knife("upload /").should_succeed <<EOM
-Created /clients/y.json
-Updated /cookbooks/x-1.0.0
-Created /cookbooks/x-2.0.0
-Created /cookbooks/y-1.0.0
-Created /data_bags/x/z.json
-Created /data_bags/y
-Created /data_bags/y/zz.json
-Created /environments/y.json
-Created /nodes/y.json
-Created /roles/y.json
-Created /users/y.json
-EOM
+ knife("upload /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x-1.0.0
+ Created /cookbooks/x-2.0.0
+ Created /cookbooks/y-1.0.0
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
knife("diff --name-status /").should_succeed ""
end
end
@@ -893,36 +962,36 @@ EOM
when_the_repository "is empty" do
it "knife upload does nothing" do
knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
it "knife upload --purge deletes nothing" do
- knife("upload --purge /").should_fail <<EOM
-ERROR: /clients cannot be deleted.
-ERROR: /cookbooks cannot be deleted.
-ERROR: /data_bags cannot be deleted.
-ERROR: /environments cannot be deleted.
-ERROR: /nodes cannot be deleted.
-ERROR: /roles cannot be deleted.
-ERROR: /users cannot be deleted.
-EOM
- knife("diff --name-status /").should_succeed <<EOM
-D\t/clients
-D\t/cookbooks
-D\t/data_bags
-D\t/environments
-D\t/nodes
-D\t/roles
-D\t/users
-EOM
+ knife("upload --purge /").should_fail <<~EOM
+ ERROR: /clients cannot be deleted.
+ ERROR: /cookbooks cannot be deleted.
+ ERROR: /data_bags cannot be deleted.
+ ERROR: /environments cannot be deleted.
+ ERROR: /nodes cannot be deleted.
+ ERROR: /roles cannot be deleted.
+ ERROR: /users cannot be deleted.
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
end
context "when current directory is top level" do
@@ -930,7 +999,7 @@ EOM
cwd "."
end
it "knife upload with no parameters reports an error" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", :stdout => /USAGE/
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
end
end
end
@@ -945,13 +1014,13 @@ EOM
end
it "knife upload of one data bag item itself succeeds" do
- knife("upload /data_bags/x/y.json").should_succeed <<EOM
-Created /data_bags/x
-Created /data_bags/x/y.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-A\t/data_bags/x/z.json
-EOM
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/z.json
+ EOM
end
end
end
@@ -968,63 +1037,63 @@ EOM
end
it "knife upload of the modified file succeeds" do
- knife("upload /data_bags/x/modified.json").should_succeed <<EOM
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-A\t/data_bags/x/added.json
-EOM
+ knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the unmodified file does nothing" do
knife("upload /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the added file succeeds" do
- knife("upload /data_bags/x/added.json").should_succeed <<EOM
-Created /data_bags/x/added.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-EOM
+ knife("upload /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ EOM
end
it "knife upload of the deleted file does nothing" do
knife("upload /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload --purge of the deleted file deletes it" do
- knife("upload --purge /data_bags/x/deleted.json").should_succeed <<EOM
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-M\t/data_bags/x/modified.json
-A\t/data_bags/x/added.json
-EOM
+ knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
end
it "knife upload of the entire data bag uploads everything" do
- knife("upload /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-EOM
- knife("diff --name-status /data_bags").should_succeed <<EOM
-D\t/data_bags/x/deleted.json
-EOM
+ knife("upload /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ EOM
end
it "knife upload --purge of the entire data bag uploads everything" do
- knife("upload --purge /data_bags/x").should_succeed <<EOM
-Created /data_bags/x/added.json
-Updated /data_bags/x/modified.json
-Deleted extra entry /data_bags/x/deleted.json (purge is on)
-EOM
+ knife("upload --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
context "when cwd is the /data_bags directory" do
@@ -1032,22 +1101,22 @@ EOM
cwd "data_bags"
end
it "knife upload fails" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", :stdout => /USAGE/
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
end
it "knife upload --purge . uploads everything" do
- knife("upload --purge .").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("upload --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
it "knife upload --purge * uploads everything" do
- knife("upload --purge *").should_succeed <<EOM
-Created x/added.json
-Updated x/modified.json
-Deleted extra entry x/deleted.json (purge is on)
-EOM
+ knife("upload --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
knife("diff --name-status /data_bags").should_succeed ""
end
end
@@ -1078,16 +1147,16 @@ EOM
# technically we shouldn't have deleted missing files. But ... cookbooks
# are a special case.
it "knife upload of the cookbook itself succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-EOM
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
it "knife upload --purge of the cookbook itself succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-EOM
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1098,9 +1167,9 @@ EOM
end
it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-EOM
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1113,9 +1182,9 @@ EOM
end
it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-EOM
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1134,14 +1203,14 @@ EOM
end
it "knife upload /cookbooks uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-M\t/cookbooks/x-1.0.0/onlyin1.0.0.rb
-D\t/cookbooks/x-1.0.1
-EOM
- knife("upload --purge /cookbooks").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x-1.0.0/onlyin1.0.0.rb
+ D\t/cookbooks/x-1.0.1
+ EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1152,10 +1221,10 @@ EOM
cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
end
it "knife upload /cookbooks uploads the local version" do
- knife("upload --purge /cookbooks").should_succeed <<EOM
-Updated /cookbooks/x-1.0.0
-Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
-EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1166,14 +1235,14 @@ EOM
end
it "knife upload /cookbooks/x uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<EOM
-D\t/cookbooks/x-1.0.1
-A\t/cookbooks/x-1.0.0
-EOM
- knife("upload --purge /cookbooks").should_succeed <<EOM
-Created /cookbooks/x-1.0.0
-Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
-EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-1.0.1
+ A\t/cookbooks/x-1.0.0
+ EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1184,10 +1253,10 @@ EOM
end
it "knife upload /cookbooks/x uploads the new version" do
- knife("upload --purge /cookbooks").should_succeed <<EOM
-Created /cookbooks/x-1.0.0
-Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
-EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1258,9 +1327,9 @@ EOM
file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
end
it "knife upload succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<EOM
-Created /cookbooks/x-1.0.0
-EOM
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ EOM
knife("diff --name-status /cookbooks").should_succeed ""
end
end
@@ -1282,7 +1351,7 @@ EOM
end
end
- when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do
+ when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
before do
user "foo", {}
user "bar", {}
@@ -1321,25 +1390,25 @@ EOM
end
it "knife upload / uploads everything" do
- knife("upload /").should_succeed <<EOM
-Updated /acls/groups/blah.json
-Created /clients/x.json
-Created /containers/x.json
-Created /cookbook_artifacts/x-1x1
-Created /cookbooks/x
-Created /data_bags/x
-Created /data_bags/x/y.json
-Created /environments/x.json
-Created /groups/x.json
-Updated /invitations.json
-Updated /members.json
-Created /nodes/x.json
-Updated /org.json
-Created /policies/blah-1.0.0.json
-Created /policies/x-1.0.0.json
-Created /policy_groups/x.json
-Created /roles/x.json
-EOM
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ Created /clients/x.json
+ Created /containers/x.json
+ Created /cookbook_artifacts/x-1x1
+ Created /cookbooks/x
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments/x.json
+ Created /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Created /nodes/x.json
+ Updated /org.json
+ Created /policies/blah-1.0.0.json
+ Created /policies/x-1.0.0.json
+ Created /policy_groups/x.json
+ Created /roles/x.json
+ EOM
expect(api.get("association_requests").map { |a| a["username"] }).to eq([ "foo" ])
expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
knife("diff --name-status --diff-filter=AMT /").should_succeed ""
@@ -1374,9 +1443,9 @@ EOM
end
it "knife upload makes no changes" do
- knife("upload /").should_succeed <<EOM
-Updated /acls/groups/blah.json
-EOM
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ EOM
end
end
@@ -1386,9 +1455,9 @@ EOM
end
it "should fail because policies are not updateable" do
- knife("upload /policies/x-1.0.0.json").should_fail <<EOM
-ERROR: /policies/x-1.0.0.json cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes.
-EOM
+ knife("upload /policies/x-1.0.0.json").should_fail <<~EOM
+ ERROR: /policies/x-1.0.0.json cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes.
+ EOM
end
end
@@ -1398,9 +1467,9 @@ EOM
end
it "should fail because cookbook_artifacts cannot be updated" do
- knife("upload /cookbook_artifacts/x-1x1").should_fail <<EOM
-ERROR: /cookbook_artifacts/x-1x1 cannot be updated: cookbook artifacts are immutable once uploaded.
-EOM
+ knife("upload /cookbook_artifacts/x-1x1").should_fail <<~EOM
+ ERROR: /cookbook_artifacts/x-1x1 cannot be updated: cookbook artifacts are immutable once uploaded.
+ EOM
end
end
@@ -1428,21 +1497,21 @@ EOM
end
it "knife upload updates everything" do
- knife("upload /").should_succeed <<EOM
-Updated /acls/groups/blah.json
-Updated /clients/x.json
-Updated /cookbooks/x
-Updated /data_bags/x/y.json
-Updated /environments/x.json
-Updated /groups/x.json
-Updated /invitations.json
-Updated /members.json
-Updated /nodes/x.json
-Updated /org.json
-Created /policies/blah-1.0.0.json
-Updated /policy_groups/x.json
-Updated /roles/x.json
-EOM
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ Updated /clients/x.json
+ Updated /cookbooks/x
+ Updated /data_bags/x/y.json
+ Updated /environments/x.json
+ Updated /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Updated /nodes/x.json
+ Updated /org.json
+ Created /policies/blah-1.0.0.json
+ Updated /policy_groups/x.json
+ Updated /roles/x.json
+ EOM
knife("diff --name-status --diff-filter=AMT /").should_succeed ""
end
end
@@ -1480,7 +1549,7 @@ EOM
end
it "knife upload / emits a warning for bar and invites foobar" do
- knife("upload /").should_succeed "Updated /invitations.json\n", :stderr => "WARN: Could not invite bar to organization foo: User bar is already in organization foo\n"
+ knife("upload /").should_succeed "Updated /invitations.json\n", stderr: "WARN: Could not invite bar to organization foo: User bar is already in organization foo\n"
expect(api.get("association_requests").map { |a| a["username"] }).to eq(%w{foo foobar})
expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
end
diff --git a/spec/integration/ohai/ohai_spec.rb b/spec/integration/ohai/ohai_spec.rb
new file mode 100644
index 0000000000..af4dd5fe38
--- /dev/null
+++ b/spec/integration/ohai/ohai_spec.rb
@@ -0,0 +1,61 @@
+require "spec_helper"
+require "chef/mixin/shell_out"
+require "benchmark" unless defined?(Benchmark)
+
+describe "ohai" do
+ include Chef::Mixin::ShellOut
+
+ let(:ohai) { "bundle exec ohai" }
+
+ describe "testing ohai performance" do
+ # The purpose of this test is to generally find misconfigured DNS on
+ # CI testers. If this fails, it is probably because the forward+reverse
+ # DNS lookup that node[:hostname] needs is timing out and failing.
+ #
+ # If it is failing spuriously, it may mean DNS is failing spuriously, the
+ # best solution will be to make sure that `hostname -f`-like behavior hits
+ # /etc/hosts and not DNS.
+ #
+ # If it still fails supriously, it is possible that the server has high
+ # CPU load (e.g. due to background processes) which are contending with the
+ # running tests (disable the screensaver on servers, stop playing Fortnite
+ # while you're running tests, etc).
+ #
+ # If this just fails due to I/O being very slow and ruby being very slow to
+ # startup then that still indicates that the tester configuration needs
+ # fixing. The fact that this will fail on a windows box on a virt that doesn't
+ # use an SSD is because we have a higher bar for the tests to run successfully
+ # and that configuration is broken, so this test is red for a reason.
+ #
+ # This will probably fail on raspberry pi's or something like that as well. That
+ # is not a bug. We will never accept a raspberry pi as a CI tester for our
+ # software. Feel free to manually delete and thereby skip this file in your
+ # own testing harness, but that is not our concern, we are testing behavior
+ # that is critical to our infrastructure and must run in our tests.
+ #
+ # XXX: unfortunately this is so slow on our windows testers (~9 seconds on one
+ # tester) that we can't enable it for windows unless we get some better perf there.
+ #
+ it "the hostname plugin must return in under 4 seconds (see comments in code)" do
+ # unfortunately this doesn't look stable enough to enable
+ skip "we need to do more performance work on windows and s390x testers before this can be enabled"
+ delta = Benchmark.realtime do
+ shell_out!("#{ohai} hostname")
+ end
+ expect(delta).to be < 4
+ end
+
+ # The purpose of this is to give some indication of if shell_out is slow or
+ # if the hostname plugin itself is slow. If this test is also failing that we
+ # almost certainly have some kind of issue with DNS timeouts, etc. If this
+ # test succeeds and the other one fails, then it can be some kind of shelling-out
+ # issue or poor performance due to I/O on starting up ruby to run ohai, etc.
+ #
+ it "the hostname plugin must return in under 2 seconds when called from pure ruby" do
+ delta = Benchmark.realtime do
+ Ohai::System.new.all_plugins(["hostname"])
+ end
+ expect(delta).to be < 2
+ end
+ end
+end
diff --git a/spec/integration/recipes/accumulator_spec.rb b/spec/integration/recipes/accumulator_spec.rb
new file mode 100644
index 0000000000..329137440c
--- /dev/null
+++ b/spec/integration/recipes/accumulator_spec.rb
@@ -0,0 +1,233 @@
+require "spec_helper"
+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", __dir__) }
+
+ # 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) { "bundle exec 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 65931d4764..48f7952af0 100644
--- a/spec/integration/recipes/lwrp_inline_resources_spec.rb
+++ b/spec/integration/recipes/lwrp_inline_resources_spec.rb
@@ -1,3 +1,4 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -5,7 +6,7 @@ describe "LWRPs with inline resources" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -16,17 +17,16 @@ describe "LWRPs with inline resources" do
# 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(:chef_client) { "bundle exec 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
- resource_name :lwrp_inline_resources_test
- actions :a, :nothing
+ class LwrpInlineResourcesTest < Chef::Resource
+ provides :lwrp_inline_resources_test
+ 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
@@ -46,20 +46,20 @@ describe "LWRPs with inline resources" do
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
+ provides :lwrp_shadowed_property_test
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
content new_resource.content
- action [:create, :delete]
+ action %i{create delete}
end
end
end
- after { File.delete(LwrpShadowedPropertyTest::PATH) if File.exists?(LwrpShadowedPropertyTest::PATH) }
+ after { File.delete(LwrpShadowedPropertyTest::PATH) if File.exist?(LwrpShadowedPropertyTest::PATH) }
# https://github.com/chef/chef/issues/4334
it "does not warn spuriously" do
@@ -73,16 +73,14 @@ describe "LWRPs with inline resources" do
end
context "with an inline_resources provider with two actions, one calling the other" do
- class LwrpInlineResourcesTest2 < Chef::Resource::LWRPBase
- resource_name :lwrp_inline_resources_test2
- actions :a, :b, :nothing
+ class LwrpInlineResourcesTest2 < Chef::Resource
+ provides :lwrp_inline_resources_test2
+ 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
@@ -108,9 +106,9 @@ describe "LWRPs with inline resources" do
r = lwrp_inline_resources_test2 "hi" do
action :b
end
- 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)
+ 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\""
end
end
@@ -133,8 +131,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
@@ -149,14 +146,14 @@ describe "LWRPs with inline resources" do
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)
- actual = result.stdout.lines.map { |l| l.chomp }.join("\n")
+ 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)
+ actual = result.stdout.lines.map(&:chomp).join("\n")
expected = <<EOM
* x_my_machine[me] action create
* x_do_nothing[a] action create (up to date)
@@ -167,7 +164,7 @@ EOM
* x_do_nothing[b] action create (up to date)
(up to date)
EOM
- expected = expected.lines.map { |l| l.chomp }.join("\n")
+ expected = expected.lines.map(&:chomp).join("\n")
expect(actual).to include(expected)
result.error!
end
diff --git a/spec/integration/recipes/lwrp_spec.rb b/spec/integration/recipes/lwrp_spec.rb
index 3bc008d4f8..b7a0589675 100644
--- a/spec/integration/recipes/lwrp_spec.rb
+++ b/spec/integration/recipes/lwrp_spec.rb
@@ -1,3 +1,4 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -5,7 +6,7 @@ describe "LWRPs" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -16,35 +17,35 @@ describe "LWRPs" do
# 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(:chef_client) { "bundle exec chef-client --minimal-ohai" }
when_the_repository "has a cookbook named l-w-r-p" do
before do
directory "cookbooks/l-w-r-p" do
- file "resources/foo.rb", <<EOM
-default_action :create
-EOM
- file "providers/foo.rb", <<EOM
-action :create do
-end
-EOM
+ file "resources/foo.rb", <<~EOM
+ default_action :create
+ EOM
+ file "providers/foo.rb", <<~EOM
+ action :create do
+ end
+ EOM
- file "recipes/default.rb", <<EOM
-l_w_r_p_foo "me"
-EOM
+ file "recipes/default.rb", <<~EOM
+ l_w_r_p_foo "me"
+ 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
+ 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 'l-w-r-p::default'", :cwd => chef_dir)
+ result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'l-w-r-p::default'", cwd: chef_dir)
expect(result.stdout).to match(/\* l_w_r_p_foo\[me\] action create \(up to date\)/)
expect(result.stdout).not_to match(/WARN: You are overriding l_w_r_p_foo/)
result.error!
diff --git a/spec/integration/recipes/noop_resource_spec.rb b/spec/integration/recipes/noop_resource_spec.rb
index e0cf47c371..8e15050704 100644
--- a/spec/integration/recipes/noop_resource_spec.rb
+++ b/spec/integration/recipes/noop_resource_spec.rb
@@ -4,9 +4,9 @@ describe "Resources with a no-op provider" do
include IntegrationSupport
context "with noop provider providing foo" do
- before(:context) do
+ before(:each) do
class NoOpFoo < Chef::Resource
- resource_name "hi_there"
+ provides "hi_there"
default_action :update
end
Chef::Provider::Noop.provides :hi_there
diff --git a/spec/integration/recipes/notifies_spec.rb b/spec/integration/recipes/notifies_spec.rb
index 000f5e37bf..7dfa70dfe5 100644
--- a/spec/integration/recipes/notifies_spec.rb
+++ b/spec/integration/recipes/notifies_spec.rb
@@ -1,3 +1,20 @@
+#
+# Copyright:: Copyright (c) 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/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -5,46 +22,80 @@ describe "notifications" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
- let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
+ let(:chef_client) { "bundle exec chef-client --minimal-ohai" }
- when_the_repository "notifies delayed one" do
+ 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
+ notify_group "foo" do
+ notifies :nothing, 'apt_update', :delayed
+ action :run
+ end
+ notify_group "bar" do
+ notifies :nothing, 'apt_update[]', :delayed
+ action :run
+ end
+ EOM
+ end
+ end
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
+ it "should complete with success" do
+ file "config/client.rb", <<~EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ log_level :warn
+ EOM
-action :run do
- log "bar" do
- notifies :write, 'log[foo]', :delayed
+ 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+\* notify_group\[foo\] action run\s+\* notify_group\[bar\] action run\s+\* apt_update\[\] action nothing \(skipped due to action :nothing\)/)
+ result.error!
+ end
end
-end
-EOM
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-notifying_test "whatever"
-log "baz"
-EOM
+ when_the_repository "notifies delayed one" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
+
+ action :run do
+ notify_group "bar" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notifying_test "whatever"
+ log "baz"
+ 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
+ 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 = 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(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/)
+ expect(result.stdout).to match(/\* notify_group\[bar\] action run\s+\* log\[baz\] action write\s+\* log\[foo\] action write/)
result.error!
end
end
@@ -53,41 +104,43 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
-
-action :run do
- log "bar" do
- notifies :write, 'log[foo]', :delayed
- end
-end
-EOM
-
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-notifying_test "whatever"
-log "baz" do
- notifies :write, 'log[foo]', :delayed
-end
-EOM
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
+
+ action :run do
+ notify_group "bar" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notifying_test "whatever"
+ notify_group "baz" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ 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
+ 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 = 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(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/)
+ expect(result.stdout).to match(/\* notify_group\[bar\] action run\s+\* notify_group\[baz\] action run\s+\* log\[foo\] action write/)
# and only run once
expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/)
result.error!
@@ -98,43 +151,45 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
-
-action :run do
- log "bar" do
- notifies :write, 'log[foo]', :delayed
- end
-end
-EOM
-
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-log "quux" do
- notifies :write, 'log[foo]', :delayed
- notifies :write, 'log[baz]', :delayed
-end
-notifying_test "whatever"
-log "baz"
-EOM
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
+
+ action :run do
+ notify_group "bar" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notify_group "quux" do
+ notifies :write, 'log[foo]', :delayed
+ notifies :write, 'log[baz]', :delayed
+ action :run
+ end
+ notifying_test "whatever"
+ log "baz"
+ 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
+ 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 = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
# the delayed notification from the sub-resource is de-duplicated by the notification already in the parent run_context
- expect(result.stdout).to match(/\* log\[quux\] action write\s+\* notifying_test\[whatever\] action run\s+\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
+ expect(result.stdout).to match(/\* notify_group\[quux\] action run\s+\* notifying_test\[whatever\] action run\s+\* notify_group\[bar\] action run\s+\* log\[baz\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
# and only run once
expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/)
result.error!
@@ -144,31 +199,33 @@ EOM
when_the_repository "notifies delayed four" do
before do
directory "cookbooks/x" do
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-log "bar" do
- notifies :write, 'log[foo]', :delayed
-end
-log "baz" do
- notifies :write, 'log[foo]', :delayed
-end
-EOM
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notify_group "bar" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ end
+ notify_group "baz" do
+ notifies :write, 'log[foo]', :delayed
+ action :run
+ 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
+ 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 = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
# the delayed notification from the sub-resource is de-duplicated by the notification already in the parent run_context
- expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/)
+ expect(result.stdout).to match(/\* notify_group\[bar\] action run\s+\* notify_group\[baz\] action run\s+\* log\[foo\] action write/)
# and only run once
expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/)
result.error!
@@ -179,38 +236,39 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
-
-action :run do
- log "bar" do
- notifies :write, 'log[foo]', :immediately
- end
-end
-EOM
-
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-notifying_test "whatever"
-log "baz"
-EOM
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
+
+ action :run do
+ notify_group "bar" do
+ notifies :write, 'log[foo]', :immediately
+ action :run
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notifying_test "whatever"
+ log "baz"
+ 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)
- expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
+ 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(/\* notify_group\[bar\] action run\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
result.error!
end
end
@@ -219,38 +277,39 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
-
-action :run do
- log "bar" do
- notifies :write, resources(log: "foo"), :immediately
- end
-end
-EOM
-
- file "recipes/default.rb", <<EOM
-log "foo" do
- action :nothing
-end
-notifying_test "whatever"
-log "baz"
-EOM
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
+
+ action :run do
+ notify_group "bar" do
+ notifies :write, resources(log: "foo"), :immediately
+ action :run
+ end
+ end
+ EOM
+
+ file "recipes/default.rb", <<~EOM
+ log "foo" do
+ action :nothing
+ end
+ notifying_test "whatever"
+ log "baz"
+ 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)
- expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
+ 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(/\* notify_group\[bar\] action run\s+\* log\[foo\] action write\s+\* log\[baz\] action write/)
result.error!
end
end
@@ -259,34 +318,35 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/notifying_test.rb", <<EOM
-default_action :run
-provides :notifying_test
-resource_name :notifying_test
+ file "resources/notifying_test.rb", <<~EOM
+ default_action :run
+ provides :notifying_test
+ resource_name :notifying_test
-action :run do
- log "bar" do
- notifies :write, "log[foo]"
- end
-end
-EOM
+ action :run do
+ notify_group "bar" do
+ notifies :write, "log[foo]"
+ action :run
+ end
+ end
+ EOM
- file "recipes/default.rb", <<EOM
-notifying_test "whatever"
-log "baz"
-EOM
+ file "recipes/default.rb", <<~EOM
+ notifying_test "whatever"
+ log "baz"
+ 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
+ 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 = 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(/Chef::Exceptions::ResourceNotFound/)
expect(result.exitstatus).not_to eql(0)
end
@@ -296,39 +356,69 @@ EOM
before do
directory "cookbooks/x" do
- file "resources/cloning_test.rb", <<EOM
-default_action :run
-provides :cloning_test
-resource_name :cloning_test
+ file "resources/cloning_test.rb", <<~EOM
+ default_action :run
+ provides :cloning_test
+ resource_name :cloning_test
-action :run do
- log "bar" do
- level :info
- end
-end
-EOM
+ action :run do
+ log "bar" do
+ level :info
+ end
+ end
+ EOM
- file "recipes/default.rb", <<EOM
-log "bar" do
- level :warn
-end
+ file "recipes/default.rb", <<~EOM
+ log "bar" do
+ level :warn
+ end
-cloning_test "whatever"
-EOM
+ cloning_test "whatever"
+ 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
+ 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 = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir)
expect(result.stdout).not_to match(/CHEF-3694/)
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
+
+ notify_group "doit" do
+ notifies :write, "log[a, b]"
+ action :run
+ 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/notifying_block_spec.rb b/spec/integration/recipes/notifying_block_spec.rb
index 6a1287c7b1..7a2c5631c1 100644
--- a/spec/integration/recipes/notifying_block_spec.rb
+++ b/spec/integration/recipes/notifying_block_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
@@ -22,8 +23,8 @@ describe "notifying_block" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
- let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
+ let(:chef_client) { "bundle exec chef-client --minimal-ohai" }
when_the_repository "notifying_block test one" do
before do
@@ -33,11 +34,13 @@ describe "notifying_block" do
log "gamma" do
action :nothing
end
- log "alpha" do
+ notify_group "alpha" do
notifies :write, "log[gamma]", :delayed
+ action :run
end
- log "beta" do
+ notify_group "beta" do
notifies :write, "log[gamma]", :delayed
+ action :run
end
end
log "delta"
@@ -45,7 +48,7 @@ describe "notifying_block" do
end
file "config/client.rb", <<-EOM
local_mode true
- cookbook_path "#{path_to('cookbooks')}"
+ cookbook_path "#{path_to("cookbooks")}"
log_level :warn
EOM
end
@@ -55,8 +58,8 @@ describe "notifying_block" do
# 2. delayed notifications are de-dup'd in the subcontext
# 3. delayed notifications (to resources inside the subcontext) are run at the end of the subcontext
it "should run alpha, beta, gamma, and delta in that order" do
- 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\[alpha\] action write\s+\* log\[beta\] action write\s+\* log\[gamma\] action write\s+Converging 1 resources\s+\* log\[delta\] action write/)
+ 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(/\* notify_group\[alpha\] action run\s+\* notify_group\[beta\] action run\s+\* log\[gamma\] action write\s+Converging 1 resources\s+\* log\[delta\] action write/)
result.error!
end
end
@@ -71,8 +74,9 @@ describe "notifying_block" do
action :run do
notifying_block do
- log "foo" do
+ notify_group "foo" do
notifies :write, 'log[bar]', :delayed
+ action :run
end
end
end
@@ -94,7 +98,7 @@ describe "notifying_block" do
end
file "config/client.rb", <<-EOM
local_mode true
- cookbook_path "#{path_to('cookbooks')}"
+ cookbook_path "#{path_to("cookbooks")}"
log_level :warn
EOM
end
@@ -103,8 +107,8 @@ describe "notifying_block" do
# 1. notifying block will correctly update wrapping new_resource updated_by_last_action status
# 2. delayed notifications from a subcontext inside a resource will notify resources in their outer run_context
it "should run foo, quux, bar, and baz in that order" do
- 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\[foo\] action write\s+\* log\[quux\] action write\s+\* log\[bar\] action write\s+\* log\[baz\] action write/)
+ 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(/\* notify_group\[foo\] action run\s+\* log\[quux\] action write\s+\* log\[bar\] action write\s+\* log\[baz\] action write/)
result.error!
end
end
diff --git a/spec/integration/recipes/provider_choice.rb b/spec/integration/recipes/provider_choice.rb
index 1895d93891..f1d57260b6 100644
--- a/spec/integration/recipes/provider_choice.rb
+++ b/spec/integration/recipes/provider_choice.rb
@@ -16,8 +16,9 @@ describe "Recipe DSL methods" do
context "And class Chef::Provider::ProviderThingy with no provides" do
before :context do
class Chef::Provider::ProviderThingy < Chef::Provider
- def load_current_resource
- end
+ provides :provider_thingy
+
+ def load_current_resource; end
def action_create
Chef::Log.warn("hello from #{self.class.name}")
@@ -29,8 +30,8 @@ describe "Recipe DSL methods" do
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
+ 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
end
end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index 456319c306..c692915afd 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -1,3 +1,4 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
describe "Recipe DSL methods" do
@@ -12,10 +13,10 @@ describe "Recipe DSL methods" do
before { Namer.current_index += 1 }
context "with resource 'base_thingy' declared as BaseThingy" do
- before(:context) do
+ before(:each) do
class BaseThingy < Chef::Resource
- resource_name "base_thingy"
+ provides :base_thingy
default_action :create
class<<self
@@ -27,9 +28,9 @@ describe "Recipe DSL methods" do
def provider
Provider
end
+
class Provider < Chef::Provider
- def load_current_resource
- end
+ def load_current_resource; end
def action_create
BaseThingy.created_name = new_resource.name
@@ -59,21 +60,29 @@ describe "Recipe DSL methods" do
expect(BaseThingy.created_resource).to eq BaseThingy
end
- it "errors out when you call base_thingy do ... end in a recipe" do
+ it "errors when you call base_thingy do ... end in a recipe" do
expect_converge do
base_thingy { ; }
- end.to raise_error(ArgumentError, "You must supply a name when declaring a base_thingy resource")
+ 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 do
- base_thingy "foo", "bar" do
+ context "nameless resources" do
+ before(:each) do
+ class NamelessThingy < BaseThingy
+ provides :nameless_thingy
+
+ property :name, String, default: ""
end
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
+
+ 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,54 +90,8 @@ 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) do
-
- 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
-
- end
-
- it "backcompat_thingy creates a Chef::Resource::BackcompatThingy" do
- recipe = converge do
- backcompat_thingy("blah") {}
- 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) do
-
- class RecipeDSLSpecNamespace::BackcompatThingy < BaseThingy
- provides :backcompat_thingy
- resource_name :backcompat_thingy
- end
-
- end
-
- it "backcompat_thingy creates a BackcompatThingy" do
- recipe = converge do
- backcompat_thingy("blah") {}
- 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) do
+ before(:each) do
class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
end
@@ -143,7 +106,7 @@ describe "Recipe DSL methods" do
end
context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
- before(:context) do
+ before(:each) do
class Chef::Resource::NoNameThingy < BaseThingy
resource_name nil
@@ -159,10 +122,10 @@ describe "Recipe DSL methods" do
end
context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
- before(:context) do
+ before(:each) do
class AnotherNoNameThingy < BaseThingy
- resource_name :another_thingy_name
+ provides :another_thingy_name
end
end
@@ -183,11 +146,11 @@ describe "Recipe DSL methods" do
end
context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
- before(:context) do
+ before(:each) do
class AnotherNoNameThingy2 < BaseThingy
- resource_name :another_thingy_name2
- resource_name :another_thingy_name3
+ provides :another_thingy_name2
+ provides :another_thingy_name3
end
end
@@ -198,10 +161,12 @@ describe "Recipe DSL methods" do
end.to raise_error(NoMethodError)
end
- it "another_thingy_name2 does not work" do
- expect_converge do
+ it "another_thingy_name2 works" do
+ recipe = converge do
another_thingy_name2("blah") {}
- end.to raise_error(NoMethodError)
+ end
+ expect(recipe.logged_warnings).to eq ""
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy2)
end
it "yet_another_thingy_name3 works" do
@@ -215,10 +180,10 @@ 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) do
+ before(:each) do
class AnotherNoNameThingy3 < BaseThingy
- resource_name :another_no_name_thingy_3
+ provides :another_no_name_thingy_3
provides :another_no_name_thingy3, os: "blarghle"
end
@@ -239,15 +204,15 @@ describe "Recipe DSL methods" do
another_no_name_thingy3("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy3)
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy3)
end
end
context "with a resource named AnotherNoNameThingy4 with two provides" do
- before(:context) do
+ before(:each) do
class AnotherNoNameThingy4 < BaseThingy
- resource_name :another_no_name_thingy_4
+ provides :another_no_name_thingy_4
provides :another_no_name_thingy4, os: "blarghle"
provides :another_no_name_thingy4, platform_family: "foo"
end
@@ -269,7 +234,7 @@ describe "Recipe DSL methods" do
another_no_name_thingy4("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy4)
end
it "and platform_family = foo, another_no_name_thingy4 works" do
@@ -279,15 +244,15 @@ describe "Recipe DSL methods" do
another_no_name_thingy4("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
+ 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) do
+ before(:each) do
class AnotherNoNameThingy5 < BaseThingy
- resource_name :another_thingy_name_for_another_no_name_thingy5
+ provides :another_thingy_name_for_another_no_name_thingy5
provides :another_no_name_thingy5, os: "blarghle"
end
@@ -308,7 +273,7 @@ describe "Recipe DSL methods" do
another_no_name_thingy5("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy5)
end
it "the new resource name can be used in a recipe" do
@@ -316,16 +281,16 @@ describe "Recipe DSL methods" do
another_thingy_name_for_another_no_name_thingy5("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
+ 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) do
+ before(:each) do
class AnotherNoNameThingy6 < BaseThingy
provides :another_no_name_thingy6, os: "blarghle"
- resource_name :another_thingy_name_for_another_no_name_thingy6
+ provides :another_thingy_name_for_another_no_name_thingy6
end
end
@@ -345,7 +310,7 @@ describe "Recipe DSL methods" do
another_no_name_thingy6("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy6)
end
it "the new resource name can be used in a recipe" do
@@ -353,26 +318,28 @@ describe "Recipe DSL methods" do
another_thingy_name_for_another_no_name_thingy6("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
+ 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) do
+ 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
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 do
+ it "and os = linux, another_thingy_name_for_another_no_name_thingy7 works" do
+ recipe = 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") {}
- end.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+ expect(recipe.logged_warnings).to eq ""
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy7)
end
it "and os = blarghle, another_thingy_name_for_another_no_name_thingy7 works" do
@@ -382,7 +349,7 @@ describe "Recipe DSL methods" do
another_thingy_name_for_another_no_name_thingy7("blah") {}
end
expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy7)
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy7)
end
it "the old resource name does not work" do
@@ -394,52 +361,15 @@ describe "Recipe DSL methods" do
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) 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 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") {}
- end.to raise_error(Chef::Exceptions::NoSuchResourceType)
- end
-
- it "and os = blarghle, another_thingy_name_for_another_no_name_thingy8 works" do
- 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") {}
- 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 do
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = "linux"
- another_thingy_name8("blah") {}
- end.to raise_error(NoMethodError)
- end
- end
end
end
context "provides" do
context "when MySupplier provides :hemlock" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::MySupplier < BaseThingy
- resource_name :hemlock
+ provides :hemlock
end
end
@@ -459,10 +389,10 @@ describe "Recipe DSL methods" do
end
context "when Thingy3 has resource_name :thingy3" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
- resource_name :thingy3
+ provides :thingy3
end
end
@@ -475,19 +405,19 @@ describe "Recipe DSL methods" do
end
context "and Thingy4 has resource_name :thingy3" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
- resource_name :thingy3
+ provides :thingy3
end
end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ 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::Thingy3
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
@@ -497,16 +427,16 @@ describe "Recipe DSL methods" do
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) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy5 < BaseThingy
- resource_name :thingy5
+ provides :thingy5
provides :thingy5reverse
provides :thingy5_2
provides :thingy5_2reverse
@@ -522,10 +452,10 @@ describe "Recipe DSL methods" do
end
context "and Thingy6 provides :thingy5" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy6 < BaseThingy
- resource_name :thingy6
+ provides :thingy6
provides :thingy5
end
@@ -538,22 +468,22 @@ describe "Recipe DSL methods" do
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
end
- it "thingy5 works in a recipe and yields Foo::Thingy5 (the alphabetical one)" do
+ 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::Thingy5
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
end
- it "resource_matching_short_name returns Thingy5" do
- expect(Chef::Resource.resource_matching_short_name(:thingy5)).to eq RecipeDSLSpecNamespace::Thingy5
+ it "resource_matching_short_name returns Thingy6" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy5)).to eq RecipeDSLSpecNamespace::Thingy6
end
context "and AThingy5 provides :thingy5reverse" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::AThingy5 < BaseThingy
- resource_name :thingy5reverse
+ provides :thingy5reverse
end
end
@@ -567,30 +497,30 @@ describe "Recipe DSL methods" do
end
context "and ZRecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
- before(:context) do
+ before(:each) do
module ZRecipeDSLSpecNamespace
class Thingy5 < BaseThingy
- resource_name :thingy5_2
+ provides :thingy5_2
end
end
end
- it "thingy5_2 works in a recipe and yields the RecipeDSLSpaceNamespace one (the alphabetical one)" do
+ 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 RecipeDSLSpecNamespace::Thingy5
+ expect(BaseThingy.created_resource).to eq ZRecipeDSLSpecNamespace::Thingy5
end
end
context "and ARecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
- before(:context) do
+ before(:each) do
module ARecipeDSLSpecNamespace
class Thingy5 < BaseThingy
- resource_name :thingy5_2reverse
+ provides :thingy5_2reverse
end
end
@@ -606,10 +536,10 @@ describe "Recipe DSL methods" do
end
context "when Thingy3 has resource_name :thingy3" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
- resource_name :thingy3
+ provides :thingy3
end
end
@@ -622,19 +552,19 @@ describe "Recipe DSL methods" do
end
context "and Thingy4 has resource_name :thingy3" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
- resource_name :thingy3
+ provides :thingy3
end
end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ 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::Thingy3
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
@@ -644,24 +574,24 @@ describe "Recipe DSL methods" do
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) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
- resource_name :thingy3
+ provides :thingy3
end
end
- it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ 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::Thingy3
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy4
end
it "thingy4 does not work in a recipe" do
@@ -671,7 +601,7 @@ describe "Recipe DSL methods" do
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,20 +609,20 @@ describe "Recipe DSL methods" do
end
context "when Thingy7 provides :thingy8" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy7 < BaseThingy
- resource_name :thingy7
+ provides :thingy7
provides :thingy8
end
end
context "and Thingy8 has resource_name :thingy8" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy8 < BaseThingy
- resource_name :thingy8
+ provides :thingy8
end
end
@@ -704,11 +634,11 @@ describe "Recipe DSL methods" do
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
end
- it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do
+ it "thingy8 works in a recipe and yields Thingy7 (last)" do
recipe = converge do
thingy8("blah") {}
end
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy8
end
it "resource_matching_short_name returns Thingy8" do
@@ -718,10 +648,10 @@ describe "Recipe DSL methods" do
end
context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy12 < BaseThingy
- resource_name :thingy12
+ provides :thingy12
provides :twizzle
provides :twizzle2
end
@@ -751,14 +681,14 @@ describe "Recipe DSL methods" do
end
context "with platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
- before(:context) do
+ before(:each) do
class MySuperThingyFoo < BaseThingy
- resource_name :my_super_thingy_foo
+ provides :my_super_thingy_foo
provides :my_super_thingy, platform: "foo"
end
class MySuperThingyBar < BaseThingy
- resource_name :my_super_thingy_bar
+ provides :my_super_thingy_bar
provides :my_super_thingy, platform: "bar"
end
end
@@ -797,9 +727,9 @@ describe "Recipe DSL methods" do
end
context "when Thingy10 provides :thingy10" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy10 < BaseThingy
- resource_name :thingy10
+ provides :thingy10
end
end
@@ -812,9 +742,9 @@ describe "Recipe DSL methods" do
end
context "when Thingy11 provides :thingy11" do
- before(:context) do
+ before(:each) do
class RecipeDSLSpecNamespace::Thingy11 < BaseThingy
- resource_name :thingy10
+ provides :thingy10
end
end
@@ -839,7 +769,7 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
- result.resource_name two_classes_one_dsl
+ result.provides two_classes_one_dsl
result
end
before { resource_class } # pull on it so it gets defined before the recipe runs
@@ -855,15 +785,15 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
- result.resource_name two_classes_one_dsl
+ result.provides 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_resource).to eq resource_class_a
@@ -885,22 +815,22 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
- result.resource_name two_classes_one_dsl
+ result.provides 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
+ it "two_classes_one_dsl resolves to Z (last)" do
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ 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,35 +839,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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ 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
- end
-
- context "when Z provides(:two_classes_one_dsl) { false }" do
- before do
- resource_class_z.provides(two_classes_one_dsl) { false }
- 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 do
- instance_eval("#{two_classes_one_dsl} 'blah'")
- end
- expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq resource_class
- end
-
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
- end
+ 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
end
@@ -948,63 +859,18 @@ 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ 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
- end
-
- context "when Z provides(:two_classes_one_dsl) { false }" do
- before do
- resource_class_z.provides(two_classes_one_dsl) { false }
- 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 do
- instance_eval("#{two_classes_one_dsl} 'blah'")
- end
- expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq resource_class
- end
-
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
- end
+ 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
end
-
- context "and a priority array [ Z ]" do
- before do
- Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
- end
-
- context "when Z provides(:two_classes_one_dsl) { false }" do
- before do
- resource_class_z.provides(two_classes_one_dsl) { false }
- 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 do
- instance_eval("#{two_classes_one_dsl} 'blah'")
- end
- expect(recipe.logged_warnings).to eq ""
- expect(BaseThingy.created_resource).to eq resource_class
- end
-
- it "resource_matching_short_name returns B" do
- expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
- end
- end
- end
-
end
context "and a provider named 'B' which provides :two_classes_one_dsl" do
@@ -1044,9 +910,9 @@ describe "Recipe DSL methods" 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class_a
@@ -1056,9 +922,9 @@ 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1084,22 +950,22 @@ describe "Recipe DSL methods" do
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
+ it "two_classes_one_dsl resolves to Z (last)" do
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class_z
@@ -1114,9 +980,9 @@ 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1128,9 +994,9 @@ 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
+ temp_two_classes_one_dsl = two_classes_one_dsl
recipe = converge do
- instance_eval("#{two_classes_one_dsl} 'blah'")
+ instance_eval("#{temp_two_classes_one_dsl} 'blah'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1151,32 +1017,32 @@ describe "Recipe DSL methods" do
def self.inspect; name.inspect; end
end
- result.resource_name two_classes_one_dsl
+ result.provides 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
+ 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
+ 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
+ expect(BaseThingy.created_resource).to eq resource_class_blarghle
end
end
end
@@ -1191,26 +1057,27 @@ describe "Recipe DSL methods" do
def to_s
"MyResource"
end
- end 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 do
- resource_class.resource_name my_resource
+ resource_class.provides my_resource
end
context "with provides? returning true to my_resource" do
before do
- my_resource = self.my_resource
+ temp_my_resource = my_resource
resource_class.define_singleton_method(:provides?) do |node, resource_name|
@called_provides = true
- resource_name == my_resource
+ resource_name == temp_my_resource
end
end
it "my_resource returns the resource and calls provides?, but does not emit a warning" do
- dsl_name = self.my_resource
+ dsl_name = my_resource
recipe = converge do
instance_eval("#{dsl_name} 'foo'")
end
@@ -1220,35 +1087,6 @@ describe "Recipe DSL methods" do
end
end
- context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
- before do
- blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
- resource_class.define_singleton_method(:provides?) do |node, resource_name|
- @called_provides = true
- resource_name == blarghle_blarghle_little_star
- end
- end
-
- it "my_resource does not return the resource" do
- dsl_name = self.my_resource
- expect_converge do
- instance_eval("#{dsl_name} 'foo'")
- end.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 do
- instance_eval("#{dsl_name} 'foo'")
- end
- 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!"
- expect(BaseThingy.created_resource).to eq resource_class
- expect(resource_class.called_provides).to be_truthy
- end
- end
-
context "and a provider" do
let(:provider_class) do
Class.new(BaseThingy::Provider) do
@@ -1281,9 +1119,9 @@ describe "Recipe DSL methods" do
end
it "my_resource runs the provider and does not emit a warning" do
- my_resource = self.my_resource
+ temp_my_resource = my_resource
recipe = converge do
- instance_eval("#{my_resource} 'foo'")
+ instance_eval("#{temp_my_resource} 'foo'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1311,9 +1149,9 @@ describe "Recipe DSL methods" do
end
it "my_resource runs the first provider" do
- my_resource = self.my_resource
+ temp_my_resource = my_resource
recipe = converge do
- instance_eval("#{my_resource} 'foo'")
+ instance_eval("#{temp_my_resource} 'foo'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1328,9 +1166,9 @@ describe "Recipe DSL methods" do
# TODO no warning? ick
it "my_resource runs the provider anyway" do
- my_resource = self.my_resource
+ temp_my_resource = my_resource
recipe = converge do
- instance_eval("#{my_resource} 'foo'")
+ instance_eval("#{temp_my_resource} 'foo'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class
@@ -1338,7 +1176,7 @@ describe "Recipe DSL methods" do
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"
@@ -1351,7 +1189,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
@@ -1360,9 +1198,9 @@ 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
+ temp_my_resource = my_resource
recipe = converge do
- instance_eval("#{my_resource} 'foo'")
+ instance_eval("#{temp_my_resource} 'foo'")
end
expect(recipe.logged_warnings).to eq ""
expect(BaseThingy.created_provider).to eq provider_class2
@@ -1370,80 +1208,24 @@ describe "Recipe DSL methods" do
end
end
end
+ end
+ end
+ end
- context "with provides? returning true" do
- before do
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- resource.declared_type == my_resource
- end
- end
-
- context "that provides :my_resource" do
- before do
- provider_class.provides my_resource
- end
-
- it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
- my_resource = self.my_resource
- recipe = converge do
- instance_eval("#{my_resource} 'foo'")
- end
- 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 do
- instance_eval("#{my_resource} 'foo'")
- end
- 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 do
- my_resource = self.my_resource
- provider_class.define_singleton_method(:provides?) do |node, resource|
- @called_provides = true
- false
- end
- end
-
- context "that provides :my_resource" do
- before do
- provider_class.provides my_resource
- end
-
- it "my_resource fails to find a provider (and calls provides)" do
- my_resource = self.my_resource
- expect_converge do
- instance_eval("#{my_resource} 'foo'")
- end.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
+ provides :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 do
- instance_eval("#{my_resource} 'foo'")
- end.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") {} # rubocop: disable Naming/AsciiIdentifiers
end
+ expect(recipe.logged_warnings).to eq ""
+ expect(BaseThingy.created_resource).to eq(UTF8Thingy)
end
end
end
@@ -1452,6 +1234,10 @@ describe "Recipe DSL methods" do
before { Namer.current_index += 1 }
context "with an LWRP that declares actions" do
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
let(:resource_class) do
Class.new(Chef::Resource::LWRPBase) do
provides :"recipe_dsl_spec#{Namer.current_index}"
@@ -1462,10 +1248,10 @@ describe "Recipe DSL methods" 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 ]
+ expect(resource_class.actions).to eq %i{nothing create}
end
it "The actions are part of allowed_actions along with :nothing" do
- expect(resource.allowed_actions).to eq [ :nothing, :create ]
+ expect(resource.allowed_actions).to eq %i{nothing create}
end
context "and a subclass that declares more actions" do
@@ -1480,41 +1266,16 @@ describe "Recipe DSL methods" do
end
it "The parent class actions are not part of actions" do
- expect(subresource_class.actions).to eq [ :nothing, :delete ]
+ expect(subresource_class.actions).to eq %i{nothing delete}
end
it "The parent class actions are not part of allowed_actions" do
- expect(subresource.allowed_actions).to eq [ :nothing, :delete ]
+ expect(subresource.allowed_actions).to eq %i{nothing delete}
end
it "The parent class actions do not change" do
- expect(resource_class.actions).to eq [ :nothing, :create ]
- expect(resource.allowed_actions).to eq [ :nothing, :create ]
+ expect(resource_class.actions).to eq %i{nothing create}
+ expect(resource.allowed_actions).to eq %i{nothing create}
end
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 do
- resource = lw_resource_with_hw_provider_test_case("blah") {}
- end
- expect(resource.created_provider).to eq(Chef::Provider::LwResourceWithHwProviderTestCase)
- end
- end
end
diff --git a/spec/integration/recipes/remote_directory.rb b/spec/integration/recipes/remote_directory.rb
index 77fe183136..4d0bcd3514 100644
--- a/spec/integration/recipes/remote_directory.rb
+++ b/spec/integration/recipes/remote_directory.rb
@@ -5,7 +5,7 @@ describe Chef::Resource::RemoteDirectory do
include Chef::Mixin::ShellOut
# Until Cheffish::RSpec has cookbook support, we have to run the whole client
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..", "bin") }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -16,13 +16,13 @@ describe Chef::Resource::RemoteDirectory do
# 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(:chef_client) { "bundle exec chef-client --minimal-ohai" }
when_the_repository "has a cookbook with a source_dir with two subdirectories, each with one file and subdir in a different alphabetical order" do
before do
file "config/client.rb", <<-EOM
local_mode true
- cookbook_path "#{path_to('cookbooks')}"
+ cookbook_path "#{path_to("cookbooks")}"
EOM
directory "cookbooks/test" do
directory "files/default/source_dir" do
@@ -50,7 +50,7 @@ describe Chef::Resource::RemoteDirectory do
end
EOM
end
- shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'test::default'", :cwd => chef_dir)
+ shell_out!("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'test::default'", cwd: chef_dir)
end
def mode_of(path)
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 60d5831a50..9bfe86c74e 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -1,5 +1,31 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
+class NoActionJackson < Chef::Resource
+ provides :no_action_jackson
+
+ def foo(value = nil)
+ @foo = value if value
+ @foo
+ end
+
+ class <<self
+ attr_accessor :action_was
+ end
+end
+
+class WeirdActionJackson < Chef::Resource
+ provides :weird_action_jackson
+
+ class <<self
+ attr_accessor :action_was
+ end
+
+ action :Straße do
+ WeirdActionJackson.action_was = action
+ end
+end
+
# Houses any classes we declare
module ResourceActionSpec
@@ -129,7 +155,7 @@ module ResourceActionSpec
ResourceActionSpec::ActionJackson.ruby_block_converged = ResourceActionSpec::ActionJackson.succeeded
end
end
- EOM
+ EOM
expect(ActionJackson.ran_action).to eq :access_attribute
expect(ActionJackson.succeeded).to eq "foo!"
expect(ActionJackson.ruby_block_converged).to eq "foo!"
@@ -138,7 +164,8 @@ module ResourceActionSpec
context "With resource 'action_jackson'" do
class ActionJackson < Chef::Resource
- use_automatic_resource_name
+ provides :action_jackson
+
def foo(value = nil)
@foo = value if value
@foo
@@ -229,9 +256,9 @@ module ResourceActionSpec
end
context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
- before(:context) do
+ before(:each) do
class ActionJackgrandson < ActionJackson
- use_automatic_resource_name
+ provides :action_jackgrandson
end
end
@@ -242,7 +269,7 @@ module ResourceActionSpec
context "And 'action_jackalope' inheriting from ActionJackson with an extra attribute, action and custom method" do
class ActionJackalope < ActionJackson
- use_automatic_resource_name
+ provides :action_jackalope
def foo(value = nil)
@foo = "#{value}alope" if value
@@ -333,19 +360,6 @@ 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 do
no_action_jackson "hi" do
@@ -357,108 +371,18 @@ module ResourceActionSpec
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 do
weird_action_jackson "hi"
end.to be_up_to_date
- expect(WeirdActionJackson.action_was).to eq :"a-b-c d"
+ 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 do
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_x
- end
- 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 do
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_x_in_non_initializer
- end
- end.to emit_no_warnings_or_errors
- end
-
- it "Using the enclosing resource to set x to 10 emits no warning" do
- expect_recipe do
- resource_action_spec_also_with_x "hi" do
- x 1
- action :set_x_to_10
- end
- end.to emit_no_warnings_or_errors
- end
-
- it "Using the enclosing resource to set x to 10 emits no warning" do
- expect_recipe do
- r = resource_action_spec_also_with_x "hi"
- r.x 1
- r.action :set_x_to_10
- end.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
+ provides :resource_action_spec_set_group_to_nil
action :set_group_to_nil do
# Access x during converge to ensure that we emit no warnings there
resource_action_spec_with_group "hi" do
@@ -469,7 +393,7 @@ module ResourceActionSpec
end
class ResourceActionSpecWithGroup < Chef::Resource
- resource_name :resource_action_spec_with_group
+ provides :resource_action_spec_with_group
def group(value = nil)
set_or_return(:group, value, {})
end
@@ -486,7 +410,8 @@ module ResourceActionSpec
context "When a resource has a property with the same name as another resource" do
class HasPropertyNamedTemplate < Chef::Resource
- use_automatic_resource_name
+ provides :has_property_named_template
+
property :template
action :create do
template "x" do
@@ -494,48 +419,31 @@ module ResourceActionSpec
end
end
end
-
- it "Raises an error when attempting to use a template in the action" do
- expect_converge do
- has_property_named_template "hi"
- end.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
+ provides :declares_action_class_methods
+
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
@@ -543,17 +451,15 @@ module ResourceActionSpec
expect_recipe do
r = declares_action_class_methods "hi"
end.to emit_no_warnings_or_errors
- expect(r.x).to eq(10)
+ 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
+ provides :declares_action_class_methods_too
+
action_class do
- def e
+ def a
5
end
end
@@ -561,10 +467,7 @@ 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
@@ -572,7 +475,33 @@ module ResourceActionSpec
expect_recipe do
r = declares_action_class_methods_too "hi"
end.to emit_no_warnings_or_errors
- expect(r.x).to eq(15)
+ 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
+ provides :declares_action_class_methods_too
+
+ 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"
+ 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 89d831ddec..8177ef060a 100644
--- a/spec/integration/recipes/resource_converge_if_changed_spec.rb
+++ b/spec/integration/recipes/resource_converge_if_changed_spec.rb
@@ -6,6 +6,7 @@ describe "Resource::ActionClass#converge_if_changed" do
module Namer
extend self
attr_accessor :current_index
+
def incrementing_value
@incrementing_value += 1
@incrementing_value
@@ -17,7 +18,7 @@ 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) do
result = Class.new(Chef::Resource) do
@@ -28,13 +29,15 @@ 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
@converged = 0
end
end
- result.resource_name resource_name
+ result.provides resource_name
result
end
let(:converged_recipe) { converge(converge_recipe) }
@@ -52,8 +55,9 @@ describe "Resource::ActionClass#converge_if_changed" do
context "and current_resource with state1=current, state2=current" do
before :each do
resource_class.load_current_value do
- state1 "current_state1"
- state2 "current_state2"
+ state1 "default_state1"
+ state2 "default_state2"
+ sensitive1 "default_dontprintme"
end
end
@@ -63,8 +67,8 @@ describe "Resource::ActionClass#converge_if_changed" do
it "the resource updates nothing" do
expect(resource.converged).to eq 0
expect(resource.updated?).to be_falsey
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create (up to date)
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create (up to date)
EOM
end
end
@@ -81,11 +85,11 @@ describe "Resource::ActionClass#converge_if_changed" do
it "the resource updates state1" 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 state1 to "new_state1" (was "current_state1")
- EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "default_state1")
+ EOM
end
end
@@ -102,12 +106,12 @@ describe "Resource::ActionClass#converge_if_changed" do
it "the resource updates state1 and state2" 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 state1 to "new_state1" (was "current_state1")
- - set state2 to "new_state2" (was "current_state2")
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "default_state1")
+ - set state2 to "new_state2" (was "default_state2")
+ EOM
end
end
@@ -125,12 +129,32 @@ EOM
it "the resource updates state1 and state2" 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 state1 to (suppressed sensitive property)
- - set state2 to (suppressed sensitive property)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to (suppressed sensitive property)
+ - set state2 to (suppressed sensitive property)
+ 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
@@ -138,7 +162,7 @@ EOM
let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
- state1 'current_state1'
+ state1 'default_state1'
state2 'new_state2'
end
EOM
@@ -147,11 +171,11 @@ EOM
it "the resource updates state2" 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 state2 to "new_state2" (was "current_state2")
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state2 to "new_state2" (was "default_state2")
+ EOM
end
end
@@ -159,8 +183,8 @@ EOM
let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
- state1 'current_state1'
- state2 'current_state2'
+ state1 'default_state1'
+ state2 'default_state2'
end
EOM
end
@@ -168,9 +192,9 @@ EOM
it "the resource updates nothing" do
expect(resource.converged).to eq 0
expect(resource.updated?).to be_falsey
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create (up to date)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create (up to date)
+ EOM
end
end
@@ -189,9 +213,9 @@ EOM
it "the resource updates nothing" do
expect(resource.converged).to eq 0
expect(resource.updated?).to be_falsey
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create (up to date)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create (up to date)
+ EOM
end
end
end
@@ -219,10 +243,10 @@ EOM
it "the resource updates identity1" 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 current_identity1
- - set identity1 to "new_identity1" (was "current_identity1")
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update current_identity1
+ - set identity1 to "new_identity1" (was "current_identity1")
EOM
end
end
@@ -241,22 +265,24 @@ EOM
it "the resource is created" 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
- - create default_identity1
- - set identity1 to "default_identity1" (default value)
- - set state1 to "default_state1" (default value)
- - set state2 to "default_state2" (default value)
-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 sensitive1 to (suppressed sensitive property) (default value)
+ EOM
end
end
- context "and state1 and state2 are set" do
+ 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
@@ -264,13 +290,14 @@ EOM
it "the resource is created" 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
- - create default_identity1
- - set identity1 to "default_identity1" (default value)
- - set state1 to "new_state1"
- - set state2 to "new_state2"
-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 sensitive1 to (suppressed sensitive property)
+ EOM
end
end
@@ -288,13 +315,14 @@ EOM
it "the resource is created" 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
- - create default_identity1
- - set identity1 to (suppressed sensitive property) (default value)
- - set state1 to (suppressed sensitive property)
- - set state2 to (suppressed sensitive property)
-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 sensitive1 to (suppressed sensitive property) (default value)
+ EOM
end
end
end
@@ -309,14 +337,17 @@ EOM
converge_if_changed :state2 do
new_resource.converged += 1
end
+ converge_if_changed :sensitive1 do
+ new_resource.converged += 1
+ end
end
end
context "and current_resource with state1=current, state2=current" do
before :each do
resource_class.load_current_value do
- state1 "current_state1"
- state2 "current_state2"
+ state1 "default_state1"
+ state2 "default_state2"
end
end
@@ -326,9 +357,9 @@ EOM
it "the resource updates nothing" do
expect(resource.converged).to eq 0
expect(resource.updated?).to be_falsey
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create (up to date)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create (up to date)
+ EOM
end
end
@@ -345,11 +376,11 @@ EOM
it "the resource updates state1" 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 state1 to "new_state1" (was "current_state1")
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "default_state1")
+ EOM
end
end
@@ -366,13 +397,13 @@ EOM
it "the resource updates state1 and state2" do
expect(resource.converged).to eq 2
expect(resource.updated?).to be_truthy
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create
- - update default_identity1
- - set state1 to "new_state1" (was "current_state1")
- - update default_identity1
- - set state2 to "new_state2" (was "current_state2")
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "default_state1")
+ - update default_identity1
+ - set state2 to "new_state2" (was "default_state2")
+ EOM
end
end
@@ -380,7 +411,7 @@ EOM
let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
- state1 'current_state1'
+ state1 'default_state1'
state2 'new_state2'
end
EOM
@@ -389,11 +420,11 @@ EOM
it "the resource updates state2" 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 state2 to "new_state2" (was "current_state2")
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - update default_identity1
+ - set state2 to "new_state2" (was "default_state2")
+ EOM
end
end
@@ -401,8 +432,8 @@ EOM
let(:converge_recipe) do
<<-EOM
#{resource_name} 'blah' do
- state1 'current_state1'
- state2 'current_state2'
+ state1 'default_state1'
+ state2 'default_state2'
end
EOM
end
@@ -410,9 +441,29 @@ EOM
it "the resource updates nothing" do
expect(resource.converged).to eq 0
expect(resource.updated?).to be_falsey
- expect(converged_recipe.stdout).to eq <<-EOM
-* #{resource_name}[blah] action create (up to date)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create (up to date)
+ 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
@@ -430,38 +481,43 @@ 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
- - create default_identity1
- - set state1 to "default_state1" (default value)
- - create default_identity1
- - set state2 to "default_state2" (default value)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - create default_identity1
+ - 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
+ 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
- - create default_identity1
- - set state1 to "new_state1"
- - create default_identity1
- - set state2 to "new_state2"
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - create default_identity1
+ - 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
@@ -477,15 +533,17 @@ 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
- - create default_identity1
- - set state1 to (suppressed sensitive property)
- - create default_identity1
- - set state2 to (suppressed sensitive property)
-EOM
+ expect(converged_recipe.stdout).to eq <<~EOM
+ * #{resource_name}[blah] action create
+ - create default_identity1
+ - 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 954fbf53a4..b3d1c7893a 100644
--- a/spec/integration/recipes/resource_load_spec.rb
+++ b/spec/integration/recipes/resource_load_spec.rb
@@ -6,6 +6,7 @@ describe "Resource.load_current_value" do
module Namer
extend self
attr_accessor :current_index
+
def incrementing_value
@incrementing_value += 1
@incrementing_value
@@ -32,10 +33,10 @@ 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.provides resource_name
result
end
@@ -45,10 +46,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
@@ -65,17 +66,17 @@ describe "Resource.load_current_value" do
end
it "current_resource is passed name but not x" do
- expect(resource.current_value.x).to eq "loaded 2 (name=blah)"
+ expect(resource.current_value.x).to eq "loaded 3 (name=blah)"
end
it "resource.current_value returns a different resource" do
- expect(resource.current_value.x).to eq "loaded 2 (name=blah)"
+ expect(resource.current_value.x).to eq "loaded 3 (name=blah)"
expect(resource.x).to eq "desired"
end
it "resource.current_value constructs the resource anew each time" do
- expect(resource.current_value.x).to eq "loaded 2 (name=blah)"
expect(resource.current_value.x).to eq "loaded 3 (name=blah)"
+ expect(resource.current_value.x).to eq "loaded 4 (name=blah)"
end
it "the provider accesses the current value of x" do
@@ -96,7 +97,7 @@ describe "Resource.load_current_value" do
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)"
+ expect(resource.current_value.x).to eq "loaded 3 (d=desired_d, i=desired_i, name=blah)"
end
end
@@ -114,35 +115,19 @@ describe "Resource.load_current_value" do
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)"
- end
- end
- end
-
- context "and a resource with no values set" do
- let(:resource) do
- e = self
- r = nil
- converge do
- r = public_send(e.resource_name, "blah") do
- end
+ expect(resource.current_value.x).to eq "loaded 3 (d=desired_d, i=desired_i, name=blah)"
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) do
+ let(:subresource_name) do
:"load_current_value_subresource_dsl#{Namer.current_index}"
end
- let (:subresource_class) do
+ 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.provides subresource_name
r
end
@@ -162,7 +147,7 @@ describe "Resource.load_current_value" do
context "and a child resource class with no load_current_value" do
it "the parent load_current_value is used" do
- expect(subresource.current_value.x).to eq "loaded 2 (name=blah)"
+ expect(subresource.current_value.x).to eq "loaded 3 (name=blah)"
end
it "load_current_value yields a copy of the child class" do
expect(subresource.current_value).to be_kind_of(subresource_class)
@@ -172,17 +157,17 @@ describe "Resource.load_current_value" do
context "And a child resource class with load_current_value" do
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(", ") })"
+ 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(", ")})"
end
end
it "the overridden load_current_value is used" do
current_resource = subresource.current_value
- expect(current_resource.x).to eq "default 3"
- expect(current_resource.y).to eq "loaded_y 2 (name=blah)"
+ expect(current_resource.x).to eq "default 4"
+ expect(current_resource.y).to eq "loaded_y 3 (name=blah)"
end
end
@@ -190,19 +175,141 @@ describe "Resource.load_current_value" do
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(", ") })"
+ 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(", ")})"
end
end
it "the original load_current_value is called as well as the child one" do
current_resource = subresource.current_value
- expect(current_resource.x).to eq "loaded 3 (name=blah)"
- expect(current_resource.y).to eq "loaded_y 4 (name=blah, x=loaded 3 (name=blah))"
+ expect(current_resource.x).to eq "loaded 5 (name=blah)"
+ expect(current_resource.y).to eq "loaded_y 6 (name=blah, x=loaded 5 (name=blah))"
end
end
end
+end
+
+describe "simple load_current_value tests" do
+ let(:resource_class) do
+ Class.new(Chef::Resource) do
+ attr_writer :index # this is our hacky global state
+
+ def index; @index ||= 1; end
+
+ property :myindex, Integer
+
+ load_current_value do |new_resource|
+ myindex new_resource.index
+ end
+
+ action :run do
+ new_resource.index += 1
+ end
+ end
+ end
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { resource_class.new("test", run_context) }
+ let(:provider) { new_resource.provider_for_action(:run) }
+ it "calling the action on the provider sets the current_resource" do
+ expect(events).to receive(:resource_current_state_loaded).with(new_resource, :run, anything)
+ provider.run_action(:run)
+ expect(provider.current_resource.myindex).to eql(1)
+ end
+
+ it "calling the action on the provider sets the after_resource" do
+ expect(events).to receive(:resource_after_state_loaded).with(new_resource, :run, anything)
+ provider.run_action(:run)
+ expect(provider.after_resource.myindex).to eql(2)
+ end
+end
+
+describe "simple load_current_resource tests" do
+ let(:provider_class) do
+ Class.new(Chef::Provider) do
+ provides :no_load_current_value
+ def load_current_resource
+ @current_resource = new_resource.dup
+ @current_resource.myindex = 1
+ end
+ action :run do
+ end
+ end
+ end
+
+ let(:resource_class) do
+ provider_class # vivify the provider_class
+ Class.new(Chef::Resource) do
+ provides :no_load_current_value
+ property :myindex, Integer
+ end
+ end
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { resource_class.new("test", run_context) }
+ let(:provider) { new_resource.provider_for_action(:run) }
+
+ it "calling the action on the provider sets the current_resource" do
+ expect(events).to receive(:resource_current_state_loaded).with(new_resource, :run, anything)
+ provider.run_action(:run)
+ expect(provider.current_resource.myindex).to eql(1)
+ end
+
+ it "calling the action on the provider sets the after_resource" do
+ expect(events).to receive(:resource_after_state_loaded).with(new_resource, :run, new_resource)
+ provider.run_action(:run)
+ expect(provider.after_resource.myindex).to eql(nil)
+ end
+end
+
+describe "simple load_current_resource and load_after_resource tests" do
+ let(:provider_class) do
+ Class.new(Chef::Provider) do
+ provides :load_after
+ def load_current_resource
+ @current_resource = new_resource.dup
+ @current_resource.myindex = 1
+ end
+
+ def load_after_resource
+ @after_resource = new_resource.dup
+ @after_resource.myindex = 2
+ end
+ action :run do
+ end
+ end
+ end
+
+ let(:resource_class) do
+ provider_class # autovivify provider class
+ Class.new(Chef::Resource) do
+ provides :load_after
+ property :myindex, Integer
+ end
+ end
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { resource_class.new("test", run_context) }
+ let(:provider) { new_resource.provider_for_action(:run) }
+
+ it "calling the action on the provider sets the current_resource" do
+ expect(events).to receive(:resource_current_state_loaded).with(new_resource, :run, anything)
+ provider.run_action(:run)
+ expect(provider.current_resource.myindex).to eql(1)
+ end
+
+ it "calling the action on the provider sets the after_resource" do
+ expect(events).to receive(:resource_after_state_loaded).with(new_resource, :run, anything)
+ provider.run_action(:run)
+ expect(provider.after_resource.myindex).to eql(2)
+ end
end
diff --git a/spec/integration/recipes/unified_mode_spec.rb b/spec/integration/recipes/unified_mode_spec.rb
new file mode 100644
index 0000000000..b2c3620fdb
--- /dev/null
+++ b/spec/integration/recipes/unified_mode_spec.rb
@@ -0,0 +1,877 @@
+require "spec_helper"
+require "support/shared/integration/integration_helper"
+require "chef/mixin/shell_out"
+
+describe "Unified Mode" do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
+
+ let(:chef_client) { "bundle exec chef-client --minimal-ohai" }
+
+ when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the second block to the first block" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ notifies :run, "ruby_block[first block]", :delayed
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the "second block" runs first after "bar" is set
+ expect(result.stdout).to include("second: bar")
+ # then the "first block" runs after "baz" in the delayed phase
+ expect(result.stdout).to include("first: baz")
+ # nothing else should fire
+ expect(result.stdout).not_to include("first: foo")
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the first block to the second block" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ notifies :run, "ruby_block[second block]", :delayed
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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' -l debug", cwd: chef_dir)
+ # the first block should fire first
+ expect(result.stdout).to include("first: foo")
+ # the second block should fire in delayed phase
+ expect(result.stdout).to include("second: baz")
+ # nothing else should fire
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: bar")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the second block to the first block" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ notifies :run, "ruby_block[first block]", :immediate
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the second resource should fire first when it is parsed
+ expect(result.stdout).to include("second: bar")
+ # the first resource should then immediately fire
+ expect(result.stdout).to include("first: bar")
+ # no other resources should fire
+ expect(result.stdout).not_to include("second: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("first: foo")
+ expect(result.stdout).not_to include("first: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to the second block" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ notifies :run, "ruby_block[second block]", :immediate
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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' -l debug", cwd: chef_dir)
+ # both blocks should run when they're declared
+ expect(result.stdout).to include("first: foo")
+ expect(result.stdout).to include("second: bar")
+ # nothing else should run
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to a block that does not exist" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ notifies :run, "ruby_block[second block]", :immediate
+ end
+ var = "bar"
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should fail the run" 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)
+ # both blocks should run when they're declared
+ expect(result.stdout).to include("first: foo")
+ # nothing else should run
+ expect(result.stdout).not_to include("second: bar")
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound")
+ expect(result.error?).to be true
+ end
+ end
+
+ when_the_repository "has a cookbook with a normal resource with an delayed notification with global resource unified mode on" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ resource_name :unified_mode
+ provides :unified_mode
+
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ notifies :run, "ruby_block[second block]", :delayed
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should complete with success" do
+ file "config/client.rb", <<~EOM
+ resource_unified_mode_default true
+ 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)
+ # the "first block" resource runs before the assignment to baz in compile time
+ expect(result.stdout).to include("first: bar")
+ # we should not run the "first block" at compile time
+ expect(result.stdout).not_to include("first: baz")
+ # (and certainly should run it this early)
+ expect(result.stdout).not_to include("first: foo")
+ # the delayed notification should still fire and run after everything else
+ expect(result.stdout).to include("second: baz")
+ # the action :nothing should suppress any other running of the second block
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: bar")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a normal resource with an immediate notification with global resource unified mode on" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ notifies :run, "ruby_block[second block]", :immediate
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should complete with success" do
+ file "config/client.rb", <<~EOM
+ resource_unified_mode_default true
+ 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)
+ # the "first block" resource runs before the assignment to baz in compile time
+ expect(result.stdout).to include("first: bar")
+ # we should not run the "first block" at compile time
+ expect(result.stdout).not_to include("first: baz")
+ # (and certainly should run it this early)
+ expect(result.stdout).not_to include("first: foo")
+ # the immediate notifiation fires immediately
+ expect(result.stdout).to include("second: bar")
+ # the action :nothing should suppress any other running of the second block
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the second resource to the first" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ subscribes :run, "ruby_block[first block]", :immediate
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the first resource fires
+ expect(result.stdout).to include("first: foo")
+ # the second resource fires when it is parsed
+ expect(result.stdout).to include("second: bar")
+ # no other actions should run
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the first resource to the second" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ subscribes :run, "ruby_block[second block]", :immediate
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the second resource fires first after bar is set
+ expect(result.stdout).to include("second: bar")
+ # the first resource then has its immediate subscribes fire at that location
+ expect(result.stdout).to include("first: bar")
+ # no other actions should run
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("first: foo")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the second resource to the first" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ subscribes :run, "ruby_block[first block]", :delayed
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the first resource fires as it is parsed
+ expect(result.stdout).to include("first: foo")
+ # the second resource then fires in the delayed notifications phase
+ expect(result.stdout).to include("second: baz")
+ # no other actions should run
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("first: baz")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: bar")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the first resource to the second" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ subscribes :run, "ruby_block[second block]", :delayed
+ action :nothing
+ end
+ var = "bar"
+ ruby_block "second block" do
+ block do
+ puts "\nsecond: \#\{var\}"
+ end
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ # the second resource fires first after bar is set
+ expect(result.stdout).to include("second: bar")
+ # the first resource then fires in the delayed notifications phase
+ expect(result.stdout).to include("first: baz")
+ # no other actions should run
+ expect(result.stdout).not_to include("first: foo")
+ expect(result.stdout).not_to include("first: bar")
+ expect(result.stdout).not_to include("second: foo")
+ expect(result.stdout).not_to include("second: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with a correct before notification" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "notified block" do
+ block do
+ puts "\nnotified: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "bar"
+ whyrun_safe_ruby_block "notifying block" do
+ block do
+ puts "\nnotifying: \#\{var\}"
+ end
+ notifies :run, "ruby_block[notified block]", :before
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ expect(result.stdout.scan(/notifying: bar/).length).to eql(2)
+ expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block")
+ expect(result.stdout).to include("notified: bar")
+ # no other actions should run
+ expect(result.stdout).not_to include("notified: foo")
+ expect(result.stdout).not_to include("notified: baz")
+ expect(result.stdout).not_to include("notifying: foo")
+ expect(result.stdout).not_to include("notifying: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with a correct before subscribes" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ ruby_block "notified block" do
+ block do
+ puts "\nnotified: \#\{var\}"
+ end
+ subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before
+ action :nothing
+ end
+ var = "bar"
+ whyrun_safe_ruby_block "notifying block" do
+ block do
+ puts "\nnotifying: \#\{var\}"
+ end
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ 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)
+ expect(result.stdout.scan(/notifying: bar/).length).to eql(2)
+ expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block")
+ expect(result.stdout).to include("notified: bar")
+ # no other actions should run
+ expect(result.stdout).not_to include("notified: foo")
+ expect(result.stdout).not_to include("notified: baz")
+ expect(result.stdout).not_to include("notifying: foo")
+ expect(result.stdout).not_to include("notifying: baz")
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with a broken/reversed before notification" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ whyrun_safe_ruby_block "notifying block" do
+ block do
+ puts "\nnotifying: \#\{var\}"
+ end
+ notifies :run, "ruby_block[notified block]", :before
+ end
+ var = "bar"
+ ruby_block "notified block" do
+ block do
+ puts "\nnotified: \#\{var\}"
+ end
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should fail the run" 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' -l debug", cwd: chef_dir)
+ # this doesn't work and we can't tell the difference between it and if we were trying to do a correct :before notification but typo'd the name
+ # so Chef::Exceptions::ResourceNotFound is the best we can do
+ expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound")
+ expect(result.error?).to be true
+ end
+ end
+
+ when_the_repository "has a cookbook with a unified resource with a broken/reversed before subscribes" do
+ before do
+ directory "cookbooks/x" do
+
+ file "resources/unified_mode.rb", <<-EOM
+ unified_mode true
+ resource_name :unified_mode
+ provides :unified_mode
+ action :doit do
+ klass = new_resource.class
+ var = "foo"
+ whyrun_safe_ruby_block "notifying block" do
+ block do
+ puts "\nnotifying: \#\{var\}"
+ end
+ end
+ var = "bar"
+ ruby_block "notified block" do
+ block do
+ puts "\nnotified: \#\{var\}"
+ end
+ subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before
+ action :nothing
+ end
+ var = "baz"
+ end
+ EOM
+
+ file "recipes/default.rb", <<-EOM
+ unified_mode "whatever"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should fail the run" 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)
+ # this fires first normally before the error
+ expect(result.stdout).to include("notifying: foo")
+ # everything else does not run
+ expect(result.stdout).not_to include("notified: foo")
+ expect(result.stdout).not_to include("notified: bar")
+ expect(result.stdout).not_to include("notified: baz")
+ expect(result.stdout).not_to include("notifying: bar")
+ expect(result.stdout).not_to include("notifying: baz")
+ expect(result.stdout).to include("Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource")
+ expect(result.error?).to be true
+ end
+ end
+
+ when_the_repository "has global resource unified mode on" do
+ before do
+ directory "cookbooks/x" do
+
+ file "recipes/default.rb", <<-EOM
+ var = "foo"
+ ruby_block "first block" do
+ block do
+ puts "\nfirst: \#\{var\}"
+ end
+ end
+ var = "bar"
+ EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "recipes should still have a compile/converge mode" do
+ file "config/client.rb", <<~EOM
+ resource_unified_mode_default true
+ 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)
+ # in recipe mode we should still run normally with a compile/converge mode
+ expect(result.stdout).to include("first: bar")
+ expect(result.stdout).not_to include("first: foo")
+ result.error!
+ end
+ end
+end
diff --git a/spec/integration/recipes/use_partial_spec.rb b/spec/integration/recipes/use_partial_spec.rb
new file mode 100644
index 0000000000..c9f1e8e509
--- /dev/null
+++ b/spec/integration/recipes/use_partial_spec.rb
@@ -0,0 +1,112 @@
+#
+# Copyright:: Copyright (c) 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/integration/integration_helper"
+require "chef/mixin/shell_out"
+
+describe "notifying_block" do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.expand_path("../../../bin", __dir__) }
+ let(:chef_client) { "bundle exec chef-client --minimal-ohai" }
+
+ when_the_repository "has a cookbook with partial resources" do
+ before do
+ directory "cookbooks/x" do
+ file "resources/_shared_properties.rb", <<-EOM
+ property :content, String
+ EOM
+ file "resources/_action_helpers.rb", <<-EOM
+ def printit(string)
+ puts "DIDIT: \#{string}"
+ end
+ EOM
+ file "resources/thing.rb", <<-EOM
+ provides :thing
+ use "shared_properties"
+ action_class do
+ use "action_helpers"
+ end
+ action :run do
+ printit(new_resource.content)
+ end
+ EOM
+ file "recipes/default.rb", <<~EOM
+ thing "whatever" do
+ content "stuff"
+ end
+ EOM
+ end
+ file "config/client.rb", <<-EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ log_level :warn
+ always_dump_stacktrace true
+ EOM
+ end
+
+ it "should run cleanly and print the output" do
+ 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(/DIDIT: stuff/)
+ result.error!
+ end
+ end
+
+ when_the_repository "has a cookbook with partial resources done differently" do
+ before do
+ directory "cookbooks/x" do
+ file "partials/_shared_properties.rb", <<-EOM
+ property :content, String
+ EOM
+ file "partials/_action_partials.rb", <<-EOM
+ def printit(string)
+ puts "DIDIT: \#{string}"
+ end
+ EOM
+ # this tests relative pathing, including the underscore and including the trailing .rb all work
+ file "resources/thing.rb", <<-EOM
+ provides :thing
+ use "../partials/_shared_properties.rb"
+ action_class do
+ use "../partials/_action_partials.rb"
+ end
+ action :run do
+ printit(new_resource.content)
+ end
+ EOM
+ file "recipes/default.rb", <<~EOM
+ thing "whatever" do
+ content "stuff"
+ end
+ EOM
+ end
+ file "config/client.rb", <<-EOM
+ local_mode true
+ cookbook_path "#{path_to("cookbooks")}"
+ log_level :warn
+ always_dump_stacktrace true
+ EOM
+ end
+
+ it "should run cleanly and print the output" do
+ 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(/DIDIT: stuff/)
+ result.error!
+ end
+ end
+end
diff --git a/spec/integration/solo/solo_spec.rb b/spec/integration/solo/solo_spec.rb
index bfcb74b45c..12a49dade6 100644
--- a/spec/integration/solo/solo_spec.rb
+++ b/spec/integration/solo/solo_spec.rb
@@ -1,21 +1,73 @@
+require "spec_helper"
require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
require "chef/run_lock"
require "chef/config"
require "timeout"
require "fileutils"
+require "chef-utils"
+require "chef/win32/security" if ChefUtils.windows?
-describe "chef-solo" do
+describe ChefUtils::Dist::Solo::EXEC do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..") }
+ let(:chef_dir) { File.join(__dir__, "..", "..", "..") }
let(:cookbook_x_100_metadata_rb) { cb_metadata("x", "1.0.0") }
let(:cookbook_ancient_100_metadata_rb) { cb_metadata("ancient", "1.0.0") }
- let(:chef_solo) { "ruby bin/chef-solo --legacy-mode --minimal-ohai" }
+ let(:chef_solo) { "bundle exec #{ChefUtils::Dist::Solo::EXEC} --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("bundle exec 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
@@ -24,26 +76,26 @@ describe "chef-solo" do
end
it "should complete with success" do
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
- result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ file_cache_path "#{path_to("config/cache")}"
+ EOM
+ result = shell_out("#{chef_solo} -c \"#{path_to("config/solo.rb")}\" -o 'x::default' -l debug", cwd: chef_dir)
result.error!
expect(result.stdout).to include("ITWORKS")
end
it "should evaluate its node.json file" do
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ file_cache_path "#{path_to("config/cache")}"
+ EOM
- file "config/node.json", <<-E
-{"run_list":["x::default"]}
-E
+ file "config/node.json", <<~E
+ {"run_list":["x::default"]}
+ E
- result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -j '#{path_to('config/node.json')}' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to("config/solo.rb")}\" -j '#{path_to("config/node.json")}' -l debug", cwd: chef_dir)
result.error!
expect(result.stdout).to include("ITWORKS")
end
@@ -60,11 +112,11 @@ E
end
it "should exit with an error" do
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
- result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ file_cache_path "#{path_to("config/cache")}"
+ EOM
+ result = shell_out("#{chef_solo} -c \"#{path_to("config/solo.rb")}\" -o 'x::default' -l debug", cwd: chef_dir)
expect(result.exitstatus).to eq(0) # For CHEF-5120 this becomes 1
expect(result.stdout).to include("WARN: MissingCookbookDependency")
end
@@ -74,14 +126,14 @@ EOM
before do
file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
file "cookbooks/x/recipes/default.rb", 'puts "ITWORKS"'
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ file_cache_path "#{path_to("config/cache")}"
+ EOM
end
it "should exit with an error" do
- result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to("config/solo.rb")}\" -o 'x::default' -l debug", cwd: chef_dir)
expect(result.exitstatus).to eq(1)
expect(result.stdout).to include("Chef::Exceptions::CookbookChefVersionMismatch")
end
@@ -91,14 +143,14 @@ EOM
before do
file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\nohai_version '~> 999.0'")
file "cookbooks/x/recipes/default.rb", 'puts "ITWORKS"'
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ file_cache_path "#{path_to("config/cache")}"
+ EOM
end
it "should exit with an error" do
- result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to("config/solo.rb")}\" -o 'x::default' -l debug", cwd: chef_dir)
expect(result.exitstatus).to eq(1)
expect(result.stdout).to include("Chef::Exceptions::CookbookOhaiVersionMismatch")
end
@@ -109,41 +161,41 @@ EOM
directory "logs"
file "logs/runs.log", ""
file "cookbooks/x/metadata.rb", cookbook_x_100_metadata_rb
- file "cookbooks/x/recipes/default.rb", <<EOM
-ruby_block "sleeping" do
- block do
- retries = 200
- while IO.read(Chef::Config[:log_location]) !~ /Chef client [0-9]+ 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
+ file "cookbooks/x/recipes/default.rb", <<~EOM
+ ruby_block "sleeping" do
+ block do
+ retries = 200
+ while IO.read(Chef::Config[:log_location][0]) !~ /.* 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
end
it "while running solo concurrently" do
- file "config/solo.rb", <<EOM
-cookbook_path "#{path_to('cookbooks')}"
-file_cache_path "#{path_to('config/cache')}"
-EOM
+ file "config/solo.rb", <<~EOM
+ cookbook_path "#{path_to("cookbooks")}"
+ 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 do
Timeout.timeout(120) do
- chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..")
+ chef_dir = File.join(__dir__, "..", "..", "..")
threads = []
# 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)
+ 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
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)
+ 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
@@ -157,10 +209,10 @@ EOM
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 [0-9]+ is running, will wait for it to finish and then run./)
+ expect(run_log).to match(/.* 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)
+ expect(run_log.lines.reject { |l| !l.include? "Run complete in" }.length).to eq(2)
end
end
diff --git a/spec/scripts/ssl-serve.rb b/spec/scripts/ssl-serve.rb
deleted file mode 100644
index 3f4e343926..0000000000
--- a/spec/scripts/ssl-serve.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# ssl-serve.rb
-# USAGE: ruby ssl-serve.rb
-#
-# ssl-serve is a script that serves a local directory over SSL.
-# You can use it to test various HTTP behaviors in chef, like chef-client's
-# `-j` and `-c` options and remote_file with https connections.
-#
-require "pp"
-require "openssl"
-require "webrick"
-require "webrick/https"
-
-$ssl = true
-
-CHEF_SPEC_DATA = File.expand_path("../../data", __FILE__)
-cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA))
-cert = OpenSSL::X509::Certificate.new(cert_text)
-key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA))
-key = OpenSSL::PKey::RSA.new(key_text)
-
-server_opts = {}
-if $ssl
- server_opts.merge!( { :SSLEnable => true,
- :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
- :SSLCertificate => cert,
- :SSLPrivateKey => key })
-end
-
-# 5 == debug, 3 == warning
-LOGGER = WEBrick::Log.new(STDOUT, 5)
-DEFAULT_OPTIONS = {
- :server => "webrick",
- :Port => 9000,
- :Host => "localhost",
- :environment => :none,
- :Logger => LOGGER,
- :DocumentRoot => File.expand_path("#{Dir.tmpdir}/chef-118-sampledata")
- #:AccessLog => [] # Remove this option to enable the access log when debugging.
-}
-
-webrick_opts = DEFAULT_OPTIONS.merge(server_opts)
-pp :webrick_opts => webrick_opts
-
-server = WEBrick::HTTPServer.new(webrick_opts)
-trap("INT") { server.shutdown }
-
-server.start
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 47a5ec7f9f..17ce1ab5b7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,34 +24,23 @@ module Shell
IRB = nil unless defined? IRB
end
-# Ruby 1.9 Compat
-$:.unshift File.expand_path("../..", __FILE__)
+$LOAD_PATH.unshift File.expand_path("..", __dir__)
+
+$LOAD_PATH.unshift File.expand_path("../chef-config/lib", __dir__)
+$LOAD_PATH.unshift File.expand_path("../chef-utils/lib", __dir__)
require "rubygems"
require "rspec/mocks"
-
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-$:.unshift(File.expand_path("../lib", __FILE__))
-$:.unshift(File.dirname(__FILE__))
-
-if ENV["COVERAGE"]
- require "simplecov"
- SimpleCov.start do
- add_filter "/spec/"
- add_group "Remote File", "remote_file"
- add_group "Resources", "/resource/"
- add_group "Providers", "/provider/"
- add_group "Knife", "knife"
- end
-end
+require "rexml/document"
+require "webmock/rspec"
require "chef"
require "chef/knife"
-Dir["lib/chef/knife/**/*.rb"].
- map { |f| f.gsub("lib/", "") }.
- map { |f| f.gsub(%r{\.rb$}, "") }.
- each { |f| require f }
+Dir["lib/chef/knife/**/*.rb"]
+ .map { |f| f.gsub("lib/", "") }
+ .map { |f| f.gsub(/\.rb$/, "") }
+ .each { |f| require f }
require "chef/resource_resolver"
require "chef/provider_resolver"
@@ -68,27 +57,34 @@ 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
# If you want to load anything into the testing environment
# without versioning it, add it to spec/support/local_gems.rb
-require "spec/support/local_gems.rb" if File.exists?(File.join(File.dirname(__FILE__), "support", "local_gems.rb"))
+require "spec/support/local_gems" if File.exist?(File.join(File.dirname(__FILE__), "support", "local_gems.rb"))
# Explicitly require spec helpers that need to load first
require "spec/support/platform_helpers"
require "spec/support/shared/unit/mock_shellout"
+require "spec/support/recipe_dsl_helper"
+
# Autoloads support files
# Excludes support/platforms by default
# Do not change the gsub.
-Dir["spec/support/**/*.rb"].
- reject { |f| f =~ %r{^spec/support/platforms} }.
- reject { |f| f =~ %r{^spec/support/pedant} }.
- map { |f| f.gsub(%r{.rb$}, "") }.
- map { |f| f.gsub(%r{spec/}, "") }.
- each { |f| require f }
+Dir["spec/support/**/*.rb"]
+ .reject { |f| f =~ %r{^spec/support/platforms} }
+ .reject { |f| f =~ %r{^spec/support/pedant} }
+ .map { |f| f.gsub(/.rb$/, "") }
+ .map { |f| f.gsub(%r{spec/}, "") }
+ .each { |f| require f }
OHAI_SYSTEM = Ohai::System.new
OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell"])
@@ -104,83 +100,117 @@ 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
+
+class UnexpectedSystemExit < RuntimeError
+ def self.from(system_exit)
+ new(system_exit.message).tap { |e| e.set_backtrace(system_exit.backtrace) }
+ end
+end
+
RSpec.configure do |config|
config.include(Matchers)
config.include(MockShellout::RSpec)
- config.filter_run :focus => true
- config.filter_run_excluding :external => true
+ config.filter_run focus: true
+ config.filter_run_excluding external: true
+ config.raise_on_warning = true
# Explicitly disable :should syntax
+ # And set max_formatted_output_length to nil to prevent RSpec from doing truncation.
config.expect_with :rspec do |c|
c.syntax = :expect
+ c.max_formatted_output_length = nil
end
config.mock_with :rspec do |c|
c.syntax = :expect
+ c.allow_message_expectations_on_nil = false
end
# Only run these tests on platforms that are also chef workstations
config.filter_run_excluding :workstation if solaris? || aix?
# Tests that randomly fail, but may have value.
- config.filter_run_excluding :volatile => true
- config.filter_run_excluding :volatile_on_solaris => true if solaris?
- config.filter_run_excluding :volatile_from_verify => false
-
- config.filter_run_excluding :skip_appveyor => true if ENV["APPVEYOR"]
- config.filter_run_excluding :appveyor_only => true unless ENV["APPVEYOR"]
- config.filter_run_excluding :skip_travis => true if ENV["TRAVIS"]
-
- config.filter_run_excluding :windows_only => true unless windows?
- 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_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 :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 :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?
- 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?
- config.filter_run_excluding :selinux_only => true unless selinux_enabled?
- config.filter_run_excluding :requires_root => true unless root?
- config.filter_run_excluding :requires_root_or_running_windows => true unless root? || windows?
- config.filter_run_excluding :requires_unprivileged_user => true if root?
- config.filter_run_excluding :uses_diff => true unless has_diff?
- config.filter_run_excluding :openssl_gte_101 => true unless openssl_gte_101?
- config.filter_run_excluding :openssl_lt_101 => true unless openssl_lt_101?
- config.filter_run_excluding :aes_256_gcm_only => true unless aes_256_gcm?
- 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 volatile: true
+ config.filter_run_excluding volatile_on_solaris: true if solaris?
+ config.filter_run_excluding volatile_from_verify: false
+
+ config.filter_run_excluding skip_buildkite: true if ENV["BUILDKITE"]
+
+ config.filter_run_excluding windows_only: true unless windows?
+ config.filter_run_excluding not_supported_on_windows: true if windows?
+ config.filter_run_excluding not_supported_on_macos: true if macos?
+ config.filter_run_excluding macos_only: true unless macos?
+ config.filter_run_excluding macos_1013: true unless macos_1013?
+ config.filter_run_excluding macos_gte_1014: true unless macos_gte_1014?
+ 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 win2012r2_only: true unless windows_2012r2?
+ config.filter_run_excluding windows64_only: true unless windows64?
+ config.filter_run_excluding windows32_only: true unless windows32?
+ 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 if 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 suse_only: true unless suse?
+ config.filter_run_excluding opensuse: true unless opensuse?
+ config.filter_run_excluding debian_family_only: true unless debian_family?
+ config.filter_run_excluding supports_cloexec: true unless supports_cloexec?
+ config.filter_run_excluding selinux_only: true unless selinux_enabled?
+ config.filter_run_excluding requires_root: true unless root?
+ config.filter_run_excluding requires_root_or_running_windows: true unless root? || windows?
+ config.filter_run_excluding requires_unprivileged_user: true if root?
+ config.filter_run_excluding openssl_gte_101: true unless openssl_gte_101?
+ config.filter_run_excluding openssl_lt_101: true unless openssl_lt_101?
+ config.filter_run_excluding aes_256_gcm_only: true unless aes_256_gcm?
+ 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 rhel6: true unless rhel6?
+ config.filter_run_excluding rhel7: true unless rhel7?
+ config.filter_run_excluding rhel8: true unless rhel8?
+ config.filter_run_excluding rhel_gte_8: true unless rhel_gte_8?
+ config.filter_run_excluding intel_64bit: true unless intel_64bit?
+ config.filter_run_excluding not_rhel: true if rhel?
+ 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)
+ # check for particular binaries we need
+ config.filter_run_excluding choco_installed: true unless choco_installed?
+ config.filter_run_excluding requires_ifconfig: true unless ifconfig?
+ config.filter_run_excluding pwsh_installed: true unless pwsh_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
@@ -201,32 +231,85 @@ 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
+ Chef::Log.setup!
+
+ Chef::ServerAPIVersions.instance.reset!
+
+ Chef::Config[:log_level] = :fatal
+ Chef::Log.level(Chef::Config[:log_level])
+
# By default, treat deprecation warnings as errors in tests.
Chef::Config.treat_deprecation_warnings_as_errors(true)
# 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
end
+
+ # Protect Rspec from accidental exit(0) causing rspec to terminate without error
+ config.around(:example) do |ex|
+
+ ex.run
+ rescue SystemExit => e
+ raise UnexpectedSystemExit.from(e)
+
+ end
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
# checker thread cannot be stopped or canceled in any easy way, and it
@@ -243,14 +326,11 @@ require "thread"
module WEBrick
module Utils
class TimeoutHandler
- def initialize
- end
+ def initialize; end
- def register(*args)
- end
+ def register(*args); end
- def cancel(*args)
- end
+ def cancel(*args); end
end
end
end
diff --git a/spec/stress/win32/file_spec.rb b/spec/stress/win32/file_spec.rb
index f1c81eb9c6..274d574919 100644
--- a/spec/stress/win32/file_spec.rb
+++ b/spec/stress/win32/file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,17 +21,17 @@ require "chef/win32/file" if windows?
describe "Chef::ReservedNames::Win32::File", :windows_only do
before(:each) do
- @path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "old_home_dir", "my-dot-emacs"))
+ @path = File.expand_path(File.join(__dir__, "..", "..", "data", "old_home_dir", "my-dot-emacs"))
end
it "should not leak significant memory", :volatile do
test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) }
- expect(test).not_to leak_memory(:warmup => 50000, :iterations => 50000)
+ expect(test).not_to leak_memory(warmup: 50000, iterations: 50000)
end
it "should not leak handles", :volatile do
test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) }
- expect(test).not_to leak_handles(:warmup => 50, :iterations => 100)
+ expect(test).not_to leak_handles(warmup: 50, iterations: 100)
end
end
diff --git a/spec/stress/win32/memory_spec.rb b/spec/stress/win32/memory_spec.rb
index f5f429fefc..b2d7a6cd74 100644
--- a/spec/stress/win32/memory_spec.rb
+++ b/spec/stress/win32/memory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/stress/win32/security_spec.rb b/spec/stress/win32/security_spec.rb
index 3c03a657b2..c1eecac888 100644
--- a/spec/stress/win32/security_spec.rb
+++ b/spec/stress/win32/security_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -50,9 +50,9 @@ describe "Chef::ReservedNames::Win32::Security", :windows_only do
it "should not leak when retrieving and reading the ACE from a file", :volatile do
expect do
- sids = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo).security_descriptor.dacl.select { |ace| ace.sid }
+ sids = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo).security_descriptor.dacl.select(&:sid)
GC.start
- end.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
@@ -63,7 +63,7 @@ describe "Chef::ReservedNames::Win32::Security", :windows_only do
Chef::ReservedNames::Win32::Security::ACE.access_denied(Chef::ReservedNames::Win32::Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL),
])
GC.start
- end.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 d0b5ad0bfd..6a0ca878cd 100644
--- a/spec/support/chef_helpers.rb
+++ b/spec/support/chef_helpers.rb
@@ -1,4 +1,4 @@
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,25 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-CHEF_SPEC_DATA = File.expand_path(File.dirname(__FILE__) + "/../data/")
-CHEF_SPEC_ASSETS = File.expand_path(File.dirname(__FILE__) + "/../functional/assets/")
-CHEF_SPEC_BACKUP_PATH = File.join(Dir.tmpdir, "test-backup-path")
-
-Chef::Config[:log_level] = :fatal
-Chef::Config[:persistent_queue] = false
-Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH
-Chef::Log.init(StringIO.new)
-Chef::Log.level(Chef::Config.log_level)
-Chef::Config.solo(false)
+CHEF_SPEC_DATA = File.expand_path(__dir__ + "/../data/")
+CHEF_SPEC_ASSETS = File.expand_path(__dir__ + "/../functional/assets/")
+CHEF_SPEC_BACKUP_PATH = File.join(Dir.tmpdir, "test-backup-path")
def sha256_checksum(path)
- OpenSSL::Digest::SHA256.hexdigest(File.read(path))
+ OpenSSL::Digest.hexdigest("SHA256", File.read(path))
end
-# From Ruby 1.9.2+
-# Here for backwards compatibility with Ruby 1.8.7
-# http://rubydoc.info/stdlib/tmpdir/1.9.2/Dir/Tmpname
+# extracted from Ruby < 2.5 to return a unique temp file name without creating it
def make_tmpname(prefix_suffix, n = nil)
case prefix_suffix
when String
@@ -49,26 +40,13 @@ def make_tmpname(prefix_suffix, n = nil)
path << suffix
end
-# NOTE:
-# 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
-end
-
# This is a helper to determine if the ruby in the PATH contains
# win32/service gem. windows_service_manager tests create a windows
# service that starts with the system ruby and requires this gem.
def system_windows_service_gem?
- windows_service_gem_check_command = %q{ruby -r "win32/daemon" -e ":noop"}
+ windows_service_gem_check_command = %{ruby -r "win32/daemon" -e ":noop" > #{File::NULL} 2>&1}
if defined?(Bundler)
- Bundler.with_clean_env do
+ Bundler.with_unbundled_env do
# This returns true if the gem can be loaded
system(windows_service_gem_check_command)
end
diff --git a/spec/support/key_helpers.rb b/spec/support/key_helpers.rb
index 33b8b32de9..2a27834050 100644
--- a/spec/support/key_helpers.rb
+++ b/spec/support/key_helpers.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,6 @@
# limitations under the License.
#
-require "spec_helper"
-
shared_examples_for "a knife key command" do
let(:stderr) { StringIO.new }
let(:command) do
diff --git a/spec/support/lib/chef/provider/easy.rb b/spec/support/lib/chef/provider/easy.rb
index 54f1bff2a3..d561236d50 100644
--- a/spec/support/lib/chef/provider/easy.rb
+++ b/spec/support/lib/chef/provider/easy.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/lib/chef/provider/openldap_includer.rb b/spec/support/lib/chef/provider/openldap_includer.rb
index 302d6002e9..8135483f04 100644
--- a/spec/support/lib/chef/provider/openldap_includer.rb
+++ b/spec/support/lib/chef/provider/openldap_includer.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/lib/chef/provider/snakeoil.rb b/spec/support/lib/chef/provider/snakeoil.rb
index a42f889e74..c527a01b95 100644
--- a/spec/support/lib/chef/provider/snakeoil.rb
+++ b/spec/support/lib/chef/provider/snakeoil.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..f81504d167 100644
--- a/spec/support/lib/chef/resource/cat.rb
+++ b/spec/support/lib/chef/resource/cat.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,7 @@
class Chef
class Resource
class Cat < Chef::Resource
+ provides :cat
attr_accessor :action
@@ -28,7 +29,7 @@ class Chef
end
def pretty_kitty(arg = nil)
- if arg == true || arg == false
+ if [true, false].include?(arg)
@pretty_kitty = arg
end
@pretty_kitty
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..da95d329f5 100644
--- a/spec/support/lib/chef/resource/one_two_three_four.rb
+++ b/spec/support/lib/chef/resource/one_two_three_four.rb
@@ -19,15 +19,14 @@
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
end
def something(arg = nil)
- if arg == true || arg == false
+ if [true, false].include?(arg)
@something = arg
end
@something
diff --git a/spec/support/lib/chef/resource/openldap_includer.rb b/spec/support/lib/chef/resource/openldap_includer.rb
index 5a7651b10d..44c03158c3 100644
--- a/spec/support/lib/chef/resource/openldap_includer.rb
+++ b/spec/support/lib/chef/resource/openldap_includer.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..98e4033e01 100644
--- a/spec/support/lib/chef/resource/with_state.rb
+++ b/spec/support/lib/chef/resource/with_state.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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_follower.rb b/spec/support/lib/chef/resource/zen_follower.rb
index 84fc71cc99..44de913f8b 100644
--- a/spec/support/lib/chef/resource/zen_follower.rb
+++ b/spec/support/lib/chef/resource/zen_follower.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,10 +22,12 @@ class Chef
class Resource
class ZenFollower < Chef::Resource
+ provides :zen_follower
+
provides :follower, platform: "zen"
def master(arg = nil)
- if !arg.nil?
+ unless arg.nil?
@master = arg
end
@master
diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb
index f5137c18ec..ba2f950bed 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,16 +22,15 @@ 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
def something(arg = nil)
- if !arg.nil?
+ unless arg.nil?
@something = arg
end
@something
diff --git a/spec/support/matchers/leak.rb b/spec/support/matchers/leak.rb
index 5f22c1c151..bd59a2755c 100644
--- a/spec/support/matchers/leak.rb
+++ b/spec/support/matchers/leak.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -60,11 +60,11 @@ module Matchers
def profiler
@profiler ||= begin
- if Chef::Platform.windows?
- require File.join(File.dirname(__FILE__), "..", "platforms", "prof", "win32")
+ if ChefUtils.windows?
+ require File.join(__dir__, "..", "platforms", "prof", "win32")
RSpec::Prof::Win32::Profiler.new
else
- require File.join(File.dirname(__FILE__), "..", "prof", "gc")
+ require File.join(__dir__, "..", "prof", "gc")
RSpec::Prof::GC::Profiler.new
end
end
diff --git a/spec/support/mock/constant.rb b/spec/support/mock/constant.rb
deleted file mode 100644
index 3a23b4d8d8..0000000000
--- a/spec/support/mock/constant.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# Allows easy mocking of global and class constants
-
-# Inspired by:
-# http://missingbit.blogspot.com/2011/07/stubbing-constants-in-rspec_20.html
-# http://digitaldumptruck.jotabout.com/?p=551
-
-def mock_constants(constants)
- saved_constants = {}
- constants.each do |constant, val|
- source_object, const_name = parse_constant(constant)
- saved_constants[constant] = source_object.const_get(const_name)
- with_warnings(nil) { source_object.const_set(const_name, val) }
- end
-
- begin
- yield
- ensure
- constants.each do |constant, val|
- source_object, const_name = parse_constant(constant)
- with_warnings(nil) { source_object.const_set(const_name, saved_constants[constant]) }
- end
- end
-end
-
-def parse_constant(constant)
- source, _, constant_name = constant.to_s.rpartition("::")
- [constantize(source), constant_name]
-end
-
-# Taken from ActiveSupport
-
-# File activesupport/lib/active_support/core_ext/kernel/reporting.rb, line 3
-#
-# Sets $VERBOSE for the duration of the block and back to its original value afterwards.
-def with_warnings(flag)
- old_verbose, $VERBOSE = $VERBOSE, flag
- yield
-ensure
- $VERBOSE = old_verbose
-end
-
-# File activesupport/lib/active_support/inflector/methods.rb, line 209
-def constantize(camel_cased_word)
- names = camel_cased_word.split("::")
- names.shift if names.empty? || names.first.empty?
-
- constant = Object
- names.each do |name|
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
- end
- constant
-end
diff --git a/spec/support/mock/platform.rb b/spec/support/mock/platform.rb
index c6670827f9..e2d2149fad 100644
--- a/spec/support/mock/platform.rb
+++ b/spec/support/mock/platform.rb
@@ -2,24 +2,32 @@
# platform-specific functionality.
#
# If a block is given yields to the block with +RUBY_PLATFORM+ set to
-# 'i386-mingw32' (windows) or 'x86_64-darwin11.2.0' (unix). Usueful for
+# 'i386-mingw32' (windows) or 'x86_64-darwin11.2.0' (unix). Useful for
# testing code that mixes in platform specific modules like +Chef::Mixin::Securable+
# or +Chef::FileAccessControl+
-def platform_mock(platform = :unix)
- allow(ChefConfig).to receive(:windows?).and_return(platform == :windows ? true : false)
- ENV["SYSTEMDRIVE"] = (platform == :windows ? "C:" : nil)
+RSpec.configure do |c|
+ c.include(Module.new do
+ def platform_mock(platform = :unix)
+ case platform
+ when :windows
+ Chef::Config.set_defaults_for_windows
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ stub_const("ENV", ENV.to_hash.merge("SYSTEMDRIVE" => "C:"))
+ stub_const("RUBY_PLATFORM", "i386-mingw32")
+ stub_const("File::PATH_SEPARATOR", ";")
+ stub_const("File::ALT_SEPARATOR", "\\")
+ when :unix
+ Chef::Config.set_defaults_for_nix
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ stub_const("ENV", ENV.to_hash.merge("SYSTEMDRIVE" => nil))
+ stub_const("RUBY_PLATFORM", "x86_64-darwin11.2.0")
+ stub_const("File::PATH_SEPARATOR", ":")
+ stub_const("File::ALT_SEPARATOR", nil)
+ else
+ raise "#{__method__}: unrecognized platform '#{platform}', expected one of ':unix' or ':windows'"
+ end
- if platform == :windows
- Chef::Config.set_defaults_for_windows
- else
- Chef::Config.set_defaults_for_nix
- end
-
- if block_given?
- mock_constants({ "RUBY_PLATFORM" => (platform == :windows ? "i386-mingw32" : "x86_64-darwin11.2.0"),
- "File::PATH_SEPARATOR" => (platform == :windows ? ";" : ":"),
- "File::ALT_SEPARATOR" => (platform == :windows ? "\\" : nil) }) do
yield
end
- end
+ end)
end
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 62b262b8a7..b29c860f30 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -1,5 +1,6 @@
require "fcntl"
require "chef/mixin/shell_out"
+require "ohai/mixin/http_helper"
require "ohai/mixin/gce_metadata"
class ShellHelpers
@@ -23,14 +24,16 @@ class DependencyProc < Proc
end
def ruby_64bit?
- !!(RbConfig::CONFIG["host_cpu"] =~ /x86_64/)
+ RbConfig::CONFIG["host_cpu"].include?("x86_64")
end
def ruby_32bit?
- !!(RbConfig::CONFIG["host_cpu"] =~ /i686/)
+ RbConfig::CONFIG["host_cpu"].include?("i686")
end
def windows?
+ # NOTE this deliberately does not use ChefUtils.windows? because otherwise it would
+ # pick up the node out of tests, while this tests the hosts running the specs.
!!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
end
@@ -43,32 +46,35 @@ require "wmi-lite/wmi" if windows?
def windows_domain_joined?
return false unless windows?
+
wmi = WmiLite::Wmi.new
computer_system = wmi.first_of("Win32_ComputerSystem")
computer_system["partofdomain"]
end
-def windows_win2k3?
+def windows_2012r2?
return false unless windows?
- wmi = WmiLite::Wmi.new
- host = wmi.first_of("Win32_OperatingSystem")
- (host["version"] && host["version"].start_with?("5.2"))
+
+ (win32_os_version && win32_os_version.start_with?("6.3"))
end
-def windows_2008r2_or_later?
+def windows_gte_10?
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|
- component.to_i
+
+ Gem::Requirement.new(">= 10").satisfied_by?(Gem::Version.new(win32_os_version))
+end
+
+def win32_os_version
+ @win32_os_version ||= begin
+ wmi = WmiLite::Wmi.new
+ host = wmi.first_of("Win32_OperatingSystem")
+ host["version"]
end
- components.length >= 2 && components[0] >= 6 && components[1] >= 1
end
def windows_powershell_dsc?
return false unless windows?
+
supports_dsc = false
begin
wmi = WmiLite::Wmi.new("root/microsoft/windows/desiredstateconfiguration")
@@ -79,35 +85,19 @@ def windows_powershell_dsc?
supports_dsc
end
-def windows_nano_server?
- require "chef/platform/query_helpers"
- Chef::Platform.windows_nano_server?
-end
-
-def mac_osx_106?
- if File.exists? "/usr/bin/sw_vers"
- result = ShellHelpers.shell_out("/usr/bin/sw_vers")
- result.stdout.each_line do |line|
- if line =~ /^ProductVersion:\s10.6.*$/
- return true
- end
- end
- end
+def windows_user_right?(right)
+ return false unless windows?
- false
+ require "chef/win32/security"
+ Chef::ReservedNames::Win32::Security.get_account_right(ENV["USERNAME"]).include?(right)
end
-def mac_osx?
- if File.exists? "/usr/bin/sw_vers"
- result = ShellHelpers.shell_out("/usr/bin/sw_vers")
- result.stdout.each_line do |line|
- if line =~ /^ProductName:\sMac OS X.*$/
- return true
- end
- end
- end
+def macos_1013?
+ macos? && Gem::Requirement.new("~> 10.13.0").satisfied_by?(Gem::Version.new(ohai[:platform_version]))
+end
- false
+def macos_gte_1014?
+ macos? && Gem::Requirement.new(">= 10.14").satisfied_by?(Gem::Version.new(ohai[:platform_version]))
end
# detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system)
@@ -120,26 +110,52 @@ def windows32?
windows? && !windows64?
end
-# def jruby?
-
def unix?
!windows?
end
def linux?
- !!(RUBY_PLATFORM =~ /linux/)
+ RUBY_PLATFORM.include?("linux")
end
-def os_x?
- !!(RUBY_PLATFORM =~ /darwin/)
+def macos?
+ RUBY_PLATFORM.include?("darwin")
end
def solaris?
- !!(RUBY_PLATFORM =~ /solaris/)
+ RUBY_PLATFORM.include?("solaris")
end
def freebsd?
- !!(RUBY_PLATFORM =~ /freebsd/)
+ RUBY_PLATFORM.include?("freebsd")
+end
+
+def intel_64bit?
+ !!(ohai[:kernel][:machine] == "x86_64")
+end
+
+def rhel?
+ !!(ohai[:platform_family] == "rhel")
+end
+
+def rhel6?
+ rhel? && !!(ohai[:platform_version].to_i == 6)
+end
+
+def opensuse?
+ suse? && !!(ohai[:platform_version].to_i >= 15)
+end
+
+def rhel7?
+ rhel? && !!(ohai[:platform_version].to_i == 7)
+end
+
+def rhel8?
+ rhel? && !!(ohai[:platform_version].to_i == 8)
+end
+
+def rhel_gte_8?
+ rhel? && !!(ohai[:platform_version].to_i >= 8)
end
def debian_family?
@@ -147,7 +163,7 @@ def debian_family?
end
def aix?
- !!(RUBY_PLATFORM =~ /aix/)
+ RUBY_PLATFORM.include?("aix")
end
def wpar?
@@ -158,36 +174,35 @@ def supports_cloexec?
Fcntl.const_defined?("F_SETFD") && Fcntl.const_defined?("FD_CLOEXEC")
end
-DEV_NULL = windows? ? "NUL" : "/dev/null"
-
def selinux_enabled?
# This code is currently copied from lib/chef/util/selinux to make
# specs independent of product.
selinuxenabled_path = which("selinuxenabled")
if selinuxenabled_path
- cmd = Mixlib::ShellOut.new(selinuxenabled_path, :returns => [0, 1])
+ cmd = Mixlib::ShellOut.new(selinuxenabled_path, returns: [0, 1])
cmd_result = cmd.run_command
case cmd_result.exitstatus
when 1
- return false
+ false
when 0
- return true
+ true
else
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
def suse?
- File.exists?("/etc/SuSE-release")
+ !!(ohai[:platform_family] == "suse")
end
def root?
return false if windows?
+
Process.euid == 0
end
@@ -207,12 +222,33 @@ 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 ifconfig?
+ which("ifconfig")
+end
+
+def choco_installed?
+ result = ShellHelpers.shell_out("choco --version")
+ result.stderr.empty?
+rescue
+ false
+end
+
+def pwsh_installed?
+ result = ShellHelpers.shell_out("pwsh.exe --version")
+ result.stderr.empty?
+rescue
+ false
+end
diff --git a/spec/support/platforms/prof/gc.rb b/spec/support/platforms/prof/gc.rb
index 2e1b45c294..87e50758f3 100644
--- a/spec/support/platforms/prof/gc.rb
+++ b/spec/support/platforms/prof/gc.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ module RSpec
# GC 1 invokes.
# Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC time(ms)
# 1 0.012 159240 212940 10647 0.00000000000001530000
- LINE_PATTERN = /^\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)$/
+ LINE_PATTERN = /^\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)$/.freeze
def start
::GC::Profiler.enable unless ::GC::Profiler.enabled?
@@ -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/platforms/prof/win32.rb b/spec/support/platforms/prof/win32.rb
index 1cfbecc95d..da95f16a3d 100644
--- a/spec/support/platforms/prof/win32.rb
+++ b/spec/support/platforms/prof/win32.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/platforms/win32/spec_service.rb b/spec/support/platforms/win32/spec_service.rb
index 5548a79afc..27f71a8bf0 100644
--- a/spec/support/platforms/win32/spec_service.rb
+++ b/spec/support/platforms/win32/spec_service.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@lambda.local>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,44 +16,42 @@
# limitations under the License.
#
-require "win32/daemon"
+if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
+ require "win32/daemon"
-class SpecService < ::Win32::Daemon
- def service_init
- @test_service_file = "#{ENV['TMP']}/spec_service_file"
- end
+ class SpecService < ::Win32::Daemon
+ def service_init
+ @test_service_file = "#{ENV["TMP"]}/spec_service_file"
+ end
- def service_main(*startup_parameters)
- while running?
- if !File.exists?(@test_service_file)
- File.open(@test_service_file, "wb") do |f|
- f.write("This file is created by SpecService")
+ def service_main(*startup_parameters)
+ while running?
+ unless File.exist?(@test_service_file)
+ File.open(@test_service_file, "wb") do |f|
+ f.write("This file is created by SpecService")
+ end
end
- end
- sleep 1
+ sleep 1
+ end
end
- end
- ################################################################################
- # Control Signal Callback Methods
- ################################################################################
+ ################################################################################
+ # Control Signal Callback Methods
+ ################################################################################
- def service_stop
- end
+ def service_stop; end
- def service_pause
- end
+ def service_pause; end
- def service_resume
- end
+ def service_resume; end
- def service_shutdown
+ def service_shutdown; end
end
-end
-# To run this file as a service, it must be called as a script from within
-# the Windows Service framework. In that case, kick off the main loop!
-if __FILE__ == $0
- SpecService.mainloop
+ # To run this file as a service, it must be called as a script from within
+ # the Windows Service framework. In that case, kick off the main loop!
+ if __FILE__ == $0
+ SpecService.mainloop
+ end
end
diff --git a/spec/support/recipe_dsl_helper.rb b/spec/support/recipe_dsl_helper.rb
new file mode 100644
index 0000000000..2542345ed4
--- /dev/null
+++ b/spec/support/recipe_dsl_helper.rb
@@ -0,0 +1,83 @@
+#
+# This is a helper for functional tests to embed the recipe DSL directly into the rspec example blocks using
+# unified mode.
+#
+# If you wind up wanting to stub/expect on internal details of the resource/provider you are not testing the
+# public API and are trying to write a unit test, which this is not designed for.
+#
+# If you want to start writing full recipes and testing them, doing notifies/subscribes/etc then you are writing
+# an integration test, and not a functional single-resource test, which this is not designed for.
+#
+# Examples:
+#
+# it "creates a file" do
+# FileUtils.rm_f("/tmp/foo.xyz")
+# file "/tmp/foo.xyz" do # please use proper tmpdir though
+# content "whatever"
+# end.should_be_updated
+# expect(IO.read("/tmp/foo.xyz").to eql("content")
+# end
+#
+# it "is idempotent" do
+# FileUtils.rm_f("/tmp/foo.xyz")
+# file "/tmp/foo.xyz" do # please use proper tmpdir though
+# content "whatever"
+# end.should_be_updated
+# file "/tmp/foo.xyz" do # please use proper tmpdir though
+# content "whatever"
+# end.should_not_be_updated
+# expect(IO.read("/tmp/foo.xyz").to eql("content")
+# end
+#
+# it "has a failure" do
+# FileUtils.rm_f("/tmp/foo.xyz")
+# expect { file "/tmp/lksjdflksjdf/foo.xyz" do
+# content "whatever"
+# end }.to raise_error(Chef::Exception::EnclosingDirectoryDoesNotExist)
+# end
+#
+module RecipeDSLHelper
+ include Chef::DSL::Recipe
+ def event_dispatch
+ @event_dispatch ||= Chef::EventDispatch::Dispatcher.new
+ end
+
+ def node
+ @node ||= Chef::Node.new.tap do |n|
+ # clone the global ohai data to keep tests fast but reasonably isolated
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ end
+ end
+
+ def run_context
+ @run_context ||= Chef::RunContext.new(node, {}, event_dispatch).tap do |rc|
+ rc.resource_collection.unified_mode = true
+ Chef::Runner.new(rc)
+ end
+ end
+
+ def cookbook_name
+ "rspec"
+ end
+
+ def recipe_name
+ "default"
+ end
+
+ def declare_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
+ created_at = caller[0]
+ rspec_context = self
+ # we slightly abuse the "enclosing_provider" method_missing magic to send methods to the rspec example block so that
+ # rspec `let` methods work as arguments to resource properties
+ resource = super(type, name, created_at: created_at, run_context: run_context, enclosing_provider: rspec_context, &resource_attrs_block)
+ # we also inject these methods to make terse expression of checking the updated status (so it is more readiable and
+ # therefore should get used more -- even though it is "should" vs. "expect")
+ resource.define_singleton_method(:should_be_updated) do
+ rspec_context.expect(self).to be_updated
+ end
+ resource.define_singleton_method(:should_not_be_updated) do
+ rspec_context.expect(self).not_to be_updated
+ end
+ resource
+ end
+end
diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb
deleted file mode 100644
index b0530ab497..0000000000
--- a/spec/support/shared/context/client.rb
+++ /dev/null
@@ -1,286 +0,0 @@
-
-require "spec_helper"
-
-# Stubs a basic client object
-shared_context "client" do
- let(:fqdn) { "hostname.example.org" }
- let(:hostname) { "hostname" }
- let(:machinename) { "machinename.example.org" }
- let(:platform) { "example-platform" }
- let(:platform_version) { "example-platform-1.0" }
-
- let(:ohai_data) do
- {
- :fqdn => fqdn,
- :hostname => hostname,
- :machinename => machinename,
- :platform => platform,
- :platform_version => platform_version,
- }
- end
-
- let(:ohai_system) do
- ohai = instance_double("Ohai::System", :all_plugins => true, :data => ohai_data)
- allow(ohai).to receive(:[]) do |k|
- ohai_data[k]
- end
- ohai
- end
-
- let(:node) do
- Chef::Node.new.tap do |n|
- n.name(fqdn)
- n.chef_environment("_default")
- end
- end
-
- let(:json_attribs) { nil }
- let(:client_opts) { {} }
-
- let(:client) do
- Chef::Config[:event_loggers] = []
- Chef::Client.new(json_attribs, client_opts).tap do |c|
- c.node = node
- end
- end
-
- before do
- Chef::Log.logger = Logger.new(StringIO.new)
-
- # Node/Ohai data
- #Chef::Config[:node_name] = fqdn
- allow(Ohai::System).to receive(:new).and_return(ohai_system)
- end
-end
-
-# Stubs a client for a client run.
-# Requires a client object be defined in the scope of this included context.
-# e.g.:
-# describe "some functionality" do
-# include_context "client"
-# include_context "a client run"
-# ...
-# end
-shared_context "a client run" do
- let(:stdout) { StringIO.new }
- let(:stderr) { StringIO.new }
-
- 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(:reporting_rest_client) { double("Chef::ServerAPI (reporting client)") }
-
- let(:runner) { instance_double("Chef::Runner") }
- let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => false) }
-
- def stub_for_register
- # --Client.register
- # Make sure Client#register thinks the client key doesn't
- # exist, so it tries to register and create one.
- allow(File).to receive(:exists?).and_call_original
- expect(File).to receive(:exists?).
- with(Chef::Config[:client_key]).
- exactly(:once).
- and_return(api_client_exists?)
-
- unless api_client_exists?
- # Client.register will register with the validation client name.
- expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run)
- end
- 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
- # previous step.
- expect(Chef::ServerAPI).to receive(:new).
- with(Chef::Config[:chef_server_url], client_name: fqdn,
- signing_key_filename: Chef::Config[:client_key]).
- exactly(:once).
- and_return(http_node_load)
-
- # --Client#build_node
- # looks up the node, which we will return, then later saves it.
- expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node)
-
- # --ResourceReporter#node_load_completed
- # gets a run id from the server for storing resource history
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::ResourceReporter).to receive(:node_load_completed)
- end
-
- def stub_rest_clean
- allow(client).to receive(:rest_clean).and_return(reporting_rest_client)
- end
-
- def stub_for_sync_cookbooks
- # --Client#setup_run_context
- # ---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(http_cookbook_sync).to receive(:post).
- with("environments/_default/cookbook_versions", { :run_list => [] }).
- and_return({})
- end
-
- def stub_for_converge
- # define me
- end
-
- def stub_for_audit
- # define me
- end
-
- def stub_for_node_save
- # define me
- end
-
- def stub_for_run
- # define me
- end
-
- before do
- Chef::Config[:client_fork] = enable_fork
- 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)
-
- stub_rest_clean
- stub_for_register
- stub_for_node_load
- stub_for_sync_cookbooks
- stub_for_converge
- stub_for_audit
- stub_for_node_save
-
- expect_any_instance_of(Chef::RunLock).to receive(:acquire)
- expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
- expect_any_instance_of(Chef::RunLock).to receive(:release)
-
- # Post conditions: check that node has been filled in correctly
- expect(client).to receive(:run_started)
-
- stub_for_run
- end
-end
-
-shared_context "converge completed" do
- def stub_for_converge
- # --Client#converge
- expect(Chef::Runner).to receive(:new).and_return(runner)
- expect(runner).to receive(:converge).and_return(true)
- end
-
- def stub_for_node_save
- allow(node).to receive(:data_for_save).and_return(node.for_json)
-
- # --Client#save_updated_node
- expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], client_name: fqdn,
- signing_key_filename: Chef::Config[:client_key], validate_utf8: false).and_return(http_node_save)
- expect(http_node_save).to receive(:put).with("nodes/#{fqdn}", node.for_json).and_return(true)
- end
-end
-
-shared_context "converge failed" do
- let(:converge_error) do
- err = Chef::Exceptions::UnsupportedAction.new("Action unsupported")
- err.set_backtrace([ "/path/recipe.rb:15", "/path/recipe.rb:12" ])
- err
- end
-
- def stub_for_converge
- expect(Chef::Runner).to receive(:new).and_return(runner)
- expect(runner).to receive(:converge).and_raise(converge_error)
- end
-
- def stub_for_node_save
- expect(client).to_not receive(:save_updated_node)
- end
-end
-
-shared_context "audit phase completed" do
- def stub_for_audit
- # -- Client#run_audits
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(audit_runner).to receive(:run).and_return(true)
- expect(client.events).to receive(:audit_phase_complete)
- end
-end
-
-shared_context "audit phase failed with error" do
- let(:audit_error) do
- err = RuntimeError.new("Unexpected audit error")
- err.set_backtrace([ "/path/recipe.rb:57", "/path/recipe.rb:55" ])
- err
- end
-
- def stub_for_audit
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
- expect(audit_runner).to receive(:run).and_raise(audit_error)
- expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
- end
-end
-
-shared_context "audit phase completed with failed controls" do
- 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)
- err.set_backtrace([ "/path/recipe.rb:108", "/path/recipe.rb:103" ])
- err
- end
-
- def stub_for_audit
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
- expect(audit_runner).to receive(:run)
- expect(Chef::Exceptions::AuditsFailed).to receive(:new).with(
- audit_runner.num_failed, audit_runner.num_total
- ).and_return(audit_error)
- expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
- end
-end
-
-shared_context "run completed" do
- def stub_for_run
- expect(client).to receive(:run_completed_successfully)
-
- # --ResourceReporter#run_completed
- # updates the server with the resource history
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed)
- # --AuditReporter#run_completed
- # posts the audit data to server.
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_completed)
- end
-end
-
-shared_context "run failed" do
- def stub_for_run
- expect(client).to receive(:run_failed)
-
- # --ResourceReporter#run_completed
- # updates the server with the resource history
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
- # --AuditReporter#run_completed
- # posts the audit data to server.
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
- end
-
- before do
- expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
- end
-end
diff --git a/spec/support/shared/context/config.rb b/spec/support/shared/context/config.rb
index bb4ff7e0d1..7c4ae8dca6 100644
--- a/spec/support/shared/context/config.rb
+++ b/spec/support/shared/context/config.rb
@@ -7,9 +7,6 @@
# Required chef files here:
require "chef/config"
-# Required spec files here:
-require "spec_helper"
-
# Basic config. Nothing fancy.
shared_context "default config options" do
before do
diff --git a/spec/support/shared/context/win32.rb b/spec/support/shared/context/win32.rb
index 98f7f906c1..c891e7f631 100644
--- a/spec/support/shared/context/win32.rb
+++ b/spec/support/shared/context/win32.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/shared/examples/client.rb b/spec/support/shared/examples/client.rb
deleted file mode 100644
index 3c13cd767e..0000000000
--- a/spec/support/shared/examples/client.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-
-require "spec_helper"
-require "spec/support/shared/context/client"
-
-# requires platform and platform_version be defined
-shared_examples "a completed run" do
- include_context "run completed"
-
- it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do
- # This is what we're testing.
- expect(client.run).to be true
-
- # fork is stubbed, so we can see the outcome of the run
- expect(node.automatic_attrs[:platform]).to eq(platform)
- expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
- end
-end
-
-shared_examples "a completed run with audit failure" do
- include_context "run completed"
-
- before do
- expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
- end
-
- it "converges, runs audits, saves the node and raises the error in a wrapping error" do
- expect { client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq(run_errors.size)
- run_errors.each do |run_error|
- expect(error.wrapped_errors).to include(run_error)
- expect(error.backtrace).to include(*run_error.backtrace)
- end
- end
-
- # fork is stubbed, so we can see the outcome of the run
- expect(node.automatic_attrs[:platform]).to eq(platform)
- expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
- end
-end
-
-shared_examples "a failed run" do
- include_context "run failed"
-
- it "skips node save and raises the error in a wrapping error" do
- expect { client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq(run_errors.size)
- run_errors.each do |run_error|
- expect(error.wrapped_errors).to include(run_error)
- expect(error.backtrace).to include(*run_error.backtrace)
- end
- end
- end
-end
diff --git a/spec/support/shared/functional/directory_resource.rb b/spec/support/shared/functional/directory_resource.rb
index 5e5e2bb360..cd05280aa8 100644
--- a/spec/support/shared/functional/directory_resource.rb
+++ b/spec/support/shared/functional/directory_resource.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -65,18 +65,20 @@ shared_examples_for "a directory resource" do
end
# Set up the context for security tests
- def allowed_acl(sid, expected_perms)
- [
- ACE.access_allowed(sid, expected_perms[:specific]),
- ACE.access_allowed(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)),
- ]
+ def allowed_acl(sid, expected_perms, flags = 0)
+ acl = [ ACE.access_allowed(sid, expected_perms[:specific], flags) ]
+ if expected_perms[:generic]
+ acl << ACE.access_allowed(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::SUBFOLDERS_AND_FILES_ONLY))
+ end
+ acl
end
- def denied_acl(sid, expected_perms)
- [
- ACE.access_denied(sid, expected_perms[:specific]),
- ACE.access_denied(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)),
- ]
+ def denied_acl(sid, expected_perms, flags = 0)
+ acl = [ ACE.access_denied(sid, expected_perms[:specific], flags) ]
+ if expected_perms[:generic]
+ acl << ACE.access_denied(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::SUBFOLDERS_AND_FILES_ONLY))
+ end
+ acl
end
def parent_inheritable_acls
@@ -171,6 +173,6 @@ shared_context Chef::Resource::Directory do
end
after(:each) do
- FileUtils.rm_r(path) if File.exists?(path)
+ FileUtils.rm_r(path) if File.exist?(path)
end
end
diff --git a/spec/support/shared/functional/execute_resource.rb b/spec/support/shared/functional/execute_resource.rb
new file mode 100644
index 0000000000..9d1c29dfac
--- /dev/null
+++ b/spec/support/shared/functional/execute_resource.rb
@@ -0,0 +1,150 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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.tr("/", '\\')}\" /grant \"authenticated users:(F)\"")
+ end
+
+ after do
+ File.delete(script_output_path) if File.exist?(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 eb7a378db9..1385d3dc30 100644
--- a/spec/support/shared/functional/file_resource.rb
+++ b/spec/support/shared/functional/file_resource.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -366,11 +366,10 @@ shared_examples_for "a configured file resource" do
include_context "diff disabled"
before do
- Chef::Log.level = :info
Chef::Config[:ssl_verify_mode] = :verify_none
end
- # note the stripping of the drive letter from the tmpdir on windows
+ # note the stripping of the drive letter from the tmpdir on windows
let(:backup_glob) { File.join(CHEF_SPEC_BACKUP_PATH, test_file_dir.sub(/^([A-Za-z]:)/, ""), "#{file_base}*") }
# Most tests update the resource, but a few do not. We need to test that the
@@ -385,21 +384,19 @@ shared_examples_for "a configured file resource" do
def selinux_security_context_restored?(path)
@restorecon_path = which("restorecon") if @restorecon_path.nil?
restorecon_test_command = "#{@restorecon_path} -n -v #{path}"
- cmdresult = shell_out(restorecon_test_command)
+ cmdresult = shell_out!(restorecon_test_command)
# restorecon will print the required changes to stdout if any is
# needed
cmdresult.stdout.empty?
end
def binread(file)
- content = File.open(file, "rb") do |f|
- f.read
- end
+ content = File.open(file, "rb", &:read)
content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding)
content
end
- context "when the target file is a symlink", :not_supported_on_win2k3 do
+ context "when the target file is a symlink" do
let(:symlink_target) do
File.join(CHEF_SPEC_DATA, "file-test-target")
end
@@ -479,12 +476,12 @@ shared_examples_for "a configured file resource" do
end
it "issues a warning/assumption in whyrun mode" do
- begin
- Chef::Config[:why_run] = true
- resource.run_action(:create) # should not raise
- ensure
- Chef::Config[:why_run] = false
- end
+
+ Chef::Config[:why_run] = true
+ resource.run_action(:create) # should not raise
+ ensure
+ Chef::Config[:why_run] = false
+
end
end
@@ -507,12 +504,12 @@ shared_examples_for "a configured file resource" do
end
it "issues a warning/assumption in whyrun mode" do
- begin
- Chef::Config[:why_run] = true
- resource.run_action(:create) # should not raise
- ensure
- Chef::Config[:why_run] = false
- end
+
+ Chef::Config[:why_run] = true
+ resource.run_action(:create) # should not raise
+ ensure
+ Chef::Config[:why_run] = false
+
end
end
@@ -538,12 +535,12 @@ shared_examples_for "a configured file resource" do
end
it "issues a warning/assumption in whyrun mode" do
- begin
- Chef::Config[:why_run] = true
- resource.run_action(:create) # should not raise
- ensure
- Chef::Config[:why_run] = false
- end
+
+ Chef::Config[:why_run] = true
+ resource.run_action(:create) # should not raise
+ ensure
+ Chef::Config[:why_run] = false
+
end
end
@@ -725,7 +722,7 @@ shared_examples_for "a configured file resource" do
end
before(:each) do
- result = shell_out("mknod #{path} b 1 2")
+ result = shell_out!("mknod #{path} b 1 2")
result.stderr.empty?
end
@@ -743,7 +740,7 @@ shared_examples_for "a configured file resource" do
end
before(:each) do
- result = shell_out("mknod #{path} c 1 2")
+ result = shell_out!("mknod #{path} c 1 2")
result.stderr.empty?
end
@@ -761,7 +758,7 @@ shared_examples_for "a configured file resource" do
end
before(:each) do
- result = shell_out("mkfifo #{path}")
+ result = shell_out!("mkfifo #{path}")
result.stderr.empty?
end
@@ -899,11 +896,11 @@ shared_examples_for "a configured file resource" do
end
# Set up the context for security tests
- def allowed_acl(sid, expected_perms)
+ def allowed_acl(sid, expected_perms, _flags = 0)
[ ACE.access_allowed(sid, expected_perms[:specific]) ]
end
- def denied_acl(sid, expected_perms)
+ def denied_acl(sid, expected_perms, _flags = 0)
[ ACE.access_denied(sid, expected_perms[:specific]) ]
end
@@ -1040,8 +1037,8 @@ shared_context Chef::Resource::File do
end
after(:each) do
- FileUtils.rm_r(path) if File.exists?(path)
- FileUtils.rm_r(CHEF_SPEC_BACKUP_PATH) if File.exists?(CHEF_SPEC_BACKUP_PATH)
+ FileUtils.rm_r(path) if File.exist?(path)
+ FileUtils.rm_r(CHEF_SPEC_BACKUP_PATH) if File.exist?(CHEF_SPEC_BACKUP_PATH)
end
after do
diff --git a/spec/support/shared/functional/http.rb b/spec/support/shared/functional/http.rb
index 76faaaed47..ffe52e2148 100644
--- a/spec/support/shared/functional/http.rb
+++ b/spec/support/shared/functional/http.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,18 +30,16 @@ module ChefHTTPShared
end
def binread(file)
- content = File.open(file, "rb") do |f|
- f.read
- end
+ content = File.open(file, "rb", &:read)
content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding)
content
end
- def start_tiny_server(server_opts = {})
+ def start_tiny_server(**server_opts)
nyan_uncompressed_size = File::Stat.new(nyan_uncompressed_filename).size
nyan_compressed_size = File::Stat.new(nyan_compressed_filename).size
- @server = TinyServer::Manager.new(server_opts)
+ @server = TinyServer::Manager.new(**server_opts)
@server.start
@api = TinyServer::API.instance
@api.clear
@@ -53,25 +51,19 @@ module ChefHTTPShared
# just a normal file
# (expected_content should be uncompressed)
@api.get("/nyan_cat.png", 200) do
- File.open(nyan_uncompressed_filename, "rb") do |f|
- f.read
- end
+ File.open(nyan_uncompressed_filename, "rb", &:read)
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" } ) do
- File.open(nyan_compressed_filename, "rb") do |f|
- f.read
- end
+ File.open(nyan_compressed_filename, "rb", &:read)
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" } ) do
- File.open(nyan_compressed_filename, "rb") do |f|
- f.read
- end
+ File.open(nyan_compressed_filename, "rb", &:read)
end
#
@@ -82,25 +74,19 @@ module ChefHTTPShared
@api.get("/nyan_cat_content_length.png", 200, nil,
{
"Content-Length" => nyan_uncompressed_size.to_s,
- }
- ) do
- File.open(nyan_uncompressed_filename, "rb") do |f|
- f.read
+ }) do
+ File.open(nyan_uncompressed_filename, "rb", &:read)
end
- end
# (expected_content should be uncompressed)
@api.get("/nyan_cat_content_length_compressed.png", 200, nil,
{
- "Content-Length" => nyan_compressed_size.to_s,
- "Content-Type" => "application/gzip",
+ "Content-Length" => nyan_compressed_size.to_s,
+ "Content-Type" => "application/gzip",
"Content-Encoding" => "gzip",
- }
- ) do
- File.open(nyan_compressed_filename, "rb") do |f|
- f.read
+ }) do
+ File.open(nyan_compressed_filename, "rb", &:read)
end
- end
#
# endpoints that simulate truncated downloads (bad content-length header)
@@ -110,25 +96,19 @@ module ChefHTTPShared
@api.get("/nyan_cat_truncated.png", 200, nil,
{
"Content-Length" => (nyan_uncompressed_size + 1).to_s,
- }
- ) do
- File.open(nyan_uncompressed_filename, "rb") do |f|
- f.read
+ }) do
+ File.open(nyan_uncompressed_filename, "rb", &:read)
end
- end
# (expected_content should be uncompressed)
@api.get("/nyan_cat_truncated_compressed.png", 200, nil,
{
- "Content-Length" => (nyan_compressed_size + 1).to_s,
- "Content-Type" => "application/gzip",
+ "Content-Length" => (nyan_compressed_size + 1).to_s,
+ "Content-Type" => "application/gzip",
"Content-Encoding" => "gzip",
- }
- ) do
- File.open(nyan_compressed_filename, "rb") do |f|
- f.read
+ }) do
+ File.open(nyan_compressed_filename, "rb", &:read)
end
- end
#
# in the presence of a transfer-encoding header, we must ignore the content-length (this bad content-length should work)
@@ -137,14 +117,11 @@ module ChefHTTPShared
# (expected_content should be uncompressed)
@api.get("/nyan_cat_transfer_encoding.png", 200, nil,
{
- "Content-Length" => (nyan_uncompressed_size + 1).to_s,
+ "Content-Length" => (nyan_uncompressed_size + 1).to_s,
"Transfer-Encoding" => "anything",
- }
- ) do
- File.open(nyan_uncompressed_filename, "rb") do |f|
- f.read
+ }) do
+ File.open(nyan_uncompressed_filename, "rb", &:read)
end
- end
#
# 403 with a Content-Length
@@ -152,8 +129,7 @@ module ChefHTTPShared
@api.get("/forbidden", 403, "Forbidden",
{
"Content-Length" => "Forbidden".bytesize.to_s,
- }
- )
+ })
@api.post("/posty", 200, "Hi!")
diff --git a/spec/support/shared/functional/knife.rb b/spec/support/shared/functional/knife.rb
index 694d0de5d1..89207e76fc 100644
--- a/spec/support/shared/functional/knife.rb
+++ b/spec/support/shared/functional/knife.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb
index 95f4f4bd49..4d3a1f2fe6 100644
--- a/spec/support/shared/functional/securable_resource.rb
+++ b/spec/support/shared/functional/securable_resource.rb
@@ -2,7 +2,7 @@
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Mark Mzyk (<mmzyk@chef.io>)
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,6 @@
#
require "etc"
-require "functional/resource/base"
shared_context "setup correct permissions" do
if windows?
@@ -81,7 +80,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 # rubocop:disable Style/ConstantName
+ SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject # rubocop:disable Naming/ConstantName
end
def get_security_descriptor(path)
@@ -89,13 +88,13 @@ shared_context "use Windows permissions", :windows_only do
end
def explicit_aces
- descriptor.dacl.select { |ace| ace.explicit? }
+ descriptor.dacl.select(&:explicit?)
end
def extract_ace_properties(aces)
hashes = []
aces.each do |ace|
- hashes << { :mask => ace.mask, :type => ace.type, :flags => ace.flags }
+ hashes << { mask: ace.mask, type: ace.type, flags: ace.flags }
end
hashes
end
@@ -103,39 +102,40 @@ shared_context "use Windows permissions", :windows_only do
# Standard expected rights
let(:expected_read_perms) do
{
- :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ,
- :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ,
+ generic: Chef::ReservedNames::Win32::API::Security::GENERIC_READ,
+ specific: Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ,
}
end
let(:expected_read_execute_perms) do
{
- :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE,
- :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE,
+ generic: Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE,
+ specific: Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE,
}
end
let(:expected_write_perms) do
{
- :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE,
- :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE,
+ specific: Chef::ReservedNames::Win32::API::Security::WRITE,
}
end
let(:expected_modify_perms) do
{
- :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE,
- :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE,
+ generic: Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE,
+ specific: Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE,
}
end
let(:expected_full_control_perms) do
{
- :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_ALL,
- :specific => Chef::ReservedNames::Win32::API::Security::FILE_ALL_ACCESS,
+ generic: Chef::ReservedNames::Win32::API::Security::GENERIC_ALL,
+ specific: Chef::ReservedNames::Win32::API::Security::FILE_ALL_ACCESS,
}
end
+ let(:write_flag) { 3 }
+
RSpec::Matchers.define :have_expected_properties do |mask, type, flags|
match do |ace|
ace.mask == mask &&
@@ -242,48 +242,100 @@ shared_examples_for "a securable resource with existing target" do
include_context "use Windows permissions"
describe "when setting owner" do
- before do
- resource.owner(SID.admin_account_name)
- resource.run_action(:create)
- end
+ context "with user name" do
+ before do
+ resource.owner(SID.admin_account_name)
+ resource.run_action(:create)
+ end
- it "should set the owner" do
- expect(descriptor.owner).to eq(SID.Administrator)
+ it "should set the owner" do
+ expect(descriptor.owner).to eq(SID.Administrator)
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
- it "is marked as updated only if changes are made" do
- expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ context "with SID" do
+ before do
+ resource.owner(SID.Administrator.to_s)
+ resource.run_action(:create)
+ end
+
+ it "should set the owner" do
+ expect(descriptor.owner).to eq(SID.Administrator)
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
end
describe "when setting group" do
- before do
- resource.group("Administrators")
- resource.run_action(:create)
- end
+ context "with group name" do
+ before do
+ resource.group("Administrators")
+ resource.run_action(:create)
+ end
- it "should set the group" do
- expect(descriptor.group).to eq(SID.Administrators)
+ it "should set the group" do
+ expect(descriptor.group).to eq(SID.Administrators)
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
- it "is marked as updated only if changes are made" do
- expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ context "with group SID" do
+ before do
+ resource.group(SID.Administrators.to_s)
+ resource.run_action(:create)
+ end
+
+ it "should set the group" do
+ expect(descriptor.group).to eq(SID.Administrators)
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
end
describe "when setting rights and deny_rights" do
- before do
- resource.deny_rights(:modify, "Guest")
- resource.rights(:read, "Guest")
- resource.run_action(:create)
- end
+ context "with user name" do
+ before do
+ resource.deny_rights(:modify, "Guest")
+ resource.rights(:read, "Guest")
+ resource.run_action(:create)
+ end
- it "should set the rights and deny_rights" do
- expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Guest, expected_read_perms))
+ it "should set the rights and deny_rights" do
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Guest, expected_read_perms))
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
- it "is marked as updated only if changes are made" do
- expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ context "with SID" do
+ before do
+ resource.deny_rights(:modify, SID.Guest.to_s)
+ resource.rights(:read, SID.Guest.to_s)
+ resource.run_action(:create)
+ end
+
+ it "should set the rights and deny_rights" do
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Guest, expected_read_perms))
+ end
+
+ it "is marked as updated only if changes are made" do
+ expect(resource.updated_by_last_action?).to eq(expect_updated?)
+ end
end
end
end
@@ -302,18 +354,24 @@ shared_examples_for "a securable resource without existing target" do
expect(descriptor.owner).to eq(SID.default_security_object_owner)
end
- it "sets owner when owner is specified" do
+ it "sets owner when owner is specified by name" do
resource.owner "Guest"
resource.run_action(:create)
expect(descriptor.owner).to eq(SID.Guest)
end
+ it "sets owner when owner is specified by SID" do
+ resource.owner SID.Guest.to_s
+ resource.run_action(:create)
+ expect(descriptor.owner).to eq(SID.Guest)
+ end
+
it "fails to set owner when owner has invalid characters" do
expect { resource.owner 'Lance "The Nose" Glindenberry III' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "sets owner when owner is specified with a \\" do
- resource.owner "#{ENV['USERDOMAIN']}\\Guest"
+ resource.owner "#{ENV["COMPUTERNAME"]}\\Guest"
resource.run_action(:create)
expect(descriptor.owner).to eq(SID.Guest)
end
@@ -339,12 +397,18 @@ shared_examples_for "a securable resource without existing target" do
expect(descriptor.group).to eq(SID.default_security_object_group)
end
- it "sets group when group is specified" do
+ it "sets group when group is specified by name" do
resource.group "Everyone"
resource.run_action(:create)
expect(descriptor.group).to eq(SID.Everyone)
end
+ it "sets group when group is specified by SID" do
+ resource.group SID.Everyone.to_s
+ resource.run_action(:create)
+ expect(descriptor.group).to eq(SID.Everyone)
+ end
+
it "fails to set group when group has invalid characters" do
expect { resource.group 'Lance "The Nose" Glindenberry III' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
@@ -363,88 +427,135 @@ shared_examples_for "a securable resource without existing target" do
expect(descriptor.group).to eq(arbitrary_non_default_group)
end
- describe "with rights and deny_rights attributes" do
-
- it "correctly sets :read rights" do
- resource.rights(:read, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms))
- end
-
- it "correctly sets :read_execute rights" do
- resource.rights(:read_execute, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms))
- end
-
- it "correctly sets :write rights" do
- resource.rights(:write, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms))
- end
-
- it "correctly sets :modify rights" do
- resource.rights(:modify, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms))
- end
-
- it "correctly sets :full_control rights" do
- resource.rights(:full_control, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms))
- end
-
- it "correctly sets deny_rights" do
- # deny is an ACE with full rights, but is a deny type ace, not an allow type
- resource.deny_rights(:full_control, "Guest")
- resource.run_action(:create)
- expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms))
- end
-
- it "Sets multiple rights" do
- resource.rights(:read, "Everyone")
- resource.rights(:modify, "Guest")
- resource.run_action(:create)
-
- expect(explicit_aces).to eq(
- allowed_acl(SID.Everyone, expected_read_perms) +
- allowed_acl(SID.Guest, expected_modify_perms)
- )
- end
-
- it "Sets deny_rights ahead of rights" do
- resource.rights(:read, "Everyone")
- resource.deny_rights(:modify, "Guest")
- resource.run_action(:create)
-
- expect(explicit_aces).to eq(
- denied_acl(SID.Guest, expected_modify_perms) +
- allowed_acl(SID.Everyone, expected_read_perms)
- )
+ describe "#allowed_acl" do
+ context "correctly sets" do
+
+ it ":read rights" do
+ resource.rights(:read, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms))
+ end
+
+ it ":read_execute rights" do
+ resource.rights(:read_execute, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms))
+ end
+
+ it ":write rights" do
+ resource.rights(:write, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms, write_flag))
+ end
+
+ it ":modify rights" do
+ resource.rights(:modify, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms))
+ end
+
+ it ":full_control rights" do
+ resource.rights(:full_control, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms))
+ end
+
+ it "multiple rights" do
+ resource.rights(:read, "Everyone")
+ resource.rights(:modify, "Guest")
+ resource.run_action(:create)
+
+ expect(explicit_aces).to eq(
+ allowed_acl(SID.Everyone, expected_read_perms) +
+ allowed_acl(SID.Guest, expected_modify_perms)
+ )
+ end
+
+ it "multiple rights with SID" do
+ resource.rights(:read, SID.Everyone.to_s)
+ resource.rights(:modify, SID.Guest.to_s)
+ resource.run_action(:create)
+
+ expect(explicit_aces).to eq(
+ allowed_acl(SID.Everyone, expected_read_perms) +
+ allowed_acl(SID.Guest, expected_modify_perms)
+ )
+ end
end
+ end
- it "Sets deny_rights ahead of rights when specified in reverse order" do
- resource.deny_rights(:modify, "Guest")
- resource.rights(:read, "Everyone")
- resource.run_action(:create)
-
- expect(explicit_aces).to eq(
- denied_acl(SID.Guest, expected_modify_perms) +
- allowed_acl(SID.Everyone, expected_read_perms)
- )
+ describe "#denied_acl" do
+ context "correctly sets" do
+
+ it ":read rights" do
+ resource.deny_rights(:read, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_perms))
+ end
+
+ it ":read_execute rights" do
+ resource.deny_rights(:read_execute, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_execute_perms))
+ end
+
+ it ":write rights" do
+ resource.deny_rights(:write, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_write_perms, write_flag))
+ end
+
+ it ":modify rights" do
+ resource.deny_rights(:modify, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms))
+ end
+
+ it ":full_control rights" do
+ # deny is an ACE with full rights, but is a deny type ace, not an allow type
+ resource.deny_rights(:full_control, "Guest")
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms))
+ end
+
+ it "using SID" do
+ resource.deny_rights(:full_control, SID.Guest.to_s)
+ resource.run_action(:create)
+ expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms))
+ end
+
+ it "deny_rights ahead of rights" do
+ resource.rights(:read, "Everyone")
+ resource.deny_rights(:modify, "Guest")
+ resource.run_action(:create)
+
+ expect(explicit_aces).to eq(
+ denied_acl(SID.Guest, expected_modify_perms) +
+ allowed_acl(SID.Everyone, expected_read_perms)
+ )
+ end
+
+ it "deny_rights ahead of rights when specified in reverse order" do
+ resource.deny_rights(:modify, "Guest")
+ resource.rights(:read, "Everyone")
+ resource.run_action(:create)
+
+ expect(explicit_aces).to eq(
+ denied_acl(SID.Guest, expected_modify_perms) +
+ allowed_acl(SID.Everyone, expected_read_perms)
+ )
+ end
end
-
end
context "with a mode attribute" do
if windows?
- Security ||= Chef::ReservedNames::Win32::API::Security # rubocop:disable Style/ConstantName
+ Security ||= Chef::ReservedNames::Win32::API::Security # rubocop:disable Naming/ConstantName
end
it "respects mode in string form as an octal number" do
- #on windows, mode cannot modify owner and/or group permissons
- #unless the owner and/or group as appropriate is specified
+ # on windows, mode cannot modify owner and/or group permissions
+ # unless the owner and/or group as appropriate is specified
resource.mode "400"
resource.owner "Guest"
resource.group "Everyone"
@@ -529,16 +640,12 @@ shared_examples_for "a securable resource without existing target" do
# On certain flavors of Windows the default list of ACLs sometimes includes
# non-inherited ACLs. Filter them out here.
- parent_inherited_acls = parent_acls.dacl.collect do |ace|
- ace.inherited?
- end
+ parent_inherited_acls = parent_acls.dacl.collect(&:inherited?)
resource.run_action(:create)
# Similarly filter out the non-inherited ACLs
- resource_inherited_acls = descriptor.dacl.collect do |ace|
- ace.inherited?
- end
+ resource_inherited_acls = descriptor.dacl.collect(&:inherited?)
expect(resource_inherited_acls).to eq(parent_inherited_acls)
end
diff --git a/spec/support/shared/functional/securable_resource_with_reporting.rb b/spec/support/shared/functional/securable_resource_with_reporting.rb
index 0bec29783a..6f816265bf 100644
--- a/spec/support/shared/functional/securable_resource_with_reporting.rb
+++ b/spec/support/shared/functional/securable_resource_with_reporting.rb
@@ -1,5 +1,4 @@
-require "functional/resource/base"
ALL_EXPANDED_PERMISSIONS = ["generic read",
"generic write",
@@ -19,7 +18,7 @@ ALL_EXPANDED_PERMISSIONS = ["generic read",
"execute / traverse",
"delete child",
"read attributes",
- "write attributes"]
+ "write attributes"].freeze
shared_examples_for "a securable resource with reporting" do
@@ -36,7 +35,7 @@ shared_examples_for "a securable resource with reporting" do
# umask
# let(:default_mode) { (0666 & ~File.umask).to_s(8) }
- describe "reading file security metadata for reporting on unix", :unix_only => true do
+ describe "reading file security metadata for reporting on unix", unix_only: true do
# According to POSIX standard created files get either the
# effective gid of the process or inherits the gid of the parent
# directory based on file system. Since it's hard to guess what
@@ -70,7 +69,7 @@ shared_examples_for "a securable resource with reporting" do
end
end
- context "and owner is specified with a String (username) in new_resource", :requires_root => true do
+ context "and owner is specified with a String (username) in new_resource", requires_root: true do
# TODO/bug: duplicated from the "securable resource" tests
@@ -91,7 +90,7 @@ shared_examples_for "a securable resource with reporting" do
end
- context "and owner is specified with an Integer (uid) in new_resource", :requires_root => true do
+ context "and owner is specified with an Integer (uid) in new_resource", requires_root: true do
# TODO: duplicated from "securable resource"
if ohai[:platform] == "aix"
@@ -113,7 +112,7 @@ shared_examples_for "a securable resource with reporting" do
end
end
- context "and group is specified with a String (group name)", :requires_root => true do
+ context "and group is specified with a String (group name)", requires_root: true do
let(:expected_group_name) { Etc.getgrent.name }
@@ -128,7 +127,7 @@ shared_examples_for "a securable resource with reporting" do
end
- context "and group is specified with an Integer (gid)", :requires_root => true do
+ context "and group is specified with an Integer (gid)", requires_root: true do
let(:expected_gid) { Etc.getgrent.gid }
before do
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
index 3199caa34f..890c28de2c 100644
--- a/spec/support/shared/functional/win32_service.rb
+++ b/spec/support/shared/functional/win32_service.rb
@@ -30,7 +30,7 @@ shared_context "using Win32::Service" do
end
# Delete the test_service_file if it exists
- if File.exists?(test_service_file)
+ if File.exist?(test_service_file)
File.delete(test_service_file)
end
end
@@ -39,11 +39,11 @@ shared_context "using Win32::Service" do
let(:test_service) do
{
- :service_name => "spec-service",
- :service_display_name => "Spec Test Service",
- :service_description => "Service for testing Chef::Application::WindowsServiceManager.",
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), "../../platforms/win32/spec_service.rb")),
- :delayed_start => true,
+ service_name: "spec-service",
+ service_display_name: "Spec Test Service",
+ service_description: "Service for testing Chef::Application::WindowsServiceManager.",
+ service_file_path: File.expand_path(File.join(__dir__, "../../platforms/win32/spec_service.rb")),
+ delayed_start: true,
}
end
@@ -52,6 +52,6 @@ shared_context "using Win32::Service" do
# for the file it creates under SYSTEM temp directory
let(:test_service_file) do
- "#{ENV['SystemDrive']}\\windows\\temp\\spec_service_file"
+ "#{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..151ad2387c 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,15 +39,11 @@ shared_context Chef::Resource::WindowsScript do
end
before(:each) do
- File.delete(script_output_path) if File.exists?(script_output_path)
+ File.delete(script_output_path) if File.exist?(script_output_path)
end
after(:each) 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)
+ File.delete(script_output_path) if File.exist?(script_output_path)
end
shared_examples_for "a script resource with architecture attribute" do
@@ -95,36 +91,75 @@ shared_context Chef::Resource::WindowsScript do
end
context "when the guard's architecture is specified as 64-bit" do
- let (:guard_architecture) { :x86_64 }
+ let(:guard_architecture) { :x86_64 }
it "executes a 64-bit guard", :windows64_only do
- resource.only_if resource_guard_command, :architecture => guard_architecture
+ resource.only_if resource_guard_command, architecture: guard_architecture
resource.run_action(:run)
expect(get_guard_process_architecture).to eq("amd64")
end
end
- context "when the guard's architecture is specified as 32-bit", :not_supported_on_nano do
- let (:guard_architecture) { :i386 }
+ context "when the guard's architecture is specified as 32-bit" do
+ let(:guard_architecture) { :i386 }
it "executes a 32-bit guard" do
- resource.only_if resource_guard_command, :architecture => guard_architecture
+ resource.only_if resource_guard_command, architecture: guard_architecture
resource.run_action(:run)
expect(get_guard_process_architecture).to eq("x86")
end
end
-
- context "when the guard's architecture is specified as 32-bit", :windows_nano_only do
- let (:guard_architecture) { :i386 }
- it "raises an error" do
- resource.only_if resource_guard_command, :architecture => guard_architecture
- expect { resource.run_action(:run) }.to raise_error(
- Chef::Exceptions::Win32ArchitectureIncorrect,
- /cannot execute script with requested architecture 'i386' on Windows Nano Server/)
- end
- end
end
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.exist?(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 +167,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
@@ -157,14 +207,17 @@ shared_context Chef::Resource::WindowsScript do
context "when evaluating guards" do
it "has a guard_interpreter attribute set to the short name of the resource" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
expect(resource.guard_interpreter).to eq(resource.resource_name)
resource.not_if "findstr.exe /thiscommandhasnonzeroexitstatus"
expect(Chef::Resource).to receive(:resource_for_node).and_call_original
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
@@ -172,7 +225,7 @@ shared_context Chef::Resource::WindowsScript do
it_behaves_like "a script resource with architecture attribute"
end
- context "when the architecture attribute is :i386", :not_supported_on_nano do
+ context "when the architecture attribute is :i386" do
let(:resource_architecture) { :i386 }
it_behaves_like "a script resource with architecture attribute"
end
@@ -181,6 +234,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
deleted file mode 100644
index 4dfa3fa155..0000000000
--- a/spec/support/shared/integration/app_server_support.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Author:: John Keiser (<jkeiser@chef.io>)
-# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, 2013-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 "rack"
-require "stringio"
-
-module AppServerSupport
- def start_app_server(app, port)
- server = nil
- thread = Thread.new do
- Rack::Handler::WEBrick.run(app,
- :Port => 9018,
- :AccessLog => [],
- :Logger => WEBrick::Log.new(StringIO.new, 7)
- ) do |found_server|
- server = found_server
- end
- end
- Timeout.timeout(30) do
- sleep(0.01) until server && server.status == :Running
- end
- [server, thread]
- end
-end
diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb
index 29f2eef50f..41f2b46995 100644
--- a/spec/support/shared/integration/integration_helper.rb
+++ b/spec/support/shared/integration/integration_helper.rb
@@ -1,7 +1,7 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,9 +23,7 @@ require "chef/config"
require "chef/json_compat"
require "chef/server_api"
require "support/shared/integration/knife_support"
-require "support/shared/integration/app_server_support"
require "cheffish/rspec/chef_run_support"
-require "spec_helper"
module Cheffish
class BasicChefClient
@@ -46,15 +44,30 @@ module IntegrationSupport
def when_the_repository(desc, *tags, &block)
context("when the chef repo #{desc}", *tags) do
- include_context "with a chef repo"
+ before :each do
+ raise "Can only create one directory per test" if @repository_dir
- module_eval(&block)
- end
- end
+ @repository_dir = Dir.mktmpdir("chef_repo")
+ Chef::Config.chef_repo_path = @repository_dir
+ %w{client cookbook data_bag environment node role user}.each do |object_name|
+ Chef::Config.delete("#{object_name}_path".to_sym)
+ end
+ end
+
+ after :each do
+ if @repository_dir
+ begin
+ # TODO: "force" actually means "silence all exceptions". this
+ # silences a weird permissions error on Windows that we should track
+ # down, but for now there's no reason for it to blow up our CI.
+ FileUtils.remove_entry_secure(@repository_dir, force = ChefUtils.windows?)
+ ensure
+ @repository_dir = nil
+ end
+ end
+ Dir.chdir(@old_cwd) if @old_cwd
+ end
- def with_versioned_cookbooks(&block)
- context("with versioned cookbooks") do
- include_context "with versioned cookbooks"
module_eval(&block)
end
end
@@ -107,48 +120,4 @@ module IntegrationSupport
@old_cwd = Dir.pwd
Dir.chdir(path_to(relative_path))
end
-
- RSpec.shared_context "with a chef repo" do
- before :each do
- raise "Can only create one directory per test" if @repository_dir
- @repository_dir = Dir.mktmpdir("chef_repo")
- Chef::Config.chef_repo_path = @repository_dir
- %w{client cookbook data_bag environment node role user}.each do |object_name|
- Chef::Config.delete("#{object_name}_path".to_sym)
- end
- end
-
- after :each do
- if @repository_dir
- begin
- %w{client cookbook data_bag environment node role user}.each do |object_name|
- Chef::Config.delete("#{object_name}_path".to_sym)
- end
- Chef::Config.delete(:chef_repo_path)
- # TODO: "force" actually means "silence all exceptions". this
- # silences a weird permissions error on Windows that we should track
- # down, but for now there's no reason for it to blow up our CI.
- FileUtils.remove_entry_secure(@repository_dir, force = Chef::Platform.windows?)
- ensure
- @repository_dir = nil
- end
- end
- Dir.chdir(@old_cwd) if @old_cwd
- end
-
- end
-
- # Versioned cookbooks
-
- RSpec.shared_context "with versioned cookbooks", :versioned_cookbooks => true do
- before(:each) { Chef::Config[:versioned_cookbooks] = true }
- after(:each) { Chef::Config.delete(:versioned_cookbooks) }
- end
-
- RSpec.shared_context "without versioned cookbooks", :versioned_cookbooks => false do
- # Just make sure this goes back to default
- before(:each) { Chef::Config[:versioned_cookbooks] = false }
- after(:each) { Chef::Config.delete(:versioned_cookbooks) }
- end
-
end
diff --git a/spec/support/shared/integration/knife_support.rb b/spec/support/shared/integration/knife_support.rb
index 1a374e1b84..af7b503d16 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ require "chef/chef_fs/file_system_cache"
module KnifeSupport
DEBUG = ENV["DEBUG"]
- def knife(*args, input: nil)
+ def knife(*args, input: nil, instance_filter: 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.
@@ -39,10 +39,7 @@ module KnifeSupport
# Work on machines where we can't access /var
Dir.mktmpdir("checksums") do |checksums_cache_dir|
- Chef::Config[:cache_options] = {
- :path => checksums_cache_dir,
- :skip_expires => true,
- }
+ Chef::Config[:syntax_check_cache_path] = checksums_cache_dir
# This is Chef::Knife.run without load_commands--we'll load stuff
# ourselves, thank you very much
@@ -55,17 +52,18 @@ module KnifeSupport
STDIN
end
- old_loggers = Chef::Log.loggers
- old_log_level = Chef::Log.level
begin
- puts "knife: #{args.join(' ')}" if DEBUG
+ puts "knife: #{args.join(" ")}" if DEBUG
subcommand_class = Chef::Knife.subcommand_class_from(args)
subcommand_class.options = Chef::Application::Knife.options.merge(subcommand_class.options)
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, disable_editing: true)
+ 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 )
@@ -80,9 +78,20 @@ module KnifeSupport
# 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)
+
+ # Set a canary that is modified by the default null_config.rb config file.
$__KNIFE_INTEGRATION_FAILSAFE_CHECK = "ole"
+
+ # Allow tweaking the knife instance before configuration.
+ instance_filter.call(instance) if instance_filter
+
instance.configure_chef
+ # The canary is incorrect, meaning the normal null_config.rb didn't run. Something is wrong.
unless $__KNIFE_INTEGRATION_FAILSAFE_CHECK == "ole ole"
raise Exception, "Potential misconfiguration of integration tests detected. Aborting test."
end
@@ -101,9 +110,7 @@ module KnifeSupport
rescue SystemExit => e
exit_code = e.status
ensure
- Chef::Log.use_log_devices(old_loggers)
- Chef::Log.level = old_log_level
- Chef::Config.delete(:cache_options)
+ Chef::Config.delete(:syntax_check_cache_path)
Chef::Config.delete(:concurrency)
end
@@ -136,7 +143,7 @@ module KnifeSupport
expected[:stderr] = arg
end
end
- expected[:exit_code] = 1 if !expected[:exit_code]
+ expected[:exit_code] = 1 unless expected[:exit_code]
should_result_in(expected)
end
@@ -155,22 +162,25 @@ module KnifeSupport
private
def should_result_in(expected)
- expected[:stdout] = "" if !expected[:stdout]
- expected[:stderr] = "" if !expected[:stderr]
- expected[:exit_code] = 0 if !expected[:exit_code]
+ expected[:stdout] = "" unless expected[:stdout]
+ expected[:stdout] = expected[:stdout].is_a?(String) ? expected[:stdout].gsub(/[ \t\f\v]+$/, "") : expected[:stdout]
+ expected[:stderr] = "" unless expected[:stderr]
+ expected[:stderr] = expected[:stderr].is_a?(String) ? expected[:stderr].gsub(/[ \t\f\v]+$/, "") : expected[:stderr]
+ expected[:exit_code] = 0 unless expected[:exit_code]
# TODO make this go away
stderr_actual = @stderr.sub(/^WARNING: No knife configuration file found\n/, "")
-
+ stderr_actual = stderr_actual.gsub(/[ \t\f\v]+$/, "")
+ stdout_actual = @stdout
+ stdout_actual = stdout_actual.gsub(/[ \t\f\v]+$/, "")
+ if ChefUtils.windows?
+ stderr_actual = stderr_actual.gsub("\r\n", "\n")
+ stdout_actual = stdout_actual.gsub("\r\n", "\n")
+ end
if expected[:stderr].is_a?(Regexp)
expect(stderr_actual).to match(expected[:stderr])
else
expect(stderr_actual).to eq(expected[:stderr])
end
- stdout_actual = @stdout
- if Chef::Platform.windows?
- stderr_actual = stderr_actual.gsub("\r\n", "\n")
- stdout_actual = stdout_actual.gsub("\r\n", "\n")
- end
expect(@exit_code).to eq(expected[:exit_code])
if expected[:stdout].is_a?(Regexp)
expect(stdout_actual).to match(expected[:stdout])
diff --git a/spec/support/shared/unit/api_error_inspector.rb b/spec/support/shared/unit/api_error_inspector.rb
index 45bfcc67da..e00385bfa5 100644
--- a/spec/support/shared/unit/api_error_inspector.rb
+++ b/spec/support/shared/unit/api_error_inspector.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,15 +28,15 @@ shared_examples_for "an api error inspector" do
before do
@node_name = "test-node.example.com"
@config = {
- :validation_client_name => "testorg-validator",
- :validation_key => "/etc/chef/testorg-validator.pem",
- :chef_server_url => "https://chef-api.example.com",
- :node_name => "testnode-name",
- :client_key => "/etc/chef/client.pem",
+ validation_client_name: "testorg-validator",
+ validation_key: "/etc/chef/testorg-validator.pem",
+ chef_server_url: "https://chef-api.example.com",
+ node_name: "testnode-name",
+ client_key: "/etc/chef/client.pem",
}
@description = Chef::Formatters::ErrorDescription.new("Error registering the node:")
@outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR)
- #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
+ # @outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
end
@@ -71,7 +71,7 @@ shared_examples_for "an api error inspector" do
@response_body = "synchronize the clock on your host"
@response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
+ @exception = Net::HTTPClientException.new("(exception) unauthorized", @response)
@inspector = described_class.new(@node_name, @exception, @config)
@inspector.add_explanation(@description)
end
@@ -87,7 +87,7 @@ shared_examples_for "an api error inspector" do
@response_body = "check your key and node name"
@response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
+ @exception = Net::HTTPClientException.new("(exception) unauthorized", @response)
@inspector = described_class.new(@node_name, @exception, @config)
@inspector.add_explanation(@description)
end
@@ -103,7 +103,7 @@ shared_examples_for "an api error inspector" do
@response_body = "forbidden"
@response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) forbidden", @response)
+ @exception = Net::HTTPClientException.new("(exception) forbidden", @response)
@inspector = described_class.new(@node_name, @exception, @config)
@inspector.add_explanation(@description)
end
@@ -119,7 +119,7 @@ shared_examples_for "an api error inspector" do
@response_body = "didn't like your data"
@response = Net::HTTPBadRequest.new("1.1", "400", "(response) bad request")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) bad request", @response)
+ @exception = Net::HTTPClientException.new("(exception) bad request", @response)
@inspector = described_class.new(@node_name, @exception, @config)
@inspector.add_explanation(@description)
end
@@ -135,7 +135,7 @@ shared_examples_for "an api error inspector" do
@response_body = "probably caused by a redirect to a get"
@response = Net::HTTPNotFound.new("1.1", "404", "(response) not found")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) not found", @response)
+ @exception = Net::HTTPClientException.new("(exception) not found", @response)
@inspector = described_class.new(@node_name, @exception, @config)
@inspector.add_explanation(@description)
end
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
index 28141b73b1..5d9d59cf9c 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,8 +19,8 @@
require "chef/exceptions"
shared_examples_for "version handling" do
- let(:response_406) { OpenStruct.new(:code => "406") }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:response_406) { OpenStruct.new(code: "406") }
+ let(:exception_406) { Net::HTTPClientException.new("406 Not Acceptable", response_406) }
before do
allow(rest_v1).to receive(http_verb).and_raise(exception_406)
@@ -38,8 +38,8 @@ shared_examples_for "version handling" do
end # version handling
shared_examples_for "user and client reregister" do
- let(:response_406) { OpenStruct.new(:code => "406") }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:response_406) { OpenStruct.new(code: "406") }
+ let(:exception_406) { Net::HTTPClientException.new("406 Not Acceptable", response_406) }
let(:generic_exception) { Exception.new }
let(:min_version) { "2" }
let(:max_version) { "5" }
diff --git a/spec/support/shared/unit/application_dot_d.rb b/spec/support/shared/unit/application_dot_d.rb
index da4eb88edd..692b0f99e1 100644
--- a/spec/support/shared/unit/application_dot_d.rb
+++ b/spec/support/shared/unit/application_dot_d.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,14 +33,19 @@ shared_examples_for "an application that loads a dot d" do
# make sure that we are correctly globbing.
let(:client_d_dir) do
Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../../data/client.d_00")) end
+ File.join(__dir__, "../../../data/client.d_00")
+ )
+ end
it "loads the configuration in order" do
+ etc_chef_client_rb = Chef::Config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/client.rb")
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")
- allow(app).to receive(:apply_config).with(anything(), Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_call_original.ordered
+ 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("", etc_chef_client_rb) # for chef-client managed nodes running this spec
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
@@ -48,7 +53,9 @@ shared_examples_for "an application that loads a dot d" do
context "when client_d_dir is set to a directory without configuration" do
let(:client_d_dir) do
Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
+ File.join(__dir__, "../../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,
@@ -64,7 +71,9 @@ shared_examples_for "an application that loads a dot d" do
# foo.rb as a directory should be ignored
let(:client_d_dir) do
Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_02")) end
+ File.join(__dir__, "../../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..bec3a047c0 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,12 +17,10 @@
# limitations under the License.
#
-require "spec_helper"
-
shared_examples_for "an execute resource" do
before(:each) do
- @resource = execute_resource
+ @resource = resource
end
it "should create a new Chef::Resource::Execute" do
@@ -57,7 +55,7 @@ shared_examples_for "an execute resource" do
end
it "should accept a hash for the environment" do
- test_hash = { :one => :two }
+ test_hash = { one: :two }
@resource.environment(test_hash)
expect(@resource.environment).to eql(test_hash)
end
@@ -76,14 +74,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).not_to respond_to(:path)
end
it "should accept an integer for the return code" do
@@ -106,6 +98,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,11 +118,42 @@ 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")
@resource.cwd("/tmp/")
- @resource.environment({ :one => :two })
+ @resource.environment({ one: :two })
@resource.group("legos")
@resource.returns(1)
@resource.user("root")
diff --git a/spec/support/shared/unit/file_system_support.rb b/spec/support/shared/unit/file_system_support.rb
index 32bdb1456e..1dc1a42787 100644
--- a/spec/support/shared/unit/file_system_support.rb
+++ b/spec/support/shared/unit/file_system_support.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,9 +23,10 @@ require "chef/chef_fs/file_system/memory/memory_file"
module FileSystemSupport
def memory_fs(pretty_name, value, cannot_be_in_regex = nil)
- if !value.is_a?(Hash)
+ unless value.is_a?(Hash)
raise "memory_fs() must take a Hash"
end
+
dir = Chef::ChefFS::FileSystem::Memory::MemoryRoot.new(pretty_name, cannot_be_in_regex)
value.each do |key, child|
dir.add_child(memory_fs_value(child, key.to_s, dir))
@@ -55,7 +56,7 @@ module FileSystemSupport
def no_blocking_calls_allowed
[ Chef::ChefFS::FileSystem::Memory::MemoryFile, Chef::ChefFS::FileSystem::Memory::MemoryDir ].each do |c|
- [ :children, :exists?, :read ].each do |m|
+ %i{children exists? read}.each do |m|
allow_any_instance_of(c).to receive(m).and_raise("#{m} should not be called")
end
end
diff --git a/spec/support/shared/unit/knife_shared.rb b/spec/support/shared/unit/knife_shared.rb
index 0af05ffb80..3c7459cfcc 100644
--- a/spec/support/shared/unit/knife_shared.rb
+++ b/spec/support/shared/unit/knife_shared.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,7 +33,7 @@ shared_examples_for "mandatory field missing" do
it "prints a relevant error message" do
expect { knife.run }.to raise_error(SystemExit)
- expect(stderr.string).to match /You must specify a #{fieldname}/
+ expect(stderr.string).to match(/You must specify a #{fieldname}/)
end
end
end
diff --git a/spec/support/shared/unit/mock_shellout.rb b/spec/support/shared/unit/mock_shellout.rb
index dac51be798..0ea8f64c4d 100644
--- a/spec/support/shared/unit/mock_shellout.rb
+++ b/spec/support/shared/unit/mock_shellout.rb
@@ -23,7 +23,7 @@
class MockShellout
module RSpec
def mock_shellout_command(command, **result)
- allow(::Mixlib::ShellOut).to receive(:new).with(command, anything).and_return MockShellout.new(result)
+ allow(::Mixlib::ShellOut).to receive(:new).with(command, anything).and_return MockShellout.new(**result)
end
end
diff --git a/spec/support/shared/unit/platform_introspector.rb b/spec/support/shared/unit/platform_introspector.rb
index 7b9cc0f94e..31b2b9ea95 100644
--- a/spec/support/shared/unit/platform_introspector.rb
+++ b/spec/support/shared/unit/platform_introspector.rb
@@ -1,7 +1,7 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,14 +36,14 @@ shared_examples_for "a platform introspector" do
@platform_family_hash = {
"debian" => "debian value",
- [:rhel, :fedora] => "redhatty value",
+ %i{rhel fedora} => "redhatty value",
"suse" => "suse value",
:default => "default value",
}
end
it "returns a default value when there is no known platform" do
- node = Hash.new
+ node = {}
expect(platform_introspector.value_for_platform(@platform_hash)).to eq("default")
end
@@ -125,7 +125,7 @@ shared_examples_for "a platform introspector" do
it "returns true if the node is a provided platform and platforms are provided as symbols" do
node.automatic_attrs[:platform] = "ubuntu"
- expect(platform_introspector.platform?([:redhat, :ubuntu])).to eq(true)
+ expect(platform_introspector.platform?(%i{redhat ubuntu})).to eq(true)
end
it "returns true if the node is a provided platform and platforms are provided as strings" do
@@ -143,7 +143,7 @@ shared_examples_for "a platform introspector" do
it "returns true if the node is in a provided platform family and families are provided as symbols" do
node.automatic_attrs[:platform_family] = "debian"
- expect(platform_introspector.platform_family?([:rhel, :debian])).to eq(true)
+ expect(platform_introspector.platform_family?(%i{rhel debian})).to eq(true)
end
it "returns true if the node is a provided platform and platforms are provided as strings" do
@@ -165,24 +165,24 @@ shared_examples_for "a platform introspector" do
describe "when the value is an array" do
before do
@platform_hash = {
- "debian" => { "4.0" => [ :restart, :reload ], "default" => [ :restart, :reload, :status ] },
- "ubuntu" => { "default" => [ :restart, :reload, :status ] },
- "centos" => { "default" => [ :restart, :reload, :status ] },
- "redhat" => { "default" => [ :restart, :reload, :status ] },
- "fedora" => { "default" => [ :restart, :reload, :status ] },
- "default" => { "default" => [:restart, :reload ] } }
+ "debian" => { "4.0" => %i{restart reload}, "default" => %i{restart reload status} },
+ "ubuntu" => { "default" => %i{restart reload status} },
+ "centos" => { "default" => %i{restart reload status} },
+ "redhat" => { "default" => %i{restart reload status} },
+ "fedora" => { "default" => %i{restart reload status} },
+ "default" => { "default" => %i{restart reload} } }
end
it "returns the correct default for a given platform" do
node.automatic_attrs[:platform] = "debian"
node.automatic_attrs[:platform_version] = "9000"
- expect(platform_introspector.value_for_platform(@platform_hash)).to eq([ :restart, :reload, :status ])
+ expect(platform_introspector.value_for_platform(@platform_hash)).to eq(%i{restart reload status})
end
it "returns the correct platform+version specific value " do
node.automatic_attrs[:platform] = "debian"
node.automatic_attrs[:platform_version] = "4.0"
- expect(platform_introspector.value_for_platform(@platform_hash)).to eq([:restart, :reload])
+ expect(platform_introspector.value_for_platform(@platform_hash)).to eq(%i{restart reload})
end
end
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index 394fdaa02f..f624a6ae44 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,6 @@
# limitations under the License.
#
-require "spec_helper"
require "tmpdir"
if windows?
require "chef/win32/file"
@@ -76,6 +75,7 @@ def setup_symlink
allow(File).to receive(:directory?).with(path).and_return(false)
allow(File).to receive(:writable?).with(path).and_return(true)
allow(file_symlink_class).to receive(:symlink?).with(path).and_return(true)
+ allow(file_symlink_class).to receive(:realpath).with(path).and_return(path)
end
allow(File).to receive(:directory?).with(enclosing_directory).and_return(true)
end
@@ -197,7 +197,7 @@ shared_examples_for Chef::Provider::File do
expect(provider.current_resource.name).to eql(resource.name)
end
- it "the loaded current_resource path should be the same as the resoure path" do
+ it "the loaded current_resource path should be the same as the resource path" do
provider.load_current_resource
expect(provider.current_resource.path).to eql(resource.path)
end
@@ -255,14 +255,14 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that exists" do
before do
# fake that we're on unix even if we're on windows
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
- stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
+ stat_struct = double("::File.stat", mode: 0600, uid: 0, gid: 0, mtime: 10000)
resource_real_path = File.realpath(resource.path)
expect(File).to receive(:stat).with(resource_real_path).at_least(:once).and_return(stat_struct)
- allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", :name => "wheel"))
- allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", :name => "root"))
+ allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", name: "wheel"))
+ allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", name: "root"))
end
context "when the new_resource does not specify any state" do
@@ -331,7 +331,7 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that does not exist" do
before do
# fake that we're on unix even if we're on windows
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
setup_missing_file
end
@@ -380,14 +380,14 @@ shared_examples_for Chef::Provider::File do
before do
# fake that we're on unix even if we're on windows
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
- stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
+ stat_struct = double("::File.stat", mode: 0600, uid: 0, gid: 0, mtime: 10000)
resource_real_path = File.realpath(resource.path)
allow(File).to receive(:stat).with(resource_real_path).and_return(stat_struct)
- allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", :name => "wheel"))
- allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", :name => "root"))
+ allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", name: "wheel"))
+ allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", name: "root"))
provider.send(:load_resource_attributes_from_file, resource)
end
@@ -416,7 +416,7 @@ shared_examples_for Chef::Provider::File do
context "when the enclosing directory does not exist" do
before { setup_missing_enclosing_directory }
- [:create, :create_if_missing, :touch].each do |action|
+ %i{create create_if_missing touch}.each do |action|
context "action #{action}" do
it "raises EnclosingDirectoryDoesNotExist" do
expect { provider.run_action(action) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
@@ -457,14 +457,24 @@ shared_examples_for Chef::Provider::File do
end
context "do_validate_content" do
- before { setup_normal_file }
+ let(:tempfile_name) { "foo-bar-baz" }
+ let(:backupfile) { "/tmp/failed_validations/#{tempfile_name}" }
let(:tempfile) do
- t = double("Tempfile", :path => "/tmp/foo-bar-baz", :closed? => true)
+ t = double("Tempfile", path: "/tmp/#{tempfile_name}", closed?: true)
allow(content).to receive(:tempfile).and_return(t)
t
end
+ before do
+ Chef::Config[:file_cache_path] = "/tmp"
+ allow(File).to receive(:dirname).and_return(tempfile)
+ allow(File).to receive(:basename).and_return(tempfile_name)
+ allow(FileUtils).to receive(:mkdir_p).and_return(true)
+ allow(FileUtils).to receive(:cp).and_return(true)
+ setup_normal_file
+ end
+
context "with user-supplied verifications" do
it "calls #verify on each verification with tempfile path" do
provider.new_resource.verify windows? ? "REM" : "true"
@@ -474,10 +484,24 @@ shared_examples_for Chef::Provider::File do
it "raises an exception if any verification fails" 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"
+ allow(provider).to receive(:tempfile).and_return(tempfile)
+ provider.new_resource.verify windows? ? "cmd.exe c exit 1" : "false"
+ provider.new_resource.verify.each do |v|
+ allow(v).to receive(:verify).and_return(false)
+ end
expect { provider.send(:do_validate_content) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
+
+ it "does not show verification for sensitive resources" do
+ allow(File).to receive(:directory?).with("C:\\Windows\\system32/cmd.exe").and_return(false)
+ allow(provider).to receive(:tempfile).and_return(tempfile)
+ provider.new_resource.sensitive true
+ provider.new_resource.verify windows? ? "cmd.exe c exit 1" : "false"
+ provider.new_resource.verify.each do |v|
+ allow(v).to receive(:verify).and_return(false)
+ end
+ expect { provider.send(:do_validate_content) }.to raise_error(Chef::Exceptions::ValidationFailed, /sensitive/)
+ end
end
end
@@ -507,7 +531,7 @@ shared_examples_for Chef::Provider::File do
before do
setup_normal_file
provider.load_current_resource
- tempfile = double("Tempfile", :path => "/tmp/foo-bar-baz")
+ tempfile = double("Tempfile", path: "/tmp/foo-bar-baz")
allow(content).to receive(:tempfile).and_return(tempfile)
expect(File).to receive(:exists?).with("/tmp/foo-bar-baz").and_return(true)
expect(tempfile).to receive(:close).once
@@ -520,8 +544,8 @@ shared_examples_for Chef::Provider::File do
let(:diff_for_reporting) { "+++\n---\n+foo\n-bar\n" }
before do
allow(provider).to receive(:contents_changed?).and_return(true)
- diff = double("Diff", :for_output => ["+++", "---", "+foo", "-bar"],
- :for_reporting => diff_for_reporting )
+ diff = double("Diff", for_output: ["+++", "---", "+foo", "-bar"],
+ for_reporting: diff_for_reporting )
allow(diff).to receive(:diff).with(resource_path, tempfile_path).and_return(true)
expect(provider).to receive(:diff).at_least(:once).and_return(diff)
expect(provider).to receive(:checksum).with(tempfile_path).and_return(tempfile_sha256)
@@ -584,13 +608,13 @@ shared_examples_for Chef::Provider::File do
end
it "raises an exception when the content object returns a tempfile with a nil path" do
- tempfile = double("Tempfile", :path => nil)
+ tempfile = double("Tempfile", path: nil)
expect(provider.send(:content)).to receive(:tempfile).at_least(:once).and_return(tempfile)
expect { provider.send(:do_contents_changes) }.to raise_error(RuntimeError)
end
it "raises an exception when the content object returns a tempfile that does not exist" do
- tempfile = double("Tempfile", :path => "/tmp/foo-bar-baz")
+ tempfile = double("Tempfile", path: "/tmp/foo-bar-baz")
expect(provider.send(:content)).to receive(:tempfile).at_least(:once).and_return(tempfile)
expect(File).to receive(:exists?).with("/tmp/foo-bar-baz").and_return(false)
expect { provider.send(:do_contents_changes) }.to raise_error(RuntimeError)
@@ -683,6 +707,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/package/package_shared.rb b/spec/support/shared/unit/provider/package/package_shared.rb
new file mode 100644
index 0000000000..e9afb4592b
--- /dev/null
+++ b/spec/support/shared/unit/provider/package/package_shared.rb
@@ -0,0 +1,95 @@
+#
+# Author:: Kapil Chouhan (<kapil.chouhan@msystechnologies.com>)
+# Copyright:: Copyright (c) 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_examples_for "given a response file" do
+ let(:cookbook_repo) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) }
+ let(:cookbook_loader) do
+ Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo)
+ Chef::CookbookLoader.new(cookbook_repo)
+ end
+ let(:cookbook_collection) do
+ cookbook_loader.load_cookbooks
+ Chef::CookbookCollection.new(cookbook_loader)
+ end
+ let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
+
+ describe "creating the cookbook file resource to fetch the response file" do
+ before do
+ expect(Chef::FileCache).to receive(:create_cache_path).with(path).and_return(tmp_path)
+ end
+
+ it "sets the preseed resource's runcontext to its own run context" do
+ cookbook_collection
+ allow(Chef::FileCache).to receive(:create_cache_path).and_return(tmp_path)
+ expect(@provider.preseed_resource(package_name, package_version).run_context).not_to be_nil
+ expect(@provider.preseed_resource(package_name, package_version).run_context).to equal(@provider.run_context)
+ end
+
+ it "should set the cookbook name of the remote file to the new resources cookbook name" do
+ expect(@provider.preseed_resource(package_name, package_version).cookbook_name).to eq(package_name)
+ end
+
+ it "should set remote files source to the new resources response file" do
+ expect(@provider.preseed_resource(package_name, package_version).source).to eq(response)
+ end
+
+ it "should never back up the cached response file" do
+ expect(@provider.preseed_resource(package_name, package_version).backup).to be_falsey
+ end
+
+ it "sets the install path of the resource to $file_cache/$cookbook/$pkg_name-$pkg_version.seed" do
+ expect(@provider.preseed_resource(package_name, package_version).path).to eq(tmp_preseed_path)
+ end
+ end
+
+ describe "when installing the preseed file to the cache location" do
+ let(:response_file_destination) { Dir.tmpdir + preseed_path }
+ let(:response_file_resource) do
+ response_file_resource = Chef::Resource::CookbookFile.new(response_file_destination, run_context)
+ response_file_resource.cookbook_name = package_name
+ response_file_resource.backup(false)
+ response_file_resource.source(response)
+ response_file_resource
+ end
+
+ before do
+ expect(@provider).to receive(:preseed_resource).with(package_name, package_version).and_return(response_file_resource)
+ end
+
+ after do
+ FileUtils.rm(response_file_destination) if ::File.exist?(response_file_destination)
+ end
+
+ it "creates the preseed file in the cache" do
+ expect(response_file_resource).to receive(:run_action).with(:create)
+ @provider.get_preseed_file(package_name, package_version)
+ end
+
+ it "returns the path to the response file if the response file was updated" do
+ expect(@provider.get_preseed_file(package_name, package_version)).to eq(response_file_destination)
+ end
+
+ it "should return false if the response file has not been updated" do
+ response_file_resource.updated_by_last_action(false)
+ expect(response_file_resource).not_to be_updated_by_last_action
+ # don't let the response_file_resource set updated to true
+ expect(response_file_resource).to receive(:run_action).with(:create)
+ expect(@provider.get_preseed_file(package_name, package_version)).to be(false)
+ end
+ end
+end
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 86076122a4..1f9f87866c 100644
--- a/spec/support/shared/unit/provider/useradd_based_user_provider.rb
+++ b/spec/support/shared/unit/provider/useradd_based_user_provider.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
#
# License:: Apache License, Version 2.0
#
@@ -68,16 +68,16 @@ 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" do
- allow(@new_resource).to receive(:supports).and_return({ :manage_home => false,
- :non_unique => false })
+ allow(@new_resource).to receive(:supports).and_return({ manage_home: false,
+ non_unique: false })
allow(@new_resource).to receive(attribute).and_return("hola")
expect(provider.universal_options).to eql([option, "hola"])
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
@@ -85,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
@@ -106,25 +106,25 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
describe "when the resource has a different home directory and supports home directory management" do
before do
- @new_resource.home "/wowaweea"
+ @new_resource.home "/banana"
@new_resource.manage_home true
end
it "should set -m -d /homedir" do
- expect(provider.universal_options).to eq(%w{-d /wowaweea})
+ expect(provider.universal_options).to eq(%w{-d /banana})
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
- @new_resource.home("/wowaweea")
+ @new_resource.home("/banana")
@new_resource.manage_home true
@new_resource.non_unique false
end
it "should set -m -d /homedir" do
- expect(provider.universal_options).to eq(%w{-d /wowaweea})
+ expect(provider.universal_options).to eq(%w{-d /banana})
expect(provider.usermod_options).to eq(%w{-m})
end
end
@@ -159,8 +159,8 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
"-u", "1000",
"-d", "/Users/mud",
"-m",
- "adam" ])
- expect(provider).to receive(:shell_out!).with(*command).and_return(true)
+ "adam"])
+ expect(provider).to receive(:shell_out_compacted!).with(*command).and_return(true)
provider.create_user
end
@@ -180,8 +180,8 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
command.concat([ "-s", "/usr/bin/zsh",
"-u", "1000",
"-r", "-m",
- "adam" ])
- expect(provider).to receive(:shell_out!).with(*command).and_return(true)
+ "adam"])
+ expect(provider).to receive(:shell_out_compacted!).with(*command).and_return(true)
provider.create_user
end
@@ -190,10 +190,15 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
end
describe "when managing a user" do
+ let(:manage_u_status) do
+ double("Mixlib::ShellOut command", exitstatus: 0, stdout: @stdout, stderr: @stderr, error!: nil)
+ end
+
before(:each) do
provider.new_resource.manage_home true
provider.new_resource.home "/Users/mud"
provider.new_resource.gid "23"
+ @stderr = ""
end
# CHEF-3423, -m must come before the username
@@ -203,8 +208,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
"-g", "23",
"-d", "/Users/mud",
"-m",
- "adam" ]
- expect(provider).to receive(:shell_out!).with(*command).and_return(true)
+ "adam"]
+ command.concat([ { returns: [0, 12] } ])
+ expect(provider).to receive(:shell_out_compacted).with(*command).and_return(manage_u_status)
provider.manage_user
end
@@ -214,8 +220,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
"-g", "23",
"-d", "/Users/mud",
"-m",
- "adam" ]
- expect(provider).to receive(:shell_out!).with(*command).and_return(true)
+ "adam"]
+ command.concat([ { returns: [0, 12] } ])
+ expect(provider).to receive(:shell_out_compacted).with(*command).and_return(manage_u_status)
provider.manage_user
end
@@ -223,8 +230,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
expect(provider).to receive(:updating_home?).at_least(:once).and_return(false)
command = ["usermod",
"-g", "23",
- "adam" ]
- expect(provider).to receive(:shell_out!).with(*command).and_return(true)
+ "adam"]
+ command.concat([ { returns: [0, 12] } ])
+ expect(provider).to receive(:shell_out_compacted).with(*command).and_return(manage_u_status)
provider.manage_user
end
end
@@ -232,24 +240,24 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
describe "when removing a user" do
it "should run userdel with the new resources user name" do
- expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true)
+ expect(provider).to receive(:shell_out_compacted!).with("userdel", @new_resource.username).and_return(true)
provider.remove_user
end
it "should run userdel with the new resources user name and -r if manage_home is true" do
@new_resource.manage_home true
- expect(provider).to receive(:shell_out!).with("userdel", "-r", @new_resource.username).and_return(true)
+ expect(provider).to receive(:shell_out_compacted!).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
- expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true)
+ expect(provider).to receive(:shell_out_compacted!).with("userdel", @new_resource.username).and_return(true)
provider.remove_user
end
it "should run userdel with the new resources user name and -f if force is true" do
@new_resource.force(true)
- expect(provider).to receive(:shell_out!).with("userdel", "-f", @new_resource.username).and_return(true)
+ expect(provider).to receive(:shell_out_compacted!).with("userdel", "-f", @new_resource.username).and_return(true)
provider.remove_user
end
end
@@ -257,7 +265,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, :error! => nil)
+ double("Mixlib::ShellOut command", exitstatus: 0, stdout: @stdout, stderr: @stderr, error!: nil)
end
before(:each) do
@@ -266,57 +274,57 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
# :nil_object => true,
# :username => "adam"
# )
- #provider = Chef::Provider::User::Useradd.new(@node, @new_resource)
+ # provider = Chef::Provider::User::Useradd.new(@node, @new_resource)
@stdout = "root P 09/02/2008 0 99999 7 -1"
@stderr = ""
end
it "should return false if status begins with P" do
- expect(provider).to receive(:shell_out).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_s_status)
+ 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)
end
it "should return false if status begins with N" do
@stdout = "root N"
- expect(provider).to receive(:shell_out).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_s_status)
+ 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)
end
it "should return true if status begins with L" do
@stdout = "root L"
- expect(provider).to receive(:shell_out).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_s_status)
+ 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 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(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(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).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).
- with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
- and_return(passwd_status)
+ passwd_status = double("Mixlib::ShellOut command", exitstatus: 0, stdout: "", stderr: "passwd: user 'chef-test' does not exist\n")
+ 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)
@@ -335,14 +343,14 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
describe "when locking the user" do
it "should run usermod -L with the new resources username" do
- expect(provider).to receive(:shell_out!).with("usermod", "-L", @new_resource.username)
+ expect(provider).to receive(:shell_out_compacted!).with("usermod", "-L", @new_resource.username)
provider.lock_user
end
end
describe "when unlocking the user" do
it "should run usermod -L with the new resources username" do
- expect(provider).to receive(:shell_out!).with("usermod", "-U", @new_resource.username)
+ expect(provider).to receive(:shell_out_compacted!).with("usermod", "-U", @new_resource.username)
provider.unlock_user
end
end
@@ -385,7 +393,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
expect(Pathname).to receive(:new).with(@new_resource.home).and_return(@new_home_mock)
expect(@new_home_mock).to receive(:cleanpath).and_return(home_check["new_resource_home"].last)
- expect(provider.updating_home?).to eq(home_check["expected_result"])
+ expect(provider.send(:updating_home?)).to eq(home_check["expected_result"])
end
end
it "should return true if the current home does not exist but a home is specified by the new resource" do
@@ -396,7 +404,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
@current_resource.home nil
@new_resource.home "/home/kitten"
- expect(provider.updating_home?).to eq(true)
+ expect(provider.send(:updating_home?)).to eq(true)
end
end
end
diff --git a/spec/support/shared/unit/resource/static_provider_resolution.rb b/spec/support/shared/unit/resource/static_provider_resolution.rb
index e68b805d7d..721cb679ed 100644
--- a/spec/support/shared/unit/resource/static_provider_resolution.rb
+++ b/spec/support/shared/unit/resource/static_provider_resolution.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 27864e1625..0256112a68 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,8 +17,6 @@
# limitations under the License.
#
-require "spec_helper"
-
shared_examples_for "a script resource" do
it "should create a new Chef::Resource::Script" do
@@ -30,14 +28,10 @@ shared_examples_for "a script resource" do
expect(script_resource.resource_name).to eql(resource_name)
end
- it "should set command to nil on the resource", chef: ">= 13" do
+ it "should set command to nil on the 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")
@@ -45,54 +39,36 @@ shared_examples_for "a script resource" do
it "should accept a string for the flags" do
script_resource.flags "-f"
- expect(script_resource.flags).to eql("-f")
+ expect(script_resource.flags.strip).to eql("-f")
end
- it "should raise an exception if users set command on the resource", chef: ">= 13" do
+ it "should raise an exception if users set command on the 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) do
- resource = script_resource
- resource.run_context = run_context
- resource.code "echo hi"
- resource
- 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) }
-
- it "inherits exactly the :cwd, :environment, :group, :path, :user, and :umask attributes from a parent resource class" do
+ it "inherits exactly the :cwd, :domain, :environment, :group, :password, :path, :user, and :umask attributes from a parent resource class" do
inherited_difference = Chef::Resource::Script.guard_inherited_attributes -
- [:cwd, :environment, :group, :path, :user, :umask ]
+ %i{cwd domain environment group password path user umask}
expect(inherited_difference).to eq([])
end
it "when guard_interpreter is set to the default value, the guard command string should be evaluated by command execution and not through a resource" do
expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:evaluate_block)
- expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).not_to receive(:evaluate_action)
+ expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).not_to receive(:evaluate)
expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:evaluate).and_return(true)
- resource.only_if "echo hi"
- expect(resource.should_skip?(:run)).to eq(nil)
+ script_resource.only_if "echo hi"
+ expect(script_resource.should_skip?(:run)).to eq(nil)
end
it "when a valid guard_interpreter resource is specified, a block should be used to evaluate the guard" do
+ expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:evaluate_block)
expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).not_to receive(:evaluate)
- expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true)
- resource.guard_interpreter :script
- resource.only_if "echo hi"
- expect(resource.should_skip?(:run)).to eq(nil)
+ expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate).and_return(true)
+ script_resource.guard_interpreter :script
+ script_resource.only_if "echo hi"
+ expect(script_resource.should_skip?(:run)).to eq(nil)
end
end
end
diff --git a/spec/support/shared/unit/user_and_client_shared.rb b/spec/support/shared/unit/user_and_client_shared.rb
index 6c31ca22d1..e92f7a702c 100644
--- a/spec/support/shared/unit/user_and_client_shared.rb
+++ b/spec/support/shared/unit/user_and_client_shared.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ shared_examples_for "user or client create" do
it "creates a new object via the API with a public_key when it exists" do
object.public_key "some_public_key"
- expect(rest_v1).to receive(:post).with(url, payload.merge({ :public_key => "some_public_key" })).and_return({})
+ expect(rest_v1).to receive(:post).with(url, payload.merge({ public_key: "some_public_key" })).and_return({})
object.create
end
@@ -49,7 +49,7 @@ shared_examples_for "user or client create" do
end
it "creates a new object via the API with create_key" do
- expect(rest_v1).to receive(:post).with(url, payload.merge({ :create_key => true })).and_return({})
+ expect(rest_v1).to receive(:post).with(url, payload.merge({ create_key: true })).and_return({})
object.create
end
end
@@ -63,7 +63,7 @@ shared_examples_for "user or client create" do
}
end
- it "puts the public key into the objectr returned by create" do
+ it "puts the public key into the object returned by create" do
expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
new_object = object.create
expect(new_object.public_key).to eq("some_public_key")
@@ -104,7 +104,7 @@ shared_examples_for "user or client create" do
it "creates a new object via the API with a public_key when it exists" do
object.public_key "some_public_key"
- expect(rest_v0).to receive(:post).with(url, payload.merge({ :public_key => "some_public_key" })).and_return({})
+ expect(rest_v0).to receive(:post).with(url, payload.merge({ public_key: "some_public_key" })).and_return({})
object.create
end
diff --git a/spec/support/shared/unit/windows_script_resource.rb b/spec/support/shared/unit/windows_script_resource.rb
index 5b559bb83b..2961b378e3 100644
--- a/spec/support/shared/unit/windows_script_resource.rb
+++ b/spec/support/shared/unit/windows_script_resource.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,65 +16,50 @@
# limitations under the License.
#
-require "spec_helper"
-
require "support/shared/unit/execute_resource"
require "support/shared/unit/script_resource"
shared_examples_for "a Windows script resource" do
- before(:each) do
- node = Chef::Node.new
-
- node.default["kernel"] = Hash.new
- node.default["kernel"][:machine] = :x86_64.to_s
-
- run_context = Chef::RunContext.new(node, nil, nil)
-
- @resource = resource_instance
-
- end
-
it "should be a kind of Chef::Resource::WindowsScript" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::WindowsScript)
+ expect(windows_script_resource).to be_a_kind_of(Chef::Resource)
+ expect(windows_script_resource).to be_a_kind_of(Chef::Resource::WindowsScript)
end
context "when evaluating guards" do
it "should have a default_guard_interpreter attribute that is the same as the resource" do
- expect(@resource.default_guard_interpreter).to eq(@resource.resource_name)
+ expect(windows_script_resource.default_guard_interpreter).to eq(windows_script_resource.resource_name)
end
it "should default to using guard_interpreter attribute that is the same as the resource" do
- expect(@resource.guard_interpreter).to eq(@resource.resource_name)
+ expect(windows_script_resource.guard_interpreter).to eq(windows_script_resource.resource_name)
end
it "should use a resource to evaluate the guard when guard_interpreter is not specified" do
- expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true)
+ expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate).and_return(true)
expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).not_to receive(:evaluate)
- @resource.only_if "echo hi"
- expect(@resource.should_skip?(:run)).to eq(nil)
+ windows_script_resource.only_if "echo hi"
+ expect(windows_script_resource.should_skip?(:run)).to eq(nil)
end
describe "when the guard is given a ruby block" do
it "should evaluate the guard if the guard_interpreter is set to its default value" do
- @resource.only_if { true }
- expect(@resource.should_skip?(:run)).to eq(nil)
+ windows_script_resource.only_if { true }
+ expect(windows_script_resource.should_skip?(:run)).to eq(nil)
end
it "should raise an exception if the guard_interpreter is overridden from its default value" do
- @resource.guard_interpreter :bash
- @resource.only_if { true }
- expect { @resource.should_skip?(:run) }.to raise_error(ArgumentError)
+ windows_script_resource.guard_interpreter :bash
+ windows_script_resource.only_if { true }
+ expect { windows_script_resource.should_skip?(:run) }.to raise_error(ArgumentError)
end
end
end
context "script with a default guard interpreter" do
- let(:script_resource) do
- resource_instance.guard_interpreter :default
- resource_instance
- end
+ let(:script_resource) { windows_script_resource }
+
+ before { windows_script_resource.guard_interpreter :default }
+
it_should_behave_like "a script resource"
end
-
end
diff --git a/spec/tiny_server.rb b/spec/tiny_server.rb
index 83c5bf4a42..786130d0d5 100644
--- a/spec/tiny_server.rb
+++ b/spec/tiny_server.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +16,9 @@
# limitations under the License.
#
-require "rubygems"
require "webrick"
require "webrick/https"
require "rack"
-require "thread"
require "singleton"
require "open-uri"
require "chef/config"
@@ -38,7 +36,7 @@ module TinyServer
# SSLEnable: options[:ssl],
# SSLCertName: [ [ 'CN', WEBrick::Utils::getservername ] ],
AccessLog: [], # Remove this option to enable the access log when debugging.
- }
+ }.freeze
def initialize(**options)
@options = DEFAULT_OPTIONS.merge(options)
@@ -101,10 +99,10 @@ module TinyServer
class API
include Singleton
- GET = "GET"
- PUT = "PUT"
- POST = "POST"
- DELETE = "DELETE"
+ GET = "GET".freeze
+ PUT = "PUT".freeze
+ POST = "POST".freeze
+ DELETE = "DELETE".freeze
attr_reader :routes
@@ -136,8 +134,8 @@ module TinyServer
if response = response_for_request(env)
response.call
else
- debug_info = { :message => "no data matches the request for #{env['REQUEST_URI']}",
- :available_routes => @routes, :request => env }
+ debug_info = { message: "no data matches the request for #{env["REQUEST_URI"]}",
+ available_routes: @routes, request: env }
# Uncomment me for glorious debugging
# pp :not_found => debug_info
[404, { "Content-Type" => "application/json" }, [ Chef::JSONCompat.to_json(debug_info) ]]
@@ -170,7 +168,7 @@ module TinyServer
end
class Response
- HEADERS = { "Content-Type" => "application/json" }
+ HEADERS = { "Content-Type" => "application/json" }.freeze
def initialize(response_code = 200, data = nil, headers = nil, &block)
@response_code, @data = response_code, data
diff --git a/spec/unit/action_collection_spec.rb b/spec/unit/action_collection_spec.rb
new file mode 100644
index 0000000000..f4237efac6
--- /dev/null
+++ b/spec/unit/action_collection_spec.rb
@@ -0,0 +1,19 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+# all the testing done on the action_collection presently is done in the
+# resource reporter and data collector tests
diff --git a/spec/unit/api_client/registration_spec.rb b/spec/unit/api_client/registration_spec.rb
index 0f036766da..b7872d7bc3 100644
--- a/spec/unit/api_client/registration_spec.rb
+++ b/spec/unit/api_client/registration_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,11 +38,11 @@ describe Chef::ApiClient::Registration do
let(:http_mock) { double("Chef::ServerAPI mock") }
let(:expected_post_data) do
- { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem }
+ { name: client_name, admin: false, public_key: generated_public_key.to_pem }
end
let(:expected_put_data) do
- { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem }
+ { name: client_name, admin: false, public_key: generated_public_key.to_pem }
end
let(:server_v10_response) do
@@ -61,7 +61,7 @@ describe Chef::ApiClient::Registration do
end
let(:response_409) { Net::HTTPConflict.new("1.1", "409", "Conflict") }
- let(:exception_409) { Net::HTTPServerException.new("409 conflict", response_409) }
+ let(:exception_409) { Net::HTTPClientException.new("409 conflict", response_409) }
let(:generated_private_key_pem) { IO.read(File.expand_path("ssl/private_key.pem", CHEF_SPEC_DATA)) }
let(:generated_private_key) { OpenSSL::PKey::RSA.new(generated_private_key_pem) }
@@ -98,7 +98,7 @@ describe Chef::ApiClient::Registration do
it "has an HTTP client configured with validator credentials" do
expect(registration.http_api).to be_a_kind_of(Chef::ServerAPI)
expect(registration.http_api.options[:client_name]).to eq("test-validator")
- auth = registration.http_api.middlewares.find { |klass| klass.kind_of? Chef::HTTP::Authenticator }
+ auth = registration.http_api.middlewares.find { |klass| klass.is_a? Chef::HTTP::Authenticator }
expect(auth.client_name).to eq("test-validator")
end
@@ -108,28 +108,28 @@ describe Chef::ApiClient::Registration do
end
it "posts a locally generated public key to the server to create a client" do
- expect(http_mock).to receive(:post).
- with("clients", expected_post_data).
- and_return(create_with_pkey_response)
+ expect(http_mock).to receive(:post)
+ .with("clients", expected_post_data)
+ .and_return(create_with_pkey_response)
expect(registration.run.public_key).to eq(create_with_pkey_response["chef_key"]["public_key"])
expect(OpenSSL::PKey::RSA.new(registration.private_key).to_s).to eq(OpenSSL::PKey::RSA.new(generated_private_key_pem).to_s)
end
it "puts a locally generated public key to the server to update a client" do
- expect(http_mock).to receive(:post).
- with("clients", expected_post_data).
- and_raise(exception_409)
- expect(http_mock).to receive(:put).
- with("clients/#{client_name}", expected_put_data).
- and_return(update_with_pkey_response)
+ expect(http_mock).to receive(:post)
+ .with("clients", expected_post_data)
+ .and_raise(exception_409)
+ expect(http_mock).to receive(:put)
+ .with("clients/#{client_name}", expected_put_data)
+ .and_return(update_with_pkey_response)
expect(registration.run.public_key).to eq(update_with_pkey_response["public_key"].to_pem)
expect(OpenSSL::PKey::RSA.new(registration.private_key).to_s).to eq(OpenSSL::PKey::RSA.new(generated_private_key_pem).to_s)
end
it "writes the generated private key to disk" do
- expect(http_mock).to receive(:post).
- with("clients", expected_post_data).
- and_return(create_with_pkey_response)
+ expect(http_mock).to receive(:post)
+ .with("clients", expected_post_data)
+ .and_return(create_with_pkey_response)
registration.run
expect(OpenSSL::PKey::RSA.new(IO.read(key_location)).to_s).to eq(OpenSSL::PKey::RSA.new(generated_private_key_pem).to_s)
end
@@ -137,9 +137,9 @@ describe Chef::ApiClient::Registration do
context "and the client already exists on a Chef 11 server" do
it "requests a new key from the server and saves it" do
expect(http_mock).to receive(:post).and_raise(exception_409)
- expect(http_mock).to receive(:put).
- with("clients/#{client_name}", expected_put_data).
- and_return(update_with_pkey_response)
+ expect(http_mock).to receive(:put)
+ .with("clients/#{client_name}", expected_put_data)
+ .and_return(update_with_pkey_response)
expect(registration.run.public_key).to eq(update_with_pkey_response["public_key"].to_pem)
expect(OpenSSL::PKey::RSA.new(registration.private_key).to_s).to eq(OpenSSL::PKey::RSA.new(generated_private_key_pem).to_s)
end
@@ -148,11 +148,11 @@ describe Chef::ApiClient::Registration do
context "when local key generation is disabled" do
let(:expected_post_data) do
- { :name => client_name, :admin => false }
+ { name: client_name, admin: false }
end
let(:expected_put_data) do
- { :name => client_name, :admin => false, :private_key => true }
+ { name: client_name, admin: false, private_key: true }
end
before do
@@ -161,9 +161,9 @@ describe Chef::ApiClient::Registration do
end
it "creates a new ApiClient on the server using the validator identity" do
- expect(http_mock).to receive(:post).
- with("clients", expected_post_data).
- and_return(server_v10_response)
+ expect(http_mock).to receive(:post)
+ .with("clients", expected_post_data)
+ .and_return(server_v10_response)
expect(registration.run.private_key).to eq(server_v10_response["private_key"])
expect(registration.private_key).to eq("--begin rsa key etc--")
end
@@ -171,9 +171,9 @@ describe Chef::ApiClient::Registration do
context "and the client already exists on a Chef 11 server" do
it "requests a new key from the server and saves it" do
expect(http_mock).to receive(:post).and_raise(exception_409)
- expect(http_mock).to receive(:put).
- with("clients/#{client_name}", expected_put_data).
- and_return(server_v11_response)
+ expect(http_mock).to receive(:put)
+ .with("clients/#{client_name}", expected_put_data)
+ .and_return(server_v11_response)
expect(registration.run).to eq(server_v11_response)
expect(registration.private_key).to eq("--begin rsa key etc--")
end
@@ -181,11 +181,11 @@ describe Chef::ApiClient::Registration do
context "and the client already exists on a Chef 10 server" do
it "requests a new key from the server and saves it" do
- expect(http_mock).to receive(:post).with("clients", expected_post_data).
- and_raise(exception_409)
- expect(http_mock).to receive(:put).
- with("clients/#{client_name}", expected_put_data).
- and_return(server_v10_response)
+ expect(http_mock).to receive(:post).with("clients", expected_post_data)
+ .and_raise(exception_409)
+ expect(http_mock).to receive(:put)
+ .with("clients/#{client_name}", expected_put_data)
+ .and_return(server_v10_response)
expect(registration.run.private_key).to eq(server_v10_response["private_key"])
expect(registration.private_key).to eq("--begin rsa key etc--")
end
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index a8ac4f747b..75fe6570e7 100644
--- a/spec/unit/api_client_spec.rb
+++ b/spec/unit/api_client_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,7 +41,7 @@ describe Chef::ApiClient do
end
it "only allows string values for the name" do
- expect { @client.name Hash.new }.to raise_error(ArgumentError)
+ expect { @client.name({}) }.to raise_error(ArgumentError)
end
it "has an admin flag attribute" do
@@ -55,7 +55,7 @@ describe Chef::ApiClient do
it "allows only boolean values for the admin flag" do
expect { @client.admin(false) }.not_to raise_error
- expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
+ expect { @client.admin({}) }.to raise_error(ArgumentError)
end
it "has a 'validator' flag attribute" do
@@ -69,7 +69,7 @@ describe Chef::ApiClient do
it "allows only boolean values for the 'validator' flag" do
expect { @client.validator(false) }.not_to raise_error
- expect { @client.validator(Hash.new) }.to raise_error(ArgumentError)
+ expect { @client.validator({}) }.to raise_error(ArgumentError)
end
it "has a public key attribute" do
@@ -79,7 +79,7 @@ describe Chef::ApiClient do
it "accepts only String values for the public key" do
expect { @client.public_key "" }.not_to raise_error
- expect { @client.public_key Hash.new }.to raise_error(ArgumentError)
+ expect { @client.public_key({}) }.to raise_error(ArgumentError)
end
it "has a private key attribute" do
@@ -89,7 +89,7 @@ describe Chef::ApiClient do
it "accepts only String values for the private key" do
expect { @client.private_key "" }.not_to raise_error
- expect { @client.private_key Hash.new }.to raise_error(ArgumentError)
+ expect { @client.private_key({}) }.to raise_error(ArgumentError)
end
describe "when serializing to JSON" do
@@ -280,13 +280,13 @@ describe Chef::ApiClient do
context "and the client does not exist on the server" do
before do
@a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
- @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
+ @a_404_exception = Net::HTTPClientException.new("404 not found exception", @a_404_response)
expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception)
end
it "raises a 404 error" do
- expect { Chef::ApiClient.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException)
+ expect { Chef::ApiClient.reregister("lost-my-key") }.to raise_error(Net::HTTPClientException)
end
end
@@ -302,9 +302,9 @@ describe Chef::ApiClient do
@api_client_with_key = Chef::ApiClient.new
@api_client_with_key.name("lost-my-key")
@api_client_with_key.private_key("the new private key")
- expect(@http_client).to receive(:put).
- with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
- and_return(@api_client_with_key)
+ expect(@http_client).to receive(:put)
+ .with("clients/lost-my-key", name: "lost-my-key", admin: false, validator: false, private_key: true)
+ .and_return(@api_client_with_key)
end
it "returns an ApiClient with a private key" do
@@ -320,9 +320,9 @@ describe Chef::ApiClient do
context "and the client exists on a Chef 10-like server" do
before do
@api_client_with_key = { "name" => "lost-my-key", "private_key" => "the new private key" }
- expect(@http_client).to receive(:put).
- with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
- and_return(@api_client_with_key)
+ expect(@http_client).to receive(:put)
+ .with("clients/lost-my-key", name: "lost-my-key", admin: false, validator: false, private_key: true)
+ .and_return(@api_client_with_key)
end
it "returns an ApiClient with a private key" do
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
index 9c643fa492..39ba895538 100644
--- a/spec/unit/api_client_v1_spec.rb
+++ b/spec/unit/api_client_v1_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,7 +36,7 @@ describe Chef::ApiClientV1 do
end
it "only allows string values for the name" do
- expect { @client.name Hash.new }.to raise_error(ArgumentError)
+ expect { @client.name({}) }.to raise_error(ArgumentError)
end
it "has an admin flag attribute" do
@@ -50,7 +50,7 @@ describe Chef::ApiClientV1 do
it "allows only boolean values for the admin flag" do
expect { @client.admin(false) }.not_to raise_error
- expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
+ expect { @client.admin({}) }.to raise_error(ArgumentError)
end
it "has an create_key flag attribute" do
@@ -64,7 +64,7 @@ describe Chef::ApiClientV1 do
it "allows only boolean values for the create_key flag" do
expect { @client.create_key(false) }.not_to raise_error
- expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
+ expect { @client.create_key({}) }.to raise_error(ArgumentError)
end
it "has a 'validator' flag attribute" do
@@ -78,7 +78,7 @@ describe Chef::ApiClientV1 do
it "allows only boolean values for the 'validator' flag" do
expect { @client.validator(false) }.not_to raise_error
- expect { @client.validator(Hash.new) }.to raise_error(ArgumentError)
+ expect { @client.validator({}) }.to raise_error(ArgumentError)
end
it "has a public key attribute" do
@@ -88,7 +88,7 @@ describe Chef::ApiClientV1 do
it "accepts only String values for the public key" do
expect { @client.public_key "" }.not_to raise_error
- expect { @client.public_key Hash.new }.to raise_error(ArgumentError)
+ expect { @client.public_key({}) }.to raise_error(ArgumentError)
end
it "has a private key attribute" do
@@ -98,7 +98,7 @@ describe Chef::ApiClientV1 do
it "accepts only String values for the private key" do
expect { @client.private_key "" }.not_to raise_error
- expect { @client.private_key Hash.new }.to raise_error(ArgumentError)
+ expect { @client.private_key({}) }.to raise_error(ArgumentError)
end
describe "when serializing to JSON" do
@@ -312,25 +312,25 @@ describe Chef::ApiClientV1 do
context "and the client does not exist on the server" do
before do
@a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
- @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
+ @a_404_exception = Net::HTTPClientException.new("404 not found exception", @a_404_response)
expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception)
end
it "raises a 404 error" do
- expect { Chef::ApiClientV1.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException)
+ expect { Chef::ApiClientV1.reregister("lost-my-key") }.to raise_error(Net::HTTPClientException)
end
end
end
describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => "406") }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:response_406) { OpenStruct.new(code: "406") }
+ let(:exception_406) { Net::HTTPClientException.new("406 Not Acceptable", response_406) }
let(:payload) do
{
- :name => "some_name",
- :validator => true,
- :admin => true,
+ name: "some_name",
+ validator: true,
+ admin: true,
}
end
@@ -389,7 +389,7 @@ describe Chef::ApiClientV1 do
end
it "updates the client with only the name" do
- expect(rest). to receive(:put).with("clients/some_name", { :name => "some_name" }).and_return({ :name => "some_name" })
+ expect(rest). to receive(:put).with("clients/some_name", { name: "some_name" }).and_return({ name: "some_name" })
@client.update
end
end
@@ -437,7 +437,7 @@ describe Chef::ApiClientV1 do
describe "reregister" do
context "when server API V0 is valid on the Chef Server receiving the request" do
it "creates a new object via the API" do
- expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({ :private_key => true })).and_return({})
+ expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({ private_key: true })).and_return({})
@client.reregister
end
end # when server API V0 is valid on the Chef Server receiving the request
diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb
index 0af3916134..f071dbd981 100644
--- a/spec/unit/application/apply_spec.rb
+++ b/spec/unit/application/apply_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Application::Apply do
before do
@recipe_file_name = "foo.rb"
@recipe_path = File.expand_path(@recipe_file_name)
- @recipe_file = double("Tempfile (mock)", :read => @recipe_text)
+ @recipe_file = double("Tempfile (mock)", read: @recipe_text)
allow(@app).to receive(:open).with(@recipe_path).and_return(@recipe_file)
allow(File).to receive(:exist?).with(@recipe_path).and_return(true)
allow(Chef::Application).to receive(:fatal!).and_return(true)
@@ -46,6 +46,7 @@ describe Chef::Application::Apply do
it "should read text properly" do
expect(@app.read_recipe_file(@recipe_file_name)[0]).to eq(@recipe_text)
end
+
it "should return a file_handle" do
expect(@app.read_recipe_file(@recipe_file_name)[1]).to be_instance_of(RSpec::Mocks::Double)
end
@@ -57,6 +58,7 @@ describe Chef::Application::Apply do
@app.read_recipe_file(nil)
end
end
+
describe "when recipe doesn't exist" do
before do
allow(File).to receive(:exist?).with(@recipe_path).and_return(false)
@@ -68,6 +70,7 @@ describe Chef::Application::Apply do
end
end
end
+
describe "temp_recipe_file" do
before do
@app.instance_variable_set(:@recipe_text, @recipe_text)
@@ -96,13 +99,13 @@ describe Chef::Application::Apply do
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(: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)
+ expect(Chef::ConfigFetcher).to receive(:new).with(json_source)
+ .and_return(config_fetcher)
end
it "reads the JSON attributes from the specified source" do
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 30fc58b84c..6977e4f108 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -85,13 +85,12 @@ describe Chef::Application::Client, "reconfigure" do
allow(app).to receive(:trap)
allow(app).to receive(:configure_logging).and_return(true)
- Chef::Config[:interval] = 10
Chef::Config[:once] = false
# 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)
+ allow(FileUtils).to receive(:rm_rf)
end
after do
@@ -107,6 +106,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
@@ -119,50 +119,80 @@ describe Chef::Application::Client, "reconfigure" do
describe "--named-run-list" do
it_behaves_like "sets the configuration",
- "--named-run-list arglebargle-example",
- :named_run_list => "arglebargle-example"
+ "--named-run-list arglebargle-example",
+ named_run_list: "arglebargle-example"
end
describe "--no-listen" do
- it_behaves_like "sets the configuration", "--no-listen", :listen => false
+ it_behaves_like "sets the configuration", "--no-listen", listen: false
end
describe "--daemonize", :unix_only do
context "with no value" do
it_behaves_like "sets the configuration", "--daemonize",
- :daemonize => true
+ daemonize: true
end
context "with an integer value" do
it_behaves_like "sets the configuration", "--daemonize 5",
- :daemonize => 5
+ daemonize: 5
end
context "with a non-integer value" do
it_behaves_like "sets the configuration", "--daemonize foo",
- :daemonize => true
+ 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", :unix_only 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"
+ 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"
+ 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
+ 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")
+ expect(Chef::Application).to receive(:fatal!).with('Unparsable config option ""', ChefConfig::ConfigurationError.new).and_raise("so ded")
ARGV.replace(["--config-option", ""])
expect { app.reconfigure }.to raise_error "so ded"
end
@@ -170,12 +200,121 @@ describe Chef::Application::Client, "reconfigure" do
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")
+ expect(Chef::Application).to receive(:fatal!).with('Unparsable config option "asdf"', ChefConfig::ConfigurationError.new).and_raise("so ded")
ARGV.replace(["--config-option", "asdf"])
expect { app.reconfigure }.to raise_error "so ded"
end
end
end
+
+ describe "--recipe-url and --local-mode" do
+ let(:archive) { double }
+ let(:config_exists) { false }
+
+ before do
+ allow(Chef::Config).to receive(:chef_repo_path).and_return("the_path_to_the_repo")
+ allow(FileUtils).to receive(:rm_rf)
+ allow(FileUtils).to receive(:mkdir_p)
+ allow(app).to receive(:fetch_recipe_tarball)
+ allow(Mixlib::Archive).to receive(:new).and_return(archive)
+ allow(archive).to receive(:extract)
+ allow(Chef::Config).to receive(:from_string)
+ allow(IO).to receive(:read).with(File.join("the_path_to_the_repo", ".chef/config.rb")).and_return("new_config")
+ allow(File).to receive(:file?).with(File.join("the_path_to_the_repo", ".chef/config.rb")).and_return(config_exists)
+ end
+
+ context "local mode not set" do
+ it "fails with a message stating local mode required" do
+ expect(Chef::Application).to receive(:fatal!).with("recipe-url can be used only in local-mode").and_raise("error occured")
+ ARGV.replace(["--recipe-url=test_url"])
+ expect { app.reconfigure }.to raise_error "error occured"
+ end
+ end
+
+ context "local mode set" do
+ before do
+ ARGV.replace(["--local-mode", "--recipe-url=test_url"])
+ end
+
+ context "--delete-entire-chef-repo" do
+ before do
+ ARGV.replace(["--local-mode", "--recipe-url=test_url", "--delete-entire-chef-repo"])
+ end
+
+ it "deletes the repo" do
+ expect(FileUtils).to receive(:rm_rf)
+ .with("the_path_to_the_repo", secure: true)
+
+ app.reconfigure
+ end
+ end
+
+ it "does not delete the repo" do
+ expect(FileUtils).not_to receive(:rm_rf)
+
+ app.reconfigure
+ end
+
+ it "sets { recipe_url: 'test_url' }" do
+ app.reconfigure
+
+ expect(Chef::Config.configuration).to include recipe_url: "test_url"
+ end
+
+ it "makes the repo path" do
+ expect(FileUtils).to receive(:mkdir_p)
+ .with("the_path_to_the_repo")
+
+ app.reconfigure
+ end
+
+ it "fetches the tarball" do
+ expect(app).to receive(:fetch_recipe_tarball)
+ .with("test_url", File.join("the_path_to_the_repo", "recipes.tgz"))
+
+ app.reconfigure
+ end
+
+ it "extracts the archive" do
+ expect(Mixlib::Archive).to receive(:new)
+ .with(File.join("the_path_to_the_repo", "recipes.tgz"))
+ .and_return(archive)
+
+ expect(archive).to receive(:extract)
+ .with("the_path_to_the_repo", perms: false, ignore: /^\.$/)
+
+ app.reconfigure
+ end
+
+ context "when there is new config" do
+ let(:config_exists) { true }
+
+ it "updates the config from the extracted config" do
+ expect(Chef::Config).to receive(:from_string)
+ .with(
+ "new_config",
+ File.join("the_path_to_the_repo", ".chef/config.rb")
+ )
+
+ app.reconfigure
+ end
+ end
+
+ context "when there is no new config" do
+ let(:config_exists) { false }
+
+ it "does not update the config" do
+ expect(Chef::Config).not_to receive(:from_string)
+ .with(
+ "new_config",
+ File.join("the_path_to_the_repo", ".chef/config.rb")
+ )
+
+ app.reconfigure
+ end
+ end
+ end
+ end
end
describe "when configured to not fork the client process" do
@@ -186,31 +325,26 @@ 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 terminate with message when interval is given" do
+ Chef::Config[:interval] = 600
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ expect(Chef::Application).to receive(:fatal!).with(
+ /Unforked .* interval runs are disabled by default\.
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
+Enable .* interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options\./
+ )
+ app.reconfigure
end
context "when interval is given on windows" do
before do
Chef::Config[:interval] = 600
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
- it "should not terminate" do
- expect(Chef::Application).not_to receive(:fatal!)
+ it "should terminate" do
+ expect(Chef::Application).to receive(:fatal!)
app.reconfigure
end
end
@@ -257,6 +391,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
@@ -294,14 +429,14 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
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(:config_fetcher) { double(Chef::ConfigFetcher, fetch_json: json_attribs) }
let(:json_source) { "https://foo.com/foo.json" }
before do
allow(app).to receive(:configure_chef).and_return(true)
Chef::Config[:json_attribs] = json_source
- expect(Chef::ConfigFetcher).to receive(:new).with(json_source).
- and_return(config_fetcher)
+ expect(Chef::ConfigFetcher).to receive(:new).with(json_source)
+ .and_return(config_fetcher)
end
it "reads the JSON attributes from the specified source" do
@@ -310,75 +445,6 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
end
- describe "audit mode" do
- shared_examples "experimental feature" do
- before do
- allow(Chef::Log).to receive(:warn)
- end
- end
-
- shared_examples "unrecognized setting" do
- it "fatals with a message including the incorrect setting" do
- expect(Chef::Application).to receive(:fatal!).with(/Unrecognized setting #{mode} for audit mode/)
- app.reconfigure
- end
- end
-
- shared_context "set via config file" do
- before do
- Chef::Config[:audit_mode] = mode
- end
- end
-
- shared_context "set via command line" do
- before do
- ARGV.replace(["--audit-mode", mode])
- end
- end
-
- describe "enabled via config file" do
- include_context "set via config file" do
- let(:mode) { :enabled }
- include_examples "experimental feature"
- end
- end
-
- describe "enabled via command line" do
- include_context "set via command line" do
- let(:mode) { "enabled" }
- include_examples "experimental feature"
- end
- end
-
- describe "audit_only via config file" do
- include_context "set via config file" do
- let(:mode) { :audit_only }
- include_examples "experimental feature"
- end
- end
-
- describe "audit-only via command line" do
- include_context "set via command line" do
- let(:mode) { "audit-only" }
- include_examples "experimental feature"
- end
- end
-
- describe "unrecognized setting via config file" do
- include_context "set via config file" do
- let(:mode) { :derp }
- include_examples "unrecognized setting"
- end
- end
-
- describe "unrecognized setting via command line" do
- include_context "set via command line" do
- let(:mode) { "derp" }
- include_examples "unrecognized setting"
- end
- end
- end
-
describe "when both the pidfile and lockfile opts are set to the same value" do
before do
@@ -420,6 +486,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
@@ -519,6 +586,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
index 73a113e554..7ede9fb86d 100644
--- a/spec/unit/application/exit_code_spec.rb
+++ b/spec/unit/application/exit_code_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Murawski (<smurawski@chef.io>)
-# Copyright:: Copyright 2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,10 +49,6 @@ describe Chef::Application::ExitCode 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
@@ -64,106 +60,22 @@ describe Chef::Application::ExitCode do
it "validates a REBOOT_FAILED return code of 41" do
expect(valid_rfc_exit_codes.include?(41)).to eq(true)
end
- end
-
- context "when Chef::Config :exit_status is not configured" do
- before do
- allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(nil)
- allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false)
- end
-
- it "writes a deprecation warning" do
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
- " In a future release, non-standard exit codes will be redefined as" \
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
- expect(Chef).to receive(:log_deprecation).with(warn)
- expect(exit_codes.normalize_exit_code(151)).to eq(151)
- end
-
- it "does not modify non-RFC exit codes" do
- expect(exit_codes.normalize_exit_code(151)).to eq(151)
- end
-
- it "returns DEPRECATED_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 SIGINT_RECEIVED when a deprecated exit code error is received" do
- expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(2)
- end
-
- it "returns GENERIC_FAILURE when an exception is specified" do
- expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1)
- end
-
- end
-
- context "when Chef::Config :exit_status is configured to not validate exit codes" do
- before do
- allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:disabled)
- allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false)
- end
-
- it "does not write a deprecation warning" do
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
- " In a future release, non-standard exit codes will be redefined as" \
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
- expect(Chef).not_to receive(:log_deprecation).with(warn)
- expect(exit_codes.normalize_exit_code(151)).to eq(151)
- end
-
- it "does not modify non-RFC exit codes" do
- expect(exit_codes.normalize_exit_code(151)).to eq(151)
- end
-
- it "returns DEPRECATED_FAILURE when no exit code is specified" do
- expect(exit_codes.normalize_exit_code()).to eq(-1)
- 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 SUCCESS when a reboot is pending" do
- allow(Chef::DSL::RebootPending).to receive(:reboot_pending?).and_return(true)
- expect(exit_codes.normalize_exit_code(0)).to eq(0)
- 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)
+ it "validates a CONFIG_FAILURE return code of 43" do
+ expect(valid_rfc_exit_codes.include?(43)).to eq(true)
end
- it "returns SIGINT_RECEIVED when a deprecated exit code error is received" do
- expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(2)
+ 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::Config :exit_status is configured to validate exit codes" do
- before do
- allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:enabled)
- allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false)
- end
+ context "when Chef validates exit codes" do
- it "does write a deprecation warning" do
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
- " In a future release, non-standard exit codes will be redefined as" \
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
- expect(Chef).to receive(:log_deprecation).with(warn)
+ it "does write a warning on non-standard exit codes" do
+ expect(Chef::Log).to receive(:warn).with(
+ /attempted to exit with a non-standard exit code of 151/
+ )
expect(exit_codes.normalize_exit_code(151)).to eq(1)
end
@@ -172,7 +84,7 @@ describe Chef::Application::ExitCode do
end
it "returns GENERIC_FAILURE when no exit code is specified" do
- expect(exit_codes.normalize_exit_code()).to eq(1)
+ expect(exit_codes.normalize_exit_code).to eq(1)
end
it "returns SIGINT_RECEIVED when a SIGINT is received" do
@@ -183,20 +95,10 @@ describe Chef::Application::ExitCode do
expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigTerm.new("BOOM"))).to eq(3)
end
- it "returns GENERIC_FAILURE when a deprecated exit code error is received" do
- expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(1)
- 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)
@@ -215,6 +117,18 @@ describe Chef::Application::ExitCode do
expect(exit_codes.normalize_exit_code(runtime_error)).to eq(37)
end
+ it "returns CONFIG_FAILURE when a configuration exception is specified" do
+ config_error = Chef::Exceptions::ConfigurationError.new("BOOM")
+ runtime_error = Chef::Exceptions::RunFailedWrappingError.new(config_error)
+ expect(exit_codes.normalize_exit_code(runtime_error)).to eq(43)
+ 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)
diff --git a/spec/unit/application/knife_spec.rb b/spec/unit/application/knife_spec.rb
index 90ecde608e..bce6b19366 100644
--- a/spec/unit/application/knife_spec.rb
+++ b/spec/unit/application/knife_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,12 +24,11 @@ describe Chef::Application::Knife do
before(:all) do
class NoopKnifeCommand < Chef::Knife
option :opt_with_default,
- :short => "-D VALUE",
- :long => "-optwithdefault VALUE",
- :default => "default-value"
+ short: "-D VALUE",
+ long: "-optwithdefault VALUE",
+ default: "default-value"
- def run
- end
+ def run; end
end
end
@@ -75,10 +74,23 @@ describe Chef::Application::Knife do
expect(@knife).to receive(:exit).with(0)
@knife.run
end
- if windows?
- expect(Chef::Config[:color]).to be_truthy
- else
- expect(Chef::Config[:color]).to be_truthy
+ expect(Chef::Config[:color]).to be_truthy
+ end
+
+ context "validate --format option" do
+ it "should set the default format summary" do
+ with_argv(*%w{noop knife command}) do
+ expect(@knife).to receive(:exit).with(0)
+ @knife.run
+ expect(@knife.default_config[:format]).to eq("summary")
+ end
+ end
+
+ it "should raise the error for invalid value" do
+ with_argv(*%w{noop knife command -F abc}) do
+ expect(STDOUT).to receive(:puts).at_least(2).times
+ expect { @knife.run }.to raise_error(SystemExit) { |e| expect(e.status).to eq(2) }
+ end
end
end
diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb
index 686ae745d8..f9302a0bb2 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 (c) 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::Application::Solo do
allow(app).to receive(:configure_chef).and_return(true)
allow(app).to receive(:configure_logging).and_return(true)
allow(app).to receive(:trap)
+ allow(app).to receive(:cli_arguments).and_return([])
Chef::Config[:json_attribs] = false
Chef::Config[:solo] = true
@@ -49,11 +50,6 @@ describe Chef::Application::Solo do
expect(Chef::Config[:solo]).to be_truthy
end
- it "should set audit-mode to :disabled" do
- app.reconfigure
- expect(Chef::Config[:audit_mode]).to be :disabled
- end
-
describe "when configured to not fork the client process" do
before do
Chef::Config[:client_fork] = false
@@ -68,18 +64,13 @@ describe Chef::Application::Solo do
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."
- )
+ expect(Chef::Application).to receive(:fatal!).with(/interval runs are (disabled|not supported)/)
app.reconfigure
end
end
end
- describe "when in daemonized mode and no interval has been set" do
+ describe "when in daemonized mode and no interval has been set", :unix_only do
before do
Chef::Config[:daemonize] = true
end
@@ -93,13 +84,13 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
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(: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)
+ expect(Chef::ConfigFetcher).to receive(:new).with(json_source)
+ .and_return(config_fetcher)
end
it "reads the JSON attributes from the specified source" do
@@ -130,7 +121,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
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)
+ config_fetcher = instance_double("Chef::ConfigFetcher", fetch_json: json_attribs)
Chef::Config[:json_attribs] = "https://foo.com/foo.json"
Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats"
@@ -173,6 +164,8 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
context "in local mode" do
+ let(:root_path) { windows? ? "C:/var/chef" : "/var/chef" }
+
before do
Chef::Config[:solo_legacy_mode] = false
end
@@ -203,20 +196,13 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
app.reconfigure
expect(ARGV.include?("--ez")).to be_falsey
end
-
- it "replaces -r with --recipe-url" do
- ARGV.push("-r", "http://junglist.gen.nz/recipes.tgz")
- app.reconfigure
- expect(ARGV.include?("-r")).to be_falsey
- expect(ARGV.include?("--recipe-url")).to be_truthy
- end
end
it "sets the repo path" do
- expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef")
+ expect(Chef::Config).to receive(:find_chef_repo_path).and_return(root_path)
app.reconfigure
- expect(Chef::Config.has_key?(:chef_repo_path)).to be_truthy
- expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef")
+ expect(Chef::Config.key?(:chef_repo_path)).to be_truthy
+ expect(Chef::Config[:chef_repo_path]).to eq(root_path)
end
it "runs chef-client in local mode" do
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 867cd3f9c2..b84f0c99f2 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,12 +22,10 @@ describe Chef::Application do
before do
@original_argv = ARGV.dup
ARGV.clear
- Chef::Log.logger = Logger.new(StringIO.new)
@app = Chef::Application.new
allow(@app).to receive(:trap)
allow(Dir).to receive(:chdir).and_return(0)
allow(@app).to receive(:reconfigure)
- Chef::Log.init(STDERR)
end
after do
@@ -87,6 +85,20 @@ describe Chef::Application do
@app.run
end
+ describe "when enforce_license is set to true" do
+ it "should check the license acceptance" do
+ expect(@app).to receive(:check_license_acceptance)
+ @app.run(enforce_license: true)
+ end
+ end
+
+ describe "when enforce_license is set to false" do
+ it "should not check the license acceptance" do
+ expect(@app).to_not receive(:check_license_acceptance)
+ @app.run(enforce_license: false)
+ end
+ end
+
it "should run the actual application" do
expect(@app).to receive(:run_application).and_return(true)
@app.run
@@ -101,12 +113,13 @@ 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
it "should parse the commandline options" do
expect(@app).to receive(:parse_options).and_return(true)
- @app.config[:config_file] = "/etc/chef/default.rb" #have a config file set, to prevent triggering error block
+ @app.config[:config_file] = "/etc/chef/default.rb" # have a config file set, to prevent triggering error block
@app.configure_chef
end
@@ -126,9 +139,9 @@ describe Chef::Application do
# force let binding to get evaluated or else we stub Pathname.new before we try to use it.
config_location_pathname
allow(Pathname).to receive(:new).with(config_location).and_return(config_location_pathname)
- expect(File).to receive(:read).
- with(config_location).
- and_return(config_content)
+ expect(File).to receive(:read)
+ .with(config_location)
+ .and_return(config_content)
end
it "should configure chef::config from a file" do
@@ -137,7 +150,7 @@ describe Chef::Application do
end
it "should merge the local config hash into chef::config" do
- #File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file)
+ # File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file)
@app.configure_chef
expect(Chef::Config.rspec_ran).to eq("true")
end
@@ -161,7 +174,7 @@ describe Chef::Application do
it "should emit a warning" do
expect(Chef::Config).not_to receive(:from_file).with("/etc/chef/default.rb")
- expect(Chef::Log).to receive(:warn).with("No config file found or specified on command line, using command line options.")
+ expect(Chef::Log).to receive(:warn).with("No config file found or specified on command line. Using command line options instead.")
@app.configure_chef
end
end
@@ -171,7 +184,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
@@ -186,57 +199,63 @@ describe Chef::Application do
it "should initialise the chef logger" 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
+ before do
+ Chef::Config.delete(:log_level)
+ end
+
it_behaves_like "log_level_is_auto"
end
@@ -261,12 +280,12 @@ describe Chef::Application do
it "it sets log_location to an instance of #{expected_class}" do
expect(expected_class).to receive(:new).with no_args
@app.configure_logging
- expect(Chef::Config[:log_location]).to be logger_instance
+ expect(Chef::Config[:log_location]).to eq([ logger_instance, STDOUT ])
end
end
end
- if Chef::Platform.windows?
+ if ChefUtils.windows?
it_behaves_like "sets log_location", :win_evt, Chef::Log::WinEvt
it_behaves_like "sets log_location", "win_evt", Chef::Log::WinEvt
else
@@ -299,16 +318,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(41).and_return(true)
+ Chef::Application.fatal! "blah", 41
+ 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
@@ -375,18 +401,104 @@ describe Chef::Application do
end
+ describe "#set_specific_recipes" do
+ let(:app) { Chef::Application.new }
+ context "when cli arguments does not contain any values" do
+ before do
+ allow(app).to receive(:cli_arguments).and_return([])
+ end
+
+ it "returns an empty array" do
+ app.set_specific_recipes
+ expect(Chef::Config[:specific_recipes]).to eq([])
+ end
+ end
+
+ context "when cli arguments contain valid recipe file path" do
+ let(:tempfile) { Tempfile.new("default.rb").path }
+ before do
+ allow(app).to receive(:cli_arguments).and_return([tempfile])
+ end
+
+ it "sets the specific recipes to config" do
+ app.set_specific_recipes
+ expect(Chef::Config[:specific_recipes]).to eq([tempfile])
+ end
+ end
+
+ context "when cli arguments contain invalid recipe file path" do
+ let(:fatal) { false }
+ before do
+ tempfile = "/root/default.rb"
+ allow(app).to receive(:cli_arguments).and_return([tempfile])
+ allow(Chef::Application).to receive(:fatal!).and_return(fatal)
+ end
+
+ it "raises an error with application exit" do
+ expect(app.set_specific_recipes).to eq(fatal)
+ end
+ end
+
+ context "when cli arguments contain empty string" do
+ let(:fatal) { false }
+ before do
+ allow(app).to receive(:cli_arguments).and_return([""])
+ allow(Chef::Application).to receive(:fatal!).and_return(fatal)
+ end
+
+ it "raises an arguments error" do
+ expect(app.set_specific_recipes).to eq(fatal)
+ end
+ end
+
+ context "when cli arguments contain any string" do
+ let(:fatal) { false }
+ before do
+ allow(app).to receive(:cli_arguments).and_return(["test"])
+ allow(Chef::Application).to receive(:fatal!).and_return(fatal)
+ end
+
+ it "raises an arguments error" do
+ expect(app.set_specific_recipes).to eq(fatal)
+ end
+ end
+
+ context "when cli arguments contain multiple invalid strings" do
+ let(:fatal) { false }
+ before do
+ allow(app).to receive(:cli_arguments).and_return(["", "test"])
+ allow(Chef::Application).to receive(:fatal!).and_return(fatal)
+ end
+
+ it "raises an arguments error" do
+ expect(app.set_specific_recipes).to eq(fatal)
+ end
+ end
+
+ context "when cli arguments contain valid recipe file path and invalid string" do
+ let(:fatal) { false }
+ before do
+ tempfile = Tempfile.new("default.rb").path
+ allow(app).to receive(:cli_arguments).and_return([tempfile, "test"])
+ allow(Chef::Application).to receive(:fatal!).and_return(fatal)
+ end
+
+ it "raises an arguments error" do
+ expect(app.set_specific_recipes).to eq(fatal)
+ end
+ end
+ end
+
describe "configuration errors" do
before do
- expect(Process).to receive(:exit)
+ allow(Process).to receive(:exit).and_return(true)
end
def raises_informative_fatals_on_configure_chef
config_file_regexp = Regexp.new @app.config[:config_file]
- expect(Chef::Log).to receive(:fatal).
- with(/Configuration error/)
- expect(Chef::Log).to receive(:fatal).
- with(config_file_regexp).
- at_least(1).times
+ expect(Chef::Log).to receive(:fatal).with(/Configuration error/)
+ expect(Chef::Log).to receive(:fatal).with(config_file_regexp)
+ expect(Process).to receive(:exit).with(43).and_return(true)
@app.configure_chef
end
@@ -408,4 +520,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
deleted file mode 100644
index 820e670f1c..0000000000
--- a/spec/unit/audit/audit_event_proxy_spec.rb
+++ /dev/null
@@ -1,318 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@chef.io>)
-# Author:: Claire McQuin (<claire@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 "chef/audit/audit_event_proxy"
-
-describe Chef::Audit::AuditEventProxy do
-
- let(:stdout) { StringIO.new }
- let(:events) { double("Chef::Events") }
- let(:audit_event_proxy) { Chef::Audit::AuditEventProxy.new(stdout) }
-
- before do
- Chef::Audit::AuditEventProxy.events = events
- end
-
- describe "#example_group_started" do
-
- let(:description) { "poots" }
- 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
-
- let(:parents) { [double("ExampleGroup")] }
-
- it "notifies control_group_started event" do
- expect(Chef::Log).to receive(:debug).
- with("Entered \`control_group\` block named poots")
- expect(events).to receive(:control_group_started).
- with(description)
- audit_event_proxy.example_group_started(notification)
- end
- end
-
- context "when notified from an inner-level example group" do
-
- let(:parents) { [double("ExampleGroup"), double("OuterExampleGroup")] }
-
- it "does nothing" do
- expect(events).to_not receive(:control_group_started)
- audit_event_proxy.example_group_started(notification)
- end
- end
- end
-
- describe "#stop" do
-
- let(:examples) { [] }
- let(:notification) { double("Notification", :examples => examples) }
- let(:exception) { nil }
- let(:example) { double("Example", :exception => exception) }
- let(:control_group_name) { "audit test" }
- let(:control_data) { double("ControlData") }
-
- before do
- allow(Chef::Log).to receive(:info) # silence messages to output stream
- end
-
- it "sends a message that audits completed" do
- expect(Chef::Log).to receive(:info).with("Successfully executed all \`control_group\` blocks and contained examples")
- audit_event_proxy.stop(notification)
- end
-
- context "when an example succeeded" do
-
- let(:examples) { [example] }
- let(:excpetion) { nil }
-
- before do
- allow(audit_event_proxy).to receive(:build_control_from).
- with(example).
- and_return([control_group_name, control_data])
- end
-
- it "notifies events" do
- expect(events).to receive(:control_example_success).
- with(control_group_name, control_data)
- audit_event_proxy.stop(notification)
- end
- end
-
- context "when an example failed" do
-
- let(:examples) { [example] }
- let(:exception) { double("ExpectationNotMet") }
-
- before do
- allow(audit_event_proxy).to receive(:build_control_from).
- with(example).
- and_return([control_group_name, control_data])
- end
-
- it "notifies events" do
- expect(events).to receive(:control_example_failure).
- with(control_group_name, control_data, exception)
- audit_event_proxy.stop(notification)
- end
- end
-
- describe "#build_control_from" do
-
- let(:examples) { [example] }
-
- let(:example) do
- double("Example", :metadata => metadata,
- :description => example_description,
- :full_description => full_description, :exception => nil) end
-
- let(:metadata) do
- {
- :described_class => described_class,
- :example_group => example_group,
- :line_number => line,
- }
- end
-
- let(:example_group) do
- {
- :description => group_description,
- :parent_example_group => parent_group,
- }
- end
-
- let(:parent_group) do
- {
- :description => control_group_name,
- :parent_example_group => nil,
- }
- end
-
- let(:line) { 27 }
-
- let(:control_data) do
- {
- :name => example_description,
- :desc => full_description,
- :resource_type => resource_type,
- :resource_name => resource_name,
- :context => context,
- :line_number => line,
- }
- end
-
- shared_examples "built control" do
-
- before do
- if described_class
- allow(described_class).to receive(:instance_variable_get).
- with(:@name).
- and_return(resource_name)
- allow(described_class.class).to receive(:name).
- and_return(described_class.class)
- end
- end
-
- it "returns the controls block name and example metadata for reporting" do
- expect(events).to receive(:control_example_success).
- with(control_group_name, control_data)
- audit_event_proxy.stop(notification)
- end
- end
-
- describe "a top-level example" do
- # controls "port 111" do
- # it "has nobody listening" do
- # expect(port("111")).to_not be_listening
- # end
- # end
-
- # Description parts
- let(:group_description) { "port 111" }
- let(:example_description) { "has nobody listening" }
- let(:full_description) { group_description + " " + example_description }
-
- # Metadata fields
- let(:described_class) { nil }
-
- # Example group (metadata[:example_group]) fields
- let(:parent_group) { nil }
-
- # Expected returns
- let(:control_group_name) { group_description }
-
- # Control data fields
- let(:resource_type) { nil }
- let(:resource_name) { nil }
- let(:context) { [] }
-
- include_examples "built control"
- end
-
- describe "an example with an implicit subject" do
- # controls "application ports" do
- # control port(111) do
- # it { is_expected.to_not be_listening }
- # end
- # end
-
- # Description parts
- let(:control_group_name) { "application ports" }
- let(:group_description) { "#{resource_type} #{resource_name}" }
- let(:example_description) { "should not be listening" }
- let(:full_description) do
- [control_group_name, group_description,
- example_description].join(" ") end
-
- # Metadata fields
- let(:described_class) do
- double("Serverspec::Type::Port",
- :class => "Serverspec::Type::Port", :name => resource_name) end
-
- # Control data fields
- let(:resource_type) { "Port" }
- let(:resource_name) { "111" }
- let(:context) { [] }
-
- include_examples "built control"
- end
-
- describe "an example in a nested context" do
- # controls "application ports" do
- # control "port 111" do
- # it "is not listening" do
- # expect(port(111)).to_not be_listening
- # end
- # end
- # end
-
- # Description parts
- let(:control_group_name) { "application ports" }
- let(:group_description) { "port 111" }
- let(:example_description) { "is not listening" }
- let(:full_description) do
- [control_group_name, group_description,
- example_description].join(" ") end
-
- # Metadata fields
- let(:described_class) { nil }
-
- # Control data fields
- let(:resource_type) { nil }
- let(:resource_name) { nil }
- let(:context) { [group_description] }
-
- include_examples "built control"
- end
-
- describe "an example in a nested context including Serverspec" do
- # controls "application directory" do
- # control file("/tmp/audit") do
- # describe file("/tmp/audit/test_file") do
- # it "is a file" do
- # expect(subject).to be_file
- # end
- # end
- # end
- # end
-
- # Description parts
- let(:control_group_name) { "application directory" }
- let(:outer_group_description) { "File \"tmp/audit\"" }
- let(:group_description) { "#{resource_type} #{resource_name}" }
- let(:example_description) { "is a file" }
- let(:full_description) do
- [control_group_name, outer_group_description,
- group_description, example_description].join(" ") end
-
- # Metadata parts
- let(:described_class) do
- double("Serverspec::Type::File",
- :class => "Serverspec::Type::File", :name => resource_name) end
-
- # Example group parts
- let(:parent_group) do
- {
- :description => outer_group_description,
- :parent_example_group => control_group,
- }
- end
-
- let(:control_group) do
- {
- :description => control_group_name,
- :parent_example_group => nil,
- }
- end
-
- # Control data parts
- let(:resource_type) { "File" }
- let(:resource_name) { "/tmp/audit/test_file" }
- let(:context) { [outer_group_description] }
-
- include_examples "built control"
- end
- end
- end
-
-end
diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb
deleted file mode 100644
index cf916266d8..0000000000
--- a/spec/unit/audit/audit_reporter_spec.rb
+++ /dev/null
@@ -1,439 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@chef.io>)
-# Author:: Claire McQuin (<claire@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::Audit::AuditReporter do
-
- let(:rest) { double("rest") }
- let(:reporter) { described_class.new(rest) }
- let(:node) { double("node", :name => "sofreshsoclean") }
- 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) 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/)
- reporter.audit_phase_start(run_status)
- end
-
- it "initializes an AuditData object" do
- expect(Chef::Audit::AuditData).to receive(:new).with(run_status.node.name, run_status.run_id)
- reporter.audit_phase_start(run_status)
- end
-
- it "saves the run status" do
- reporter.audit_phase_start(run_status)
- expect(reporter.instance_variable_get(:@run_status)).to eq run_status
- end
- end
-
- describe "#run_completed" do
-
- let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) }
- let(:run_data) { audit_data.to_hash }
-
- before do
- allow(reporter).to receive(:auditing_enabled?).and_return(true)
- allow(reporter).to receive(:run_status).and_return(run_status)
- allow(rest).to receive(:post).and_return(true)
- allow(reporter).to receive(:audit_data).and_return(audit_data)
- allow(reporter).to receive(:run_status).and_return(run_status)
- allow(audit_data).to receive(:to_hash).and_return(run_data)
- end
-
- describe "a successful run with auditing enabled" do
- it "sets run start and end times" do
- iso_start_time = "2014-12-03T17:31:05Z"
- iso_end_time = "2014-12-03T17:36:14Z"
-
- reporter.run_completed(node)
- expect(audit_data.start_time).to eq iso_start_time
- expect(audit_data.end_time).to eq iso_end_time
- end
-
- it "posts audit data to server endpoint" do
- headers = {
- "X-Ops-Audit-Report-Protocol-Version" => Chef::Audit::AuditReporter::PROTOCOL_VERSION,
- }
-
- expect(rest).to receive(:post).
- with("controls", run_data, headers)
- reporter.run_completed(node)
- end
-
- context "when audit phase failed" do
-
- 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)
- end
-
- it "reports an error" do
- reporter.run_completed(node)
- expect(run_data).to have_key(:error)
- expect(run_data).to have_key(:error)
- expect(run_data[:error]).to eq <<-EOM.strip!
-Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp
-/path/recipe.rb:57
-/path/library.rb:106
-EOM
- end
-
- end
-
- context "when unable to post to server" do
-
- let(:error) do
- e = StandardError.new
- e.set_backtrace(caller)
- e
- end
-
- before do
- expect(rest).to receive(:post).and_raise(error)
- allow(error).to receive(:respond_to?).and_call_original
- end
-
- context "the error is an http error" do
-
- 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/)
- allow(error).to receive(:response).and_return(response)
- expect(error).to receive(:respond_to?).with(:response).and_return(true)
- end
-
- context "when the code is 404" do
-
- 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/)
- reporter.run_completed(node)
- end
- end
-
- shared_examples "non-404 error code" do
-
- it "saves the error report" do
- expect(Chef::FileCache).to receive(:store).
- with("failed-audit-data.json", an_instance_of(String), 0640).
- and_return(true)
- expect(Chef::FileCache).to receive(:load).
- with("failed-audit-data.json", false).
- and_return(true)
- expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/)
- reporter.run_completed(node)
- end
-
- end
-
- context "when the code is not 404" do
- include_examples "non-404 error code" do
- let(:code) { "505" }
- end
- end
-
- context "when there is no code" do
- include_examples "non-404 error code" do
- let(:code) { nil }
- end
- end
-
- end
-
- context "the error is not an http error" do
-
- it "logs the error" do
- expect(error).to receive(:respond_to?).with(:response).and_return(false)
- expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/)
- reporter.run_completed(node)
- end
-
- end
-
- context "when reporting url fatals are enabled" do
-
- before do
- allow(Chef::Config).to receive(:[]).
- with(:enable_reporting_url_fatals).
- and_return(true)
- end
-
- it "raises the error" do
- expect(error).to receive(:respond_to?).with(:response).and_return(false)
- allow(Chef::Log).to receive(:error).and_return(true)
- expect(Chef::Log).to receive(:error).with(/Reporting fatals enabled. Aborting run./)
- expect { reporter.run_completed(node) }.to raise_error(error)
- end
-
- end
- end
- end
-
- context "when auditing is not enabled" do
-
- before do
- allow(Chef::Log).to receive(:debug)
- 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.")
- reporter.run_completed(node)
- end
-
- end
-
- context "when the run fails before audits" do
-
- before do
- allow(Chef::Log).to receive(:debug)
- 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")
- reporter.run_completed(node)
- end
-
- end
- end
-
- describe "#run_failed" do
-
- let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) }
- let(:run_data) { audit_data.to_hash }
-
- 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) 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)
- allow(reporter).to receive(:run_status).and_return(run_status)
- allow(reporter).to receive(:audit_data).and_return(audit_data)
- allow(audit_data).to receive(:to_hash).and_return(run_data)
- end
-
- context "when no prior exception is stored" do
- it "reports no error" do
- expect(rest).to receive(:post)
- reporter.run_failed(run_error)
- expect(run_data).to_not have_key(:error)
- end
- end
-
- context "when some prior exception is stored" do
- before do
- reporter.instance_variable_set(:@audit_phase_error, audit_error)
- end
-
- it "reports the prior error" do
- expect(rest).to receive(:post)
- reporter.run_failed(run_error)
- expect(run_data).to have_key(:error)
- expect(run_data[:error]).to eq <<-EOM.strip!
-Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp
-/path/recipe.rb:57
-/path/library.rb:106
-EOM
- end
- end
- end
-
- shared_context "audit data" do
-
- 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) do
- {
- "foo" => control_group_foo,
- "bar" => control_group_bar,
- }
- end
-
- let(:audit_data) do
- instance_double(Chef::Audit::AuditData,
- :add_control_group => true) end
-
- 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)
- allow(reporter).to receive(:audit_data).and_return(audit_data)
- allow(reporter).to receive(:run_status).and_return(run_status)
- allow(run_status).to receive(:run_context).and_return(run_context)
- end
- end
-
- 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/)
- reporter.audit_phase_complete("Output from audit mode")
- end
-
- it "collects audit data" do
- ordered_control_groups.each do |_name, group|
- expect(audit_data).to receive(:add_control_group).with(group)
- end
- reporter.audit_phase_complete("Output from audit mode")
- end
- end
-
- describe "#audit_phase_failed" do
- include_context "audit data"
-
- let(:error) { double("Exception") }
-
- it "notifies audit phase failed to debug log" do
- expect(Chef::Log).to receive(:debug).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|
- expect(audit_data).to receive(:add_control_group).with(group)
- end
- reporter.audit_phase_failed(error, "Output from audit mode")
- end
- end
-
- describe "#control_group_started" do
- include_context "audit data"
-
- let(:name) { "bat" }
- let(:control_group) do
- instance_double(Chef::Audit::ControlGroupData,
- :metadata => double("metadata")) end
-
- before do
- allow(Chef::Audit::ControlGroupData).to receive(:new).
- with(name, control_group.metadata).
- and_return(control_group)
- end
-
- it "stores the control group" do
- expect(ordered_control_groups).to receive(:has_key?).with(name).and_return(false)
- allow(run_context.audits).to receive(:[]).with(name).and_return(control_group)
- expect(ordered_control_groups).to receive(:store).
- with(name, control_group).
- and_call_original
- reporter.control_group_started(name)
- # stubbed :has_key? above, which is used by the have_key matcher,
- # so instead we check the response to Hash's #key? because luckily
- # #key? does not call #has_key?
- expect(ordered_control_groups.key?(name)).to be true
- expect(ordered_control_groups[name]).to eq control_group
- end
-
- context "when a control group with the same name has been seen" do
- it "raises an exception" do
- expect(ordered_control_groups).to receive(:has_key?).with(name).and_return(true)
- expect { reporter.control_group_started(name) }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
- end
- end
- end
-
- describe "#control_example_success" do
- include_context "audit data"
-
- let(:name) { "foo" }
- let(:example_data) { double("example data") }
-
- it "notifies the control group the example succeeded" do
- expect(control_group_foo).to receive(:example_success).with(example_data)
- reporter.control_example_success(name, example_data)
- end
- end
-
- describe "#control_example_failure" do
- include_context "audit data"
-
- let(:name) { "bar" }
- let(:example_data) { double("example data") }
- let(:error) { double("Exception", :message => "oopsie") }
-
- it "notifies the control group the example failed" do
- expect(control_group_bar).to receive(:example_failure).
- with(example_data, error.message)
- reporter.control_example_failure(name, example_data, error)
- end
- end
-
- describe "#auditing_enabled?" do
- shared_examples "enabled?" do |true_or_false|
-
- it "returns #{true_or_false}" do
- expect(Chef::Config).to receive(:[]).
- with(:audit_mode).
- and_return(audit_setting)
- expect(reporter.auditing_enabled?).to be true_or_false
- end
- end
-
- context "when auditing is disabled" do
- include_examples "enabled?", false do
- let(:audit_setting) { :disabled }
- end
- end
-
- context "when auditing in audit-only mode" do
- include_examples "enabled?", true do
- let(:audit_setting) { :audit_only }
- end
- end
-
- context "when auditing is enabled" do
- include_examples "enabled?", true do
- let(:audit_setting) { :enabled }
- end
- end
- end
-
-end
diff --git a/spec/unit/audit/control_group_data_spec.rb b/spec/unit/audit/control_group_data_spec.rb
deleted file mode 100644
index ea4ac260f9..0000000000
--- a/spec/unit/audit/control_group_data_spec.rb
+++ /dev/null
@@ -1,482 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@chef.io>)
-# Author:: Claire McQuin (<claire@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 "securerandom"
-
-describe Chef::Audit::AuditData do
-
- let(:node_name) { "noodles" }
- let(:run_id) { SecureRandom.uuid }
- let(:audit_data) { described_class.new(node_name, run_id) }
-
- let(:control_group_1) { double("control group 1") }
- let(:control_group_2) { double("control group 2") }
-
- describe "#add_control_group" do
- context "when no control groups have been added" do
- it "stores the control group" do
- audit_data.add_control_group(control_group_1)
- expect(audit_data.control_groups).to include(control_group_1)
- end
-
- end
-
- context "when adding additional control groups" do
-
- before do
- audit_data.add_control_group(control_group_1)
- end
-
- it "stores the control group" do
- audit_data.add_control_group(control_group_2)
- expect(audit_data.control_groups).to include(control_group_2)
- end
-
- it "stores all control groups" do
- audit_data.add_control_group(control_group_2)
- expect(audit_data.control_groups).to include(control_group_1)
- end
- end
- end
-
- describe "#to_hash" do
-
- let(:audit_data_hash) { audit_data.to_hash }
-
- it "returns a hash" do
- expect(audit_data_hash).to be_a(Hash)
- end
-
- it "describes a Chef::Audit::AuditData object" do
- keys = [:node_name, :run_id, :start_time, :end_time, :control_groups]
- expect(audit_data_hash.keys).to match_array(keys)
- end
-
- describe ":control_groups" do
-
- let(:control_hash_1) { { :name => "control group 1" } }
- let(:control_hash_2) { { :name => "control group 2" } }
-
- let(:control_groups) { audit_data_hash[:control_groups] }
-
- context "with no control groups added" do
- it "is an empty list" do
- expect(control_groups).to eq []
- end
- end
-
- context "with one control group added" do
-
- before do
- allow(audit_data).to receive(:control_groups).and_return([control_group_1])
- end
-
- it "is a one-element list containing the control group hash" do
- expect(control_group_1).to receive(:to_hash).once.and_return(control_hash_1)
- expect(control_groups.size).to eq 1
- expect(control_groups).to include(control_hash_1)
- end
- end
-
- context "with multiple control groups added" do
-
- before do
- allow(audit_data).to receive(:control_groups).and_return([control_group_1, control_group_2])
- end
-
- it "is a list of control group hashes" do
- expect(control_group_1).to receive(:to_hash).and_return(control_hash_1)
- expect(control_group_2).to receive(:to_hash).and_return(control_hash_2)
- expect(control_groups.size).to eq 2
- expect(control_groups).to include(control_hash_1)
- expect(control_groups).to include(control_hash_2)
- end
- end
- end
- end
-end
-
-describe Chef::Audit::ControlData do
-
- let(:name) { "ramen" }
- let(:resource_type) { double("Service") }
- let(:resource_name) { "mysql" }
- let(:context) { nil }
- let(:line_number) { 27 }
-
- 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
-
- let(:control_data_hash) { control_data.to_hash }
-
- it "returns a hash" do
- expect(control_data_hash).to be_a(Hash)
- end
-
- it "describes a Chef::Audit::ControlData object" do
- keys = [:name, :resource_type, :resource_name, :context, :status, :details]
- expect(control_data_hash.keys).to match_array(keys)
- end
-
- context "when context is nil" do
-
- it "sets :context to an empty array" do
- expect(control_data_hash[:context]).to eq []
- end
-
- end
-
- context "when context is non-nil" do
-
- let(:context) { ["outer"] }
-
- it "sets :context to its value" do
- expect(control_data_hash[:context]).to eq context
- end
- end
- end
-end
-
-describe Chef::Audit::ControlGroupData do
-
- let(:name) { "balloon" }
- let(:control_group_data) { described_class.new(name) }
-
- shared_context "control data" do
-
- let(:name) { "" }
- let(:resource_type) { nil }
- let(:resource_name) { nil }
- let(:context) { nil }
- let(:line_number) { 0 }
-
- let(:control_data) do
- {
- :name => name,
- :resource_type => resource_type,
- :resource_name => resource_name,
- :context => context,
- :line_number => line_number,
- }
- end
-
- end
-
- shared_context "control" do
- include_context "control data"
-
- 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).
- with(name: name, resource_type: resource_type,
- resource_name: resource_name, context: context,
- line_number: line_number).
- and_return(control)
- end
- end
-
- describe "#new" do
- it "has status \"success\"" do
- expect(control_group_data.status).to eq "success"
- end
- end
-
- describe "#example_success" do
- include_context "control"
-
- def notify_success
- control_group_data.example_success(control_data)
- end
-
- it "increments the number of successful audits" do
- num_success = control_group_data.number_succeeded
- notify_success
- expect(control_group_data.number_succeeded).to eq (num_success + 1)
- end
-
- it "does not increment the number of failed audits" do
- num_failed = control_group_data.number_failed
- notify_success
- expect(control_group_data.number_failed).to eq (num_failed)
- end
-
- it "marks the audit's status as success" do
- notify_success
- expect(control.status).to eq "success"
- end
-
- it "does not modify its own status" do
- expect(control_group_data).to_not receive(:status=)
- status = control_group_data.status
- notify_success
- expect(control_group_data.status).to eq status
- end
-
- it "saves the control" do
- controls = control_group_data.controls
- expect(controls).to_not include(control)
- notify_success
- expect(controls).to include(control)
- end
- end
-
- describe "#example_failure" do
- include_context "control"
-
- let(:details) { "poop" }
-
- def notify_failure
- control_group_data.example_failure(control_data, details)
- end
-
- it "does not increment the number of successful audits" do
- num_success = control_group_data.number_succeeded
- notify_failure
- expect(control_group_data.number_succeeded).to eq num_success
- end
-
- it "increments the number of failed audits" do
- num_failed = control_group_data.number_failed
- notify_failure
- expect(control_group_data.number_failed).to eq (num_failed + 1)
- end
-
- it "marks the audit's status as failure" do
- notify_failure
- expect(control.status).to eq "failure"
- end
-
- it "marks its own status as failure" do
- notify_failure
- expect(control_group_data.status).to eq "failure"
- end
-
- it "saves the control" do
- controls = control_group_data.controls
- expect(controls).to_not include(control)
- notify_failure
- expect(controls).to include(control)
- end
-
- context "when details are not provided" do
-
- let(:details) { nil }
-
- it "does not save details to the control" do
- default_details = control.details
- expect(control).to_not receive(:details=)
- notify_failure
- expect(control.details).to eq default_details
- end
- end
-
- context "when details are provided" do
-
- let(:details) { "yep that didn't work" }
-
- it "saves details to the control" do
- notify_failure
- expect(control.details).to eq details
- end
- end
- end
-
- shared_examples "multiple audits" do |success_or_failure|
- include_context "control"
-
- let(:num_success) { 0 }
- let(:num_failure) { 0 }
-
- before do
- if num_failure == 0
- num_success.times { control_group_data.example_success(control_data) }
- elsif num_success == 0
- num_failure.times { control_group_data.example_failure(control_data, nil) }
- end
- end
-
- it "counts the number of successful audits" do
- expect(control_group_data.number_succeeded).to eq num_success
- end
-
- it "counts the number of failed audits" do
- expect(control_group_data.number_failed).to eq num_failure
- end
-
- it "marks its status as \"#{success_or_failure}\"" do
- expect(control_group_data.status).to eq success_or_failure
- end
- end
-
- context "when all audits pass" do
- include_examples "multiple audits", "success" do
- let(:num_success) { 3 }
- end
- end
-
- context "when one audit fails" do
- shared_examples "mixed audit results" do
- include_examples "multiple audits", "failure" do
-
- let(:audit_results) { [] }
- let(:num_success) { audit_results.count("success") }
- let(:num_failure) { 1 }
-
- before do
- audit_results.each do |result|
- if result == "success"
- control_group_data.example_success(control_data)
- else
- control_group_data.example_failure(control_data, nil)
- end
- end
- end
- end
- end
-
- context "and it's the first audit" do
- include_examples "mixed audit results" do
- let(:audit_results) { %w{failure success success} }
- end
- end
-
- context "and it's an audit in the middle" do
- include_examples "mixed audit results" do
- let(:audit_results) { %w{success failure success} }
- end
- end
-
- context "and it's the last audit" do
- include_examples "mixed audit results" do
- let(:audit_results) { %w{success success failure} }
- end
- end
- end
-
- context "when all audits fail" do
- include_examples "multiple audits", "failure" do
- let(:num_failure) { 3 }
- end
- end
-
- describe "#to_hash" do
-
- let(:control_group_data_hash) { control_group_data.to_hash }
-
- it "returns a hash" do
- expect(control_group_data_hash).to be_a(Hash)
- end
-
- it "describes a Chef::Audit::ControlGroupData object" do
- keys = [:name, :status, :number_succeeded, :number_failed,
- :controls, :id]
- expect(control_group_data_hash.keys).to match_array(keys)
- end
-
- describe ":controls" do
-
- let(:control_group_controls) { control_group_data_hash[:controls] }
-
- context "with no controls added" do
- it "is an empty list" do
- expect(control_group_controls).to eq []
- end
- end
-
- context "with one control added" do
- include_context "control"
-
- let(:control_list) { [control_data] }
- let(:control_hash) { control.to_hash }
-
- before do
- expect(control_group_data).to receive(:controls).twice.and_return(control_list)
- expect(control_data).to receive(:to_hash).and_return(control_hash)
- end
-
- it "is a one-element list containing the control hash" do
- expect(control_group_controls.size).to eq 1
- expect(control_group_controls).to include(control_hash)
- end
-
- it "adds a sequence number to the control" do
- control_group_data.to_hash
- expect(control_hash).to have_key(:sequence_number)
- end
-
- end
-
- context "with multiple controls added" do
-
- let(:control_hash_1) { { :line_number => 27 } }
- let(:control_hash_2) { { :line_number => 13 } }
- let(:control_hash_3) { { :line_number => 35 } }
-
- let(:control_1) do
- double("control 1",
- :line_number => control_hash_1[:line_number],
- :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) end
- let(:control_3) do
- double("control 3",
- :line_number => control_hash_3[:line_number],
- :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] }
-
- before do
- # Another way to do this would be to call #example_success
- # or #example_failure per control hash, but we'd have to
- # then stub #create_control and it's a lot of extra stubbing work.
- # We can't stub the controls reader to return a list of
- # controls because of the call to sort! and the following
- # reading of controls.
- control_group_data.instance_variable_set(:@controls, control_list)
- end
-
- it "is a list of control group hashes ordered by line number" do
- expect(control_group_controls.size).to eq 3
- expect(control_group_controls).to eq ordered_control_hashes
- end
-
- it "assigns sequence numbers in order" do
- control_group_data.to_hash
- ordered_control_hashes.each_with_index do |control_hash, idx|
- expect(control_hash[:sequence_number]).to eq idx + 1
- end
- end
- end
- end
- end
-
-end
diff --git a/spec/unit/audit/logger_spec.rb b/spec/unit/audit/logger_spec.rb
deleted file mode 100644
index 51a32d906e..0000000000
--- a/spec/unit/audit/logger_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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::Audit::Logger do
-
- before(:each) do
- Chef::Audit::Logger.instance_variable_set(:@buffer, nil)
- end
-
- it "calling puts creates @buffer and adds the message" do
- Chef::Audit::Logger.puts("Output message")
- expect(Chef::Audit::Logger.read_buffer).to eq("Output message\n")
- end
-
- it "calling puts multiple times adds to the message" do
- Chef::Audit::Logger.puts("Output message")
- Chef::Audit::Logger.puts("Output message")
- Chef::Audit::Logger.puts("Output message")
- expect(Chef::Audit::Logger.read_buffer).to eq("Output message\nOutput message\nOutput message\n")
- end
-
- it "calling it before @buffer is set returns an empty string" do
- expect(Chef::Audit::Logger.read_buffer).to eq("")
- end
-
-end
diff --git a/spec/unit/audit/rspec_formatter_spec.rb b/spec/unit/audit/rspec_formatter_spec.rb
deleted file mode 100644
index 8c266fbb8b..0000000000
--- a/spec/unit/audit/rspec_formatter_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@chef.io>)
-# Author:: Claire McQuin (<claire@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 "chef/audit/rspec_formatter"
-
-describe Chef::Audit::RspecFormatter do
- let(:formatter) { Chef::Audit::RspecFormatter.new(nil) }
- it "should respond to close" do
- expect(formatter).to respond_to(:close)
- end
-end
diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb
deleted file mode 100644
index 4c03cab1d3..0000000000
--- a/spec/unit/audit/runner_spec.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-#
-# Author:: Tyler Ball (<tball@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 "rspec/core/sandbox"
-require "chef/audit/runner"
-require "chef/audit/audit_event_proxy"
-require "chef/audit/rspec_formatter"
-require "rspec/support/spec/in_sub_process"
-require "rspec/support/spec/stderr_splitter"
-
-describe Chef::Audit::Runner do
- include RSpec::Support::InSubProcess
-
- let(:events) { double("events") }
- let(:run_context) { instance_double(Chef::RunContext, :events => events) }
- let(:runner) { Chef::Audit::Runner.new(run_context) }
-
- around(:each) do |ex|
- RSpec::Core::Sandbox.sandboxed { ex.run }
- end
-
- context "when we run in audit mode" do
- let(:paths) { [ "/opt/chef/lib/chef/", 'C:\windows/here/lib/chef/' , "/opt/chef/extra/folders/lib/chef/"] }
- it "excludes the current path from backtrace" do
- paths.each do |path|
- expect(runner.exclusion_pattern).to match(path)
- end
- end
- end
-
- describe "#initialize" do
- it "correctly sets the run_context during initialization" do
- expect(runner.instance_variable_get(:@run_context)).to eq(run_context)
- end
- end
-
- context "during #run" do
-
- describe "#setup" do
- let(:log_location) { File.join(Dir.tmpdir, "audit_log") }
- let(:color) { false }
-
- before do
- Chef::Config[:log_location] = log_location
- Chef::Config[:color] = color
- end
-
- it "sets all the config values" do
- # This runs the Serverspec includes - we don't want these hanging around in all subsequent tests so
- # we run this in a forked process. Keeps Serverspec files from getting loaded into main process.
- in_sub_process do
- runner.send(:setup)
-
- expect(RSpec.configuration.output_stream).to eq(Chef::Audit::Logger)
- expect(RSpec.configuration.error_stream).to eq(Chef::Audit::Logger)
-
- expect(RSpec.configuration.formatters.size).to eq(2)
- expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::AuditEventProxy))
- expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::RspecFormatter))
- expect(Chef::Audit::AuditEventProxy.class_variable_get(:@@events)).to eq(run_context.events)
-
- expect(RSpec.configuration.expectation_frameworks).to eq([RSpec::Matchers])
- expect(RSpec::Matchers.configuration.syntax).to eq([:expect])
-
- expect(RSpec.configuration.color).to eq(color)
- expect(RSpec.configuration.expose_dsl_globally?).to eq(false)
- expect(RSpec.configuration.backtrace_exclusion_patterns).to include(runner.exclusion_pattern)
-
- expect(Specinfra.configuration.backend).to eq(:exec)
- end
- end
- end
-
- describe "#register_control_groups" do
- let(:audits) { [] }
- let(:run_context) { instance_double(Chef::RunContext, :audits => audits) }
-
- it "adds the control group aliases" do
- runner.send(:register_control_groups)
-
- expect(RSpec::Core::DSL.example_group_aliases).to include(:__control_group__)
- expect(RSpec::Core::DSL.example_group_aliases).to include(:control)
- end
-
- context "audits exist" do
- let(:audits) { { "audit_name" => group } }
- let(:group) { Struct.new(:args, :block).new(["group_name"], nil) }
-
- it "sends the audits to the world" do
- runner.send(:register_control_groups)
-
- expect(RSpec.world.example_groups.size).to eq(1)
- # For whatever reason, `kind_of` is not working
- # expect(RSpec.world.example_groups).to include(kind_of(RSpec::Core::ExampleGroup)) => FAIL
- g = RSpec.world.example_groups[0]
- expect(g.ancestors).to include(RSpec::Core::ExampleGroup)
- expect(g.description).to eq("group_name")
- end
- end
- end
-
- describe "#do_run" do
- let(:rspec_runner) { instance_double(RSpec::Core::Runner) }
-
- it "executes the runner" do
- expect(RSpec::Core::Runner).to receive(:new).with(nil).and_return(rspec_runner)
- expect(rspec_runner).to receive(:run_specs).with([])
-
- runner.send(:do_run)
- end
- end
- end
-
- describe "counters" do
- it "correctly calculates failed?" do
- expect(runner.failed?).to eq(false)
- end
-
- it "correctly calculates num_failed" do
- expect(runner.num_failed).to eq(0)
- end
-
- it "correctly calculates num_total" do
- expect(runner.num_total).to eq(0)
- end
- end
-
-end
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index 21987c01ab..1f687d76ac 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -107,4 +107,134 @@ describe "Chef class" do
end.to raise_error(Chef::Exceptions::InvalidEventType)
end
end
+
+ describe "Deprecation system" do
+ context "with treat_deprecation_warnings_as_errors false" do
+ before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
+
+ it "displays a simple deprecation warning" do
+ expect(Chef::Log).to receive(:warn).with(%r{spec/unit/chef_class_spec\.rb.*?I'm a little teapot.*?Please see}m)
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ end
+
+ it "allows silencing all warnings" do
+ Chef::Config[:silence_deprecation_warnings] = true
+ expect(Chef::Log).to_not receive(:warn)
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific types" do
+ Chef::Config[:silence_deprecation_warnings] = [:internal_api]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific IDs" do
+ Chef::Config[:silence_deprecation_warnings] = [0]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific IDs without matching the line number" do
+ Chef::Config[:silence_deprecation_warnings] = [__LINE__ + 4]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/Short and stout/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific IDs using the CHEF- syntax" do
+ Chef::Config[:silence_deprecation_warnings] = ["CHEF-0"]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific IDs using the chef- syntax" do
+ Chef::Config[:silence_deprecation_warnings] = ["chef-0"]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.")
+ Chef.deprecated(:generic, "This is my handle.")
+ end
+
+ it "allows silencing specific lines" do
+ Chef::Config[:silence_deprecation_warnings] = ["chef_class_spec.rb:#{__LINE__ + 4}"]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:generic, "Short and stout.")
+ Chef.deprecated(:internal_api, "This is my handle.")
+ end
+
+ it "allows silencing all via inline comments" do
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation
+ Chef.deprecated(:internal_api, "This is my handle.")
+ end
+
+ it "allows silencing specific types via inline comments" do
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation:generic
+ Chef.deprecated(:internal_api, "This is my handle.")
+ end
+
+ it "does not silence via inline comments when the types don't match" do
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/Short and stout/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:internal_api, "Short and stout.") # chef:silence_deprecation:generic
+ Chef.deprecated(:internal_api, "This is my handle.")
+ end
+
+ it "allows silencing all via inline comments with other stuff in the comment" do
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my handle/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:generic, "Short and stout.") # rubocop:something chef:silence_deprecation other stuff
+ Chef.deprecated(:internal_api, "This is my handle.")
+ end
+
+ it "handles multiple silence configurations at the same time" do
+ Chef::Config[:silence_deprecation_warnings] = ["exit_code", "chef_class_spec.rb:#{__LINE__ + 6}"]
+ expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once
+ expect(Chef::Log).to receive(:warn).with(/This is my spout/).once
+ expect(Chef::Log).to receive(:warn).with(/Hear me shout/).once
+ Chef.deprecated(:generic, "I'm a little teapot.")
+ Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation
+ Chef.deprecated(:internal_api, "This is my handle.")
+ Chef.deprecated(:internal_api, "This is my spout.")
+ Chef.deprecated(:exit_code, "When I get all steamed up.")
+ Chef.deprecated(:generic, "Hear me shout.")
+ end
+ end
+
+ context "with treat_deprecation_warnings_as_errors true" do
+ # This is already turned on globally for Chef's unit tests, but just for clarity do it here too.
+ before { Chef::Config[:treat_deprecation_warnings_as_errors] = true }
+
+ it "displays a simple deprecation error" do
+ expect(Chef::Log).to receive(:error).with(%r{spec/unit/chef_class_spec\.rb.*?I'm a little teapot.*?Please see}m)
+ expect { Chef.deprecated(:generic, "I'm a little teapot.") }.to raise_error(/I'm a little teapot./)
+ end
+ end
+ end
end
diff --git a/spec/unit/chef_fs/config_spec.rb b/spec/unit/chef_fs/config_spec.rb
index 0adcbbfbfb..fc44374ae7 100644
--- a/spec/unit/chef_fs/config_spec.rb
+++ b/spec/unit/chef_fs/config_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jess Mink (<jmink@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,12 +18,12 @@
require "spec_helper"
require "chef/exceptions"
-require "lib/chef/chef_fs/config.rb"
+require "lib/chef/chef_fs/config"
describe Chef::ChefFS::Config do
describe "initialize" do
it "warns when hosted setups use 'everything'" do
- base_config = Hash.new()
+ base_config = {}
base_config[:repo_mode] = "everything"
base_config[:chef_server_url] = "http://foo.com/organizations/fake_org/"
@@ -34,7 +34,7 @@ describe Chef::ChefFS::Config do
end
it "doesn't warn when hosted setups use 'hosted_everything'" do
- base_config = Hash.new()
+ base_config = {}
base_config[:repo_mode] = "hosted_everything"
base_config[:chef_server_url] = "http://foo.com/organizations/fake_org/"
@@ -45,7 +45,7 @@ describe Chef::ChefFS::Config do
end
it "doesn't warn when non-hosted setups use 'everything'" do
- base_config = Hash.new()
+ base_config = {}
base_config[:repo_mode] = "everything"
base_config[:chef_server_url] = "http://foo.com/"
@@ -203,8 +203,8 @@ describe Chef::ChefFS::Config do
})
end
- let (:path) { "/roles/foo.json" }
- let (:entry) { Entry.new(path) }
+ 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")
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..b85d811003
--- /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 (c) 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"
+
+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.to_s, "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/data_handler_base_spec.rb b/spec/unit/chef_fs/data_handler/data_handler_base_spec.rb
new file mode 100644
index 0000000000..49e5ea272c
--- /dev/null
+++ b/spec/unit/chef_fs/data_handler/data_handler_base_spec.rb
@@ -0,0 +1,65 @@
+#
+# Author:: Jeremy Miller (<jm@chef.io>)
+# Copyright:: Copyright (c) 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_handler_base"
+
+describe Chef::ChefFS::DataHandler::DataHandlerBase do
+ describe "#normalize_hash" do
+ let(:some_item) do
+ { "name" => "grizzly",
+ "gender" => "female",
+ "age" => 3,
+ "food" => "honey",
+ }
+ end
+
+ let(:item_defaults) do
+ { "family" => "ursidae",
+ "hibernate" => true,
+ "food" => "berries",
+ "avg_lifespan_years" => 22,
+ }
+ end
+
+ let(:normalized) do
+ { "name" => "grizzly",
+ "gender" => "female",
+ "family" => "ursidae",
+ "hibernate" => true,
+ "avg_lifespan_years" => 22,
+ "age" => 3,
+ "food" => "honey",
+ }
+ end
+
+ let(:handler) { described_class.new }
+
+ it "normalizes the Hash, filling in default values" do
+ expect(handler.normalize_hash(some_item, item_defaults)).to eq(normalized)
+ end
+
+ it "prefers already existing values over default values" do
+ expect(handler.normalize_hash(some_item, item_defaults)["food"]).to eq("honey")
+ end
+
+ it "handles being passed a nil value instead of Hash" do
+ expect(handler.normalize_hash(nil, item_defaults)).to eq(item_defaults)
+ 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 ac0252162f..909221c73a 100644
--- a/spec/unit/chef_fs/data_handler/group_handler_spec.rb
+++ b/spec/unit/chef_fs/data_handler/group_handler_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Ryan Cragun (<ryan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/chef_fs/diff_spec.rb b/spec/unit/chef_fs/diff_spec.rb
index d946fcb9e7..726adf8e50 100644
--- a/spec/unit/chef_fs/diff_spec.rb
+++ b/spec/unit/chef_fs/diff_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,70 +20,70 @@ require "spec_helper"
require "chef/chef_fs/file_pattern"
require "chef/chef_fs/command_line"
-# Removes the date stamp from the diff and replaces it with ' DATE'
-# example match: "/dev/null\t2012-10-16 16:15:54.000000000 +0000"
-# windows match: "--- /dev/null\tTue Oct 16 18:04:34 2012"
-def remove_os_differences(diff)
- diff = diff.gsub(/([+-]{3}.*)\t.*/, '\1 DATE')
- diff.gsub(/^@@ -\d(,\d)? \+\d(,\d)? @@/, "CONTEXT_LINE_NUMBERS")
-end
-
-describe "diff", :uses_diff => true do
+describe "diff", uses_diff: true do
include FileSystemSupport
+ # Removes the date stamp from the diff and replaces it with ' DATE'
+ # example match: "/dev/null\t2012-10-16 16:15:54.000000000 +0000"
+ # windows match: "--- /dev/null\tTue Oct 16 18:04:34 2012"
+ def remove_os_differences(diff)
+ diff = diff.gsub(/([+-]{3}.*)\t.*/, '\1 DATE')
+ diff.gsub(/^@@ -\d(,\d)? \+\d(,\d)? @@/, "CONTEXT_LINE_NUMBERS")
+ end
+
context "with two filesystems with all types of difference" do
let(:a) do
memory_fs("a", {
- :both_dirs => {
- :sub_both_dirs => { :subsub => nil },
- :sub_both_files => nil,
- :sub_both_files_different => "a\n",
- :sub_both_dirs_empty => {},
- :sub_dirs_empty_in_a_filled_in_b => {},
- :sub_dirs_empty_in_b_filled_in_a => { :subsub => nil },
- :sub_a_only_dir => { :subsub => nil },
- :sub_a_only_file => nil,
- :sub_dir_in_a_file_in_b => {},
- :sub_file_in_a_dir_in_b => nil,
+ both_dirs: {
+ sub_both_dirs: { subsub: nil },
+ sub_both_files: nil,
+ sub_both_files_different: "a\n",
+ sub_both_dirs_empty: {},
+ sub_dirs_empty_in_a_filled_in_b: {},
+ sub_dirs_empty_in_b_filled_in_a: { subsub: nil },
+ sub_a_only_dir: { subsub: nil },
+ sub_a_only_file: nil,
+ sub_dir_in_a_file_in_b: {},
+ sub_file_in_a_dir_in_b: nil,
},
- :both_files => nil,
- :both_files_different => "a\n",
- :both_dirs_empty => {},
- :dirs_empty_in_a_filled_in_b => {},
- :dirs_empty_in_b_filled_in_a => { :subsub => nil },
- :dirs_in_a_cannot_be_in_b => {},
- :file_in_a_cannot_be_in_b => nil,
- :a_only_dir => { :subsub => nil },
- :a_only_file => nil,
- :dir_in_a_file_in_b => {},
- :file_in_a_dir_in_b => nil,
+ both_files: nil,
+ both_files_different: "a\n",
+ both_dirs_empty: {},
+ dirs_empty_in_a_filled_in_b: {},
+ dirs_empty_in_b_filled_in_a: { subsub: nil },
+ dirs_in_a_cannot_be_in_b: {},
+ file_in_a_cannot_be_in_b: nil,
+ a_only_dir: { subsub: nil },
+ a_only_file: nil,
+ dir_in_a_file_in_b: {},
+ file_in_a_dir_in_b: nil,
}, /cannot_be_in_a/)
end
let(:b) do
memory_fs("b", {
- :both_dirs => {
- :sub_both_dirs => { :subsub => nil },
- :sub_both_files => nil,
- :sub_both_files_different => "b\n",
- :sub_both_dirs_empty => {},
- :sub_dirs_empty_in_a_filled_in_b => { :subsub => nil },
- :sub_dirs_empty_in_b_filled_in_a => {},
- :sub_b_only_dir => { :subsub => nil },
- :sub_b_only_file => nil,
- :sub_dir_in_a_file_in_b => nil,
- :sub_file_in_a_dir_in_b => {},
+ both_dirs: {
+ sub_both_dirs: { subsub: nil },
+ sub_both_files: nil,
+ sub_both_files_different: "b\n",
+ sub_both_dirs_empty: {},
+ sub_dirs_empty_in_a_filled_in_b: { subsub: nil },
+ sub_dirs_empty_in_b_filled_in_a: {},
+ sub_b_only_dir: { subsub: nil },
+ sub_b_only_file: nil,
+ sub_dir_in_a_file_in_b: nil,
+ sub_file_in_a_dir_in_b: {},
},
- :both_files => nil,
- :both_files_different => "b\n",
- :both_dirs_empty => {},
- :dirs_empty_in_a_filled_in_b => { :subsub => nil },
- :dirs_empty_in_b_filled_in_a => {},
- :dirs_in_b_cannot_be_in_a => {},
- :file_in_b_cannot_be_in_a => nil,
- :b_only_dir => { :subsub => nil },
- :b_only_file => nil,
- :dir_in_a_file_in_b => nil,
- :file_in_a_dir_in_b => {},
+ both_files: nil,
+ both_files_different: "b\n",
+ both_dirs_empty: {},
+ dirs_empty_in_a_filled_in_b: { subsub: nil },
+ dirs_empty_in_b_filled_in_a: {},
+ dirs_in_b_cannot_be_in_a: {},
+ file_in_b_cannot_be_in_a: nil,
+ b_only_dir: { subsub: nil },
+ b_only_file: nil,
+ 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
diff --git a/spec/unit/chef_fs/file_pattern_spec.rb b/spec/unit/chef_fs/file_pattern_spec.rb
index c4d076ef90..b1e1a59119 100644
--- a/spec/unit/chef_fs/file_pattern_spec.rb
+++ b/spec/unit/chef_fs/file_pattern_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -157,7 +157,7 @@ describe Chef::ChefFS::FilePattern do
end
end
- context 'with simple pattern "a\*\b"', :skip => (Chef::Platform.windows?) do
+ context 'with simple pattern "a\*\b"', skip: (ChefUtils.windows?) do
let(:pattern) { Chef::ChefFS::FilePattern.new('a\*\b') }
it "match?" do
expect(pattern.match?("a*b")).to be_truthy
@@ -264,7 +264,7 @@ describe Chef::ChefFS::FilePattern do
end
end
- context 'with star pattern "/abc/d[a-z][0-9]f/ghi"', :skip => (Chef::Platform.windows?) do
+ context 'with star pattern "/abc/d[a-z][0-9]f/ghi"', skip: (ChefUtils.windows?) do
let(:pattern) { Chef::ChefFS::FilePattern.new("/abc/d[a-z][0-9]f/ghi") }
it "match?" do
expect(pattern.match?("/abc/de1f/ghi")).to be_truthy
diff --git a/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb b/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb
index 92ea8d0cea..2ea9a13391 100644
--- a/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb
+++ b/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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 7f3eb6efe2..101a002003 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
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -25,11 +25,9 @@ describe Chef::ChefFS::FileSystem::OperationFailedError do
context "has a cause attribute and HTTP result code is 400" do
it "include error cause" do
- allow_message_expectations_on_nil
response_body = '{"error":["Invalid key test in request body"]}'
- allow(@response).to receive(:code).and_return("400")
- allow(@response).to receive(:body).and_return(response_body)
- exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
+ response = double(:response, code: "400", body: response_body)
+ exception = Net::HTTPClientException.new("(exception) unauthorized", response)
expect do
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, exception), error_message
end.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}")
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 625ef32dca..43d2ee8178 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
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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 6e53e52966..c8d71f976c 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ require "chef/chef_fs/file_system/base_fs_object"
require "chef/chef_fs/file_system/exceptions"
require "chef/chef_fs/file_system/nonexistent_fs_object"
-CHILD_FILES = %w{ test1.json test2.json skip test3.json skip2 test4 }
+CHILD_FILES = %w{ test1.json test2.json skip test3.json skip2 test4 }.freeze
class TestDirectory < Chef::ChefFS::FileSystem::Repository::Directory
def make_child_entry(name)
@@ -88,7 +88,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do
end
it "returns a non existent object otherwise" do
- file_double = instance_double(TestFile, :name_valid? => false)
+ file_double = instance_double(TestFile, name_valid?: false)
expect(TestFile).to receive(:new).with("test_child", test_directory).and_return(file_double)
expect(test_directory.child("test_child")).to be_an_instance_of(Chef::ChefFS::FileSystem::NonexistentFSObject)
end
@@ -106,7 +106,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do
end
it "filters invalid names" do
- expect(test_directory.children.map { |c| c.name }).to eql %w{ test1.json test2.json test3.json }
+ expect(test_directory.children.map(&:name)).to eql %w{ test1.json test2.json test3.json }
end
end
@@ -152,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 5c74729557..99ea6049ff 100644
--- a/spec/unit/chef_fs/file_system_spec.rb
+++ b/spec/unit/chef_fs/file_system_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -57,17 +57,17 @@ describe Chef::ChefFS::FileSystem do
context "with a populated filesystem" do
let(:fs) do
memory_fs("", {
- :a => {
- :aa => {
- :c => "",
- :zz => "",
+ a: {
+ aa: {
+ c: "",
+ zz: "",
},
- :ab => {
- :c => "",
+ ab: {
+ c: "",
},
},
- :x => "",
- :y => {},
+ x: "",
+ y: {},
})
end
context "list" do
@@ -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
deleted file mode 100644
index 84637f7283..0000000000
--- a/spec/unit/chef_fs/parallelizer.rb
+++ /dev/null
@@ -1,477 +0,0 @@
-require "spec_helper"
-require "chef/chef_fs/parallelizer"
-
-describe Chef::ChefFS::Parallelizer do
- before :each do
- @start_time = Time.now
- end
-
- def elapsed_time
- Time.now - @start_time
- end
-
- after :each do
- parallelizer.kill
- end
-
- context "With a Parallelizer with 5 threads" do
- let :parallelizer do
- Chef::ChefFS::Parallelizer.new(5)
- end
-
- def parallelize(inputs, options = {}, &block)
- parallelizer.parallelize(inputs, { :main_thread_processing => false }.merge(options), &block)
- end
-
- it "parallel_do creates unordered output as soon as it is available" do
- outputs = []
- parallelizer.parallel_do([0.5, 0.3, 0.1]) do |val|
- sleep val
- outputs << val
- end
- expect(elapsed_time).to be < 0.6
- expect(outputs).to eq([ 0.1, 0.3, 0.5 ])
- end
-
- context "With :ordered => false (unordered output)" do
- it "An empty input produces an empty output" do
- expect(parallelize([], :ordered => false) do
- sleep 10
- end.to_a).to eql([])
- expect(elapsed_time).to be < 0.1
- end
-
- it "10 sleep(0.2)s complete within 0.5 seconds" do
- expect(parallelize(1.upto(10), :ordered => false) do |i|
- sleep 0.2
- "x"
- end.to_a).to eq(%w{x x x x x x x x x x})
- expect(elapsed_time).to be < 0.5
- end
-
- it "The output comes as soon as it is available" do
- enum = parallelize([0.5, 0.3, 0.1], :ordered => false) do |val|
- sleep val
- val
- end
- expect(enum.map do |value|
- expect(elapsed_time).to be < value + 0.1
- value
- end).to eq([ 0.1, 0.3, 0.5 ])
- end
-
- it "An exception in input is passed through but does NOT stop processing" do
- input = TestEnumerable.new(0.5, 0.3, 0.1) do
- raise "hi"
- end
- enum = parallelize(input, :ordered => false) { |x| sleep(x); x }
- results = []
- expect { enum.each { |value| results << value } }.to raise_error "hi"
- expect(results).to eq([ 0.1, 0.3, 0.5 ])
- expect(elapsed_time).to be < 0.6
- end
-
- it "Exceptions in output are raised after all processing is done" do
- processed = 0
- enum = parallelize([1, 2, "x", 3], :ordered => false) do |x|
- if x == "x"
- sleep 0.1
- raise "hi"
- end
- sleep 0.2
- processed += 1
- x
- end
- results = []
- expect { enum.each { |value| results << value } }.to raise_error "hi"
- expect(results.sort).to eq([ 1, 2, 3 ])
- expect(elapsed_time).to be < 0.3
- expect(processed).to eq(3)
- end
-
- it "Exceptions with :stop_on_exception are raised after all processing is done" do
- processed = 0
- parallelized = parallelize([0.3, 0.3, "x", 0.3, 0.3, 0.3, 0.3, 0.3], :ordered => false, :stop_on_exception => true) do |x|
- if x == "x"
- sleep(0.1)
- raise "hi"
- end
- sleep(x)
- processed += 1
- x
- end
- expect { parallelized.to_a }.to raise_error "hi"
- expect(processed).to eq(4)
- end
- end
-
- context "With :ordered => true (ordered output)" do
- it "An empty input produces an empty output" do
- expect(parallelize([]) do
- sleep 10
- end.to_a).to eql([])
- expect(elapsed_time).to be < 0.1
- end
-
- it "10 sleep(0.2)s complete within 0.5 seconds" do
- expect(parallelize(1.upto(10), :ordered => true) do |i|
- sleep 0.2
- "x"
- end.to_a).to eq(%w{x x x x x x x x x x})
- expect(elapsed_time).to be < 0.5
- end
-
- it "Output comes in the order of the input" do
- enum = parallelize([0.5, 0.3, 0.1]) do |val|
- sleep val
- val
- end.enum_for(:each_with_index)
- expect(enum.next).to eq([ 0.5, 0 ])
- expect(enum.next).to eq([ 0.3, 1 ])
- expect(enum.next).to eq([ 0.1, 2 ])
- expect(elapsed_time).to be < 0.6
- end
-
- it "Exceptions in input are raised in the correct sequence but do NOT stop processing" do
- input = TestEnumerable.new(0.5, 0.3, 0.1) do
- raise "hi"
- end
- results = []
- enum = parallelize(input) { |x| sleep(x); x }
- expect { enum.each { |value| results << value } }.to raise_error "hi"
- expect(elapsed_time).to be < 0.6
- expect(results).to eq([ 0.5, 0.3, 0.1 ])
- end
-
- it "Exceptions in output are raised in the correct sequence and running processes do NOT stop processing" do
- processed = 0
- enum = parallelize([1, 2, "x", 3]) do |x|
- if x == "x"
- sleep(0.1)
- raise "hi"
- end
- sleep(0.2)
- processed += 1
- x
- end
- results = []
- expect { enum.each { |value| results << value } }.to raise_error "hi"
- expect(results).to eq([ 1, 2 ])
- expect(elapsed_time).to be < 0.3
- expect(processed).to eq(3)
- end
-
- it "Exceptions with :stop_on_exception are raised after all processing is done" do
- processed = 0
- parallelized = parallelize([0.3, 0.3, "x", 0.3, 0.3, 0.3, 0.3, 0.3], :ordered => false, :stop_on_exception => true) do |x|
- if x == "x"
- sleep(0.1)
- raise "hi"
- end
- sleep(x)
- processed += 1
- x
- end
- expect { parallelized.to_a }.to raise_error "hi"
- expect(processed).to eq(4)
- end
- end
-
- it "When the input is slow, output still proceeds" do
- input = TestEnumerable.new do |&block|
- block.call(1)
- sleep 0.1
- block.call(2)
- sleep 0.1
- block.call(3)
- sleep 0.1
- end
- enum = parallelize(input) { |x| x }
- expect(enum.map do |value|
- expect(elapsed_time).to be < (value + 1) * 0.1
- value
- end).to eq([ 1, 2, 3 ])
- end
- end
-
- context "With a Parallelizer with 1 thread" do
- let :parallelizer do
- Chef::ChefFS::Parallelizer.new(1)
- end
-
- context "when the thread is occupied with a job" do
- before :each do
- parallelizer
- started = false
- @occupying_job_finished = occupying_job_finished = [ false ]
- @thread = Thread.new do
- parallelizer.parallelize([0], :main_thread_processing => false) do |x|
- started = true
- sleep(0.3)
- occupying_job_finished[0] = true
- end.wait
- end
- sleep(0.01) until started
- end
-
- after :each do
- if RUBY_VERSION.to_f > 1.8
- Thread.kill(@thread)
- end
- end
-
- it "parallelize with :main_thread_processing = true does not block" do
- expect(parallelizer.parallelize([1]) do |x|
- sleep(0.1)
- x
- end.to_a).to eq([ 1 ])
- expect(elapsed_time).to be < 0.2
- end
-
- it "parallelize with :main_thread_processing = false waits for the job to finish" do
- expect(parallelizer.parallelize([1], :main_thread_processing => false) do |x|
- sleep(0.1)
- x + 1
- end.to_a).to eq([ 2 ])
- expect(elapsed_time).to be > 0.3
- end
-
- it "resizing the Parallelizer to 0 waits for the job to stop" do
- expect(elapsed_time).to be < 0.2
- parallelizer.resize(0)
- expect(parallelizer.num_threads).to eq(0)
- expect(elapsed_time).to be > 0.25
- expect(@occupying_job_finished).to eq([ true ])
- end
-
- it "stopping the Parallelizer waits for the job to finish" do
- expect(elapsed_time).to be < 0.2
- parallelizer.stop
- expect(parallelizer.num_threads).to eq(0)
- expect(elapsed_time).to be > 0.25
- expect(@occupying_job_finished).to eq([ true ])
- end
-
- it "resizing the Parallelizer to 2 does not stop the job" do
- expect(elapsed_time).to be < 0.2
- parallelizer.resize(2)
- expect(parallelizer.num_threads).to eq(2)
- expect(elapsed_time).to be < 0.2
- sleep(0.3)
- expect(@occupying_job_finished).to eq([ true ])
- end
- end
-
- context "enumerable methods should run efficiently" do
- it ".count does not process anything" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.count).to eq(6)
- expect(outputs_processed).to eq(0)
- expect(input_mapper.num_processed).to eq(6)
- end
-
- it ".count with arguments works normally" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 1, 1, 1, 2, 2, 2, 3, 3, 4)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- x
- end
- expect(enum.count { |x| x > 1 }).to eq(6)
- expect(enum.count(2)).to eq(3)
- expect(outputs_processed).to eq(20)
- expect(input_mapper.num_processed).to eq(20)
- end
-
- it ".first does not enumerate anything other than the first result(s)" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.first).to eq(1)
- expect(enum.first(2)).to eq([1, 2])
- expect(outputs_processed).to eq(3)
- expect(input_mapper.num_processed).to eq(3)
- end
-
- it ".take does not enumerate anything other than the first result(s)" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.take(2)).to eq([1, 2])
- expect(outputs_processed).to eq(2)
- expect(input_mapper.num_processed).to eq(2)
- end
-
- it ".drop does not process anything other than the last result(s)" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.drop(2)).to eq([3, 4, 5, 6])
- expect(outputs_processed).to eq(4)
- expect(input_mapper.num_processed).to eq(6)
- end
-
- if Enumerable.method_defined?(:lazy)
- it ".lazy.take does not enumerate anything other than the first result(s)" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.lazy.take(2).to_a).to eq([1, 2])
- expect(outputs_processed).to eq(2)
- expect(input_mapper.num_processed).to eq(2)
- end
-
- it ".drop does not process anything other than the last result(s)" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- expect(enum.lazy.drop(2).to_a).to eq([3, 4, 5, 6])
- expect(outputs_processed).to eq(4)
- expect(input_mapper.num_processed).to eq(6)
- end
-
- it "lazy enumerable is actually lazy" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- sleep(0.05) # Just enough to yield and get other inputs in the queue
- x
- end
- enum.lazy.take(2)
- enum.lazy.drop(2)
- sleep(0.1)
- expect(outputs_processed).to eq(0)
- expect(input_mapper.num_processed).to eq(0)
- end
- end
- end
-
- context "running enumerable multiple times should function correctly" do
- it ".map twice on the same parallel enumerable returns the correct results and re-processes the input" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- x
- end
- expect(enum.map { |x| x }).to eq([1, 2, 3])
- expect(enum.map { |x| x }).to eq([1, 2, 3])
- expect(outputs_processed).to eq(6)
- expect(input_mapper.num_processed).to eq(6)
- end
-
- it ".first and then .map on the same parallel enumerable returns the correct results and re-processes the input" do
- outputs_processed = 0
- input_mapper = TestEnumerable.new(1, 2, 3)
- enum = parallelizer.parallelize(input_mapper) do |x|
- outputs_processed += 1
- x
- end
- expect(enum.first).to eq(1)
- expect(enum.map { |x| x }).to eq([1, 2, 3])
- expect(outputs_processed).to be >= 4
- expect(input_mapper.num_processed).to be >= 4
- end
-
- it "two simultaneous enumerations throws an exception" do
- enum = parallelizer.parallelize([1, 2, 3]) { |x| x }
- a = enum.enum_for(:each)
- a.next
- expect do
- b = enum.enum_for(:each)
- b.next
- end.to raise_error
- end
- end
- end
-
- context "With a Parallelizer with 0 threads" do
- let :parallelizer do
- Chef::ChefFS::Parallelizer.new(0)
- end
-
- context "And main_thread_processing on" do
- it "succeeds in running" do
- expect(parallelizer.parallelize([0.5]) { |x| x * 2 }.to_a).to eq([1])
- end
- end
- end
-
- context "With a Parallelizer with 10 threads" do
- let :parallelizer do
- Chef::ChefFS::Parallelizer.new(10)
- end
-
- it "does not have contention issues with large numbers of inputs" do
- expect(parallelizer.parallelize(1.upto(500)) { |x| x + 1 }.to_a).to eq(2.upto(501).to_a)
- end
-
- it "does not have contention issues with large numbers of inputs with ordering off" do
- expect(parallelizer.parallelize(1.upto(500), :ordered => false) { |x| x + 1 }.to_a.sort).to eq(2.upto(501).to_a)
- end
-
- it "does not have contention issues with large numbers of jobs and inputs with ordering off" do
- parallelizers = 0.upto(99).map do
- parallelizer.parallelize(1.upto(500)) { |x| x + 1 }
- end
- outputs = []
- threads = 0.upto(99).map do |i|
- Thread.new { outputs[i] = parallelizers[i].to_a }
- end
- threads.each { |thread| thread.join }
- outputs.each { |output| expect(output.sort).to eq(2.upto(501).to_a) }
- end
- end
-
- class TestEnumerable
- include Enumerable
-
- def initialize(*values, &block)
- @values = values
- @block = block
- @num_processed = 0
- end
-
- attr_reader :num_processed
-
- def each
- @values.each do |value|
- @num_processed += 1
- yield(value)
- end
- if @block
- @block.call do |value|
- @num_processed += 1
- yield(value)
- end
- end
- end
- end
-end
diff --git a/spec/unit/chef_fs/parallelizer_spec.rb b/spec/unit/chef_fs/parallelizer_spec.rb
new file mode 100644
index 0000000000..519a628347
--- /dev/null
+++ b/spec/unit/chef_fs/parallelizer_spec.rb
@@ -0,0 +1,479 @@
+require "spec_helper"
+require "chef/chef_fs/parallelizer"
+
+# FIXME: these are disabled on MacOS due to timing issues in our anka build cluster
+# these issues should be fixed and the tests should be re-eenabled. If we are getting
+# omnibus test phases on mac tests which are reasonable and not ~3 hours long, then the
+# condition to avoid this testing on macs can be deleted
+describe Chef::ChefFS::Parallelizer, :not_supported_on_macos do
+ before :each do
+ @start_time = Time.now
+ end
+
+ def elapsed_time
+ Time.now - @start_time
+ end
+
+ after :each do
+ parallelizer.kill
+ end
+
+ context "With a Parallelizer with 5 threads" do
+ let :parallelizer do
+ Chef::ChefFS::Parallelizer.new(5)
+ end
+
+ def parallelize(inputs, options = {}, &block)
+ parallelizer.parallelize(inputs, { main_thread_processing: false }.merge(options), &block)
+ end
+
+ it "parallel_do creates unordered output as soon as it is available" do
+ outputs = []
+ parallelizer.parallel_do([0.5, 0.3, 0.1]) do |val|
+ sleep val
+ outputs << val
+ end
+ expect(elapsed_time).to be < 0.6
+ expect(outputs).to eq([ 0.1, 0.3, 0.5 ])
+ end
+
+ context "With :ordered => false (unordered output)" do
+ it "An empty input produces an empty output" do
+ expect(parallelize([], ordered: false) do
+ sleep 10
+ end.to_a).to eql([])
+ expect(elapsed_time).to be < 0.1
+ end
+
+ it "10 sleep(0.2)s complete within 0.5 seconds" do
+ expect(parallelize(1.upto(10), ordered: false) do |i|
+ sleep 0.2
+ "x"
+ end.to_a).to eq(%w{x x x x x x x x x x})
+ expect(elapsed_time).to be < 0.5
+ end
+
+ it "The output comes as soon as it is available" do
+ enum = parallelize([0.5, 0.3, 0.1], ordered: false) do |val|
+ sleep val
+ val
+ end
+ expect(enum.map do |value|
+ expect(elapsed_time).to be < value + 0.1
+ value
+ end).to eq([ 0.1, 0.3, 0.5 ])
+ end
+
+ it "An exception in input is passed through but does NOT stop processing" do
+ input = TestEnumerable.new(0.5, 0.3, 0.1) do
+ raise "hi"
+ end
+ enum = parallelize(input, ordered: false) { |x| sleep(x); x }
+ results = []
+ expect { enum.each { |value| results << value } }.to raise_error "hi"
+ expect(results).to eq([ 0.1, 0.3, 0.5 ])
+ expect(elapsed_time).to be < 0.6
+ end
+
+ it "Exceptions in output are raised after all processing is done" do
+ processed = 0
+ enum = parallelize([1, 2, "x", 3], ordered: false) do |x|
+ if x == "x"
+ sleep 0.1
+ raise "hi"
+ end
+ sleep 0.2
+ processed += 1
+ x
+ end
+ results = []
+ expect { enum.each { |value| results << value } }.to raise_error "hi"
+ expect(results.sort).to eq([ 1, 2, 3 ])
+ expect(elapsed_time).to be < 0.3
+ expect(processed).to eq(3)
+ end
+
+ it "Exceptions with :stop_on_exception are raised after all processing is done" do
+ processed = 0
+ parallelized = parallelize([0.3, 0.3, "x", 0.3, 0.3, 0.3, 0.3, 0.3], ordered: false, stop_on_exception: true) do |x|
+ if x == "x"
+ sleep(0.1)
+ raise "hi"
+ end
+ sleep(x)
+ processed += 1
+ x
+ end
+ expect { parallelized.to_a }.to raise_error "hi"
+ expect(processed).to eq(4)
+ end
+ end
+
+ context "With :ordered => true (ordered output)" do
+ it "An empty input produces an empty output" do
+ expect(parallelize([]) do
+ sleep 10
+ end.to_a).to eql([])
+ expect(elapsed_time).to be < 0.1
+ end
+
+ it "10 sleep(0.2)s complete within 0.5 seconds" do
+ expect(parallelize(1.upto(10), ordered: true) do |i|
+ sleep 0.2
+ "x"
+ end.to_a).to eq(%w{x x x x x x x x x x})
+ expect(elapsed_time).to be < 0.5
+ end
+
+ it "Output comes in the order of the input" do
+ enum = parallelize([0.5, 0.3, 0.1]) do |val|
+ sleep val
+ val
+ end.enum_for(:each_with_index)
+ expect(enum.next).to eq([ 0.5, 0 ])
+ expect(enum.next).to eq([ 0.3, 1 ])
+ expect(enum.next).to eq([ 0.1, 2 ])
+ expect(elapsed_time).to be < 0.6
+ end
+
+ it "Exceptions in input are raised in the correct sequence but do NOT stop processing" do
+ input = TestEnumerable.new(0.5, 0.3, 0.1) do
+ raise "hi"
+ end
+ results = []
+ enum = parallelize(input) { |x| sleep(x); x }
+ expect { enum.each { |value| results << value } }.to raise_error "hi"
+ expect(elapsed_time).to be < 0.6
+ expect(results).to eq([ 0.5, 0.3, 0.1 ])
+ end
+
+ it "Exceptions in output are raised in the correct sequence and running processes do NOT stop processing" do
+ processed = 0
+ enum = parallelize([1, 2, "x", 3]) do |x|
+ if x == "x"
+ sleep(0.1)
+ raise "hi"
+ end
+ sleep(0.2)
+ processed += 1
+ x
+ end
+ results = []
+ expect { enum.each { |value| results << value } }.to raise_error "hi"
+ expect(results).to eq([ 1, 2 ])
+ expect(elapsed_time).to be < 0.3
+ expect(processed).to eq(3)
+ end
+
+ it "Exceptions with :stop_on_exception are raised after all processing is done" do
+ processed = 0
+ parallelized = parallelize([0.3, 0.3, "x", 0.3, 0.3, 0.3, 0.3, 0.3], ordered: false, stop_on_exception: true) do |x|
+ if x == "x"
+ sleep(0.1)
+ raise "hi"
+ end
+ sleep(x)
+ processed += 1
+ x
+ end
+ expect { parallelized.to_a }.to raise_error "hi"
+ expect(processed).to eq(4)
+ end
+ end
+
+ it "When the input is slow, output still proceeds" do
+ input = TestEnumerable.new do |&block|
+ block.call(1)
+ sleep 0.1
+ block.call(2)
+ sleep 0.1
+ block.call(3)
+ sleep 0.1
+ end
+ enum = parallelize(input) { |x| x }
+ expect(enum.map do |value|
+ expect(elapsed_time).to be < (value + 1) * 0.1
+ value
+ end).to eq([ 1, 2, 3 ])
+ end
+ end
+
+ context "With a Parallelizer with 1 thread" do
+ let :parallelizer do
+ Chef::ChefFS::Parallelizer.new(1)
+ end
+
+ context "when the thread is occupied with a job" do
+ before :each do
+ parallelizer
+ started = false
+ @occupying_job_finished = occupying_job_finished = [ false ]
+ @thread = Thread.new do
+ parallelizer.parallelize([0], main_thread_processing: false) do |x|
+ started = true
+ sleep(0.3)
+ occupying_job_finished[0] = true
+ end.wait
+ end
+ sleep(0.01) until started
+ end
+
+ after :each do
+ Thread.kill(@thread)
+ end
+
+ it "parallelize with :main_thread_processing = true does not block" do
+ expect(parallelizer.parallelize([1]) do |x|
+ sleep(0.1)
+ x
+ end.to_a).to eq([ 1 ])
+ expect(elapsed_time).to be < 0.2
+ end
+
+ it "parallelize with :main_thread_processing = false waits for the job to finish" do
+ expect(parallelizer.parallelize([1], main_thread_processing: false) do |x|
+ sleep(0.1)
+ x + 1
+ end.to_a).to eq([ 2 ])
+ expect(elapsed_time).to be > 0.3
+ end
+
+ it "resizing the Parallelizer to 0 waits for the job to stop" do
+ expect(elapsed_time).to be < 0.2
+ parallelizer.resize(0)
+ expect(parallelizer.num_threads).to eq(0)
+ expect(elapsed_time).to be > 0.25
+ expect(@occupying_job_finished).to eq([ true ])
+ end
+
+ it "stopping the Parallelizer waits for the job to finish" do
+ expect(elapsed_time).to be < 0.2
+ parallelizer.stop
+ expect(parallelizer.num_threads).to eq(0)
+ expect(elapsed_time).to be > 0.25
+ expect(@occupying_job_finished).to eq([ true ])
+ end
+
+ it "resizing the Parallelizer to 2 does not stop the job" do
+ expect(elapsed_time).to be < 0.2
+ parallelizer.resize(2)
+ expect(parallelizer.num_threads).to eq(2)
+ expect(elapsed_time).to be < 0.2
+ sleep(0.3)
+ expect(@occupying_job_finished).to eq([ true ])
+ end
+ end
+
+ context "enumerable methods should run efficiently" do
+ it ".count does not process anything" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.count).to eq(6)
+ expect(outputs_processed).to eq(0)
+ expect(input_mapper.num_processed).to eq(6)
+ end
+
+ it ".count with arguments works normally" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 1, 1, 1, 2, 2, 2, 3, 3, 4)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ x
+ end
+ expect(enum.count { |x| x > 1 }).to eq(6)
+ expect(enum.count(2)).to eq(3)
+ expect(outputs_processed).to eq(20)
+ expect(input_mapper.num_processed).to eq(20)
+ end
+
+ it ".first does not enumerate anything other than the first result(s)" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.first).to eq(1)
+ expect(enum.first(2)).to eq([1, 2])
+ expect(outputs_processed).to eq(3)
+ expect(input_mapper.num_processed).to eq(3)
+ end
+
+ it ".take does not enumerate anything other than the first result(s)" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.take(2)).to eq([1, 2])
+ expect(outputs_processed).to eq(2)
+ expect(input_mapper.num_processed).to eq(2)
+ end
+
+ it ".drop does not process anything other than the last result(s)" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.drop(2)).to eq([3, 4, 5, 6])
+ expect(outputs_processed).to eq(4)
+ expect(input_mapper.num_processed).to eq(6)
+ end
+
+ if Enumerable.method_defined?(:lazy)
+ it ".lazy.take does not enumerate anything other than the first result(s)" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.lazy.take(2).to_a).to eq([1, 2])
+ expect(outputs_processed).to eq(2)
+ expect(input_mapper.num_processed).to eq(2)
+ end
+
+ it ".drop does not process anything other than the last result(s)" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ expect(enum.lazy.drop(2).to_a).to eq([3, 4, 5, 6])
+ expect(outputs_processed).to eq(4)
+ expect(input_mapper.num_processed).to eq(6)
+ end
+
+ it "lazy enumerable is actually lazy" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3, 4, 5, 6)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ sleep(0.05) # Just enough to yield and get other inputs in the queue
+ x
+ end
+ enum.lazy.take(2)
+ enum.lazy.drop(2)
+ sleep(0.1)
+ expect(outputs_processed).to eq(0)
+ expect(input_mapper.num_processed).to eq(0)
+ end
+ end
+ end
+
+ context "running enumerable multiple times should function correctly" do
+ it ".map twice on the same parallel enumerable returns the correct results and re-processes the input" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ x
+ end
+ expect(enum.map { |x| x }).to eq([1, 2, 3])
+ expect(enum.map { |x| x }).to eq([1, 2, 3])
+ expect(outputs_processed).to eq(6)
+ expect(input_mapper.num_processed).to eq(6)
+ end
+
+ it ".first and then .map on the same parallel enumerable returns the correct results and re-processes the input" do
+ outputs_processed = 0
+ input_mapper = TestEnumerable.new(1, 2, 3)
+ enum = parallelizer.parallelize(input_mapper) do |x|
+ outputs_processed += 1
+ x
+ end
+ expect(enum.first).to eq(1)
+ expect(enum.map { |x| x }).to eq([1, 2, 3])
+ expect(outputs_processed).to be >= 4
+ expect(input_mapper.num_processed).to be >= 4
+ end
+
+ it "two simultaneous enumerations throws an exception" do
+ enum = parallelizer.parallelize([1, 2, 3]) { |x| x }
+ a = enum.enum_for(:each)
+ a.next
+ expect do
+ b = enum.enum_for(:each)
+ b.next
+ end.to raise_error(RuntimeError, "each() called on parallel enumerable twice simultaneously! Bad mojo")
+ end
+ end
+ end
+
+ context "With a Parallelizer with 0 threads" do
+ let :parallelizer do
+ Chef::ChefFS::Parallelizer.new(0)
+ end
+
+ context "And main_thread_processing on" do
+ it "succeeds in running" do
+ expect(parallelizer.parallelize([0.5]) { |x| x * 2 }.to_a).to eq([1])
+ end
+ end
+ end
+
+ context "With a Parallelizer with 10 threads" do
+ let :parallelizer do
+ Chef::ChefFS::Parallelizer.new(10)
+ end
+
+ it "does not have contention issues with large numbers of inputs" do
+ expect(parallelizer.parallelize(1.upto(500)) { |x| x + 1 }.to_a).to eq(2.upto(501).to_a)
+ end
+
+ it "does not have contention issues with large numbers of inputs with ordering off" do
+ expect(parallelizer.parallelize(1.upto(500), ordered: false) { |x| x + 1 }.to_a.sort).to eq(2.upto(501).to_a)
+ end
+
+ it "does not have contention issues with large numbers of jobs and inputs with ordering off" do
+ parallelizers = 0.upto(99).map do
+ parallelizer.parallelize(1.upto(500)) { |x| x + 1 }
+ end
+ outputs = []
+ threads = 0.upto(99).map do |i|
+ Thread.new { outputs[i] = parallelizers[i].to_a }
+ end
+ threads.each(&:join)
+ outputs.each { |output| expect(output.sort).to eq(2.upto(501).to_a) }
+ end
+ end
+
+ class TestEnumerable
+ include Enumerable
+
+ def initialize(*values, &block)
+ @values = values
+ @block = block
+ @num_processed = 0
+ end
+
+ attr_reader :num_processed
+
+ def each
+ @values.each do |value|
+ @num_processed += 1
+ yield(value)
+ end
+ if @block
+ @block.call do |value|
+ @num_processed += 1
+ yield(value)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/chef_fs/path_util_spec.rb b/spec/unit/chef_fs/path_util_spec.rb
index 93205a1815..4056c240cc 100644
--- a/spec/unit/chef_fs/path_util_spec.rb
+++ b/spec/unit/chef_fs/path_util_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kartik Null Cating-Subramanian (<ksubramanian@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -61,7 +61,7 @@ describe Chef::ChefFS::PathUtils do
end
context "invoking realest_path" do
- let(:good_path) { File.dirname(__FILE__) }
+ let(:good_path) { __dir__ }
let(:parent_path) { File.dirname(good_path) }
it "handles paths with no wildcards or globs" do
@@ -81,7 +81,7 @@ describe Chef::ChefFS::PathUtils do
end
it "handles root correctly" do
- if Chef::Platform.windows?
+ if ChefUtils.windows?
expect(Chef::ChefFS::PathUtils.realest_path("C:/")).to eq("C:/")
else
expect(Chef::ChefFS::PathUtils.realest_path("/")).to eq("/")
@@ -91,7 +91,7 @@ describe Chef::ChefFS::PathUtils do
context "invoking descendant_path" do
it "handles paths with various casing on windows" do
- allow(Chef::ChefFS).to receive(:windows?) { true }
+ allow(ChefUtils).to receive(:windows?) { true }
expect(Chef::ChefFS::PathUtils.descendant_path("C:/ab/b/c", "C:/AB/B")).to eq("c")
expect(Chef::ChefFS::PathUtils.descendant_path("C:/ab/b/c", "c:/ab/B")).to eq("c")
end
diff --git a/spec/unit/chef_spec.rb b/spec/unit/chef_spec.rb
index bbab793841..8153fdbbfc 100644
--- a/spec/unit/chef_spec.rb
+++ b/spec/unit/chef_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index ec3f70b9b0..b6a321c8e8 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,6 @@
#
require "spec_helper"
-require "spec/support/shared/context/client"
-require "spec/support/shared/examples/client"
-
require "chef/run_context"
require "chef/server_api"
require "rbconfig"
@@ -29,6 +26,262 @@ require "rbconfig"
class FooError < RuntimeError
end
+#
+# SHARED CONTEXTS
+#
+
+# Stubs a basic client object
+shared_context "client" do
+ let(:fqdn) { "hostname.example.org" }
+ let(:hostname) { "hostname" }
+ let(:machinename) { "machinename.example.org" }
+ let(:platform) { "example-platform" }
+ let(:platform_version) { "example-platform-1.0" }
+
+ let(:ohai_data) do
+ {
+ fqdn: fqdn,
+ hostname: hostname,
+ machinename: machinename,
+ platform: platform,
+ platform_version: platform_version,
+ }
+ end
+
+ let(:ohai_system) do
+ ohai = instance_double("Ohai::System", all_plugins: true, data: ohai_data, logger: logger)
+ allow(ohai).to receive(:[]) do |k|
+ ohai_data[k]
+ end
+ ohai
+ end
+
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.name(fqdn)
+ n.chef_environment("_default")
+ end
+ end
+
+ let(:json_attribs) { nil }
+ let(:client_opts) { {} }
+
+ let(:stdout) { STDOUT }
+ let(:stderr) { STDERR }
+
+ let(:client) do
+ Chef::Config[:event_loggers] = []
+ 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
+
+ let(:logger) { instance_double("Mixlib::Log::Child", trace: nil, debug: nil, warn: nil, info: nil, error: nil, fatal: nil) }
+
+ 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
+
+# Stubs a client for a client run.
+# Requires a client object be defined in the scope of this included context.
+# e.g.:
+# describe "some functionality" do
+# include_context "client"
+# include_context "a client run"
+# ...
+# end
+shared_context "a client run" do
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+
+ 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(:reporting_rest_client) { double("Chef::ServerAPI (reporting client)") }
+
+ let(:runner) { instance_double("Chef::Runner") }
+
+ def stub_for_register
+ # --Client.register
+ # Make sure Client#register thinks the client key doesn't
+ # exist, so it tries to register and create one.
+ allow(File).to receive(:exists?).and_call_original
+ expect(File).to receive(:exists?)
+ .with(Chef::Config[:client_key])
+ .exactly(:once)
+ .and_return(api_client_exists?)
+
+ unless api_client_exists?
+ # Client.register will register with the validation client name.
+ expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run)
+ end
+ end
+
+ def stub_for_event_registration
+ expect(client.events).to receive(:register).with(instance_of(Chef::DataCollector::Reporter))
+ expect(client.events).to receive(:register).with(instance_of(Chef::ResourceReporter))
+ expect(client.events).to receive(:register).with(instance_of(Chef::ActionCollection))
+ expect(client.events).to receive(:register).with(instance_of(Chef::Compliance::Runner))
+ 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
+ # previous step.
+ expect(Chef::ServerAPI).to receive(:new)
+ .with(Chef::Config[:chef_server_url], client_name: fqdn,
+ signing_key_filename: Chef::Config[:client_key])
+ .exactly(:once)
+ .and_return(http_node_load)
+
+ # --Client#build_node
+ # looks up the node, which we will return, then later saves it.
+ expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node)
+ end
+
+ def stub_rest_clean
+ allow(client).to receive(:rest_clean).and_return(reporting_rest_client)
+ end
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---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], 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::HTTPClientException.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
+
+ def stub_for_node_save
+ # define me
+ end
+
+ def stub_for_run
+ # define me
+ end
+
+ before do
+ Chef::Config[:client_fork] = enable_fork
+ Chef::Config[:cache_path] = windows? ? 'C:\chef' : "/var/chef"
+ Chef::Config[:why_run] = false
+ Chef::Config[:chef_guid] = "default-guid"
+
+ stub_rest_clean
+ stub_for_register
+ stub_for_event_registration
+ stub_for_node_load
+ stub_for_sync_cookbooks
+ stub_for_required_recipe
+ stub_for_converge
+ stub_for_node_save
+
+ expect_any_instance_of(Chef::RunLock).to receive(:acquire)
+ expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
+ expect_any_instance_of(Chef::RunLock).to receive(:release)
+
+ # Post conditions: check that node has been filled in correctly
+ expect(client).to receive(:run_started)
+
+ stub_for_run
+ end
+end
+
+shared_context "converge completed" do
+ def stub_for_converge
+ # --Client#converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_return(true)
+ end
+
+ def stub_for_node_save
+ allow(node).to receive(:data_for_save).and_return(node.for_json)
+
+ # --Client#save_updated_node
+ expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], client_name: fqdn,
+ signing_key_filename: Chef::Config[:client_key], validate_utf8: false).and_return(http_node_save)
+ expect(http_node_save).to receive(:put).with("nodes/#{fqdn}", node.for_json).and_return(true)
+ end
+end
+
+shared_context "converge failed" do
+ let(:converge_error) do
+ err = Chef::Exceptions::UnsupportedAction.new("Action unsupported")
+ err.set_backtrace([ "/path/recipe.rb:15", "/path/recipe.rb:12" ])
+ err
+ end
+
+ def stub_for_converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_raise(converge_error)
+ end
+
+ def stub_for_node_save
+ expect(client).to_not receive(:save_updated_node)
+ end
+end
+
+shared_context "run completed" do
+ def stub_for_run
+ expect(client).to receive(:run_completed_successfully)
+ end
+end
+
+shared_context "run failed" do
+ def stub_for_run
+ expect(client).to receive(:run_failed)
+ end
+
+ before do
+ expect(Chef::Application).to receive(:debug_stacktrace).with(converge_error)
+ end
+end
+
+#
+# SHARED EXAMPLES
+#
+
+# requires platform and platform_version be defined
+shared_examples "a completed run" do
+ include_context "run completed"
+
+ it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges" do
+ # This is what we're testing.
+ expect(client.run).to be true
+
+ # fork is stubbed, so we can see the outcome of the run
+ expect(node.automatic_attrs[:platform]).to eq(platform)
+ expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
+ end
+end
+
+shared_examples "a failed run" do
+ include_context "run failed"
+
+ it "skips node save and raises the error in a wrapping error" do
+ expect { client.run }.to raise_error(converge_error)
+ end
+end
+
describe Chef::Client do
include_context "client"
@@ -38,12 +291,39 @@ 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 init_package}
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(Ohai::Exceptions::CriticalPluginFailure)
+ end
+ end
+
+ describe "eol release warning" do
+ it "warns when running an EOL release" do
+ stub_const("Chef::VERSION", 15)
+ allow(Time).to receive(:now).and_return(Time.new(2021, 5, 1, 5))
+ expect(logger).to receive(:warn).with(/This release of.*became end of life \(EOL\) on May 1st 2021/)
+ client.warn_if_eol
+ end
+
+ it "does not warn when running an non-EOL release" do
+ stub_const("Chef::VERSION", 15)
+ allow(Time).to receive(:now).and_return(Time.new(2021, 4, 31))
+ expect(logger).to_not receive(:warn).with(/became end of life/)
+ client.warn_if_eol
+ end
+ end
+
describe "authentication protocol selection" do
context "when FIPS is disabled" do
before do
@@ -71,48 +351,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, nil]])
+ 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, nil]])
+ 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, nil]])
end
end
-
end
context "when a formatter is configured" do
@@ -159,7 +431,6 @@ describe Chef::Client do
shared_examples_for "a successful client run" do
include_context "a client run"
include_context "converge completed"
- include_context "audit phase completed"
include_examples "a completed run"
end
@@ -176,22 +447,22 @@ describe Chef::Client do
context "when an override run list is given" do
it "permits spaces in overriding run list" do
- Chef::Client.new(nil, :override_runlist => "role[a], role[b]")
+ Chef::Client.new(nil, override_runlist: "role[a], role[b]")
end
describe "calling run" do
include_examples "a successful client run" do
- let(:client_opts) { { :override_runlist => "recipe[override_recipe]" } }
+ let(:client_opts) { { override_runlist: "recipe[override_recipe]" } }
def stub_for_sync_cookbooks
# --Client#setup_run_context
# ---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(http_cookbook_sync).to receive(:post).
- with("environments/_default/cookbook_versions", { :run_list => ["override_recipe"] }).
- and_return({})
+ 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({})
end
def stub_for_node_save
@@ -215,17 +486,17 @@ describe Chef::Client do
include_examples "a successful client run" do
let(:new_runlist) { "recipe[new_run_list_recipe]" }
- let(:client_opts) { { :runlist => new_runlist } }
+ let(:client_opts) { { runlist: new_runlist } }
def stub_for_sync_cookbooks
# --Client#setup_run_context
# ---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(http_cookbook_sync).to receive(:post).
- with("environments/_default/cookbook_versions", { :run_list => ["new_run_list_recipe"] }).
- and_return({})
+ 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({})
end
before do
@@ -239,58 +510,20 @@ describe Chef::Client do
describe "when converge completes successfully" do
include_context "a client run"
include_context "converge completed"
- context "when audit mode is enabled" do
- describe "when audit phase errors" do
- include_context "audit phase failed with error"
- include_examples "a completed run with audit failure" do
- let(:run_errors) { [audit_error] }
- end
- end
-
- describe "when audit phase completed" do
- include_context "audit phase completed"
- include_examples "a completed run"
- end
-
- describe "when audit phase completed with failed controls" do
- include_context "audit phase completed with failed controls"
- include_examples "a completed run with audit failure" do
- let(:run_errors) { [audit_error] }
- end
- end
- end
end
describe "when converge errors" do
include_context "a client run"
include_context "converge failed"
-
- describe "when audit phase errors" do
- include_context "audit phase failed with error"
- include_examples "a failed run" do
- let(:run_errors) { [converge_error, audit_error] }
- end
- end
-
- describe "when audit phase completed" do
- include_context "audit phase completed"
- include_examples "a failed run" do
- let(:run_errors) { [converge_error] }
- end
- end
-
- describe "when audit phase completed with failed controls" do
- include_context "audit phase completed with failed controls"
- include_examples "a failed run" do
- let(:run_errors) { [converge_error, audit_error] }
- end
+ include_examples "a failed run" do
+ let(:run_errors) { [converge_error] }
end
end
end
describe "when handling run failures" do
it "should remove the run_lock on failure of #load_node" do
- @run_lock = double("Chef::RunLock", :acquire => true)
+ @run_lock = double("Chef::RunLock", acquire: true)
allow(Chef::RunLock).to receive(:new).and_return(@run_lock)
@events = double("Chef::EventDispatch::Dispatcher").as_null_object
@@ -402,10 +635,59 @@ 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::HTTPClientException.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
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
end
it "shouldn't be called" do
@@ -416,7 +698,7 @@ describe Chef::Client do
context "platform is windows" do
before do
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
it "should be called" do
@@ -430,13 +712,13 @@ 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
context "fatal admin check is configured" do
it "should not raise an exception" do
- client.do_windows_admin_check #should not raise
+ client.do_windows_admin_check # should not raise
end
end
end
@@ -447,7 +729,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
@@ -468,9 +750,12 @@ describe Chef::Client do
context "when any directory of cookbook_path contains no cookbook" do
it "raises CookbookNotFound error" do
+ invalid_cookbook_path = windows? ? "C:/path/to/invalid/cookbook_path" : "/path/to/invalid/cookbook_path"
+ msg = "None of the cookbook paths set in Chef::Config[:cookbook_path], [\"#{invalid_cookbook_path}\"], contain any cookbooks"
+
expect do
client.send(:assert_cookbook_path_not_empty, nil)
- end.to raise_error(Chef::Exceptions::CookbookNotFound, 'None of the cookbook paths set in Chef::Config[:cookbook_path], ["/path/to/invalid/cookbook_path"], contain any cookbooks')
+ end.to raise_error(Chef::Exceptions::CookbookNotFound, msg)
end
end
end
@@ -508,7 +793,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
@@ -517,28 +801,21 @@ describe Chef::Client do
# fail on the first thing in begin block
allow_any_instance_of(Chef::RunLock).to receive(:save_pid).and_raise(NoMethodError)
end
+ end
- context "when audit mode is enabled" do
- before do
- Chef::Config[:audit_mode] = :enabled
- end
- it "should run exception handlers on early fail" do
- expect(subject).to receive(:run_failed)
- expect { subject.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq 1
- expect(error.wrapped_errors).to include(NoMethodError)
- end
- end
+ describe "deprecated enforce_path_sanity" do
+ include_context "a client run"
+ include_context "converge completed"
+
+ it "print a warning, when enforce_path_sanity is passed" do
+ Chef::Config[:enforce_path_sanity] = true
+ expect(logger).to receive(:warn).with("`enforce_path_sanity` is deprecated, please use `enforce_default_paths` instead!")
+ client.run
end
- context "when audit mode is disabled" do
- before do
- Chef::Config[:audit_mode] = :disabled
- end
- it "should run exception handlers on early fail" do
- expect(subject).to receive(:run_failed)
- expect { subject.run }.to raise_error(NoMethodError)
- end
+ it "should not print a warning, when enforce_path_sanity is not passed" do
+ expect(logger).not_to receive(:warn).with("`enforce_path_sanity` is deprecated, please use `enforce_default_paths` instead!")
+ client.run
end
end
end
diff --git a/spec/unit/compliance/fetcher/automate_spec.rb b/spec/unit/compliance/fetcher/automate_spec.rb
new file mode 100644
index 0000000000..bc2125aaa7
--- /dev/null
+++ b/spec/unit/compliance/fetcher/automate_spec.rb
@@ -0,0 +1,134 @@
+require "spec_helper"
+require "chef/compliance/fetcher/automate"
+
+describe Chef::Compliance::Fetcher::Automate do
+ describe ".resolve" do
+ before do
+ Chef::Config[:data_collector] = {
+ server_url: "https://automate.test/data_collector",
+ token: token,
+ }
+ end
+
+ let(:token) { "fake_token" }
+
+ context "when target is a string" do
+ it "should resolve a compliance URL" do
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "raises an exception with no data collector token" do
+ Chef::Config[:data_collector].delete(:token)
+
+ expect {
+ Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
+ }.to raise_error(/No data-collector token set/)
+ end
+
+ it "includes the data collector token" do
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
+ "https://automate.test/compliance/profiles/namespace/profile_name/tar",
+ hash_including("token" => token)
+ ).and_call_original
+
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "returns nil with a non-compliance URL" do
+ res = Chef::Compliance::Fetcher::Automate.resolve("http://github.com/chef-cookbooks/audit")
+
+ expect(res).to eq(nil)
+ end
+ end
+
+ context "when target is a hash" do
+ it "should resolve a target with a version" do
+ res = Chef::Compliance::Fetcher::Automate.resolve(
+ compliance: "namespace/profile_name",
+ version: "1.2.3"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/version/1.2.3/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "should resolve a target without a version" do
+ res = Chef::Compliance::Fetcher::Automate.resolve(
+ compliance: "namespace/profile_name"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "uses url key when present" do
+ res = Chef::Compliance::Fetcher::Automate.resolve(
+ compliance: "namespace/profile_name",
+ version: "1.2.3",
+ url: "https://profile.server.test/profiles/profile_name/1.2.3"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://profile.server.test/profiles/profile_name/1.2.3"
+ expect(res.target).to eq(expected)
+ end
+
+ it "does not include token in the config when url key is present" do
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
+ "https://profile.server.test/profiles/profile_name/1.2.3",
+ hash_including("token" => nil)
+ ).and_call_original
+
+ res = Chef::Compliance::Fetcher::Automate.resolve(
+ compliance: "namespace/profile_name",
+ version: "1.2.3",
+ url: "https://profile.server.test/profiles/profile_name/1.2.3"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://profile.server.test/profiles/profile_name/1.2.3"
+ expect(res.target).to eq(expected)
+ end
+
+ it "raises an exception with no data collector token" do
+ Chef::Config[:data_collector].delete(:token)
+
+ expect {
+ Chef::Compliance::Fetcher::Automate.resolve(compliance: "namespace/profile_name")
+ }.to raise_error(Inspec::FetcherFailure, /No data-collector token set/)
+ end
+
+ it "includes the data collector token" do
+ expect(Chef::Compliance::Fetcher::Automate).to receive(:new).with(
+ "https://automate.test/compliance/profiles/namespace/profile_name/tar",
+ hash_including("token" => token)
+ ).and_call_original
+
+ res = Chef::Compliance::Fetcher::Automate.resolve(compliance: "namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
+ expected = "https://automate.test/compliance/profiles/namespace/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "returns nil with a non-profile Hash" do
+ res = Chef::Compliance::Fetcher::Automate.resolve(
+ profile: "namespace/profile_name",
+ version: "1.2.3"
+ )
+
+ expect(res).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/unit/compliance/fetcher/chef_server_spec.rb b/spec/unit/compliance/fetcher/chef_server_spec.rb
new file mode 100644
index 0000000000..fc1c229989
--- /dev/null
+++ b/spec/unit/compliance/fetcher/chef_server_spec.rb
@@ -0,0 +1,93 @@
+require "spec_helper"
+require "chef/compliance/fetcher/chef_server"
+
+describe Chef::Compliance::Fetcher::ChefServer do
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.default["audit"] = {}
+ end
+ end
+
+ before :each do
+ allow(Chef).to receive(:node).and_return(node)
+
+ Chef::Config[:chef_server_url] = "http://127.0.0.1:8889/organizations/my_org"
+ end
+
+ describe ".resolve" do
+ context "when target is a string" do
+ it "should resolve a compliance URL" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "should add /compliance URL prefix if needed" do
+ node.default["audit"]["fetcher"] = "chef-server"
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/compliance/organizations/my_org/owners/namespace/compliance/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "includes user in the URL if present" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("compliance://username@namespace/profile_name")
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/username@namespace/compliance/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "returns nil with a non-compliance URL" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve("http://github.com/chef-cookbooks/audit")
+
+ expect(res).to eq(nil)
+ end
+ end
+
+ context "when target is a hash" do
+ it "should resolve a target with a version" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
+ compliance: "namespace/profile_name",
+ version: "1.2.3"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/version/1.2.3/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "should resolve a target without a version" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
+ compliance: "namespace/profile_name"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/namespace/compliance/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "includes user in the URL if present" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
+ compliance: "username@namespace/profile_name"
+ )
+
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::ChefServer)
+ expected = "http://127.0.0.1:8889/organizations/my_org/owners/username@namespace/compliance/profile_name/tar"
+ expect(res.target).to eq(expected)
+ end
+
+ it "returns nil with a non-profile Hash" do
+ res = Chef::Compliance::Fetcher::ChefServer.resolve(
+ profile: "namespace/profile_name",
+ version: "1.2.3"
+ )
+
+ expect(res).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/unit/compliance/reporter/automate_spec.rb b/spec/unit/compliance/reporter/automate_spec.rb
new file mode 100644
index 0000000000..e0a33892b0
--- /dev/null
+++ b/spec/unit/compliance/reporter/automate_spec.rb
@@ -0,0 +1,427 @@
+require "spec_helper"
+require "json" # For .to_json
+
+describe Chef::Compliance::Reporter::Automate do
+ let(:reporter) { Chef::Compliance::Reporter::Automate.new(opts) }
+
+ let(:opts) do
+ {
+ entity_uuid: "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
+ run_id: "3f0536f7-3361-4bca-ae53-b45118dceb5d",
+ node_info: {
+ node: "chef-client.solo",
+ environment: "My Prod Env",
+ roles: %w{base_linux apache_linux},
+ recipes: ["some_cookbook::some_recipe", "some_cookbook"],
+ policy_name: "test_policy_name",
+ policy_group: "test_policy_group",
+ chef_tags: ["mylinux", "my.tag", "some=tag"],
+ organization_name: "test_org",
+ source_fqdn: "api.chef.io",
+ ipaddress: "192.168.56.33",
+ fqdn: "lb1.prod.example.com",
+ },
+ run_time_limit: 1.1,
+ control_results_limit: 2,
+ timestamp: Time.parse("2016-07-19T19:19:19+01:00"),
+ }
+ end
+
+ let(:inspec_report) do
+ {
+ "version": "1.2.1",
+ "profiles":
+ [{ "name": "tmp_compliance_profile",
+ "title": "/tmp Compliance Profile",
+ "summary": "An Example Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ "version": "0.1.1",
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
+ "license": "Apache 2.0 License",
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
+ "supports": [],
+ "controls":
+ [{ "title": "A /tmp directory must exist",
+ "desc": "A /tmp directory must exist",
+ "impact": 0.3,
+ "refs": [],
+ "tags": {},
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
+ "id": "tmp-1.0",
+ "results": [
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ },
+ { "title": "/tmp directory is owned by the root user",
+ "desc": "The /tmp directory must be owned by the root user",
+ "impact": 0.3,
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
+ "id": "tmp-1.1",
+ "results": [
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
+ ],
+ },
+ ],
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ }
+ end
+
+ describe "#send_report" do
+ before :each do
+ WebMock.disable_net_connect!
+
+ Chef::Config[:data_collector] = { token: token, server_url: "https://automate.test/data_collector" }
+ end
+
+ let(:token) { "fake_token" }
+
+ it "sends report successfully to ChefAutomate with missing profiles" do
+ metasearch_stub = stub_request(:post, "https://automate.test/compliance/profiles/metasearch")
+ .with(
+ body: '{"sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}',
+ headers: {
+ "Accept-Encoding" => "identity",
+ "X-Chef-Version" => Chef::VERSION,
+ "X-Data-Collector-Auth" => "version=1.0",
+ "X-Data-Collector-Token" => token,
+ }
+ ).to_return(
+ status: 200,
+ body: '{"missing_sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}'
+ )
+
+ report_stub = stub_request(:post, "https://automate.test/data_collector")
+ .with(
+ body: {
+ "version": "1.2.1",
+ "profiles": [
+ {
+ "name": "tmp_compliance_profile",
+ "title": "/tmp Compliance Profile",
+ "summary": "An Example Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ "version": "0.1.1",
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
+ "license": "Apache 2.0 License",
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
+ "supports": [],
+ "controls": [
+ {
+ "title": "A /tmp directory must exist",
+ "desc": "A /tmp directory must exist",
+ "impact": 0.3,
+ "refs": [],
+ "tags": {},
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
+ "id": "tmp-1.0",
+ "results": [
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ },
+ {
+ "title": "/tmp directory is owned by the root user",
+ "desc": "The /tmp directory must be owned by the root user",
+ "impact": 0.3,
+ "refs": [
+ { "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" },
+ ],
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
+ "id": "tmp-1.1",
+ "results": [
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
+ },
+ ],
+ "groups": [
+ { "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" },
+ ],
+ "attributes": [
+ { "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } },
+ ],
+ },
+ ],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ "type": "inspec_report",
+ "node_name": "chef-client.solo",
+ "end_time": "2016-07-19T18:19:19Z",
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
+ "environment": "My Prod Env",
+ "roles": %w{base_linux apache_linux},
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
+ "source_fqdn": "api.chef.io",
+ "organization_name": "test_org",
+ "policy_group": "test_policy_group",
+ "policy_name": "test_policy_name",
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
+ "ipaddress": "192.168.56.33",
+ "fqdn": "lb1.prod.example.com",
+ "run_time_limit": 1.1,
+ },
+ headers: {
+ "Accept-Encoding" => "identity",
+ "X-Chef-Version" => Chef::VERSION,
+ "X-Data-Collector-Auth" => "version=1.0",
+ "X-Data-Collector-Token" => token,
+ }
+ ).to_return(status: 200)
+
+ expect(reporter.send_report(inspec_report)).to eq(true)
+
+ expect(metasearch_stub).to have_been_requested
+ expect(report_stub).to have_been_requested
+ end
+
+ it "sends report successfully to ChefAutomate with seen profiles" do
+ metasearch_stub = stub_request(:post, "https://automate.test/compliance/profiles/metasearch")
+ .with(
+ body: '{"sha256": ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"]}',
+ headers: {
+ "Accept-Encoding" => "identity",
+ "X-Chef-Version" => Chef::VERSION,
+ "X-Data-Collector-Auth" => "version=1.0",
+ "X-Data-Collector-Token" => token,
+ }
+ ).to_return(
+ status: 200,
+ body: '{"missing_sha256": []}'
+ )
+
+ report_stub = stub_request(:post, "https://automate.test/data_collector")
+ .with(
+ body: {
+ "version": "1.2.1",
+ "profiles": [
+ {
+ "title": "/tmp Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ "version": "0.1.1",
+ "controls": [
+ {
+ "id": "tmp-1.0",
+ "results": [
+ { "status": "passed", "code_desc": "File /tmp should be directory" },
+ ],
+ },
+ {
+ "id": "tmp-1.1",
+ "results": [
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
+ },
+ ],
+ "attributes": [
+ { "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } },
+ ],
+ },
+ ],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ "type": "inspec_report",
+ "node_name": "chef-client.solo",
+ "end_time": "2016-07-19T18:19:19Z",
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
+ "environment": "My Prod Env",
+ "roles": %w{base_linux apache_linux},
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
+ "source_fqdn": "api.chef.io",
+ "organization_name": "test_org",
+ "policy_group": "test_policy_group",
+ "policy_name": "test_policy_name",
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
+ "ipaddress": "192.168.56.33",
+ "fqdn": "lb1.prod.example.com",
+ "run_time_limit": 1.1,
+ },
+ headers: {
+ "Accept-Encoding" => "identity",
+ "X-Chef-Version" => Chef::VERSION,
+ "X-Data-Collector-Auth" => "version=1.0",
+ "X-Data-Collector-Token" => token,
+ }
+ ).to_return(status: 200)
+
+ expect(reporter.send_report(inspec_report)).to eq(true)
+
+ expect(metasearch_stub).to have_been_requested
+ expect(report_stub).to have_been_requested
+ end
+
+ it "does not send report when entity_uuid is missing" do
+ opts.delete(:entity_uuid)
+ reporter = Chef::Compliance::Reporter::Automate.new(opts)
+ expect(reporter.send_report(inspec_report)).to eq(false)
+ end
+ end
+
+ describe "#truncate_controls_results" do
+ let(:report) do
+ {
+ "version": "1.2.1",
+ "profiles":
+ [{ "name": "tmp_compliance_profile",
+ "title": "/tmp Compliance Profile",
+ "summary": "An Example Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215ff",
+ "version": "0.1.1",
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
+ "license": "Apache 2.0 License",
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
+ "supports": [],
+ "controls":
+ [{ "id": "tmp-2.0",
+ "title": "A bunch of directories must exist",
+ "desc": "A bunch of directories must exist for testing",
+ "impact": 0.3,
+ "refs": [],
+ "tags": {},
+ "code": "control 'tmp-2.0' do\n impact 0.3\n title 'A bunch of directories must exist'\n desc 'A bunch of directories must exist for testing'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
+ "results": [
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "passed", "code_desc": "File /etc should be directory", "run_time": 0.002314, "start_time": "2016-10-19 11:09:45 -0400" },
+ { "status": "passed", "code_desc": "File /opt should be directory", "run_time": 0.002315, "start_time": "2016-10-19 11:09:46 -0400" },
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002316, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002317, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
+ { "status": "skipped", "code_desc": "No-op", "run_time": 0.002318, "start_time": "2016-10-19 11:09:44 -0400", "skip_message": "4 testing" },
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
+ { "status": "failed", "code_desc": "File /etc/passwd should be directory", "run_time": 0.002313, "start_time": "2016-10-19 11:09:44 -0400" },
+ ],
+ },
+ { "id": "tmp-2.1",
+ "title": "/tmp directory is owned by the root user",
+ "desc": "The /tmp directory must be owned by the root user",
+ "impact": 0.3,
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
+ "code": "control 'tmp-2.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
+ "results": [
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "passed", "code_desc": 'File /etc should be owned by "root"', "run_time": 1.238845, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ },
+ ],
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ }
+ end
+
+ it "truncates controls results 1" do
+ truncated_report = reporter.truncate_controls_results(report, 5)
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(5)
+ statuses = truncated_report[:profiles][0][:controls][0][:results].map { |r| r[:status] }
+ expect(statuses).to eq(%w{failed failed failed skipped skipped})
+ expect(truncated_report[:profiles][0][:controls][0][:removed_results_counts]).to eq(failed: 0, skipped: 1, passed: 3)
+ end
+
+ it "truncates controls results 2" do
+ truncated_report = reporter.truncate_controls_results(report, 5)
+ expect(truncated_report[:profiles][0][:controls][1][:results].length).to eq(2)
+ statuses = truncated_report[:profiles][0][:controls][1][:results].map { |r| r[:status] }
+ expect(statuses).to eq(%w{passed passed})
+ expect(truncated_report[:profiles][0][:controls][1][:removed_results_counts]).to eq(nil)
+ end
+
+ it "truncates controls results 3" do
+ truncated_report = reporter.truncate_controls_results(report, 0)
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(9)
+ end
+
+ it "truncates controls results 4" do
+ truncated_report = reporter.truncate_controls_results(report, 1)
+ expect(truncated_report[:profiles][0][:controls][0][:results].length).to eq(1)
+ end
+ end
+
+ describe "#strip_profiles_meta" do
+ it "removes the metadata from seen profiles" do
+ expected = {
+ other_checks: [],
+ profiles: [
+ {
+ attributes: [
+ {
+ name: "syslog_pkg",
+ options: {
+ default: "rsyslog",
+ description: "syslog package...",
+ },
+ },
+ ],
+ controls: [
+ {
+ id: "tmp-1.0",
+ results: [
+ {
+ code_desc: "File /tmp should be directory",
+ status: "passed",
+ },
+ ],
+ },
+ {
+ id: "tmp-1.1",
+ results: [
+ {
+ code_desc: 'File /tmp should be owned by "root"',
+ run_time: 1.228845,
+ start_time: "2016-10-19 11:09:43 -0400",
+ status: "passed",
+ },
+ {
+ code_desc: 'File /tmp should be owned by "root"',
+ run_time: 1.228845,
+ start_time: "2016-10-19 11:09:43 -0400",
+ status: "skipped",
+ },
+ {
+ code_desc: "File /etc/hosts is expected to be directory",
+ message: "expected `File /etc/hosts.directory?` to return true, got false",
+ run_time: 1.228845,
+ start_time: "2016-10-19 11:09:43 -0400",
+ status: "failed",
+ },
+ ],
+ },
+ ],
+ sha256: "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ title: "/tmp Compliance Profile",
+ version: "0.1.1",
+ },
+ ],
+ run_time_limit: 1.1,
+ statistics: {
+ duration: 0.032332,
+ },
+ version: "1.2.1",
+ }
+ expect(reporter.strip_profiles_meta(inspec_report, [], 1.1)).to eq(expected)
+ end
+
+ it "does not remove the metadata from missing profiles" do
+ expected = inspec_report.merge(run_time_limit: 1.1)
+ expect(reporter.strip_profiles_meta(inspec_report, ["7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd"], 1.1)).to eq(expected)
+ end
+ end
+end
diff --git a/spec/unit/compliance/reporter/chef_server_automate_spec.rb b/spec/unit/compliance/reporter/chef_server_automate_spec.rb
new file mode 100644
index 0000000000..e45a7157ee
--- /dev/null
+++ b/spec/unit/compliance/reporter/chef_server_automate_spec.rb
@@ -0,0 +1,177 @@
+require "spec_helper"
+
+describe Chef::Compliance::Reporter::ChefServerAutomate do
+ before do
+ WebMock.disable_net_connect!
+
+ Chef::Config[:client_key] = File.expand_path("../../../data/ssl/private_key.pem", __dir__)
+ Chef::Config[:node_name] = "spec-node"
+ end
+
+ let(:reporter) { Chef::Compliance::Reporter::ChefServerAutomate.new(opts) }
+
+ let(:opts) do
+ {
+ entity_uuid: "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
+ run_id: "3f0536f7-3361-4bca-ae53-b45118dceb5d",
+ node_info: {
+ node: "chef-client.solo",
+ environment: "My Prod Env",
+ roles: %w{base_linux apache_linux},
+ recipes: ["some_cookbook::some_recipe", "some_cookbook"],
+ policy_name: "test_policy_name",
+ policy_group: "test_policy_group",
+ chef_tags: ["mylinux", "my.tag", "some=tag"],
+ organization_name: "test_org",
+ source_fqdn: "api.chef.io",
+ ipaddress: "192.168.56.33",
+ fqdn: "lb1.prod.example.com",
+ },
+ url: "https://chef.server/data_collector",
+ control_results_limit: 2,
+ timestamp: Time.parse("2016-07-19T19:19:19+01:00"),
+ }
+ end
+
+ let(:inspec_report) do
+ {
+ "version": "1.2.1",
+ "profiles":
+ [{ "name": "tmp_compliance_profile",
+ "title": "/tmp Compliance Profile",
+ "summary": "An Example Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ "version": "0.1.1",
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
+ "license": "Apache 2.0 License",
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
+ "supports": [],
+ "controls":
+ [{ "title": "A /tmp directory must exist",
+ "desc": "A /tmp directory must exist",
+ "impact": 0.3,
+ "refs": [],
+ "tags": {},
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
+ "id": "tmp-1.0",
+ "results": [
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ },
+ { "title": "/tmp directory is owned by the root user",
+ "desc": "The /tmp directory must be owned by the root user",
+ "impact": 0.3,
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
+ "id": "tmp-1.1",
+ "results": [
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
+ ],
+ },
+ ],
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ }
+ end
+
+ let(:enriched_report) do
+ {
+ "version": "1.2.1",
+ "profiles": [
+ {
+ "name": "tmp_compliance_profile",
+ "title": "/tmp Compliance Profile",
+ "summary": "An Example Compliance Profile",
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
+ "version": "0.1.1",
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
+ "license": "Apache 2.0 License",
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
+ "supports": [],
+ "controls": [
+ {
+ "title": "A /tmp directory must exist",
+ "desc": "A /tmp directory must exist",
+ "impact": 0.3,
+ "refs": [],
+ "tags": {},
+ "code":
+ "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
+ "id": "tmp-1.0",
+ "results": [{ "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" }],
+ },
+ {
+ "title": "/tmp directory is owned by the root user",
+ "desc": "The /tmp directory must be owned by the root user",
+ "impact": 0.3,
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
+ "id": "tmp-1.1",
+ "results": [
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
+ ],
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
+ },
+ ],
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }],
+ },
+ ],
+ "other_checks": [],
+ "statistics": { "duration": 0.032332 },
+ "type": "inspec_report",
+ "node_name": "chef-client.solo",
+ "end_time": "2016-07-19T18:19:19Z",
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
+ "environment": "My Prod Env",
+ "roles": %w{base_linux apache_linux},
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
+ "source_fqdn": "api.chef.io",
+ "organization_name": "test_org",
+ "policy_group": "test_policy_group",
+ "policy_name": "test_policy_name",
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
+ "ipaddress": "192.168.56.33",
+ "fqdn": "lb1.prod.example.com",
+ }
+ end
+
+ it "sends report successfully" do
+ # TODO: Had to change 'X-Ops-Server-Api-Version' from 1 to 2, is that correct?
+ report_stub = stub_request(:post, "https://chef.server/data_collector")
+ .with(
+ body: enriched_report,
+ headers: {
+ "X-Chef-Version" => Chef::VERSION,
+ "X-Ops-Authorization-1" => /.+/,
+ "X-Ops-Authorization-2" => /.+/,
+ "X-Ops-Authorization-3" => /.+/,
+ "X-Ops-Authorization-4" => /.+/,
+ "X-Ops-Authorization-5" => /.+/,
+ "X-Ops-Authorization-6" => /.+/,
+ "X-Ops-Content-Hash" => "yfck5nQDcRWta06u45Q+J463LYY=",
+ "X-Ops-Server-Api-Version" => "2",
+ "X-Ops-Sign" => "algorithm=sha1;version=1.1;",
+ "X-Ops-Timestamp" => /.+/,
+ "X-Ops-Userid" => "spec-node",
+ "X-Remote-Request-Id" => /.+/,
+ }
+ ).to_return(status: 200)
+
+ expect(reporter.send_report(inspec_report)).to eq(true)
+
+ expect(report_stub).to have_been_requested
+ end
+end
diff --git a/spec/unit/compliance/reporter/compliance_enforcer_spec.rb b/spec/unit/compliance/reporter/compliance_enforcer_spec.rb
new file mode 100644
index 0000000000..ae63cf0853
--- /dev/null
+++ b/spec/unit/compliance/reporter/compliance_enforcer_spec.rb
@@ -0,0 +1,48 @@
+require "spec_helper"
+
+describe Chef::Compliance::Reporter::AuditEnforcer do
+ let(:reporter) { Chef::Compliance::Reporter::AuditEnforcer.new }
+
+ it "does not raise error for a successful InSpec report" do
+ report = {
+ "profiles": [
+ {
+ "controls": [
+ { "id": "c1", "results": [{ "status": "passed" }] },
+ { "id": "c2", "results": [{ "status": "passed" }] },
+ ],
+ },
+ ],
+ }
+
+ expect(reporter.send_report(report)).to eq(true)
+ end
+
+ it "does not raise error for an InSpec report with no controls" do
+ report = { "profiles": [{ "name": "empty" }] }
+
+ expect(reporter.send_report(report)).to eq(true)
+ end
+
+ it "does not raise error for an InSpec report with controls but no results" do
+ report = { "profiles": [{ "controls": [{ "id": "empty" }] }] }
+ expect(reporter.send_report(report)).to eq(true)
+ end
+
+ it "raises an error for a failed InSpec report" do
+ report = {
+ "profiles": [
+ {
+ "controls": [
+ { "id": "c1", "results": [{ "status": "passed" }] },
+ { "id": "c2", "results": [{ "status": "failed" }] },
+ ],
+ },
+ ],
+ }
+
+ expect {
+ reporter.send_report(report)
+ }.to raise_error(Chef::Compliance::Reporter::AuditEnforcer::ControlFailure, "Audit c2 has failed. Aborting chef-client run.")
+ end
+end
diff --git a/spec/unit/compliance/runner_spec.rb b/spec/unit/compliance/runner_spec.rb
new file mode 100644
index 0000000000..d4d2ba563f
--- /dev/null
+++ b/spec/unit/compliance/runner_spec.rb
@@ -0,0 +1,168 @@
+require "spec_helper"
+
+describe Chef::Compliance::Runner do
+ let(:logger) { double(:logger).as_null_object }
+ let(:node) { Chef::Node.new(logger: logger) }
+
+ let(:runner) do
+ described_class.new.tap do |r|
+ r.node = node
+ r.run_id = "my_run_id"
+ r.recipes = []
+ end
+ end
+
+ describe "#enabled?" do
+ it "is true if the node attributes have audit profiles and the audit cookbook is not present" do
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
+ runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
+
+ expect(runner).to be_enabled
+ end
+
+ it "is false if the node attributes have audit profiles and the audit cookbook is present" do
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
+ runner.recipes = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
+
+ expect(runner).not_to be_enabled
+ end
+
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is not present" do
+ node.normal["audit"]["profiles"] = {}
+ runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
+
+ expect(runner).not_to be_enabled
+ end
+
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is present" do
+ node.normal["audit"]["profiles"] = {}
+ runner.recipes = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
+
+ expect(runner).not_to be_enabled
+ end
+
+ it "is false if the node attributes do not have audit attributes and the audit cookbook is not present" do
+ runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
+ expect(runner).not_to be_enabled
+ end
+ end
+
+ describe "#inspec_profiles" do
+ it "returns an empty list with no profiles defined" do
+ expect(runner.inspec_profiles).to eq([])
+ end
+
+ it "converts from the attribute format to the format Inspec expects" do
+ node.normal["audit"]["profiles"]["linux-baseline"] = {
+ 'compliance': "user/linux-baseline",
+ 'version': "2.1.0",
+ }
+
+ node.normal["audit"]["profiles"]["ssh"] = {
+ 'supermarket': "hardening/ssh-hardening",
+ }
+
+ expected = [
+ {
+ compliance: "user/linux-baseline",
+ name: "linux-baseline",
+ version: "2.1.0",
+ },
+ {
+ name: "ssh",
+ supermarket: "hardening/ssh-hardening",
+ },
+ ]
+
+ expect(runner.inspec_profiles).to eq(expected)
+ end
+
+ it "raises an error when the profiles are in the old audit-cookbook format" do
+ node.normal["audit"]["profiles"] = [
+ {
+ name: "Windows 2019 Baseline",
+ compliance: "admin/windows-2019-baseline",
+ },
+ ]
+
+ expect { runner.inspec_profiles }.to raise_error(/profiles specified in an unrecognized format, expected a hash of hashes./)
+ end
+ end
+
+ describe "#warn_for_deprecated_config_values!" do
+ it "logs a warning when deprecated config values are present" do
+ node.normal["audit"]["owner"] = "my_org"
+ node.normal["audit"]["inspec_version"] = "90210"
+
+ expect(logger).to receive(:warn).with(/config values 'inspec_version', 'owner' are not supported/)
+
+ runner.warn_for_deprecated_config_values!
+ end
+
+ it "does not log a warning with no deprecated config values" do
+ node.normal["audit"]["profiles"]["linux-baseline"] = {
+ 'compliance': "user/linux-baseline",
+ 'version': "2.1.0",
+ }
+
+ expect(logger).not_to receive(:warn)
+
+ runner.warn_for_deprecated_config_values!
+ end
+ end
+
+ describe "#reporter" do
+ context "chef-server-automate reporter" do
+ it "uses the correct URL when 'server' attribute is set" do
+ Chef::Config[:chef_server_url] = "https://chef_config_url.example.com/my_org"
+ node.normal["audit"]["server"] = "https://server_attribute_url.example.com/application/sub_application"
+
+ reporter = runner.reporter("chef-server-automate")
+
+ expect(reporter).to be_kind_of(Chef::Compliance::Reporter::ChefServerAutomate)
+ expect(reporter.url).to eq(URI("https://server_attribute_url.example.com/application/sub_application/organizations/my_org/data-collector"))
+ end
+
+ it "falls back to chef_server_url for URL when 'server' attribute is not set" do
+ Chef::Config[:chef_server_url] = "https://chef_config_url.example.com/my_org"
+
+ reporter = runner.reporter("chef-server-automate")
+
+ expect(reporter).to be_kind_of(Chef::Compliance::Reporter::ChefServerAutomate)
+ expect(reporter.url).to eq(URI("https://chef_config_url.example.com/organizations/my_org/data-collector"))
+ end
+ end
+
+ it "fails with unexpected reporter value" do
+ expect { runner.reporter("tacos") }.to raise_error(/'tacos' is not a supported reporter for Compliance Phase/)
+ end
+ end
+
+ describe "#inspec_opts" do
+ it "does not include chef_node in inputs by default" do
+ node.normal["audit"]["attributes"] = {
+ "tacos" => "lunch",
+ "nachos" => "dinner",
+ }
+
+ inputs = runner.inspec_opts[:inputs]
+
+ expect(inputs["tacos"]).to eq("lunch")
+ expect(inputs.key?("chef_node")).to eq(false)
+ end
+
+ it "includes chef_node in inputs with chef_node_attribute_enabled set" do
+ node.normal["audit"]["chef_node_attribute_enabled"] = true
+ node.normal["audit"]["attributes"] = {
+ "tacos" => "lunch",
+ "nachos" => "dinner",
+ }
+
+ inputs = runner.inspec_opts[:inputs]
+
+ expect(inputs["tacos"]).to eq("lunch")
+ expect(inputs["chef_node"]["audit"]["reporter"]).to eq("json-file")
+ expect(inputs["chef_node"]["chef_environment"]).to eq("_default")
+ end
+ end
+end
diff --git a/spec/unit/config_fetcher_spec.rb b/spec/unit/config_fetcher_spec.rb
index 6847ee5fd3..8dadd9e681 100644
--- a/spec/unit/config_fetcher_spec.rb
+++ b/spec/unit/config_fetcher_spec.rb
@@ -2,12 +2,12 @@ require "spec_helper"
require "chef/config_fetcher"
describe Chef::ConfigFetcher do
- let(:valid_json) { Chef::JSONCompat.to_json({ :a => "b" }) }
+ let(:valid_json) { Chef::JSONCompat.to_json({ a: "b" }) }
let(:invalid_json) { %q[{"syntax-error": "missing quote}] }
let(:http) { double("Chef::HTTP::Simple") }
let(:config_location_regex) { Regexp.escape(config_location) }
- let(:invalid_json_error_regex) { %r{Could not parse the provided JSON file \(#{config_location_regex}\)} }
+ let(:invalid_json_error_regex) { /Could not parse the provided JSON file \(#{config_location_regex}\)/ }
let(:fetcher) { Chef::ConfigFetcher.new(config_location) }
@@ -16,9 +16,9 @@ describe Chef::ConfigFetcher do
let(:config_content) { "# The client.rb content" }
it "reads the file from disk" do
- expect(::File).to receive(:read).
- with(config_location).
- and_return(config_content)
+ expect(::File).to receive(:read)
+ .with(config_location)
+ .and_return(config_content)
expect(fetcher.read_config).to eq(config_content)
end
@@ -42,9 +42,9 @@ describe Chef::ConfigFetcher do
let(:config_location) { "/etc/chef/first-boot.json" }
it "returns the parsed JSON" do
- expect(::File).to receive(:read).
- with(config_location).
- and_return(valid_json)
+ expect(::File).to receive(:read)
+ .with(config_location)
+ .and_return(valid_json)
expect(fetcher.fetch_json).to eq({ "a" => "b" })
end
@@ -53,12 +53,12 @@ describe Chef::ConfigFetcher do
it "reports the JSON error" do
- expect(::File).to receive(:read).
- with(config_location).
- and_return(invalid_json)
+ expect(::File).to receive(:read)
+ .with(config_location)
+ .and_return(invalid_json)
- expect(Chef::Application).to receive(:fatal!).
- with(invalid_json_error_regex, Chef::Exceptions::DeprecatedExitCode.new)
+ expect(Chef::Application).to receive(:fatal!)
+ .with(invalid_json_error_regex)
fetcher.fetch_json
end
end
@@ -78,14 +78,14 @@ describe Chef::ConfigFetcher do
describe "reading the file" do
before do
- expect(Chef::HTTP::Simple).to receive(:new).
- with(config_location).
- and_return(http)
+ expect(Chef::HTTP::Simple).to receive(:new)
+ .with(config_location)
+ .and_return(http)
end
it "reads the file over HTTP" do
- expect(http).to receive(:get).
- with("").and_return(config_content)
+ expect(http).to receive(:get)
+ .with("").and_return(config_content)
expect(fetcher.read_config).to eq(config_content)
end
@@ -93,18 +93,18 @@ describe Chef::ConfigFetcher do
let(:config_location) { "https://example.com/foo.json" }
it "fetches the file and parses it" do
- expect(http).to receive(:get).
- with("").and_return(valid_json)
+ expect(http).to receive(:get)
+ .with("").and_return(valid_json)
expect(fetcher.fetch_json).to eq({ "a" => "b" })
end
context "and the JSON is invalid" do
it "reports the JSON error" do
- expect(http).to receive(:get).
- with("").and_return(invalid_json)
+ expect(http).to receive(:get)
+ .with("").and_return(invalid_json)
- expect(Chef::Application).to receive(:fatal!).
- with(invalid_json_error_regex, Chef::Exceptions::DeprecatedExitCode.new)
+ expect(Chef::Application).to receive(:fatal!)
+ .with(invalid_json_error_regex)
fetcher.fetch_json
end
end
diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb
index 68cb589251..c36f66f18a 100644
--- a/spec/unit/config_spec.rb
+++ b/spec/unit/config_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe Chef::Config do
shared_examples_for "deprecated by ohai but not deprecated" do
it "does not emit a deprecation warning when set" do
- expect(Chef::Log).to_not receive(:warn).
- with(/Ohai::Config\[:#{option}\] is deprecated/)
+ expect(Chef::Log).to_not receive(:warn)
+ .with(/Ohai::Config\[:#{option}\] is deprecated/)
Chef::Config[option] = value
expect(Chef::Config[option]).to eq(value)
end
diff --git a/spec/unit/cookbook/chefignore_spec.rb b/spec/unit/cookbook/chefignore_spec.rb
index 95b9295f50..2d07d99106 100644
--- a/spec/unit/cookbook/chefignore_spec.rb
+++ b/spec/unit/cookbook/chefignore_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,32 +18,52 @@
require "spec_helper"
describe Chef::Cookbook::Chefignore do
- before do
- @chefignore = Chef::Cookbook::Chefignore.new(File.join(CHEF_SPEC_DATA, "cookbooks"))
- end
+ let(:chefignore) { described_class.new(File.join(CHEF_SPEC_DATA, "cookbooks")) }
it "loads the globs in the chefignore file" do
- expect(@chefignore.ignores).to match_array(%w{recipes/ignoreme.rb ignored})
+ expect(chefignore.ignores).to match_array(%w{recipes/ignoreme.rb ignored})
end
it "removes items from an array that match the ignores" do
file_list = %w{ recipes/ignoreme.rb recipes/dontignoreme.rb }
- expect(@chefignore.remove_ignores_from(file_list)).to eq(%w{recipes/dontignoreme.rb})
+ expect(chefignore.remove_ignores_from(file_list)).to eq(%w{recipes/dontignoreme.rb})
end
it "determines if a file is ignored" do
- expect(@chefignore.ignored?("ignored")).to be_truthy
- expect(@chefignore.ignored?("recipes/ignoreme.rb")).to be_truthy
- expect(@chefignore.ignored?("recipes/dontignoreme.rb")).to be_falsey
+ expect(chefignore.ignored?("ignored")).to be_truthy
+ expect(chefignore.ignored?("recipes/ignoreme.rb")).to be_truthy
+ expect(chefignore.ignored?("recipes/dontignoreme.rb")).to be_falsey
end
context "when using the single cookbook pattern" do
- before do
- @chefignore = Chef::Cookbook::Chefignore.new(File.join(CHEF_SPEC_DATA, "standalone_cookbook"))
+ let(:chefignore) { described_class.new(File.join(CHEF_SPEC_DATA, "cookbooks/starter")) }
+
+ it "loads the globs in the chefignore file" do
+ expect(chefignore.ignores).to match_array(%w{recipes/default.rb ignored})
+ end
+ end
+
+ context "when cookbook has it's own chefignore" do
+ let(:chefignore) { described_class.new(File.join(CHEF_SPEC_DATA, "cookbooks/starter")) }
+
+ it "loads the globs in the chefignore file" do
+ expect(chefignore.ignores).to match_array(%w{recipes/default.rb ignored})
end
+ end
+
+ context "when cookbook don't have own chefignore" do
+ let(:chefignore) { described_class.new(File.join(CHEF_SPEC_DATA, "cookbooks/apache2")) }
+
+ it "loads the globs in the chefignore file of cookbooks dir" do
+ expect(chefignore.ignores).to match_array(%w{recipes/ignoreme.rb ignored})
+ end
+ end
+
+ context "when using the single cookbook pattern" do
+ let(:chefignore) { described_class.new(File.join(CHEF_SPEC_DATA, "standalone_cookbook")) }
it "loads the globs in the chefignore file" do
- expect(@chefignore.ignores).to match_array(%w{recipes/ignoreme.rb ignored vendor/bundle/*})
+ expect(chefignore.ignores).to match_array(%w{recipes/ignoreme.rb ignored vendor/bundle/*})
end
end
end
diff --git a/spec/unit/cookbook/cookbook_version_loader_spec.rb b/spec/unit/cookbook/cookbook_version_loader_spec.rb
index 87d0f1e032..f18a2237fd 100644
--- a/spec/unit/cookbook/cookbook_version_loader_spec.rb
+++ b/spec/unit/cookbook/cookbook_version_loader_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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::Cookbook::CookbookVersionLoader do
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
end
describe "loading a cookbook" do
@@ -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") }
@@ -112,8 +124,9 @@ describe Chef::Cookbook::CookbookVersionLoader do
expect { cookbook_loader.load! }.to raise_error(Chef::Exceptions::CookbookNotFoundInRepo)
end
- it "skips the cookbook when called with #load" do
- expect { cookbook_loader.load }.to_not raise_error
+ it "gives deprecation warning called with #load" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ cookbook_loader.load
end
end
@@ -136,7 +149,8 @@ describe Chef::Cookbook::CookbookVersionLoader do
expect { cookbook_loader.load! }.to raise_error("THIS METADATA HAS A BUG")
end
- it "raises an error when called with #load" do
+ it "gives deprecation warning to us load! when called with #load and raises error" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect { cookbook_loader.load }.to raise_error("THIS METADATA HAS A BUG")
end
@@ -161,14 +175,15 @@ describe Chef::Cookbook::CookbookVersionLoader do
let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "incomplete-metadata-chef-repo/incomplete-metadata") }
let(:error_message) do
- "Cookbook loaded at path(s) [#{cookbook_path}] has invalid metadata: The `name' attribute is required in cookbook metadata"
+ "Cookbook loaded at path [#{cookbook_path}] has invalid metadata: The `name' attribute is required in cookbook metadata"
end
it "raises an error when loading with #load!" do
expect { cookbook_loader.load! }.to raise_error(Chef::Exceptions::MetadataNotValid, error_message)
end
- it "raises an error when called with #load" do
+ it "gives deprecation warning to use load! method when called with #load and raises error for invalid metadata" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect { cookbook_loader.load }.to raise_error(Chef::Exceptions::MetadataNotValid, error_message)
end
diff --git a/spec/unit/cookbook/file_vendor_spec.rb b/spec/unit/cookbook/file_vendor_spec.rb
index 139a5932f9..f020cb3683 100644
--- a/spec/unit/cookbook/file_vendor_spec.rb
+++ b/spec/unit/cookbook/file_vendor_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 contains 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..58843ac826
--- /dev/null
+++ b/spec/unit/cookbook/gem_installer_spec.rb
@@ -0,0 +1,114 @@
+require "spec_helper"
+require "bundler"
+
+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", { "git" => "https://github.com/nahi/httpclient" }]]
+ )
+ ),
+ test4: double(
+ :cookbook,
+ metadata: double(
+ :metadata,
+ gems: [["httpclient", { "path" => "./gems/httpclient" }]]
+ )
+ ),
+ }
+ 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("")
+
+ end
+
+ it "generates a valid Gemfile" do
+ expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
+ 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).to receive(:shell_out!).and_return(shell_out)
+ 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
+ expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
+ Chef::Config[:rubygems_url] = "https://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
+ expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
+ Chef::Config[:rubygems_url] = [ "https://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 "skip metadata installation when Chef::Config[:skip_gem_metadata_installation] is set to true" do
+ Chef::Config[:skip_gem_metadata_installation] = true
+ expect(gem_installer).to_not receive(:shell_out!)
+ expect(gem_installer.install).to be_nil
+ end
+
+ it "install metadata when Chef::Config[:skip_gem_metadata_installation] is not true" do
+ expect(gem_installer).to receive(:shell_out!).and_return(shell_out)
+ expect(Chef::Log).to receive(:info).and_return("")
+ expect(gem_installer.install).to be_nil
+ end
+
+ it "install from local cache when Chef::Config[:gem_installer_bundler_options] is set to local" do
+ Chef::Config[:gem_installer_bundler_options] = "--local"
+ expect(gem_installer).to receive(:shell_out!).with(["bundle", "install", "--local"], any_args).and_return(shell_out)
+ expect(Chef::Log).to receive(:info).and_return("")
+ expect(gem_installer.install).to be_nil
+ 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..ad54fc48e1
--- /dev/null
+++ b/spec/unit/cookbook/manifest_v0_spec.rb
@@ -0,0 +1,133 @@
+#
+# Copyright:: Copyright (c) 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 root_files/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..92535798ca
--- /dev/null
+++ b/spec/unit/cookbook/manifest_v2_spec.rb
@@ -0,0 +1,70 @@
+#
+# Copyright:: Copyright (c) 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 27666eb338..0dbee4bc21 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,12 +26,10 @@ describe Chef::Cookbook::Metadata do
describe "when comparing for equality" do
before do
- @fields = [ :name, :description, :long_description, :maintainer,
- :maintainer_email, :license, :platforms, :dependencies,
- :recommendations, :suggestions, :conflicting, :providing,
- :replacing, :attributes, :groupings, :recipes, :version,
- :source_url, :issues_url, :privacy, :ohai_versions, :chef_versions,
- :gems ]
+ @fields = %i{name description long_description maintainer
+ maintainer_email license platforms dependencies
+ providing recipes version source_url issues_url
+ privacy ohai_versions chef_versions gems eager_load_libraries}
end
it "does not depend on object identity for equality" do
@@ -99,11 +97,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 +112,6 @@ 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
@@ -153,6 +127,10 @@ describe Chef::Cookbook::Metadata do
it "is not private" do
expect(metadata.privacy).to eq(false)
end
+
+ it "has eager_load_libraries set to true" do
+ expect(metadata.eager_load_libraries).to eq(true)
+ end
end
describe "validation" do
@@ -196,17 +174,18 @@ describe Chef::Cookbook::Metadata do
describe "meta-data attributes" do
params = {
- :maintainer => "Adam Jacob",
- :maintainer_email => "adam@opscode.com",
- :license => "Apache v2.0",
- :description => "Foobar!",
- :long_description => "Much Longer\nSeriously",
- :version => "0.6.0",
- :source_url => "http://example.com",
- :issues_url => "http://example.com/issues",
- :privacy => true,
+ maintainer: "Adam Jacob",
+ maintainer_email: "adam@opscode.com",
+ license: "Apache v2.0",
+ description: "Foobar!",
+ long_description: "Much Longer\nSeriously",
+ version: "0.6.0",
+ source_url: "http://example.com",
+ issues_url: "http://example.com/issues",
+ privacy: true,
+ eager_load_libraries: false,
}
- 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)
@@ -233,14 +212,10 @@ describe Chef::Cookbook::Metadata do
describe "describing dependencies" 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" ],
+ depends: [ :dependencies, "foo::bar", "> 0.2" ],
+ provides: [ :providing, "foo::bar", "<= 0.2" ],
}
- 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
@@ -254,14 +229,10 @@ describe Chef::Cookbook::Metadata do
end
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" ],
+ depends: [ :dependencies, "foo::bar", ">0.2", "> 0.2" ],
+ provides: [ :providing, "foo::bar", "<=0.2", "<= 0.2" ],
}
- 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
@@ -277,12 +248,8 @@ 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" ],
+ depends: [ "foo::bar", "> 0.2", "< 1.0" ],
+ provides: [ "foo::bar", "> 0.2", "< 1.0" ],
}
dep_types.each do |dep, dep_args|
@@ -294,12 +261,8 @@ 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"],
+ depends: [ "foo::bar", ">> 0.2"],
+ provides: [ "foo::bar", ">> 0.2"],
}
dep_types.each do |dep, dep_args|
@@ -309,19 +272,35 @@ describe Chef::Cookbook::Metadata do
end
end
- it "strips out self-dependencies", chef: "< 13" do
+ it "errors on self-dependencies" do
metadata.name("foo")
- expect(Chef::Log).to receive(:warn).with(
- "Ignoring self-dependency in cookbook foo, please remove it (in the future this will be fatal)."
- )
- metadata.depends("foo")
- expect(metadata.dependencies).to eql({})
+ expect { metadata.depends("foo") }.to raise_error(RuntimeError, /Cookbook depends on itself/)
+ end
+ end
+
+ describe "eager_load_libraries" do
+ it "can be set to true" do
+ metadata.send(:eager_load_libraries, true)
+ expect(metadata.send(:eager_load_libraries)).to eql(true)
end
- it "errors on self-dependencies", chef: ">= 13" do
- metadata.name("foo")
- expect { metadata.depends("foo") }.to raise_error
- # FIXME: add the error type
+ it "can be set to false" do
+ metadata.send(:eager_load_libraries, false)
+ expect(metadata.send(:eager_load_libraries)).to eql(false)
+ end
+
+ it "can be set to a string" do
+ metadata.send(:eager_load_libraries, "default.rb")
+ expect(metadata.send(:eager_load_libraries)).to eql("default.rb")
+ end
+
+ it "can be set to an array" do
+ metadata.send(:eager_load_libraries, [ "default.rb", "foo/*/**.rb" ])
+ expect(metadata.send(:eager_load_libraries)).to eql([ "default.rb", "foo/*/**.rb" ])
+ end
+
+ it "cannot be set to a number" do
+ expect { metadata.send(:eager_load_libraries, 1) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
@@ -452,320 +431,13 @@ 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 do
- metadata.grouping("db/mysql/databases", :title => "foo")
- end.not_to raise_error
- expect do
- metadata.grouping("db/mysql/databases", :title => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but a string for the description" do
- expect do
- metadata.grouping("db/mysql/databases", :description => "foo")
- end.not_to raise_error
- expect do
- metadata.grouping("db/mysql/databases", :description => Hash.new)
- end.to raise_error(ArgumentError)
- end
- end
-
- describe "cookbook attributes" do
- it "should allow you set an attributes metadata" do
- attrs = {
- "display_name" => "MySQL Databases",
- "description" => "Description of MySQL",
- "choice" => %w{dedicated shared},
- "calculated" => false,
- "type" => "string",
- "required" => "recommended",
- "recipes" => [ "mysql::server", "mysql::master" ],
- "default" => [ ],
- "source_url" => "http://example.com",
- "issues_url" => "http://example.com/issues",
- "privacy" => true,
- }
- expect(metadata.attribute("/db/mysql/databases", attrs)).to eq(attrs)
- end
-
- it "should not accept anything but a string for display_name" do
- expect do
- metadata.attribute("db/mysql/databases", :display_name => "foo")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :display_name => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but a string for the description" do
- expect do
- metadata.attribute("db/mysql/databases", :description => "foo")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :description => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but a string for the source_url" do
- expect do
- metadata.attribute("db/mysql/databases", :source_url => "foo")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :source_url => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but a string for the issues_url" do
- expect do
- metadata.attribute("db/mysql/databases", :issues_url => "foo")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :issues_url => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but true or false for the privacy flag" do
- expect do
- metadata.attribute("db/mysql/databases", :privacy => true)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :privacy => false)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :privacy => "true")
- end.to raise_error(ArgumentError)
- end
-
- it "should not accept anything but an array of strings for choice" do
- expect do
- metadata.attribute("db/mysql/databases", :choice => %w{dedicated shared})
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :choice => [10, "shared"])
- end.to raise_error(ArgumentError)
- expect do
- metadata.attribute("db/mysql/databases", :choice => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should set choice to empty array by default" do
- metadata.attribute("db/mysql/databases", {})
- expect(metadata.attributes["db/mysql/databases"][:choice]).to eq([])
- end
-
- it "should let calculated be true or false" do
- expect do
- metadata.attribute("db/mysql/databases", :calculated => true)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :calculated => false)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :calculated => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should set calculated to false by default" do
- metadata.attribute("db/mysql/databases", {})
- expect(metadata.attributes["db/mysql/databases"][:calculated]).to eq(false)
- end
-
- it "accepts String for the attribute type" do
- expect do
- metadata.attribute("db/mysql/databases", :type => "string")
- end.not_to raise_error
- end
-
- it "accepts Array for the attribute type" do
- expect do
- metadata.attribute("db/mysql/databases", :type => "array")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :type => Array.new)
- end.to raise_error(ArgumentError)
- end
-
- it "accepts symbol for the attribute type" do
- expect do
- metadata.attribute("db/mysql/databases", :type => "symbol")
- end.not_to raise_error
- end
-
- it "should let type be hash (backwards compatibility only)" do
- expect do
- metadata.attribute("db/mysql/databases", :type => "hash")
- end.not_to raise_error
- end
-
- it "should let required be required, recommended or optional" do
- expect do
- metadata.attribute("db/mysql/databases", :required => "required")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :required => "recommended")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :required => "optional")
- end.not_to raise_error
- end
-
- it "should convert required true to required" do
- expect do
- metadata.attribute("db/mysql/databases", :required => true)
- end.not_to raise_error
- #attrib = metadata.attributes["db/mysql/databases"][:required].should == "required"
- end
-
- it "should convert required false to optional" do
- expect do
- metadata.attribute("db/mysql/databases", :required => false)
- end.not_to raise_error
- #attrib = metadata.attributes["db/mysql/databases"][:required].should == "optional"
- end
-
- it "should set required to 'optional' by default" do
- metadata.attribute("db/mysql/databases", {})
- expect(metadata.attributes["db/mysql/databases"][:required]).to eq("optional")
- end
-
- it "should make sure recipes is an array" do
- expect do
- metadata.attribute("db/mysql/databases", :recipes => [])
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :required => Hash.new)
- end.to raise_error(ArgumentError)
- end
-
- it "should set recipes to an empty array by default" do
- metadata.attribute("db/mysql/databases", {})
- expect(metadata.attributes["db/mysql/databases"][:recipes]).to eq([])
- end
-
- it "should allow the default value to be a string, array, hash, boolean or numeric" do
- expect do
- metadata.attribute("db/mysql/databases", :default => [])
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :default => {})
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :default => "alice in chains")
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :default => 1337)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :default => true)
- end.not_to raise_error
- expect do
- metadata.attribute("db/mysql/databases", :required => :not_gonna_do_it)
- end.to raise_error(ArgumentError)
- end
-
- it "should limit the types allowed in the choice array" do
- options = {
- :type => "string",
- :choice => %w{test1 test2},
- :default => "test1",
- }
- expect do
- metadata.attribute("test_cookbook/test", options)
- end.not_to raise_error
-
- options = {
- :type => "boolean",
- :choice => [ true, false ],
- :default => true,
- }
- expect do
- metadata.attribute("test_cookbook/test", options)
- end.not_to raise_error
-
- options = {
- :type => "numeric",
- :choice => [ 1337, 420 ],
- :default => 1337,
- }
- expect do
- metadata.attribute("test_cookbook/test", options)
- end.not_to raise_error
-
- options = {
- :type => "numeric",
- :choice => [ true, "false" ],
- :default => false,
- }
- expect do
- metadata.attribute("test_cookbook/test", options)
- end.to raise_error(Chef::Exceptions::ValidationFailed)
- end
-
- it "should error if default used with calculated" do
- expect do
- attrs = {
- :calculated => true,
- :default => [ "I thought you said calculated" ],
- }
- metadata.attribute("db/mysql/databases", attrs)
- end.to raise_error(ArgumentError)
- expect do
- attrs = {
- :calculated => true,
- :default => "I thought you said calculated",
- }
- metadata.attribute("db/mysql/databases", attrs)
- end.to raise_error(ArgumentError)
- end
-
- it "should allow a default that is a choice" do
- expect do
- attrs = {
- :choice => %w{a b c},
- :default => "b",
- }
- metadata.attribute("db/mysql/databases", attrs)
- 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)
- end.not_to raise_error
- end
-
- it "should error if default is not a choice" do
- expect do
- attrs = {
- :choice => %w{a b c},
- :default => "d",
- }
- metadata.attribute("db/mysql/databases", attrs)
- 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)
- 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
@@ -785,8 +457,8 @@ describe Chef::Cookbook::Metadata do
end
it "should automatically provide each recipe" do
- expect(metadata.providing.has_key?("test_cookbook")).to eq(true)
- expect(metadata.providing.has_key?("test_cookbook::enlighten")).to eq(true)
+ expect(metadata.providing.key?("test_cookbook")).to eq(true)
+ expect(metadata.providing.key?("test_cookbook::enlighten")).to eq(true)
end
end
@@ -801,21 +473,16 @@ 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"
metadata.version "1.2.3"
metadata.gem "foo", "~> 1.2"
metadata.gem "bar", ">= 2.2", "< 4.0"
- metadata.chef_version ">= 11.14.2", "< 11.18.10"
- metadata.chef_version ">= 12.2.1", "< 12.5.1"
- metadata.ohai_version ">= 7.1.0", "< 7.5.0"
- metadata.ohai_version ">= 8.0.1", "< 8.6.0"
+ metadata.chef_version "< 11.18.10", ">= 11.14.2"
+ metadata.chef_version "< 12.5.1", ">= 12.2.1"
+ metadata.ohai_version "< 7.5.0", ">= 7.1.0"
+ metadata.ohai_version "< 8.6.0", ">= 8.0.1"
+ metadata.eager_load_libraries [ "default.rb", "foo/*/**.rb" ]
end
it "should produce the same output from to_json and Chef::JSONCompat" do
@@ -840,12 +507,7 @@ describe Chef::Cookbook::Metadata do
license
platforms
dependencies
- suggestions
- recommendations
- conflicting
providing
- replacing
- attributes
recipes
version
source_url
@@ -885,12 +547,7 @@ describe Chef::Cookbook::Metadata do
license
platforms
dependencies
- suggestions
- recommendations
- conflicting
providing
- replacing
- attributes
recipes
version
source_url
@@ -911,42 +568,43 @@ 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 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 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 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 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 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 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 82876273e7..8df5c1d73f 100644
--- a/spec/unit/cookbook/synchronizer_spec.rb
+++ b/spec/unit/cookbook/synchronizer_spec.rb
@@ -1,6 +1,7 @@
require "spec_helper"
require "chef/cookbook/synchronizer"
require "chef/cookbook_version"
+require "chef-utils/dist"
describe Chef::CookbookCacheCleaner do
describe "when cleaning up unused cookbook components" do
@@ -49,7 +50,7 @@ describe Chef::CookbookCacheCleaner do
cleaner.cleanup_file_cache
end
- it "does not remove anything on chef-solo" do
+ it "does not remove anything on #{ChefUtils::Dist::Solo::EXEC}" do
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)
@@ -62,7 +63,8 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_default_recipe) do
{
"path" => "recipes/default.rb",
- "url" => "http://chef.example.com/abc123",
+ "name" => "recipes/default.rb",
+ "url" => "http://chef.example.com/abc123",
"checksum" => "abc123",
}
end
@@ -70,7 +72,8 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_default_attrs) do
{
"path" => "attributes/default.rb",
- "url" => "http://chef.example.com/abc456",
+ "name" => "attributes/default.rb",
+ "url" => "http://chef.example.com/abc456",
"checksum" => "abc456",
}
end
@@ -78,6 +81,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 +90,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 +117,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
@@ -195,223 +199,225 @@ describe Chef::CookbookSynchronizer do
let(:cookbook_a_default_recipe_tempfile) do
double("Tempfile for cookbook_a default.rb recipe",
- :path => "/tmp/cookbook_a_recipes_default_rb")
+ path: "/tmp/cookbook_a_recipes_default_rb")
end
let(:cookbook_a_default_attribute_tempfile) do
double("Tempfile for cookbook_a default.rb attr file",
- :path => "/tmp/cookbook_a_attributes_default_rb")
+ path: "/tmp/cookbook_a_attributes_default_rb")
end
let(:cookbook_a_file_default_tempfile) do
double("Tempfile for cookbook_a megaman.conf file",
- :path => "/tmp/cookbook_a_file_default_tempfile")
+ path: "/tmp/cookbook_a_file_default_tempfile")
end
let(:cookbook_a_template_default_tempfile) do
double("Tempfile for cookbook_a apache.conf.erb template",
- :path => "/tmp/cookbook_a_template_default_tempfile")
+ path: "/tmp/cookbook_a_template_default_tempfile")
end
+ let(:root) { windows? ? "C:/file-cache/cookbooks/cookbook_a" : "/file-cache/cookbooks/cookbook_a" }
+
def setup_common_files_missing_expectations
# Files are not in the cache:
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/recipes/default.rb").
- and_return(false)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/attributes/default.rb").
- and_return(false)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/recipes/default.rb")
+ .and_return(false)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/attributes/default.rb")
+ .and_return(false)
# Fetch and copy default.rb recipe
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/abc123").
- and_return(cookbook_a_default_recipe_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/recipes/default.rb", false).
- and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/abc123")
+ .and_return(cookbook_a_default_recipe_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/recipes/default.rb", false)
+ .and_return("#{root}/recipes/default.rb")
# Fetch and copy default.rb attribute file
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/abc456").
- and_return(cookbook_a_default_attribute_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/attributes/default.rb", false).
- and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/abc456")
+ .and_return(cookbook_a_default_attribute_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/attributes/default.rb", false)
+ .and_return("#{root}/attributes/default.rb")
end
def setup_no_lazy_files_and_templates_missing_expectations
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/files/default/megaman.conf").
- and_return(false)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb").
- and_return(false)
-
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/megaman.conf").
- and_return(cookbook_a_file_default_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/files/default/megaman.conf", false).
- and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf")
-
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/ffffff").
- and_return(cookbook_a_template_default_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false).
- and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf")
+ .and_return(false)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ .and_return(false)
+
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/megaman.conf")
+ .and_return(cookbook_a_file_default_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf", false)
+ .and_return("#{root}/default/megaman.conf")
+
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/ffffff")
+ .and_return(cookbook_a_template_default_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false)
+ .and_return("#{root}/templates/default/apache2.conf.erb")
end
def setup_common_files_chksum_mismatch_expectations
# Files are in the cache:
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/recipes/default.rb").
- and_return(true)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/attributes/default.rb").
- and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/recipes/default.rb")
+ .and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/attributes/default.rb")
+ .and_return(true)
# Fetch and copy default.rb recipe
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/abc123").
- and_return(cookbook_a_default_recipe_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/recipes/default.rb", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/abc123")
+ .and_return(cookbook_a_default_recipe_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/recipes/default.rb", false)
+ .twice
+ .and_return("#{root}/recipes/default.rb")
# 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")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/recipes/default.rb")
+ .and_return("fff000").at_least(:once)
# Fetch and copy default.rb attribute file
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/abc456").
- and_return(cookbook_a_default_attribute_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/attributes/default.rb", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/abc456")
+ .and_return(cookbook_a_default_attribute_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/attributes/default.rb", false)
+ .twice
+ .and_return("#{root}/attributes/default.rb")
# 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")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/attributes/default.rb")
+ .and_return("fff000").at_least(:once)
end
def setup_no_lazy_files_and_templates_chksum_mismatch_expectations
# Files are in the cache:
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/files/default/megaman.conf").
- and_return(true)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb").
- and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf")
+ .and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ .and_return(true)
# Fetch and copy megaman.conf
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/megaman.conf").
- and_return(cookbook_a_file_default_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/files/default/megaman.conf", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/megaman.conf")
+ .and_return(cookbook_a_file_default_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf", false)
+ .twice
+ .and_return("#{root}/default/megaman.conf")
# Fetch and copy apache2.conf template
- expect(server_api).to receive(:streaming_request).
- with("http://chef.example.com/ffffff").
- and_return(cookbook_a_template_default_tempfile)
- expect(file_cache).to receive(:move_to).
- with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ expect(server_api).to receive(:streaming_request)
+ .with("http://chef.example.com/ffffff")
+ .and_return(cookbook_a_template_default_tempfile)
+ expect(file_cache).to receive(:move_to)
+ .with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false)
+ .twice
+ .and_return("#{root}/templates/default/apache2.conf.erb")
# Current file has fff000
- expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
- with("/file-cache/cookbooks/cookbook_a/default/megaman.conf").
- and_return("fff000")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/default/megaman.conf")
+ .and_return("fff000")
# Current file has fff000
- expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
- with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb").
- and_return("fff000")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/templates/default/apache2.conf.erb")
+ .and_return("fff000")
end
def setup_common_files_present_expectations
# Files are in the cache:
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/recipes/default.rb").
- and_return(true)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/attributes/default.rb").
- and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/recipes/default.rb")
+ .and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/attributes/default.rb")
+ .and_return(true)
# 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")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/recipes/default.rb")
+ .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")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/attributes/default.rb")
+ .and_return("abc456").at_least(:once)
# :load called twice
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/recipes/default.rb", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/attributes/default.rb", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/recipes/default.rb", false)
+ .twice
+ .and_return("#{root}/recipes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/attributes/default.rb", false)
+ .twice
+ .and_return("#{root}/attributes/default.rb")
end
def setup_no_lazy_files_and_templates_present_expectations
# Files are in the cache:
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/files/default/megaman.conf").
- and_return(true)
- expect(file_cache).to receive(:has_key?).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb").
- and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf")
+ .and_return(true)
+ expect(file_cache).to receive(:key?)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb")
+ .and_return(true)
# Current file has abc124, want abc124
- expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
- with("/file-cache/cookbooks/cookbook_a/default/megaman.conf").
- and_return("abc124")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/default/megaman.conf")
+ .and_return("abc124")
# Current file has abc125, want abc125
- expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file).
- with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb").
- and_return("abc125")
+ expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file)
+ .with("#{root}/templates/default/apache2.conf.erb")
+ .and_return("abc125")
# :load called twice
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/files/default/megaman.conf", false).
- twice.
- and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf")
- expect(file_cache).to receive(:load).
- with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false).
- twice.
- 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)
+ .twice
+ .and_return("#{root}/default/megaman.conf")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false)
+ .twice
+ .and_return("#{root}/templates/default/apache2.conf.erb")
end
describe "#server_api" do
@@ -448,8 +454,8 @@ describe Chef::CookbookSynchronizer do
it "does not fetch templates or cookbook files" do
# Implicitly tested in previous test; this test is just for behavior specification.
- expect(server_api).not_to receive(:streaming_request).
- with("http://chef.example.com/ffffff")
+ expect(server_api).not_to receive(:streaming_request)
+ .with("http://chef.example.com/ffffff")
synchronizer.sync_cookbooks
end
@@ -523,5 +529,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("#{root}/recipes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/attributes/default.rb", false)
+ .once
+ .and_return("#{root}/attributes/default.rb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false)
+ .once
+ .and_return("#{root}/templates/default/apache2.conf.erb")
+ expect(file_cache).to receive(:load)
+ .with("cookbooks/cookbook_a/files/default/megaman.conf", false)
+ .once
+ .and_return("#{root}/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..f9e7bc0d25 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,10 +21,11 @@ require "chef/cookbook/syntax_check"
describe Chef::Cookbook::SyntaxCheck do
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
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 eef5d2afd5..83e9f7a28e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,17 +20,20 @@ require "spec_helper"
describe Chef::CookbookLoader do
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
end
let(:repo_paths) do
[
- File.expand_path(File.join(CHEF_SPEC_DATA, "kitchen")),
File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")),
]
end
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|
@@ -40,26 +43,29 @@ describe Chef::CookbookLoader do
cookbook_paths.delete_if { |path| File.basename(path) == "chefignore" }
cookbook_paths.each do |cookbook_path|
- expect(Chef::Cookbook::CookbookVersionLoader).to receive(:new).
- with(cookbook_path, anything).
- once.
- and_call_original
+ expect(Chef::Cookbook::CookbookVersionLoader).to receive(:new)
+ .with(cookbook_path, anything)
+ .once
+ .and_call_original
end
- expect(Chef::Log).to receive(:deprecation).with(/The cookbook\(s\): openldap exist in multiple places in your cookbook_path./)
cookbook_loader.load_cookbooks
end
- context "after loading all cookbooks" do
- before(:each) do
- expect(Chef::Log).to receive(:deprecation).with(/The cookbook\(s\): openldap exist in multiple places in your cookbook_path./)
- cookbook_loader.load_cookbooks
+ context "removed cookbook merging" do
+ let(:repo_paths) do
+ [
+ File.expand_path(File.join(CHEF_SPEC_DATA, "kitchen")),
+ File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")),
+ ]
+ end
+ it "should not support multiple merged cookbooks in the cookbook path" do
+ expect { cookbook_loader.load_cookbooks }.to raise_error(Chef::Exceptions::CookbookMergingError)
end
+ end
- it "should be possible to reload all the cookbooks without triggering deprecation warnings on all of them" do
- start_merged_cookbooks = cookbook_loader.merged_cookbooks
- expect(Chef::Log).to receive(:deprecation).with(/The cookbook\(s\): openldap exist in multiple places in your cookbook_path./)
+ context "after loading all cookbooks" do
+ before(:each) do
cookbook_loader.load_cookbooks
- expect(cookbook_loader.merged_cookbooks).to eql(start_merged_cookbooks)
end
describe "[]" do
@@ -82,8 +88,8 @@ 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|
+ seen = {}
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).to have_key("openldap")
@@ -91,17 +97,11 @@ describe Chef::CookbookLoader do
end
it "should iterate in alphabetical order" do
- seen = Array.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ seen = []
+ 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 irssi java name-mismatch openldap preseed starter supports-platform-constraints wget}
end
end
@@ -111,64 +111,31 @@ describe Chef::CookbookLoader do
expect(cookbook_loader).to have_key(:apache2)
end
- it "should allow you to override an attribute file via cookbook_path" do
- expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
- f =~ /cookbooks\/openldap\/attributes\/default.rb/
- end).not_to eql(nil)
- expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
- f =~ /kitchen\/openldap\/attributes\/default.rb/
- end).to eql(nil)
- end
-
it "should load different attribute files from deeper paths" do
- expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
- f =~ /kitchen\/openldap\/attributes\/robinson.rb/
- 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 do |f|
- f =~ /cookbooks\/openldap\/definitions\/client.rb/
+ expect(full_paths_for_part(:openldap, "attributes").detect do |f|
+ f =~ %r{cookbooks/openldap/attributes/smokey.rb}
end).not_to eql(nil)
- expect(cookbook_loader[:openldap].definition_filenames.detect do |f|
- f =~ /kitchen\/openldap\/definitions\/client.rb/
- end).to eql(nil)
end
it "should load definition files from deeper paths" do
- expect(cookbook_loader[:openldap].definition_filenames.detect do |f|
- f =~ /kitchen\/openldap\/definitions\/drewbarrymore.rb/
+ expect(full_paths_for_part(:openldap, "definitions").detect do |f|
+ f =~ %r{cookbooks/openldap/definitions/server.rb}
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 do |f|
- f =~ /cookbooks\/openldap\/recipes\/gigantor.rb/
- end).not_to eql(nil)
- expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
- f =~ /kitchen\/openldap\/recipes\/gigantor.rb/
- end).to eql(nil)
- end
-
it "should load recipe files from deeper paths" do
- expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
- f =~ /kitchen\/openldap\/recipes\/woot.rb/
+ expect(full_paths_for_part(:openldap, "recipes").detect do |f|
+ f =~ %r{cookbooks/openldap/recipes/one.rb}
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 do |f|
- f =~ /kitchen\/openldap\/recipes\/ignoreme.rb/
- end).to eql(nil)
- end
-
it "should find files that start with a ." do
- expect(cookbook_loader[:openldap].file_filenames.detect do |f|
+ expect(full_paths_for_part(:openldap, "files").detect do |f|
f =~ /\.dotfile$/
end).to match(/\.dotfile$/)
- expect(cookbook_loader[:openldap].file_filenames.detect do |f|
- f =~ /\.ssh\/id_rsa$/
- end).to match(/\.ssh\/id_rsa$/)
+ expect(full_paths_for_part(:openldap, "files").detect do |f|
+ f =~ %r{\.ssh/id_rsa$}
+ end).to match(%r{\.ssh/id_rsa$})
end
it "should load the metadata for the cookbook" do
@@ -184,7 +151,6 @@ describe Chef::CookbookLoader do
let(:repo_paths) do
[
- File.join(CHEF_SPEC_DATA, "kitchen"),
File.join(CHEF_SPEC_DATA, "cookbooks"),
File.join(CHEF_SPEC_DATA, "invalid-metadata-chef-repo"),
]
@@ -207,8 +173,8 @@ describe Chef::CookbookLoader do
end
it "should have loaded the correct cookbook" do
- seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ seen = {}
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).to have_key("openldap")
@@ -230,13 +196,15 @@ describe Chef::CookbookLoader do
end
it "should not load the cookbook again when accessed" do
- expect(cookbook_loader).not_to receive("load_cookbook")
+ cookbook_loader.send(:cookbook_version_loaders).each do |cbv_loader|
+ expect(cbv_loader).not_to receive(:load)
+ end
cookbook_loader["openldap"]
end
it "should not load the other cookbooks" do
- seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ seen = {}
+ cookbook_loader.each_key do |cookbook_name|
seen[cookbook_name] = true
end
expect(seen).not_to have_key("apache2")
@@ -250,7 +218,6 @@ describe Chef::CookbookLoader do
let(:repo_paths) do
[
- File.join(CHEF_SPEC_DATA, "kitchen"),
File.join(CHEF_SPEC_DATA, "cookbooks"),
File.join(CHEF_SPEC_DATA, "invalid-metadata-chef-repo"),
]
@@ -268,13 +235,12 @@ describe Chef::CookbookLoader do
describe "loading all cookbooks after loading only one cookbook" do
before(:each) do
- expect(Chef::Log).to receive(:deprecation).with(/The cookbook\(s\): openldap exist in multiple places in your cookbook_path./)
cookbook_loader.load_cookbooks
end
it "should load all cookbooks" do
- seen = Hash.new
- cookbook_loader.each do |cookbook_name, cookbook|
+ seen = {}
+ 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..fb49a76f8b 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 (c) 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
@@ -94,22 +89,14 @@ describe Chef::CookbookManifest do
{
"chef_type" => "cookbook_version",
- "name" => "tatft-1.2.3",
- "version" => "1.2.3",
+ "name" => "tatft-1.2.3",
+ "version" => "1.2.3",
"cookbook_name" => "tatft",
- "metadata" => metadata,
+ "metadata" => metadata,
"frozen?" => false,
- "recipes" => [],
- "definitions" => [],
- "libraries" => [],
- "attributes" => [],
- "files" => [],
- "templates" => [],
- "resources" => [],
- "providers" => [],
- "root_files" => [],
+ "all_files" => [],
}
end
@@ -123,30 +110,34 @@ 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])
+ elsif parts.length == 1
+ "root_files/#{parts[0]}"
+ else
+ relative_path
+ end
+
{
- "name" => File.basename(path),
- "path" => relative_path,
- "checksum" => Chef::Digester.generate_md5_checksum_for_file(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
@@ -154,47 +145,41 @@ describe Chef::CookbookManifest do
{
"chef_type" => "cookbook_version",
- "name" => "tatft-1.2.3",
- "version" => "1.2.3",
+ "name" => "tatft-1.2.3",
+ "version" => "1.2.3",
"cookbook_name" => "tatft",
- "metadata" => metadata,
+ "metadata" => metadata,
"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..af714094d0 100644
--- a/spec/unit/cookbook_site_streaming_uploader_spec.rb
+++ b/spec/unit/cookbook_site_streaming_uploader_spec.rb
@@ -25,8 +25,7 @@ class FakeTempfile
@basename = basename
end
- def close
- end
+ def close; end
def path
"#{@basename}.ZZZ"
@@ -49,10 +48,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
@@ -106,22 +101,22 @@ describe Chef::CookbookSiteStreamingUploader do
it "should be able to receive files to attach as argument" do
Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, "bill", @secret_filename, {
- :myfile => File.new(File.join(CHEF_SPEC_DATA, "config.rb")), # a dummy file
+ myfile: File.new(File.join(CHEF_SPEC_DATA, "config.rb")), # a dummy file
})
end
it "should be able to receive strings to attach as argument" do
Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, "bill", @secret_filename, {
- :mystring => "Lorem ipsum",
+ mystring: "Lorem ipsum",
})
end
it "should be able to receive strings and files as argument at the same time" do
Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, "bill", @secret_filename, {
- :myfile1 => File.new(File.join(CHEF_SPEC_DATA, "config.rb")),
- :mystring1 => "Lorem ipsum",
- :myfile2 => File.new(File.join(CHEF_SPEC_DATA, "config.rb")),
- :mystring2 => "Dummy text",
+ myfile1: File.new(File.join(CHEF_SPEC_DATA, "config.rb")),
+ mystring1: "Lorem ipsum",
+ myfile2: File.new(File.join(CHEF_SPEC_DATA, "config.rb")),
+ mystring2: "Dummy text",
})
end
diff --git a/spec/unit/cookbook_spec.rb b/spec/unit/cookbook_spec.rb
index 33b4a3ccd8..04b729c127 100644
--- a/spec/unit/cookbook_spec.rb
+++ b/spec/unit/cookbook_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,10 @@
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(__dir__, "..", "data", "cookbooks", "openldap"))
+
before(:each) do
- @cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
+ @cookbook_repo = File.expand_path(File.join(__dir__, "..", "data", "cookbooks"))
cl = Chef::CookbookLoader.new(@cookbook_repo)
cl.load_cookbooks
@cookbook_collection = Chef::CookbookCollection.new(cl)
@@ -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..faae7bf073 100644
--- a/spec/unit/cookbook_uploader_spec.rb
+++ b/spec/unit/cookbook_uploader_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,15 @@
#
require "spec_helper"
+require_relative "../../lib/chef/cookbook_uploader"
describe Chef::CookbookUploader do
let(:http_client) { double("Chef::ServerAPI") }
+ let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks") }
let(:cookbook_loader) do
- loader = Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks"))
+ loader = Chef::CookbookLoader.new(cookbook_path)
loader.load_cookbooks
loader.cookbooks_by_name["apache2"].identifier = apache2_identifier
loader.cookbooks_by_name["java"].identifier = java_identifier
@@ -55,6 +57,10 @@ describe Chef::CookbookUploader do
let(:uploader) { described_class.new(cookbooks_to_upload, rest: http_client, policy_mode: policy_mode) }
+ before do
+ allow(Chef::Config).to receive(:cookbook_path) { cookbook_path }
+ end
+
it "defaults to not enabling policy mode" do
expect(described_class.new(cookbooks_to_upload, rest: http_client).policy_mode?).to be(false)
end
@@ -65,7 +71,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
@@ -85,9 +91,9 @@ describe Chef::CookbookUploader do
end
def expect_sandbox_create
- expect(http_client).to receive(:post).
- with("sandboxes", { :checksums => checksums_set }).
- and_return(sandbox_response)
+ expect(http_client).to receive(:post)
+ .with("sandboxes", { checksums: checksums_set })
+ .and_return(sandbox_response)
end
def expect_checksum_upload
@@ -96,12 +102,12 @@ describe Chef::CookbookUploader do
upload_headers = {
"content-type" => "application/x-binary",
- "content-md5" => an_instance_of(String),
- "accept" => "application/json",
+ "content-md5" => an_instance_of(String),
+ "accept" => "application/json",
}
- expect(http_client).to receive(:put).
- with(url_for(md5), IO.binread(file_path), upload_headers)
+ expect(http_client).to receive(:put)
+ .with(url_for(md5), IO.binread(file_path), upload_headers)
end
end
@@ -111,14 +117,14 @@ describe Chef::CookbookUploader do
end
def expect_sandbox_commit
- expect(http_client).to receive(:put).with(sandbox_commit_uri, { :is_completed => true })
+ expect(http_client).to receive(:put).with(sandbox_commit_uri, { is_completed: true })
end
def expect_cookbook_create
cookbooks_to_upload.each do |cookbook|
- expect(http_client).to receive(:put).
- with(expected_save_url(cookbook), cookbook)
+ expect(http_client).to receive(:put)
+ .with(expected_save_url(cookbook), cookbook)
end
end
diff --git a/spec/unit/cookbook_version_file_specificity_spec.rb b/spec/unit/cookbook_version_file_specificity_spec.rb
index 3b5450cb2d..1e80be53ec 100644
--- a/spec/unit/cookbook_version_file_specificity_spec.rb
+++ b/spec/unit/cookbook_version_file_specificity_spec.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,179 +23,213 @@ 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",
- :path => "files/host-examplehost.example.org/afile.rb",
- :checksum => "csum-host",
- :specificity => "host-examplehost.example.org",
+ 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",
- :path => "files/ubuntu-9.10/afile.rb",
- :checksum => "csum-platver-full",
- :specificity => "ubuntu-9.10",
+ 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",
- :path => "files/newubuntu-9/afile.rb",
- :checksum => "csum-platver-partial",
- :specificity => "newubuntu-9",
+ 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",
- :path => "files/ubuntu/afile.rb",
- :checksum => "csum-plat",
- :specificity => "ubuntu",
+ 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",
- :path => "files/default/afile.rb",
- :checksum => "csum-default",
- :specificity => "default",
+ 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",
- :path => "files/fakeos-2.0.rc.1/bfile.rb",
- :checksum => "csum2-platver-full",
- :specificity => "fakeos-2.0.rc.1",
+ 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",
- :path => "files/newfakeos-2.0.rc/bfile.rb",
- :checksum => "csum2-platver-partial",
- :specificity => "newfakeos-2.0.rc",
+ 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",
- :path => "files/fakeos-maple tree/bfile.rb",
- :checksum => "csum3-platver-full",
- :specificity => "maple tree",
+ 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",
- :path => "files/fakeos-1/bfile.rb",
- :checksum => "csum4-platver-full",
- :specificity => "fakeos-1",
+ 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",
- :path => "files/host-examplehost.example.org/adirectory/anotherfile1.rb.host",
- :checksum => "csum-host-1",
- :specificity => "host-examplehost.example.org",
+ name: "files/anotherfile1.rb",
+ path: "files/root_directory/anotherfile1.rb.root",
+ checksum: "csum-root-directory",
+ specificity: "root_directory",
+ },
+
+ {
+ 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",
- :path => "files/host-examplehost.example.org/adirectory/anotherfile2.rb.host",
- :checksum => "csum-host-2",
- :specificity => "host-examplehost.example.org",
+ 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",
- :path => "files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum-platver-full-1",
- :specificity => "ubuntu-9.10",
+ 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",
- :path => "files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum-platver-full-2",
- :specificity => "ubuntu-9.10",
+ 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",
- :path => "files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version",
- :checksum => "csum-platver-partial-1",
- :specificity => "newubuntu-9",
+ 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",
- :path => "files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version",
- :checksum => "csum-platver-partial-2",
- :specificity => "nweubuntu-9",
+ 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",
- :path => "files/ubuntu/adirectory/anotherfile1.rb.platform",
- :checksum => "csum-plat-1",
- :specificity => "ubuntu",
+ 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",
- :path => "files/ubuntu/adirectory/anotherfile2.rb.platform",
- :checksum => "csum-plat-2",
- :specificity => "ubuntu",
+ 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",
- :path => "files/default/adirectory/anotherfile1.rb.default",
- :checksum => "csum-default-1",
- :specificity => "default",
+ 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",
- :path => "files/default/adirectory/anotherfile2.rb.default",
- :checksum => "csum-default-2",
- :specificity => "default",
+ 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",
- :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum2-platver-full-1",
- :specificity => "fakeos-2.0.rc.1",
+ 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",
- :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum2-platver-full-2",
- :specificity => "fakeos-2.0.rc.1",
+ 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",
- :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version",
- :checksum => "csum2-platver-partial-1",
- :specificity => "newfakeos-2.0.rc",
+ 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",
- :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version",
- :checksum => "csum2-platver-partial-2",
- :specificity => "newfakeos-2.0.rc",
+ 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",
- :path => "files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum3-platver-full-1",
- :specificity => "fakeos-maple tree",
+ 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",
- :path => "files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum3-platver-full-2",
- :specificity => "fakeos-maple tree",
+ 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",
- :path => "files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum4-platver-full-1",
- :specificity => "fakeos-1",
+ 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",
- :path => "files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum4-platver-full-2",
- :specificity => "fakeos-1",
+ 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",
},
],
}
@@ -337,7 +371,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-host-1", "csum-host-2"])
+ expect(checksums.sort).to eq(%w{csum-host-1 csum-host-2})
end
it "should return a directory of manifest records based on priority preference: platform & full version" do
@@ -351,7 +385,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-platver-full-1", "csum-platver-full-2"])
+ expect(checksums.sort).to eq(%w{csum-platver-full-1 csum-platver-full-2})
end
it "should return a directory of manifest records based on priority preference: platform & partial version" do
@@ -365,7 +399,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-platver-partial-1", "csum-platver-partial-2"])
+ expect(checksums.sort).to eq(%w{csum-platver-partial-1 csum-platver-partial-2})
end
it "should return a directory of manifest records based on priority preference: platform only" do
@@ -379,7 +413,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-plat-1", "csum-plat-2"])
+ expect(checksums.sort).to eq(%w{csum-plat-1 csum-plat-2})
end
it "should return a directory of manifest records based on priority preference: default" do
@@ -393,7 +427,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-default-1", "csum-default-2"])
+ expect(checksums.sort).to eq(%w{csum-default-1 csum-default-2})
end
it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do
@@ -407,7 +441,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum2-platver-full-1", "csum2-platver-full-2"])
+ expect(checksums.sort).to eq(%w{csum2-platver-full-1 csum2-platver-full-2})
end
it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do
@@ -421,7 +455,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum2-platver-partial-1", "csum2-platver-partial-2"])
+ expect(checksums.sort).to eq(%w{csum2-platver-partial-1 csum2-platver-partial-2})
end
it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do
@@ -435,7 +469,7 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum3-platver-full-1", "csum3-platver-full-2"])
+ expect(checksums.sort).to eq(%w{csum3-platver-full-1 csum3-platver-full-2})
end
it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do
@@ -449,13 +483,26 @@ describe Chef::CookbookVersion, "file specificity" do
expect(manifest_records.size).to eq(2)
checksums = manifest_records.map { |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum4-platver-full-1", "csum4-platver-full-2"])
+ expect(checksums.sort).to eq(%w{csum4-platver-full-1 csum4-platver-full-2})
end
end
## Globbing the relative paths out of the manifest records ##
describe "when globbing for relative file paths based on filespecificity" do
+ it "should return a list of relative paths based on priority preference: root directory" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "examplehost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "root_directory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(1)
+
+ expect(filenames.sort).to eq(["anotherfile1.rb.root"])
+ end
+
it "should return a list of relative paths based on priority preference: host" do
node = Chef::Node.new
node.automatic_attrs[:platform] = "ubuntu"
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 81ea161bfe..01345e32e7 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -77,22 +45,12 @@ describe Chef::CookbookVersion do
end
describe "with a cookbook directory named tatft" do
- MD5 = /[0-9a-f]{32}/
+ MD5 = /[0-9a-f]{32}/.freeze
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")),
+ # Dunno if the paths here are representative of what is set by CookbookLoader...
+ all_files: Dir[File.join(cookbook_root, "**", "**")],
}
end
@@ -102,18 +60,9 @@ 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
@@ -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,16 +125,7 @@ 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
@@ -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 ae3d626113..f91210277b 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 (c) 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 = # Struct::Passwd.new(nil, nil, 111, 111)
+ 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
@@ -122,8 +126,8 @@ describe Chef::Daemon do
allow(Process::UID).to receive(:change_privilege).and_return(nil)
allow(Process::GID).to receive(:change_privilege).and_return(nil)
- @pw_user = double("Struct::Passwd", :uid => 501)
- @pw_group = double("Struct::Group", :gid => 20)
+ @pw_user = double("Struct::Passwd", uid: 501)
+ @pw_group = double("Struct::Group", gid: 20)
allow(Process).to receive(:initgroups).and_return(true)
@@ -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 e83f0ca0ec..9a12804443 100644
--- a/spec/unit/data_bag_item_spec.rb
+++ b/spec/unit/data_bag_item_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,7 +43,7 @@ describe Chef::DataBagItem do
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { data_bag_item.data_bag Hash.new }.to raise_error(ArgumentError)
+ expect { data_bag_item.data_bag({}) }.to raise_error(ArgumentError)
end
end
@@ -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
@@ -144,12 +148,12 @@ describe Chef::DataBagItem do
end
it "implements all the methods of Hash" do
- methods = [:rehash, :to_hash, :[], :fetch, :[]=, :store, :default,
- :default=, :default_proc, :index, :size, :length,
- :empty?, :each_value, :each_key, :each_pair, :each, :keys, :values,
- :values_at, :delete, :delete_if, :reject!, :clear,
- :invert, :update, :replace, :merge!, :merge, :has_key?, :has_value?,
- :key?, :value?]
+ methods = %i{rehash to_hash [] fetch []= store default
+ default= default_proc index size length
+ empty? each_value each_key each_pair each keys values
+ values_at delete delete_if reject! clear
+ invert update replace merge! merge has_key? has_value?
+ key? value?}
methods.each do |m|
expect(data_bag_item).to respond_to(m)
end
@@ -293,8 +297,8 @@ describe Chef::DataBagItem do
end
it "should create if the item is not found" do
- exception = double("404 error", :code => "404")
- expect(server).to receive(:put).and_raise(Net::HTTPServerException.new("foo", exception))
+ exception = double("404 error", code: "404")
+ expect(server).to receive(:put).and_raise(Net::HTTPClientException.new("foo", exception))
expect(server).to receive(:post).with("data/books", data_bag_item)
data_bag_item.save
end
diff --git a/spec/unit/data_bag_spec.rb b/spec/unit/data_bag_spec.rb
index cadd60936e..2a7ddddd5b 100644
--- a/spec/unit/data_bag_spec.rb
+++ b/spec/unit/data_bag_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ require "chef/data_bag"
describe Chef::DataBag do
before(:each) do
@data_bag = Chef::DataBag.new
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
end
describe "initialize" do
@@ -46,7 +46,7 @@ describe Chef::DataBag do
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @data_bag.name Hash.new }.to raise_error(ArgumentError)
+ expect { @data_bag.name({}) }.to raise_error(ArgumentError)
end
[ ".", "-", "_", "1"].each do |char|
@@ -88,8 +88,8 @@ describe Chef::DataBag do
end
it "should silently proceed when the data bag already exists" do
- exception = double("409 error", :code => "409")
- expect(@rest).to receive(:post).and_raise(Net::HTTPServerException.new("foo", exception))
+ exception = double("409 error", code: "409")
+ expect(@rest).to receive(:post).and_raise(Net::HTTPClientException.new("foo", exception))
@data_bag.save
end
@@ -240,20 +240,23 @@ describe Chef::DataBag do
it "should raise an error if the configured data_bag_path is invalid" do
file_dir_stub(@paths.first, false)
+ msg = "Data bag path '#{windows? ? "C:/var/chef" : "/var/chef"}/data_bags' not found. Please create this directory."
expect do
Chef::DataBag.load("foo")
- end.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid"
+ end.to raise_error Chef::Exceptions::InvalidDataBagPath, msg
end
end
describe "data bag with string path" do
- it_should_behave_like "data bag in solo mode", "/var/chef/data_bags"
+ it_should_behave_like "data bag in solo mode", "#{windows? ? "C:/var/chef" : "/var/chef"}/data_bags"
end
describe "data bag with array path" do
- it_should_behave_like "data bag in solo mode", ["/var/chef/data_bags", "/var/chef/data_bags_2"]
+ it_should_behave_like "data bag in solo mode", %w{data_bags data_bags_2}.map { |data_bag|
+ "#{windows? ? "C:/var/chef" : "/var/chef"}/#{data_bag}"
+ }
end
end
diff --git a/spec/unit/data_collector/config_validation_spec.rb b/spec/unit/data_collector/config_validation_spec.rb
new file mode 100644
index 0000000000..239ef4b249
--- /dev/null
+++ b/spec/unit/data_collector/config_validation_spec.rb
@@ -0,0 +1,208 @@
+#
+# Copyright:: Copyright (c) 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/config_validation"
+
+describe Chef::DataCollector::ConfigValidation do
+ describe "#should_be_enabled?" do
+ shared_examples_for "a solo-like run" do
+ it "is disabled in solo-legacy without a data_collector url and token" do
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is disabled in solo-legacy with only a url" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is disabled in solo-legacy with only a token" do
+ Chef::Config[:data_collector][:token] = "admit_one"
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is enabled in solo-legacy with both a token and url" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:token] = "no_cash_value"
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is enabled in solo-legacy with only an output location to a file" do
+ Chef::Config[:data_collector][:output_locations] = { files: [ "/always/be/counting/down" ] }
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is disabled in solo-legacy with only an output location to a uri" do
+ Chef::Config[:data_collector][:output_locations] = { urls: [ "https://esa.local/ariane5" ] }
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is enabled in solo-legacy with only an output location to a uri with a token" do
+ Chef::Config[:data_collector][:output_locations] = { urls: [ "https://esa.local/ariane5" ] }
+ Chef::Config[:data_collector][:token] = "good_for_one_fare"
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is enabled in solo-legacy when the mode is :solo" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:token] = "non_redeemable"
+ Chef::Config[:data_collector][:mode] = :solo
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is enabled in solo-legacy when the mode is :both" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:token] = "non_negotiable"
+ Chef::Config[:data_collector][:mode] = :both
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is disabled in solo-legacy when the mode is :client" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:token] = "NYCTA"
+ Chef::Config[:data_collector][:mode] = :client
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is disabled in solo-legacy mode when the mode is :nonsense" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:token] = "MTA"
+ Chef::Config[:data_collector][:mode] = :nonsense
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+ end
+
+ it "by default it is enabled" do
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is disabled in why-run" do
+ Chef::Config[:why_run] = true
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ describe "a solo legacy run" do
+ before(:each) do
+ Chef::Config[:solo_legacy_mode] = true
+ end
+
+ it_behaves_like "a solo-like run"
+ end
+
+ describe "a local mode run" do
+ before(:each) do
+ Chef::Config[:local_mode] = true
+ end
+
+ it_behaves_like "a solo-like run"
+ end
+
+ it "is enabled in client mode when the mode is :both" do
+ Chef::Config[:data_collector][:mode] = :both
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+
+ it "is disabled in client mode when the mode is :solo" do
+ Chef::Config[:data_collector][:mode] = :solo
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is disabled in client mode when the mode is :nonsense" do
+ Chef::Config[:data_collector][:mode] = :nonsense
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be false
+ end
+
+ it "is still enabled if you set a token in client mode" do
+ Chef::Config[:data_collector][:token] = "good_for_one_ride"
+ expect(Chef::DataCollector::ConfigValidation.should_be_enabled?).to be true
+ end
+ end
+
+ describe "validate_server_url!" do
+ it "with valid server url" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ expect(Chef::DataCollector::ConfigValidation.validate_server_url!).to be_nil
+ end
+
+ it "with invalid server URL" do
+ Chef::Config[:data_collector][:server_url] = "not valid URL"
+ expect { Chef::DataCollector::ConfigValidation.validate_server_url! }.to raise_error(Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] (not valid URL) is not a valid URI.")
+ end
+
+ it "with invalid server URL without host" do
+ Chef::Config[:data_collector][:server_url] = "no-host"
+ expect { Chef::DataCollector::ConfigValidation.validate_server_url! }.to raise_error(Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] (no-host) is a URI with no host. Please supply a valid URL.")
+ end
+
+ it "skip validation if output_locations is set" do
+ Chef::Config[:data_collector][:output_locations] = { files: ["https://www.esa.local/ariane5"] }
+ expect(Chef::DataCollector::ConfigValidation.validate_server_url!).to be_nil
+ end
+
+ it "skip validation if output_locations & server_url both are set" do
+ Chef::Config[:data_collector][:server_url] = "https://www.esa.local/ariane5"
+ Chef::Config[:data_collector][:output_locations] = { files: ["https://www.esa.local/ariane5"] }
+ expect(Chef::DataCollector::ConfigValidation.validate_server_url!).to be_nil
+ end
+ end
+
+ describe "validate_output_locations!" do
+ it "with nil or not set skip validation" do
+ Chef::Config[:data_collector][:output_locations] = nil
+ expect(Chef::DataCollector::ConfigValidation.validate_output_locations!).to be_nil
+ end
+
+ it "with empty value raise validation error" do
+ Chef::Config[:data_collector][:output_locations] = {}
+ expect { Chef::DataCollector::ConfigValidation.validate_output_locations! }.to raise_error(Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:output_locations] is empty. Please supply an hash of valid URLs and / or local file paths.")
+ end
+
+ it "with valid URLs options" do
+ Chef::Config[:data_collector][:output_locations] = { urls: ["https://www.esa.local/ariane5/data-collector"] }
+ expect { Chef::DataCollector::ConfigValidation.validate_output_locations! }.not_to raise_error
+ end
+
+ context "output_locations contains files" do
+ let(:file_path) { "/tmp/client-runs.txt" }
+
+ before(:each) do
+ allow(File).to receive(:exist?).with(file_path).and_return(true)
+ allow(File).to receive(:readable?).with(file_path).and_return(true)
+ allow(File).to receive(:writable?).with(file_path).and_return(true)
+ allow(File).to receive(:expand_path).with(file_path).and_return(file_path)
+ end
+
+ it "with valid files options" do
+ Chef::Config[:data_collector][:output_locations] = { files: [file_path] }
+ expect { Chef::DataCollector::ConfigValidation.validate_output_locations! }.not_to raise_error
+ end
+
+ it "with valid files & URLs options" do
+ Chef::Config[:data_collector][:output_locations] = { urls: ["https://www.esa.local/ariane5/data-collector"], files: [file_path] }
+ expect { Chef::DataCollector::ConfigValidation.validate_output_locations! }.not_to raise_error
+ end
+
+ it "with valid files options & String location value" do
+ Chef::Config[:data_collector][:output_locations] = { files: file_path }
+ expect { Chef::DataCollector::ConfigValidation.validate_output_locations! }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/unit/data_collector/messages/helpers_spec.rb b/spec/unit/data_collector/messages/helpers_spec.rb
deleted file mode 100644
index b0d9f4d09d..0000000000
--- a/spec/unit/data_collector/messages/helpers_spec.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-#
-# 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
- 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 can be read" do
- it "returns the read-in node UUID" do
- 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
- 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
deleted file mode 100644
index 394f18dce0..0000000000
--- a/spec/unit/data_collector/messages_spec.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-#
-# 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
- }
- 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
- }
- 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_spec.rb b/spec/unit/data_collector_spec.rb
index 37df758ff2..63531663ee 100644
--- a/spec/unit/data_collector_spec.rb
+++ b/spec/unit/data_collector_spec.rb
@@ -1,8 +1,5 @@
#
-# Author:: Adam Leff (<adamleff@chef.io)
-# Author:: Ryan Cragun (<ryan@chef.io>)
-#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,539 +17,887 @@
require "spec_helper"
require "chef/data_collector"
-require "chef/resource_builder"
+require "socket"
+#
+# FIXME FIXME FIXME: What we need to do here is have the ability to construct a real resource collection
+# with some test resources that will correctly be up-to-date/updated/skipped/failed/unprocessed and really
+# converge a client with them (sort of chefspec-like construction of a Chef::Client but with the actions
+# actually running -- another testing requirement similar to the integration testing framework in cheffish as well)
+#
describe Chef::DataCollector do
- describe ".register_reporter?" do
- context "when no data collector URL is configured" do
- it "returns false" do
- Chef::Config[:data_collector][:server_url] = nil
- expect(Chef::DataCollector.register_reporter?).to be_falsey
- end
- end
+ let(:node) { Chef::Node.new }
+
+ let(:rest_client) { double("Chef::ServerAPI (mock)") }
+
+ let(:data_collector) { Chef::DataCollector::Reporter.new(events) }
+
+ let(:new_resource) do
+ new_resource = Chef::Resource::File.new("/tmp/a-file.txt")
+ new_resource.checksum nil
+ # This next line is a hack to work around the fact that the name property will not have been autovivified yet
+ # in these unit tests which breaks some assumptions. Really the Formatters::ErrorMapper.resource_failed and
+ # related APIs need to properly walk and get properties on the resource rather than walking through instance
+ # variables, but the ErrorMappers probably pre-date the conversion to properties. But this next line is necesary
+ # to populate the name_property instance variable (which will happen naturally during the course of running the
+ # provider, so I don't think this is a user-visibile bug).
+ new_resource.path
+ new_resource
+ end
- context "when a data collector URL is configured" do
- before do
- Chef::Config[:data_collector][:server_url] = "http://data_collector"
- end
+ let(:current_resource) do
+ current_resource = Chef::Resource::File.new("/tmp/a-file.txt")
+ current_resource.checksum "1234123412341234123412341234123412341234123412341234123412341234"
+ current_resource
+ 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
+ let(:after_resource) do
+ after_resource = Chef::Resource::File.new("/tmp/a-file.txt")
+ after_resource.checksum "6789678967896789678967896789678967896789678967896789678967896789"
+ after_resource
+ end
- context "when not operating in why_run mode" do
- before do
- Chef::Config[:why_run] = false
- end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
- 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
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
- 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
+ let(:run_status) { Chef::RunStatus.new(node, events) }
- 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
+ let(:start_time) { Time.new }
- 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
+ let(:end_time) { Time.new + 20 }
- 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
+ let(:run_list) { node.run_list }
- 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
+ let(:cookbooks) { node.fetch("cookbooks", {}) }
- context "when running in local mode" do
- before do
- Chef::Config[:solo] = false
- Chef::Config[:local_mode] = true
- end
+ let(:run_id) { run_status.run_id }
- 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
+ let(:expansion) { Chef::RunList::RunListExpansion.new("_default", run_list.run_list_items) }
- 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
+ let(:cookbook_name) { "monkey" }
- 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
+ let(:recipe_name) { "atlas" }
- context "when running in client mode" do
- before do
- Chef::Config[:solo] = false
- Chef::Config[:local_mode] = false
- end
+ let(:node_name) { "spitfire" }
- 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
+ let(:cookbook_version) { double("Cookbook::Version", version: "1.2.3") }
- 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
+ let(:resource_record) { [] }
- 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
+ let(:exception) { nil }
+
+ let(:action_collection) { Chef::ActionCollection.new(events) }
-describe Chef::DataCollector::Reporter do
- let(:reporter) { described_class.new }
- let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
+ let(:expected_node) { node }
+
+ let(:expected_expansion) { expansion }
+
+ let(:expected_run_list) { run_list.for_json }
+
+ let(:node_uuid) { "779196c6-f94f-4501-9dae-af8081ab4d3a" }
+
+ let(:request_id) { "5db5d686-d18d-4234-a86a-28848c35dfc2" }
before do
- Chef::Config[:data_collector][:server_url] = "http://my-data-collector-server.mycompany.com"
+ allow(Time).to receive(:now).and_return(start_time, end_time)
+ allow(Chef::HTTP::SimpleJSON).to receive(:new).and_return(rest_client)
+ allow(Chef::ServerAPI).to receive(:new).and_return(rest_client)
+ node.name(node_name) unless node.is_a?(Hash)
+ new_resource.cookbook_name = cookbook_name
+ new_resource.recipe_name = recipe_name
+ allow(new_resource).to receive(:cookbook_version).and_return(cookbook_version)
+
+ run_list << "recipe[lobster]" << "role[rage]" << "recipe[fist]"
+ events.register(data_collector)
+ events.register(action_collection)
+ run_status.run_id = request_id
+ events.run_start(Chef::VERSION, run_status)
+ Chef::Config[:chef_guid] = node_uuid
+ # we're guaranteed that those events are processed or else the data collector has no hope
+ # all other events could see the chef-client crash before executing them and the data collector
+ # still needs to work in those cases, so must come later, and the failure cases must be tested.
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
+ def expect_start_message(keys = nil)
+ keys ||= {
+ "chef_server_fqdn" => "localhost",
+ "entity_uuid" => node_uuid,
+ "id" => request_id,
+ "message_type" => "run_start",
+ "message_version" => "1.0.0",
+ "node_name" => node_name,
+ "organization_name" => "unknown_organization",
+ "run_id" => request_id,
+ "source" => "chef_client",
+ "start_time" => start_time.utc.iso8601,
+ }
+ expect(rest_client).to receive(:post).with(
+ nil,
+ hash_including(keys),
+ { "Content-Type" => "application/json" }
+ )
+ end
+
+ def expect_converge_message(keys)
+ keys["message_type"] = "run_converge"
+ keys["message_version"] = "1.1.0"
+ expect(rest_client).to receive(:post).with(
+ nil,
+ hash_including(keys),
+ { "Content-Type" => "application/json" }
+ )
+ end
+
+ def resource_has_diff(new_resource, status)
+ new_resource.respond_to?(:diff) && %w{updated failed}.include?(status)
+ end
+
+ def resource_record_for(new_resource, before_resource, after_resource, action, status, duration)
+ {
+ "after" => after_resource&.state_for_resource_reporter || {},
+ "before" => before_resource&.state_for_resource_reporter || {},
+ "cookbook_name" => cookbook_name,
+ "cookbook_version" => cookbook_version.version,
+ "delta" => resource_has_diff(new_resource, status) ? new_resource.diff : "",
+ "duration" => duration,
+ "id" => new_resource.identity,
+ "ignore_failure" => new_resource.ignore_failure,
+ "name" => new_resource.name,
+ "recipe_name" => recipe_name,
+ "result" => action.to_s,
+ "status" => status,
+ "type" => new_resource.resource_name.to_sym,
+ }
+ end
- it "updates the run status" do
- expect(reporter).to receive(:update_run_status).with(run_status)
- reporter.run_started(run_status)
+ def send_run_failed_or_completed_event
+ status == "success" ? events.run_completed(node, run_status) : events.run_failed(exception, run_status)
+ end
+
+ shared_examples_for "sends a converge message" do
+ it "has a chef_server_fqdn" do
+ expect_converge_message("chef_server_fqdn" => "localhost")
+ send_run_failed_or_completed_event
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)
+ it "has a start_time" do
+ expect_converge_message("start_time" => start_time.utc.iso8601)
+ send_run_failed_or_completed_event
end
- end
- describe "#run_completed" do
- it "sends the run completion" do
- node = Chef::Node.new
+ it "has a end_time" do
+ expect_converge_message("end_time" => end_time.utc.iso8601)
+ send_run_failed_or_completed_event
+ end
- expect(reporter).to receive(:send_run_completion).with(status: "success")
- reporter.run_completed(node)
+ it "has a entity_uuid" do
+ expect_converge_message("entity_uuid" => node_uuid)
+ send_run_failed_or_completed_event
end
- end
- describe "#run_failed" do
- it "updates the exception and sends the run completion" do
- expect(reporter).to receive(:send_run_completion).with(status: "failure")
- reporter.run_failed("test_exception")
+ it "has a expanded_run_list" do
+ expect_converge_message("expanded_run_list" => expected_expansion)
+ send_run_failed_or_completed_event
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")
+ it "has a node" do
+ expect_converge_message("node" => expected_node)
+ send_run_failed_or_completed_event
end
- end
- describe "#converge_complete" do
- it "detects and processes any unprocessed resources" do
- expect(reporter).to receive(:detect_unprocessed_resources)
- reporter.converge_complete
+ it "has a node_name" do
+ expect_converge_message("node_name" => node_name)
+ send_run_failed_or_completed_event
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")
+ it "has an organization" do
+ expect_converge_message("organization_name" => "unknown_organization")
+ send_run_failed_or_completed_event
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
+ it "has a policy_group" do
+ expect_converge_message("policy_group" => nil)
+ send_run_failed_or_completed_event
end
- context "when resource is not a nested resource" do
- it "creates the resource report and stores it as the current one" do
- allow(reporter).to receive(:nested_resource?).and_return(false)
- expect(reporter).to receive(:create_resource_report)
- .with(new_resource, action, current_resource)
- .and_return(resource_report)
- expect(reporter).to receive(:update_current_resource_report).with(resource_report)
- reporter.resource_current_state_loaded(new_resource, action, current_resource)
- end
+ it "has a policy_name" do
+ expect_converge_message("policy_name" => nil)
+ send_run_failed_or_completed_event
end
- end
- describe "#resource_up_to_date" do
- let(:new_resource) { double("new_resource") }
- let(:action) { double("action") }
- let(:resource_report) { double("resource_report") }
+ it "has a run_id" do
+ expect_converge_message("run_id" => request_id)
+ send_run_failed_or_completed_event
+ end
- 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)
+ it "has a run_list" do
+ expect_converge_message("run_list" => expected_run_list)
+ send_run_failed_or_completed_event
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
+ it "has a cookbooks" do
+ expect_converge_message("cookbooks" => cookbooks)
+ send_run_failed_or_completed_event
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
+ it "has a source" do
+ expect_converge_message("source" => "chef_client")
+ send_run_failed_or_completed_event
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") }
+ it "has a status" do
+ expect_converge_message("status" => status)
+ send_run_failed_or_completed_event
+ end
- before do
- allow(reporter).to receive(:nested_resource?)
- allow(reporter).to receive(:create_resource_report).and_return(resource_report)
- allow(resource_report).to receive(:skipped)
+ it "has no deprecations" do
+ expect_converge_message("deprecations" => [])
+ send_run_failed_or_completed_event
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)
+ it "has an error field" do
+ if exception
+ expect_converge_message(
+ "error" => {
+ "class" => exception.class,
+ "message" => exception.message,
+ "backtrace" => exception.backtrace,
+ "description" => error_description,
+ }
+ )
+ else
+ expect(rest_client).to receive(:post).with(
+ nil,
+ hash_excluding("error"),
+ { "Content-Type" => "application/json" }
+ )
end
+ send_run_failed_or_completed_event
end
- context "when the resource is not a nested resource" do
- it "creates the resource report and stores it as the current one" do
- allow(reporter).to receive(:nested_resource?).and_return(false)
- expect(reporter).to receive(:create_resource_report)
- .with(new_resource, action)
- .and_return(resource_report)
- expect(reporter).to receive(:update_current_resource_report).with(resource_report)
- reporter.resource_skipped(new_resource, action, conditional)
- end
+ it "has a total resource count of zero" do
+ expect_converge_message("total_resource_count" => total_resource_count)
+ send_run_failed_or_completed_event
+ end
- it "marks the resource report as skipped" do
- allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
- expect(resource_report).to receive(:skipped).with(conditional)
- reporter.resource_skipped(new_resource, action, conditional)
- end
+ it "has a updated resource count of zero" do
+ expect_converge_message("updated_resource_count" => updated_resource_count)
+ send_run_failed_or_completed_event
+ end
+
+ it "includes the resource record" do
+ expect_converge_message("resources" => resource_record)
+ send_run_failed_or_completed_event
end
end
- describe "#resource_updated" do
- let(:resource_report) { double("resource_report") }
+ describe "when the run fails during node load" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.registration_failed(node_name, exception, Chef::Config).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+ let(:expected_node) { {} } # no node because that failed
+ let(:expected_run_list) { [] } # no run_list without a node
+ let(:expected_expansion) { {} } # no run_list expansion without a run_list
+ let(:resource_record) { [] } # and certainly no resources
before do
- allow(reporter).to receive(:current_resource_report).and_return(resource_report)
- allow(resource_report).to receive(:updated)
+ events.registration_failed(node_name, exception, Chef::Config)
+ run_status.stop_clock
+ run_status.exception = exception
+ expect_start_message
end
- it "marks the resource report as updated" do
- expect(resource_report).to receive(:updated)
- reporter.resource_updated("new_resource", "action")
+ it_behaves_like "sends a converge message"
+ end
+
+ describe "when the run fails during node load" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.node_load_failed(node_name, exception, Chef::Config).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+ let(:expected_node) { {} } # no node because that failed
+ let(:expected_run_list) { [] } # no run_list without a node
+ let(:expected_expansion) { {} } # no run_list expansion without a run_list
+ let(:resource_record) { [] } # and certainly no resources
+
+ before do
+ events.node_load_failed(node_name, exception, Chef::Config)
+ run_status.stop_clock
+ run_status.exception = exception
+ expect_start_message
end
+
+ it_behaves_like "sends a converge message"
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") }
+ describe "when the run fails during run_list_expansion" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.run_list_expand_failed(node, exception).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+ let(:expected_expansion) { {} } # no run_list expanasion when it failed
+ let(:resource_record) { [] } # and no resources
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
+ events.node_load_success(node)
+ run_status.node = node
+ events.run_list_expand_failed(node, exception)
+ run_status.stop_clock
+ run_status.exception = exception
+ expect_start_message
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
+ it_behaves_like "sends a converge message"
+ end
+
+ describe "when the run fails during cookbook resolution" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.cookbook_resolution_failed(node, exception).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+ let(:resource_record) { [] } # and no resources
+
+ before do
+ events.node_load_success(node)
+ run_status.node = node
+ events.run_list_expanded(expansion)
+ run_status.start_clock
+ expect_start_message
+ events.cookbook_resolution_failed(node, exception)
+ run_status.stop_clock
+ run_status.exception = exception
end
+
+ it_behaves_like "sends a converge message"
end
- describe "#resource_completed" do
- let(:new_resource) { double("new_resource") }
- let(:resource_report) { double("resource_report") }
+ describe "when the run fails during cookbook synchronization" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.cookbook_sync_failed(node, exception).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+ let(:resource_record) { [] } # and no resources
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)
+ events.node_load_success(node)
+ run_status.node = node
+ events.run_list_expanded(expansion)
+ run_status.start_clock
+ expect_start_message
+ events.cookbook_sync_failed(node, exception)
+ run_status.stop_clock
+ run_status.exception = exception
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
+ it_behaves_like "sends a converge message"
+ end
+
+ describe "after successfully starting the run" do
+ before do
+ # these events happen in this order in the client
+ events.node_load_success(node)
+ run_status.node = node
+ events.run_list_expanded(expansion)
+ run_status.start_clock
end
- context "when there is a current resource report" do
- before do
- allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+ describe "run_start_message" do
+ it "sends a run_start_message" do
+ expect_start_message
+ events.run_started(run_status)
+ end
+
+ it "extracts the hostname from the chef_server_url" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local"
+ expect_start_message("chef_server_fqdn" => "spacex.rockets.local")
+ events.run_started(run_status)
+ end
+
+ it "extracts the organization from the chef_server_url" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc"
+ expect_start_message("organization_name" => "gnc")
+ events.run_started(run_status)
+ end
+
+ it "extracts the organization from the chef_server_url if there are extra slashes" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local///organizations///gnc"
+ expect_start_message("organization_name" => "gnc")
+ events.run_started(run_status)
+ end
+
+ it "extracts the organization from the chef_server_url if there is a trailing slash" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/"
+ expect_start_message("organization_name" => "gnc")
+ events.run_started(run_status)
+ end
+
+ it "sets 'unknown_organization' if the cher_server_url does not contain one" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local"
+ expect_start_message("organization_name" => "unknown_organization")
+ events.run_started(run_status)
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)
+ it "still uses the chef_server_url in non-solo mode even if the data_collector organization is set" do
+ Chef::Config[:data_collector][:organization] = "blue-origin"
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/"
+ expect_start_message("organization_name" => "gnc")
+ events.run_started(run_status)
+ end
+
+ describe "in legacy mode" do
+ before do
+ Chef::Config[:solo_legacy_mode] = true
+ Chef::Config[:data_collector][:server_url] = "https://nasa.rockets.local/organizations/sls"
+ end
+
+ it "we get the data collector organization" do
+ Chef::Config[:data_collector][:organization] = "blue-origin"
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/" # should be ignored
+ expect_start_message("organization_name" => "blue-origin")
+ events.run_started(run_status)
+ end
+
+ it "if the data collector org is unset we get 'chef_solo'" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/" # should be ignored
+ expect_start_message("organization_name" => "chef_solo")
+ events.run_started(run_status)
+ end
+
+ it "sets the source" do
+ expect_start_message("source" => "chef_solo")
+ events.run_started(run_status)
end
end
- context "when the resource is not a nested resource" do
+ describe "in local mode" do
before do
- allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
+ Chef::Config[:local_mode] = true
+ Chef::Config[:data_collector][:server_url] = "https://nasa.rockets.local/organizations/sls"
+ end
+
+ it "we get the data collector organization" do
+ Chef::Config[:data_collector][:organization] = "blue-origin"
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/" # should be ignored
+ expect_start_message("organization_name" => "blue-origin")
+ events.run_started(run_status)
end
- it "marks the current resource report as finished" do
- expect(resource_report).to receive(:finish)
- reporter.resource_completed(new_resource)
+ it "if the data collector org is unset we get 'chef_solo'" do
+ Chef::Config[:chef_server_url] = "https://spacex.rockets.local/organizations/gnc/" # should be ignored
+ expect_start_message("organization_name" => "chef_solo")
+ events.run_started(run_status)
end
- it "nils out the current resource report" do
- expect(reporter).to receive(:update_current_resource_report).with(nil)
- reporter.resource_completed(new_resource)
+ it "sets the source" do
+ expect_start_message("source" => "chef_solo")
+ events.run_started(run_status)
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 "converge messages" do
+ before do
+ expect_start_message
+ events.run_started(run_status)
+ events.cookbook_compilation_start(run_context)
+ end
- describe "#run_list_expand_failed" do
- let(:node) { double("node") }
- let(:error_mapper) { double("error_mapper") }
- let(:exception) { double("exception") }
+ context "with no resources" do
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) { [ ] }
+ let(:status) { "success" }
- 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
+ before do
+ run_status.stop_clock
+ 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_behaves_like "sends a converge message"
- 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
+ it "sets the policy_group" do
+ node.policy_group = "acceptionsal"
+ expect_converge_message("policy_group" => "acceptionsal")
+ send_run_failed_or_completed_event
+ end
- end
+ it "has a policy_name" do
+ node.policy_name = "webappdb"
+ expect_converge_message("policy_name" => "webappdb")
+ send_run_failed_or_completed_event
+ end
- describe "#cookbook_sync_failed" do
- let(:cookbooks) { double("cookbooks") }
- let(:error_mapper) { double("error_mapper") }
- let(:exception) { double("exception") }
+ it "collects deprecation messages" do
+ location = Chef::Log.caller_location
+ events.deprecation(Chef::Deprecated.create(:internal_api, "deprecation warning", location))
+ expect_converge_message("deprecations" => [{ location: location, message: "deprecation warning", url: "https://docs.chef.io/deprecations_internal_api/" }])
+ send_run_failed_or_completed_event
+ end
+ end
- 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
+ context "when the run contains a file resource that is up-to-date" do
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) { [ resource_record_for(new_resource, current_resource, after_resource, :create, "up-to-date", "1234") ] }
+ let(:status) { "success" }
- 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 }
+ before do
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_up_to_date(new_resource, :create)
+ events.resource_after_state_loaded(new_resource, :create, after_resource)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ end
+
+ it_behaves_like "sends a converge message"
end
- it "does not raise an exception" do
- expect { reporter.send(:disable_reporter_on_error) { true } }.not_to raise_error
+ context "when the run contains a file resource that is updated" do
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 1 }
+ let(:resource_record) { [ resource_record_for(new_resource, current_resource, after_resource, :create, "updated", "1234") ] }
+ let(:status) { "success" }
+
+ before do
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_updated(new_resource, :create)
+ events.resource_after_state_loaded(new_resource, :create, after_resource)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ end
+
+ it_behaves_like "sends a converge message"
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)
+ context "When there is an embedded resource, it includes the sub-resource in the report" do
+ let(:total_resource_count) { 2 }
+ let(:updated_resource_count) { 2 }
+ let(:implementation_resource) do
+ r = Chef::Resource::CookbookFile.new("/preseed-file.txt")
+ r.cookbook_name = cookbook_name
+ r.recipe_name = recipe_name
+ allow(r).to receive(:cookbook_version).and_return(cookbook_version)
+ r
+ end
+ let(:resource_record) { [ resource_record_for(implementation_resource, implementation_resource, implementation_resource, :create, "updated", "2345"), resource_record_for(new_resource, current_resource, after_resource, :create, "updated", "1234") ] }
+ let(:status) { "success" }
+
+ before do
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+
+ events.resource_action_start(implementation_resource , :create)
+ events.resource_current_state_loaded(implementation_resource, :create, implementation_resource)
+ events.resource_updated(implementation_resource, :create)
+ events.resource_after_state_loaded(implementation_resource, :create, implementation_resource)
+ implementation_resource.instance_variable_set(:@elapsed_time, 2.3456)
+ events.resource_completed(implementation_resource)
+
+ events.resource_updated(new_resource, :create)
+ events.resource_after_state_loaded(new_resource, :create, after_resource)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ end
+
+ it_behaves_like "sends a converge message"
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") }
+ context "when the run contains a file resource that is skipped due to a block conditional" do
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) do
+ rec = resource_record_for(new_resource, nil, nil, :create, "skipped", "1234")
+ rec["conditional"] = "not_if { #code block }" # FIXME: "#code block" is poor, is there some way to fix this?
+ [ rec ]
+ end
+ let(:status) { "success" }
+
+ before do
+ conditional = (new_resource.not_if { true }).first
+ events.resource_action_start(new_resource, :create)
+ events.resource_skipped(new_resource, :create, conditional)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ end
+
+ it_behaves_like "sends a converge message"
+ end
+
+ context "when the run contains a file resource that is skipped due to a string conditional" do
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) do
+ rec = resource_record_for(new_resource, nil, nil, :create, "skipped", "1234")
+ rec["conditional"] = 'not_if "true"'
+ [ rec ]
+ end
+ let(:status) { "success" }
+
+ before do
+ conditional = (new_resource.not_if "true").first
+ events.resource_action_start(new_resource, :create)
+ events.resource_skipped(new_resource, :create, conditional)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ end
+
+ it_behaves_like "sends a converge message"
+ end
+
+ context "when the run contains a file resource that threw an exception" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.resource_failed(new_resource, :create, exception).for_json }
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) do
+ rec = resource_record_for(new_resource, current_resource, nil, :create, "failed", "1234")
+ rec["error_message"] = "imperial to metric conversion error"
+ rec["error"] = {
+ "class" => exception.class,
+ "message" => exception.message,
+ "backtrace" => exception.backtrace,
+ "description" => error_description,
+ }
+
+ [ rec ]
+ end
+ let(:status) { "failure" }
+
+ before do
+ exception.set_backtrace(caller)
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_failed(new_resource, :create, exception)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_complete
+ run_status.stop_clock
+ run_status.exception = exception
+ end
+
+ it_behaves_like "sends a converge message"
+ end
+
+ context "when the run contains a file resource that threw an exception in load_current_resource" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.resource_failed(new_resource, :create, exception).for_json }
+ let(:total_resource_count) { 1 }
+ let(:updated_resource_count) { 0 }
+ let(:resource_record) do
+ rec = resource_record_for(new_resource, nil, nil, :create, "failed", "1234")
+ rec["before"] = {}
+ rec["error_message"] = "imperial to metric conversion error"
+ rec["error"] = {
+ "class" => exception.class,
+ "message" => exception.message,
+ "backtrace" => exception.backtrace,
+ "description" => error_description,
+ }
+
+ [ rec ]
+ end
+ let(:status) { "failure" }
+
+ before do
+ exception.set_backtrace(caller)
+ events.resource_action_start(new_resource, :create)
+ # resource_current_state_loaded is skipped
+ events.resource_failed(new_resource, :create, exception)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ events.converge_failed(exception)
+ run_status.stop_clock
+ run_status.exception = exception
+ end
+
+ it_behaves_like "sends a converge message"
+ end
+
+ context "when the resource collection contains a resource that was unprocessed due to prior errors" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.resource_failed(new_resource, :create, exception).for_json }
+ let(:total_resource_count) { 2 }
+ let(:updated_resource_count) { 0 }
+ let(:unprocessed_resource) do
+ res = Chef::Resource::Service.new("unprocessed service")
+ res.cookbook_name = cookbook_name
+ res.recipe_name = recipe_name
+ allow(res).to receive(:cookbook_version).and_return(cookbook_version)
+ res
+ end
+ let(:resource_record) do
+ rec1 = resource_record_for(new_resource, current_resource, nil, :create, "failed", "1234")
+ rec1["error_message"] = "imperial to metric conversion error"
+ rec1["error"] = {
+ "class" => exception.class,
+ "message" => exception.message,
+ "backtrace" => exception.backtrace,
+ "description" => error_description,
+ }
+
+ rec2 = resource_record_for(unprocessed_resource, nil, nil, :nothing, "unprocessed", "")
+ [ rec1, rec2 ]
end
+ let(:status) { "failure" }
+
+ before do
+ run_context.resource_collection << new_resource
+ run_context.resource_collection << unprocessed_resource
+ exception.set_backtrace(caller)
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_failed(new_resource, :create, exception)
+ new_resource.instance_variable_set(:@elapsed_time, 1.2345)
+ events.resource_completed(new_resource)
+ new_resource.executed_by_runner = true
+ events.converge_failed(exception)
+ run_status.stop_clock
+ run_status.exception = exception
+ end
+
+ it_behaves_like "sends a converge message"
+ 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
+ context "when cookbook resolution fails" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.cookbook_resolution_failed(expansion, exception).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+
+ before do
+ events.cookbook_resolution_failed(expansion, exception)
+ run_status.stop_clock
+ run_status.exception = exception
end
- context "when raise-on-failure is disabled" do
- it "logs a warning and does not raise an exception" do
- Chef::Config[:data_collector][:raise_on_failure] = false
- expect(Chef::Log).to receive(:warn)
- expect { reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } }.not_to raise_error
- end
+ it_behaves_like "sends a converge message"
+ end
+
+ context "When cookbook synchronization fails" do
+ let(:exception) { Exception.new("imperial to metric conversion error") }
+ let(:error_description) { Chef::Formatters::ErrorMapper.cookbook_sync_failed({}, exception).for_json }
+ let(:total_resource_count) { 0 }
+ let(:updated_resource_count) { 0 }
+ let(:status) { "failure" }
+
+ before do
+ events.cookbook_sync_failed(expansion, exception)
+ run_status.stop_clock
+ run_status.exception = exception
end
+
+ it_behaves_like "sends a converge message"
end
+
+ end
+ end
+
+ describe "#send_to_file_location(file_name, message)" do
+ let(:tempfile) { Tempfile.new("rspec-chef-datacollector-out") }
+ let(:shift_jis) { "I have no idea what this character is:\n #{0x83.chr}#{0x80.chr}.\n" }
+ it "handles invalid UTF-8 properly" do
+ data_collector.send(:send_to_file_location, tempfile, { invalid: shift_jis })
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)
+ describe "#send_to_datacollector" do
+ def stub_http_client(exception = nil)
+ if exception.nil?
+ expect(http_client).to receive(:post).with(nil, message, data_collector.send(:headers))
+ else
+ expect(http_client).to receive(:post).with(nil, message, data_collector.send(:headers)).and_raise(exception)
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)
+ let(:message) { "message" }
+ let(:http_client) { instance_double(Chef::ServerAPI) }
+
+ before do
+ expect(data_collector).to receive(:setup_http_client).and_return(http_client)
+ end
+
+ it "does not disable the data_collector when no exception is raised" do
+ stub_http_client
+ expect(data_collector.events).not_to receive(:unregister)
+ data_collector.send(:send_to_data_collector, message)
+ end
+
+ errors = [ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
+ Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+ Errno::EHOSTDOWN ]
+
+ errors.each do |exception_class|
+ context "when the client raises a #{exception_class} exception" do
+ before do
+ stub_http_client(exception_class)
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
+ it "disables the reporter" do
+ expect(data_collector.events).to receive(:unregister).with(data_collector)
+ data_collector.send(:send_to_data_collector, message)
+ end
+ it "logs an error and raises when raise_on_failure is enabled" do
+ Chef::Config[:data_collector][:raise_on_failure] = true
+ expect(Chef::Log).to receive(:error)
+ expect { data_collector.send(:send_to_data_collector, message) }.to raise_error(exception_class)
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
+ it "logs a warn message and does not raise an exception when raise_on_failure is disabled" do
+ Chef::Config[:data_collector][:raise_on_failure] = false
+ expect(Chef::Log).to receive(:warn)
+ data_collector.send(:send_to_data_collector, message)
end
end
end
+
+ context "when the client raises a 404 exception" do
+ let(:err) do
+ response = double("Net::HTTP response", code: "404")
+ Net::HTTPClientException.new("Not Found", response)
+ end
+
+ before do
+ stub_http_client(err)
+ end
+
+ it "disables the reporter" do
+ expect(data_collector.events).to receive(:unregister).with(data_collector)
+ data_collector.send(:send_to_data_collector, message)
+ end
+
+ it "logs an error and raises when raise_on_failure is enabled" do
+ Chef::Config[:data_collector][:raise_on_failure] = true
+ expect(Chef::Log).to receive(:error)
+ expect { data_collector.send(:send_to_data_collector, message) }.to raise_error(err)
+ end
+
+ # this is different specifically for 404s
+ it "logs an info message and does not raise an exception when raise_on_failure is disabled" do
+ Chef::Config[:data_collector][:raise_on_failure] = false
+ expect(Chef::Log).to receive(:debug).with(/This is normal if you do not have Chef Automate/)
+ data_collector.send(:send_to_data_collector, message)
+ end
+ end
end
end
diff --git a/spec/unit/decorator/lazy_array_spec.rb b/spec/unit/decorator/lazy_array_spec.rb
index 0c5c2eeee0..2d47ff8b15 100644
--- a/spec/unit/decorator/lazy_array_spec.rb
+++ b/spec/unit/decorator/lazy_array_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/decorator/lazy_spec.rb b/spec/unit/decorator/lazy_spec.rb
index 4ea8301b63..cdfc8e42c1 100644
--- a/spec/unit/decorator/lazy_spec.rb
+++ b/spec/unit/decorator/lazy_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Decorator::Lazy do
let(:decorator) do
@a = 0
- Chef::Decorator::Lazy.new { @a = @a + 1 }
+ Chef::Decorator::Lazy.new { @a += 1 }
end
it "decorates an object" do
diff --git a/spec/unit/decorator_spec.rb b/spec/unit/decorator_spec.rb
index 6d73db2cc4..e88708bea7 100644
--- a/spec/unit/decorator_spec.rb
+++ b/spec/unit/decorator_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,37 +18,37 @@
require "spec_helper"
-def impersonates_a(klass)
- it "#is_a?(#{klass}) is true" do
- expect(decorator.is_a?(klass)).to be true
- end
+describe Chef::Decorator do
+ def self.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 "#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?(#{klass}) is true" do
+ expect(decorator.is_a?(klass)).to be true
+ end
- it "#kind_of?(Chef::Decorator) is true" do
- expect(decorator.kind_of?(Chef::Decorator)).to be true
- end
+ it "#kind_of?(Chef::Decorator) is true" do
+ expect(decorator.is_a?(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?(#{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 "#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)
+ it "#class is Chef::Decorator" do
+ expect(decorator.class).to eql(Chef::Decorator)
+ end
end
-end
-describe Chef::Decorator do
let(:obj) {}
let(:decorator) { Chef::Decorator.new(obj) }
diff --git a/spec/unit/deprecated_spec.rb b/spec/unit/deprecated_spec.rb
new file mode 100644
index 0000000000..4bad8adfc2
--- /dev/null
+++ b/spec/unit/deprecated_spec.rb
@@ -0,0 +1,65 @@
+#
+# Copyright:: Copyright (c) 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
+ target 999, "test"
+ end
+
+ context "loading a deprecation class" do
+ it "loads the correct class" do
+ expect(Chef::Deprecated.create(:test_deprecation, nil, nil)).to be_an_instance_of(TestDeprecation)
+ end
+
+ it "sets a message" do
+ deprecation = Chef::Deprecated.create(:test_deprecation, "A test message", nil)
+ expect(deprecation.message).to eql("A test message")
+ end
+
+ it "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(:message) { "A test message" }
+ let(:location) { "the location" }
+
+ it "displays the full URL" do
+ expect(TestDeprecation.new.url).to eql("https://docs.chef.io/deprecations_test/")
+ end
+
+ it "formats a complete deprecation message" do
+ expect(TestDeprecation.new(message, location).to_s).to eql("Deprecation CHEF-999 from the location\n\n A test message\n\nPlease see https://docs.chef.io/deprecations_test/ for further details and information on how to correct this problem.")
+ end
+ end
+
+ it "has no overlapping deprecation IDs" do
+ id_map = {}
+ ObjectSpace.each_object(Class).select { |cls| cls < Chef::Deprecated::Base }.each do |cls|
+ (id_map[cls.deprecation_id] ||= []) << cls
+ end
+ collisions = id_map.select { |k, v| v.size != 1 }
+ unless collisions.empty?
+ raise "Found deprecation ID collisions:\n#{collisions.map { |k, v| "* #{k} #{v.map(&:name).join(", ")}" }.join("\n")}"
+ end
+ end
+end
diff --git a/spec/unit/deprecation_spec.rb b/spec/unit/deprecation_spec.rb
index af8e34850b..f2895f3369 100644
--- a/spec/unit/deprecation_spec.rb
+++ b/spec/unit/deprecation_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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/digester_spec.rb b/spec/unit/digester_spec.rb
index 2684ac8f7d..592f2b2496 100644
--- a/spec/unit/digester_spec.rb
+++ b/spec/unit/digester_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2009-2016, Daniel DeLeo
# License:: Apache License, Version 2.0
#
diff --git a/spec/unit/dsl/audit_spec.rb b/spec/unit/dsl/audit_spec.rb
deleted file mode 100644
index 42e543fdb2..0000000000
--- a/spec/unit/dsl/audit_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-
-require "spec_helper"
-require "chef/dsl/audit"
-
-class AuditDSLTester < Chef::Recipe
- include Chef::DSL::Audit
-end
-
-class BadAuditDSLTester
- include Chef::DSL::Audit
-end
-
-describe Chef::DSL::Audit do
- let(:auditor) { AuditDSLTester.new("cookbook_name", "recipe_name", run_context) }
- let(:run_context) { instance_double(Chef::RunContext, :audits => audits, :cookbook_collection => cookbook_collection) }
- let(:audits) { {} }
- let(:cookbook_collection) { {} }
-
- it "raises an error when a block of audits is not provided" do
- expect { auditor.control_group "name" }.to raise_error(Chef::Exceptions::NoAuditsProvided)
- end
-
- it "raises an error when no audit name is given" do
- 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") {} }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
- end
- end
-
- context "included in a class without recipe DSL" do
- let(:auditor) { BadAuditDSLTester.new }
-
- it "fails because it relies on the recipe DSL existing" do
- expect { auditor.control_group("unique") {} }.to raise_error(NoMethodError, /undefined method `cookbook_name'/)
- end
- end
-
-end
diff --git a/spec/unit/dsl/data_query_spec.rb b/spec/unit/dsl/data_query_spec.rb
index f93f07bc52..5ede954e02 100644
--- a/spec/unit/dsl/data_query_spec.rb
+++ b/spec/unit/dsl/data_query_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ class DataQueryDSLTester
end
describe Chef::DSL::DataQuery do
- let(:node) { Hash.new }
+ let(:node) { {} }
let(:language) do
language = DataQueryDSLTester.new
@@ -68,7 +68,8 @@ describe Chef::DSL::DataQuery do
"a1" => [1, 2, 3],
"a2" => { "b1" => true },
},
- } end
+ }
+ 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 57a7fd7f18..53b0713c42 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@ require "spec_helper"
describe Chef::ResourceCollection do
let(:run_context) do
- cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "cookbooks"))
+ cookbook_repo = File.expand_path(File.join(__dir__, "..", "..", "data", "cookbooks"))
cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
cookbook_loader.load_cookbooks
node = Chef::Node.new
@@ -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 = %i{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
diff --git a/spec/unit/dsl/platform_introspection_spec.rb b/spec/unit/dsl/platform_introspection_spec.rb
index fd1f9b23b5..875de03f8e 100644
--- a/spec/unit/dsl/platform_introspection_spec.rb
+++ b/spec/unit/dsl/platform_introspection_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,7 @@ require "chef/dsl/platform_introspection"
class LanguageTester
attr_reader :node
+
def initialize(node)
@node = node
end
@@ -39,8 +40,8 @@ end
describe Chef::DSL::PlatformIntrospection::PlatformDependentValue do
before do
platform_hash = {
- :openbsd => { :default => "free, functional, secure" },
- [:redhat, :centos, :fedora, :scientific] => { :default => '"stable"' },
+ :openbsd => { default: "free, functional, secure" },
+ %i{redhat centos fedora scientific} => { default: '"stable"' },
:ubuntu => { "10.04" => "using upstart more", :default => "using init more" },
:default => "bork da bork",
}
@@ -48,24 +49,24 @@ describe Chef::DSL::PlatformIntrospection::PlatformDependentValue do
end
it "returns the default value when the platform doesn't match" do
- expect(@platform_specific_value.value_for_node(:platform => :dos)).to eq("bork da bork")
+ expect(@platform_specific_value.value_for_node(platform: :dos)).to eq("bork da bork")
end
it "returns a value for a platform set as a group" do
- expect(@platform_specific_value.value_for_node(:platform => :centos)).to eq('"stable"')
+ expect(@platform_specific_value.value_for_node(platform: :centos)).to eq('"stable"')
end
it "returns a value for the platform when it was set as a symbol but fetched as a string" do
- expect(@platform_specific_value.value_for_node(:platform => "centos")).to eq('"stable"')
+ expect(@platform_specific_value.value_for_node(platform: "centos")).to eq('"stable"')
end
it "returns a value for a specific platform version" do
- node = { :platform => "ubuntu", :platform_version => "10.04" }
+ node = { platform: "ubuntu", platform_version: "10.04" }
expect(@platform_specific_value.value_for_node(node)).to eq("using upstart more")
end
it "returns a platform-default value if the platform version doesn't match an explicit one" do
- node = { :platform => "ubuntu", :platform_version => "9.10" }
+ node = { platform: "ubuntu", platform_version: "9.10" }
expect(@platform_specific_value.value_for_node(node)).to eq("using init more")
end
@@ -73,18 +74,18 @@ describe Chef::DSL::PlatformIntrospection::PlatformDependentValue do
# this matches the behavior in the original implementation.
# whether or not it's correct is another matter.
platform_specific_value = Chef::DSL::PlatformIntrospection::PlatformDependentValue.new({})
- expect(platform_specific_value.value_for_node(:platform => "foo")).to be_nil
+ expect(platform_specific_value.value_for_node(platform: "foo")).to be_nil
end
it "raises an argument error if the platform hash is not correctly structured" do
- bad_hash = { :ubuntu => :foo } # should be :ubuntu => {:default => 'foo'}
+ bad_hash = { ubuntu: :foo } # should be :ubuntu => {:default => 'foo'}
expect { Chef::DSL::PlatformIntrospection::PlatformDependentValue.new(bad_hash) }.to raise_error(ArgumentError)
end
end
describe Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue do
before do
- @array_values = [:stop, :start, :reload]
+ @array_values = %i{stop start reload}
@platform_family_hash = {
"debian" => "debian value",
@@ -98,32 +99,62 @@ describe Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue do
end
it "returns the default value when the platform family doesn't match" do
- expect(@platform_family_value.value_for_node(:platform_family => :os2)).to eq("default value")
+ expect(@platform_family_value.value_for_node(platform_family: :os2)).to eq("default value")
end
it "returns a value for the platform family when it was set as a string but fetched as a symbol" do
- expect(@platform_family_value.value_for_node(:platform_family => :debian)).to eq("debian value")
+ expect(@platform_family_value.value_for_node(platform_family: :debian)).to eq("debian value")
end
it "returns a value for the platform family when it was set as a symbol but fetched as a string" do
- expect(@platform_family_value.value_for_node(:platform_family => "gentoo")).to eq("gentoo value")
+ expect(@platform_family_value.value_for_node(platform_family: "gentoo")).to eq("gentoo value")
end
it "returns an array value stored for a platform family" do
- expect(@platform_family_value.value_for_node(:platform_family => "suse")).to eq(@array_values)
+ expect(@platform_family_value.value_for_node(platform_family: "suse")).to eq(@array_values)
end
it "returns a value for the platform family when it was set within an array hash key as a symbol" do
- expect(@platform_family_value.value_for_node(:platform_family => :rhel)).to eq("redhatty value")
+ expect(@platform_family_value.value_for_node(platform_family: :rhel)).to eq("redhatty value")
end
it "returns a value for the platform family when it was set within an array hash key as a string" do
- expect(@platform_family_value.value_for_node(:platform_family => "fedora")).to eq("redhatty value")
+ expect(@platform_family_value.value_for_node(platform_family: "fedora")).to eq("redhatty value")
end
it "returns nil if there is no default and no platforms match" do
platform_specific_value = Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue.new({})
- expect(platform_specific_value.value_for_node(:platform_family => "foo")).to be_nil
+ expect(platform_specific_value.value_for_node(platform_family: "foo")).to be_nil
end
end
+
+describe "ChefHelper functions mixed into classes" do
+ METHODS = %i{windows? fedora_derived? bsd_based? rhel? aix? gentoo?}.freeze
+
+ METHODS.each do |method|
+ it "mixes #{method} into Chef::Resource instances" do
+ expect(Chef::Resource.instance_methods.include?(method)).to be true
+ end
+
+ it "mixes #{method} into Chef::Resource classes (provides lines, etc)" do
+ expect(Chef::Resource.respond_to?(method)).to be true
+ end
+
+ it "mixes #{method} into Chef::Provider instances (actions)" do
+ expect(Chef::Provider.instance_methods.include?(method)).to be true
+ end
+
+ it "mixes #{method} into Chef::Provider classes (provides lines, etc)" do
+ expect(Chef::Provider.respond_to?(method)).to be true
+ end
+
+ it "mixes #{method} into Chef::Recipe instances (recipe files)" do
+ expect(Chef::Recipe.instance_methods.include?(method)).to be true
+ end
+
+ it "mixes #{method} into Chef::Node instances (attribute files)" do
+ expect(Chef::Recipe.instance_methods.include?(method)).to be true
+ end
+ end
+end
diff --git a/spec/unit/dsl/reboot_pending_spec.rb b/spec/unit/dsl/reboot_pending_spec.rb
index 5cd7c7794f..4fed3be442 100644
--- a/spec/unit/dsl/reboot_pending_spec.rb
+++ b/spec/unit/dsl/reboot_pending_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,7 +36,7 @@ describe Chef::DSL::RebootPending do
end
it 'should return true if "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations" exists' do
- allow(recipe).to receive(:registry_value_exists?).with('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => "PendingFileRenameOperations" }).and_return(true)
+ allow(recipe).to receive(:registry_value_exists?).with('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { name: "PendingFileRenameOperations" }).and_return(true)
expect(recipe.reboot_pending?).to be_truthy
end
@@ -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..395dfc9373 100644
--- a/spec/unit/dsl/recipe_spec.rb
+++ b/spec/unit/dsl/recipe_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -66,10 +53,6 @@ describe Chef::DSL::Recipe do
it "responds to shell_out" do
expect(recipe.respond_to?(:shell_out!)).to be true
end
-
- it "responds to shell_out" do
- expect(recipe.respond_to?(:shell_out_with_systems_locale)).to be true
- end
end
context "when included in a class that defines the required interface directly" do
diff --git a/spec/unit/dsl/registry_helper_spec.rb b/spec/unit/dsl/registry_helper_spec.rb
index 45c7e73979..5ef8caa053 100644
--- a/spec/unit/dsl/registry_helper_spec.rb
+++ b/spec/unit/dsl/registry_helper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Resource::RegistryKey do
- before (:all) do
+ before(:all) do
events = Chef::EventDispatch::Dispatcher.new
node = Chef::Node.new
node.consume_external_attrs(OHAI_SYSTEM.data, {})
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 7e885f8818..9476dadb9c 100644
--- a/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
+++ b/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,7 +47,8 @@ describe Chef::EncryptedDataBagItem::CheckEncrypted do
"a1" => [1, 2, 3],
"a2" => { "b1" => true },
},
- } end
+ }
+ end
let(:version) { 1 }
let(:encoded_data) do
@@ -84,7 +85,7 @@ describe Chef::EncryptedDataBagItem::CheckEncrypted do
end
end
- context "when encryption version is 3", :aes_256_gcm_only, ruby: "~> 2.0.0" do
+ context "when encryption version is 3", :aes_256_gcm_only do
include_examples "encryption detected" do
let(:version) { 3 }
let(:encryptor) { Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor }
diff --git a/spec/unit/encrypted_data_bag_item_spec.rb b/spec/unit/encrypted_data_bag_item_spec.rb
index a8fb144bf7..d158f29856 100644
--- a/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/spec/unit/encrypted_data_bag_item_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -97,7 +97,7 @@ describe Chef::EncryptedDataBagItem::Encryptor do
Chef::Config[:data_bag_encrypt_version] = 3
end
- context "on supported platforms", :aes_256_gcm_only, ruby: "~> 2.0.0" do
+ context "on supported platforms", :aes_256_gcm_only do
it "creates a version 3 encryptor" do
expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor)
@@ -166,7 +166,7 @@ describe Chef::EncryptedDataBagItem::Decryptor do
context "when decrypting a version 3 (JSON+aes-256-gcm+random iv+auth tag) encrypted value" do
- context "on supported platforms", :aes_256_gcm_only, ruby: "~> 2.0.0" do
+ context "on supported platforms", :aes_256_gcm_only do
let(:encrypted_value) do
Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.new(plaintext_data, encryption_key).for_encrypted_item
@@ -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
@@ -325,7 +325,8 @@ describe Chef::EncryptedDataBagItem do
"id" => "item_name",
"greeting" => "hello",
"nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
- } end
+ }
+ end
let(:secret) { "abc123SECRET" }
let(:encoded_data) { subject.encrypt_data_bag_item(plaintext_data, secret) }
@@ -336,7 +337,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"]
@@ -346,7 +347,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 63c96ad93e..33553aed35 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,8 +48,8 @@ describe Chef::Environment do
end
it "should not accept anything but strings" do
- expect { @environment.name(Array.new) }.to raise_error(ArgumentError)
- expect { @environment.name(Hash.new) }.to raise_error(ArgumentError)
+ expect { @environment.name([]) }.to raise_error(ArgumentError)
+ expect { @environment.name({}) }.to raise_error(ArgumentError)
expect { @environment.name(2) }.to raise_error(ArgumentError)
end
end
@@ -65,47 +65,47 @@ describe Chef::Environment do
end
it "should not accept anything but strings" do
- expect { @environment.description(Array.new) }.to raise_error(ArgumentError)
- expect { @environment.description(Hash.new) }.to raise_error(ArgumentError)
+ expect { @environment.description([]) }.to raise_error(ArgumentError)
+ expect { @environment.description({}) }.to raise_error(ArgumentError)
expect { @environment.description(42) }.to raise_error(ArgumentError)
end
end
describe "default attributes" do
it "should let you set the attributes hash explicitly" do
- expect(@environment.default_attributes({ :one => "two" })).to eq({ :one => "two" })
+ expect(@environment.default_attributes({ one: "two" })).to eq({ one: "two" })
end
it "should let you return the attributes hash" do
- @environment.default_attributes({ :one => "two" })
- expect(@environment.default_attributes).to eq({ :one => "two" })
+ @environment.default_attributes({ one: "two" })
+ expect(@environment.default_attributes).to eq({ one: "two" })
end
it "should throw an ArgumentError if we aren't a kind of hash" do
- expect { @environment.default_attributes(Array.new) }.to raise_error(ArgumentError)
+ expect { @environment.default_attributes([]) }.to raise_error(ArgumentError)
end
end
describe "override attributes" do
it "should let you set the attributes hash explicitly" do
- expect(@environment.override_attributes({ :one => "two" })).to eq({ :one => "two" })
+ expect(@environment.override_attributes({ one: "two" })).to eq({ one: "two" })
end
it "should let you return the attributes hash" do
- @environment.override_attributes({ :one => "two" })
- expect(@environment.override_attributes).to eq({ :one => "two" })
+ @environment.override_attributes({ one: "two" })
+ expect(@environment.override_attributes).to eq({ one: "two" })
end
it "should throw an ArgumentError if we aren't a kind of hash" do
- expect { @environment.override_attributes(Array.new) }.to raise_error(ArgumentError)
+ expect { @environment.override_attributes([]) }.to raise_error(ArgumentError)
end
end
describe "cookbook_versions" do
before(:each) do
@cookbook_versions = {
- "apt" => "= 1.0.0",
- "god" => "= 2.0.0",
+ "apt" => "= 1.0.0",
+ "god" => "= 2.0.0",
"apache2" => "= 4.2.0",
}
end
@@ -121,7 +121,7 @@ describe Chef::Environment do
it "should not accept anything but a hash" do
expect { @environment.cookbook_versions("I am a string!") }.to raise_error(ArgumentError)
- expect { @environment.cookbook_versions(Array.new) }.to raise_error(ArgumentError)
+ expect { @environment.cookbook_versions([]) }.to raise_error(ArgumentError)
expect { @environment.cookbook_versions(42) }.to raise_error(ArgumentError)
end
@@ -167,7 +167,7 @@ describe Chef::Environment do
before(:each) do
@environment.name("spec")
@environment.description("Where we run the spec tests")
- @environment.cookbook_versions({ :apt => "= 1.2.3" })
+ @environment.cookbook_versions({ apt: "= 1.2.3" })
@hash = @environment.to_hash
end
@@ -190,7 +190,7 @@ describe Chef::Environment do
before(:each) do
@environment.name("spec")
@environment.description("Where we run the spec tests")
- @environment.cookbook_versions({ :apt => "= 1.2.3" })
+ @environment.cookbook_versions({ apt: "= 1.2.3" })
@json = @environment.to_json
end
@@ -243,21 +243,21 @@ describe Chef::Environment do
describe "self.validate_cookbook_versions" do
before(:each) do
@cookbook_versions = {
- "apt" => "= 1.0.0",
- "god" => "= 2.0.0",
+ "apt" => "= 1.0.0",
+ "god" => "= 2.0.0",
"apache2" => "= 4.2.0",
}
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)
end
it "should return false if anything other than a hash is passed as the argument" do
- expect(Chef::Environment.validate_cookbook_versions(Array.new)).to eq(false)
+ expect(Chef::Environment.validate_cookbook_versions([])).to eq(false)
expect(Chef::Environment.validate_cookbook_versions(42)).to eq(false)
expect(Chef::Environment.validate_cookbook_versions(Chef::CookbookVersion.new("meta"))).to eq(false)
expect(Chef::Environment.validate_cookbook_versions("cookbook => 1.2.3")).to eq(false)
@@ -295,11 +295,11 @@ describe Chef::Environment do
Chef::Config[:solo_legacy_mode] = false
end
- it "should raise and exception" do
+ it "should raise an exception" do
expect do
Chef::Environment.validate_cookbook_version("= 1.2.3.4")
end.to raise_error Chef::Exceptions::IllegalVersionConstraint,
- "Environment cookbook version constraints not allowed in chef-solo"
+ /Environment cookbook version constraints not allowed in .*/
end
end
@@ -311,17 +311,17 @@ describe Chef::Environment do
end
it "updates the name from parameters[:name]" do
- @environment.update_from_params(:name => "kurrupt")
+ @environment.update_from_params(name: "kurrupt")
expect(@environment.name).to eq("kurrupt")
end
it "validates the name given in the params" do
- expect(@environment.update_from_params(:name => "@$%^&*()")).to be_falsey
- expect(@environment.invalid_fields[:name]).to eq(%q{Option name's value @$%^&*() does not match regular expression /^[\-[:alnum:]_]+$/})
+ expect(@environment.update_from_params(name: "@$%^&*()")).to be_falsey
+ expect(@environment.invalid_fields[:name]).to eq(%q{Property name's value @$%^&*() does not match regular expression /^[\-[:alnum:]_]+$/})
end
it "updates the description from parameters[:description]" do
- @environment.update_from_params(:description => "wow, writing your own object mapper is kinda painful")
+ @environment.update_from_params(description: "wow, writing your own object mapper is kinda painful")
expect(@environment.description).to eq("wow, writing your own object mapper is kinda painful")
end
@@ -329,13 +329,13 @@ describe Chef::Environment do
# NOTE: I'm only choosing this (admittedly weird) structure for the hash b/c the better more obvious
# one, i.e, {:cookbook_version_constraints => {COOKBOOK_NAME => CONSTRAINT}} is difficult to implement
# the way merb does params
- params = { :name => "superbowl", :cookbook_version => { "0" => "apache2 ~> 1.0.0", "1" => "nginx < 2.0.0" } }
+ params = { name: "superbowl", cookbook_version: { "0" => "apache2 ~> 1.0.0", "1" => "nginx < 2.0.0" } }
@environment.update_from_params(params)
expect(@environment.cookbook_versions).to eq({ "apache2" => "~> 1.0.0", "nginx" => "< 2.0.0" })
end
it "validates the cookbook constraints" do
- params = { :cookbook_version => { "0" => "apache2 >>> 1.0.0" } }
+ params = { cookbook_version: { "0" => "apache2 >>> 1.0.0" } }
expect(@environment.update_from_params(params)).to be_falsey
err_msg = @environment.invalid_fields[:cookbook_version]["0"]
expect(err_msg).to eq("apache2 >>> 1.0.0 is not a valid cookbook constraint")
@@ -352,12 +352,12 @@ describe Chef::Environment do
end
it "updates default attributes from a JSON string in params[:attributes]" do
- @environment.update_from_params(:name => "fuuu", :default_attributes => %q|{"fuuu":"RAGE"}|)
+ @environment.update_from_params(name: "fuuu", default_attributes: %q|{"fuuu":"RAGE"}|)
expect(@environment.default_attributes).to eq({ "fuuu" => "RAGE" })
end
it "updates override attributes from a JSON string in params[:attributes]" do
- @environment.update_from_params(:name => "fuuu", :override_attributes => %q|{"foo":"override"}|)
+ @environment.update_from_params(name: "fuuu", override_attributes: %q|{"foo":"override"}|)
expect(@environment.override_attributes).to eq({ "foo" => "override" })
end
@@ -374,7 +374,7 @@ describe Chef::Environment do
describe "list" do
describe "inflated" do
it "should return a hash of environment names and objects" do
- e1 = double("Chef::Environment", :name => "one")
+ e1 = double("Chef::Environment", name: "one")
expect(@query).to receive(:search).with(:environment).and_yield(e1)
r = Chef::Environment.list(true)
expect(r["one"]).to eq(e1)
@@ -402,9 +402,10 @@ describe Chef::Environment do
it "should get the environment from the environment_path" do
expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true)
- 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")).exactly(2).times.and_return(true)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(false)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.rb")).exactly(1).times.and_return(true)
expect(File).to receive(:readable?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(true)
+ expect(File).to receive(:file?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(true)
role_dsl = "name \"foo\"\ndescription \"desc\"\n"
expect(IO).to receive(:read).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(role_dsl)
Chef::Environment.load("foo")
@@ -412,7 +413,7 @@ describe Chef::Environment do
it "should return a Chef::Environment object from JSON" do
expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true)
- expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(true)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(true)
environment_hash = {
"name" => "foo",
"default_attributes" => {
@@ -435,9 +436,10 @@ describe Chef::Environment do
it "should return a Chef::Environment object from Ruby DSL" do
expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true)
- 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")).exactly(2).times.and_return(true)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(false)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.rb")).exactly(1).times.and_return(true)
expect(File).to receive(:readable?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(true)
+ expect(File).to receive(:file?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(true)
role_dsl = "name \"foo\"\ndescription \"desc\"\n"
expect(IO).to receive(:read).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(role_dsl)
environment = Chef::Environment.load("foo")
@@ -452,13 +454,17 @@ describe Chef::Environment do
expect do
Chef::Environment.load("foo")
- end.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid"
+ end.to raise_error(
+ an_instance_of(Chef::Exceptions::InvalidEnvironmentPath).and having_attributes(
+ message: "Environment path '#{windows? ? "C:/var/chef/environments" : "/var/chef/environments"}' is invalid"
+ )
+ )
end
it "should raise an error if the file does not exist" do
expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true)
- 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(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(false)
+ expect(File).to receive(:exist?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(false)
expect do
Chef::Environment.load("foo")
diff --git a/spec/unit/event_dispatch/dispatcher_spec.rb b/spec/unit/event_dispatch/dispatcher_spec.rb
index 5061a9845f..7942e8eb6b 100644
--- a/spec/unit/event_dispatch/dispatcher_spec.rb
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,9 +43,9 @@ describe Chef::EventDispatch::Dispatcher do
it "forwards events to the subscribed event sink" do
# the events all have different arity and such so we just hit a few different events:
-
- expect(event_sink).to receive(:run_start).with("12.4.0")
- dispatcher.run_start("12.4.0")
+ run_status = Chef::RunStatus.new({}, {})
+ expect(event_sink).to receive(:run_start).with("12.4.0", run_status)
+ dispatcher.run_start("12.4.0", run_status)
cookbook_version = double("cookbook_version")
expect(event_sink).to receive(:synchronized_cookbook).with("apache2", cookbook_version)
@@ -61,6 +61,7 @@ describe Chef::EventDispatch::Dispatcher do
let(:event_sink) do
Class.new(Chef::EventDispatch::Base) do
attr_reader :synchronized_cookbook_args
+
def synchronized_cookbook(cookbook_name)
@synchronized_cookbook_args = [cookbook_name]
end
@@ -79,6 +80,7 @@ describe Chef::EventDispatch::Dispatcher do
let(:event_sink_1) do
Class.new(Chef::EventDispatch::Base) do
attr_reader :synchronized_cookbook_args
+
def synchronized_cookbook(cookbook_name)
@synchronized_cookbook_args = [cookbook_name]
end
@@ -87,6 +89,7 @@ describe Chef::EventDispatch::Dispatcher do
let(:event_sink_2) do
Class.new(Chef::EventDispatch::Base) do
attr_reader :synchronized_cookbook_args
+
def synchronized_cookbook(cookbook_name, cookbook)
@synchronized_cookbook_args = [cookbook_name, cookbook]
end
@@ -119,4 +122,51 @@ describe Chef::EventDispatch::Dispatcher do
end
end
end
+
+ context "events that queue events" do
+ class Accumulator
+ def self.sequence
+ @sequence ||= []
+ end
+ end
+
+ let(:event_sink_1) do
+ Class.new(Chef::EventDispatch::Base) do
+ def synchronized_cookbook(dispatcher, arg)
+ dispatcher.enqueue(:event_two, arg)
+ Accumulator.sequence << [ :sink_1_event_1, arg ]
+ end
+
+ def event_two(arg)
+ Accumulator.sequence << [ :sink_1_event_2, arg ]
+ end
+ end.new
+ end
+ let(:event_sink_2) do
+ Class.new(Chef::EventDispatch::Base) do
+ def synchronized_cookbook(dispatcher, arg)
+ Accumulator.sequence << [ :sink_2_event_1, arg ]
+ end
+
+ def event_two(arg)
+ Accumulator.sequence << [ :sink_2_event_2, arg ]
+ end
+ end.new
+ end
+
+ before do
+ dispatcher.register(event_sink_1)
+ dispatcher.register(event_sink_2)
+ end
+
+ it "runs the events in the correct order without interleaving the enqueued event" do
+ dispatcher.synchronized_cookbook(dispatcher, "two")
+ expect(Accumulator.sequence).to eql([
+ [:sink_1_event_1, "two"], # the call to enqueue the event happens here
+ [:sink_2_event_1, "two"], # event 1 fully finishes
+ [:sink_1_event_2, "two"],
+ [:sink_2_event_2, "two"], # then event 2 runs and finishes
+ ])
+ end
+ end
end
diff --git a/spec/unit/event_dispatch/dsl_spec.rb b/spec/unit/event_dispatch/dsl_spec.rb
index 979b067fb6..009242f4fb 100644
--- a/spec/unit/event_dispatch/dsl_spec.rb
+++ b/spec/unit/event_dispatch/dsl_spec.rb
@@ -63,7 +63,7 @@ describe Chef::EventDispatch::DSL do
resource = Chef::Resource::RubyBlock.new("foo", run_context)
resource.block {}
resource.run_action(:run)
- expect(calls).to eq([:started, :updated])
+ expect(calls).to eq(%i{started updated})
end
it "preserve instance variables across handler callbacks" do
diff --git a/spec/unit/exceptions_spec.rb b/spec/unit/exceptions_spec.rb
index 940edfec2e..03254221c6 100644
--- a/spec/unit/exceptions_spec.rb
+++ b/spec/unit/exceptions_spec.rb
@@ -3,7 +3,7 @@
# Author:: Christopher Walters (<cw@chef.io>)
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
# Copyright:: Copyright 2010-2016, Thomas Bishop
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 ee806b5c3a..dfa0bcf673 100644
--- a/spec/unit/file_access_control_spec.rb
+++ b/spec/unit/file_access_control_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,7 @@ describe Chef::FileAccessControl do
# we have to re-load the file so the proper
# platform specific module is mixed in
@node = Chef::Node.new
- load File.join(File.dirname(__FILE__), "..", "..", "lib", "chef", "file_access_control.rb")
+ load File.join(__dir__, "..", "..", "lib", "chef", "file_access_control.rb")
@resource = Chef::Resource::File.new("/tmp/a_file.txt")
@resource.owner("toor")
@resource.group("wheel")
@@ -36,7 +36,7 @@ describe Chef::FileAccessControl do
@run_context = Chef::RunContext.new(@node, {}, @events)
@current_resource = Chef::Resource::File.new("/tmp/different_file.txt")
@provider_requirements = Chef::Provider::ResourceRequirements.new(@resource, @run_context)
- @provider = double("File provider", :requirements => @provider_requirements, :manage_symlink_access? => false)
+ @provider = double("File provider", requirements: @provider_requirements, manage_symlink_access?: false)
@fac = Chef::FileAccessControl.new(@current_resource, @resource, @provider)
end
@@ -61,7 +61,7 @@ describe Chef::FileAccessControl do
end
it "determines the uid of the owner specified by the resource" do
- expect(Etc).to receive(:getpwnam).with("toor").and_return(OpenStruct.new(:uid => 2342))
+ expect(Etc).to receive(:getpwnam).with("toor").and_return(OpenStruct.new(uid: 2342))
expect(@fac.target_uid).to eq(2342)
end
@@ -93,13 +93,13 @@ describe Chef::FileAccessControl do
end
it "wraps uids to their negative complements to correctly handle negative uids" do
- # More: Mac OS X (at least) has negative UIDs for 'nobody' and some other
+ # More: macOS (at least) has negative UIDs for 'nobody' and some other
# users. Ruby doesn't believe in negative UIDs so you get the diminished radix
# complement (i.e., it wraps around the maximum size of C unsigned int) of these
# uids. So we have to get ruby and negative uids to smoke the peace pipe
# with each other.
@resource.owner("nobody")
- expect(Etc).to receive(:getpwnam).with("nobody").and_return(OpenStruct.new(:uid => (4294967294)))
+ expect(Etc).to receive(:getpwnam).with("nobody").and_return(OpenStruct.new(uid: (4294967294)))
expect(@fac.target_uid).to eq(-2)
end
@@ -107,7 +107,7 @@ describe Chef::FileAccessControl do
# More: when OSX userIDs are created by ActiveDirectory sync, it tends to use huge numbers
# which had been incorrectly wrapped. It does not look like the OSX IDs go below -2
@resource.owner("bigdude")
- expect(Etc).to receive(:getpwnam).with("bigdude").and_return(OpenStruct.new(:uid => (4294967286)))
+ expect(Etc).to receive(:getpwnam).with("bigdude").and_return(OpenStruct.new(uid: (4294967286)))
expect(@fac.target_uid).to eq(4294967286)
end
@@ -153,7 +153,7 @@ describe Chef::FileAccessControl do
end
it "determines the gid of the group specified by the resource" do
- expect(Etc).to receive(:getgrnam).with("wheel").and_return(OpenStruct.new(:gid => 2342))
+ expect(Etc).to receive(:getgrnam).with("wheel").and_return(OpenStruct.new(gid: 2342))
expect(@fac.target_gid).to eq(2342)
end
@@ -273,7 +273,7 @@ describe Chef::FileAccessControl do
it "sets the file's mode as specified in the resource when the current modes are incorrect" do
# stat returns modes like 0100644 (octal) => 33188 (decimal)
- #@fac.stub(:stat).and_return(OpenStruct.new(:mode => 33188))
+ # @fac.stub(:stat).and_return(OpenStruct.new(:mode => 33188))
@current_resource.mode("0644")
expect(File).to receive(:chmod).with(256, "/tmp/different_file.txt")
@fac.set_mode
@@ -286,7 +286,7 @@ describe Chef::FileAccessControl do
end
it "does not set the file's mode when the current modes are correct" do
- #@fac.stub(:stat).and_return(OpenStruct.new(:mode => 0100400))
+ # @fac.stub(:stat).and_return(OpenStruct.new(:mode => 0100400))
@current_resource.mode("0400")
expect(File).not_to receive(:chmod)
@fac.set_mode
@@ -294,7 +294,7 @@ describe Chef::FileAccessControl do
end
it "sets all access controls on a file" do
- allow(@fac).to receive(:stat).and_return(OpenStruct.new(:owner => 99, :group => 99, :mode => 0100444))
+ allow(@fac).to receive(:stat).and_return(OpenStruct.new(owner: 99, group: 99, mode: 0100444))
@resource.mode(0400)
@resource.owner(0)
@resource.group(0)
diff --git a/spec/unit/file_cache_spec.rb b/spec/unit/file_cache_spec.rb
index 388b0e746a..6c645bf9e2 100644
--- a/spec/unit/file_cache_spec.rb
+++ b/spec/unit/file_cache_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/file_content_management/deploy/cp_spec.rb b/spec/unit/file_content_management/deploy/cp_spec.rb
index cbdb4f1425..9d41fa835c 100644
--- a/spec/unit/file_content_management/deploy/cp_spec.rb
+++ b/spec/unit/file_content_management/deploy/cp_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/file_content_management/deploy/mv_unix_spec.rb b/spec/unit/file_content_management/deploy/mv_unix_spec.rb
index 569fd898a7..a514667c71 100644
--- a/spec/unit/file_content_management/deploy/mv_unix_spec.rb
+++ b/spec/unit/file_content_management/deploy/mv_unix_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,9 +38,9 @@ describe Chef::FileContentManagement::Deploy::MvUnix do
let(:target_file_mode) { 0644 }
let(:target_file_stat) do
double "File::Stat struct for target file",
- :mode => target_file_mode,
- :uid => target_file_uid,
- :gid => target_file_gid
+ mode: target_file_mode,
+ uid: target_file_uid,
+ gid: target_file_gid
end
before do
@@ -76,7 +76,7 @@ describe Chef::FileContentManagement::Deploy::MvUnix do
context "when the user does not have permissions to set file ownership" do
# The test code does not care what these values are. These values are
- # chosen because they're representitive of the case that chef-client is
+ # chosen because they're representative of the case that chef-client is
# running as non-root and is managing a file that got ownership set to
# root somehow. In this example, gid==20 is something like "staff" which
# the user running chef-client is a member of (but it's not that user's
@@ -98,4 +98,16 @@ describe Chef::FileContentManagement::Deploy::MvUnix do
end
end
+
+ describe "when testing against real files", unix_only: true do
+ it "preserves sticky bits" do
+ staging_file = Tempfile.new("staging_file")
+ target_file = Tempfile.new("target_file")
+ File.chmod(04755, target_file.path)
+ content_deployer.deploy(staging_file.path, target_file.path)
+ expect(::File.stat(target_file.path).mode & 07777).to eql(04755)
+ staging_file.unlink
+ target_file.unlink
+ end
+ end
end
diff --git a/spec/unit/file_content_management/deploy/mv_windows_spec.rb b/spec/unit/file_content_management/deploy/mv_windows_spec.rb
index 30a62c4da9..bbe16773d8 100644
--- a/spec/unit/file_content_management/deploy/mv_windows_spec.rb
+++ b/spec/unit/file_content_management/deploy/mv_windows_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
require "spec_helper"
-unless Chef::Platform.windows?
+unless ChefUtils.windows?
class Chef
module ReservedNames
module Win32
@@ -59,14 +59,14 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
end
before do
- allow(Chef::ReservedNames::Win32::Security::SecurableObject).
- to receive(:new).
- with(target_file_path).
- and_return(target_file_security_object, updated_target_security_object)
+ allow(Chef::ReservedNames::Win32::Security::SecurableObject)
+ .to receive(:new)
+ .with(target_file_path)
+ .and_return(target_file_security_object, updated_target_security_object)
end
- context "when run without adminstrator privileges" do
+ context "when run without administrator privileges" do
before do
expect(target_file_security_object).to receive(:security_descriptor).and_raise(Chef::Exceptions::Win32APIError)
end
@@ -84,8 +84,8 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
let(:target_file_security_descriptor) do
double "security descriptor for target file",
- :group => original_target_file_group,
- :owner => original_target_file_owner
+ group: original_target_file_group,
+ owner: original_target_file_owner
end
let(:updated_target_security_descriptor) do
@@ -147,19 +147,19 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(false)
allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl)
- expect(Chef::ReservedNames::Win32::Security::ACL).
- to receive(:create).
- with([]).
- and_return(empty_dacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL)
+ .to receive(:create)
+ .with([])
+ .and_return(empty_dacl)
allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(false)
allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl)
- expect(Chef::ReservedNames::Win32::Security::ACL).
- to receive(:create).
- with([]).
- and_return(empty_sacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL)
+ .to receive(:create)
+ .with([])
+ .and_return(empty_sacl)
expect(updated_target_security_object).to receive(:set_dacl).with(empty_dacl, false)
expect(updated_target_security_object).to receive(:set_sacl).with(empty_sacl, false)
@@ -171,13 +171,13 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
end
context "and the target has a dacl and sacl" do
- let(:inherited_dacl_ace) { double("Windows dacl ace (inherited)", :inherited? => true) }
- let(:not_inherited_dacl_ace) { double("Windows dacl ace (not inherited)", :inherited? => false) }
+ let(:inherited_dacl_ace) { double("Windows dacl ace (inherited)", inherited?: true) }
+ let(:not_inherited_dacl_ace) { double("Windows dacl ace (not inherited)", inherited?: false) }
let(:original_target_file_dacl) { [inherited_dacl_ace, not_inherited_dacl_ace] }
- let(:inherited_sacl_ace) { double("Windows sacl ace (inherited)", :inherited? => true) }
- let(:not_inherited_sacl_ace) { double("Windows sacl ace (not inherited)", :inherited? => false) }
+ let(:inherited_sacl_ace) { double("Windows sacl ace (inherited)", inherited?: true) }
+ let(:not_inherited_sacl_ace) { double("Windows sacl ace (not inherited)", inherited?: false) }
let(:original_target_file_sacl) { [inherited_sacl_ace, not_inherited_sacl_ace] }
let(:custom_dacl) { double("Windows ACL for non-inherited dacl aces") }
@@ -188,19 +188,19 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(dacl_inherits?)
allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl)
- expect(Chef::ReservedNames::Win32::Security::ACL).
- to receive(:create).
- with([not_inherited_dacl_ace]).
- and_return(custom_dacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL)
+ .to receive(:create)
+ .with([not_inherited_dacl_ace])
+ .and_return(custom_dacl)
allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(sacl_inherits?)
allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl)
- expect(Chef::ReservedNames::Win32::Security::ACL).
- to receive(:create).
- with([not_inherited_sacl_ace]).
- and_return(custom_sacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL)
+ .to receive(:create)
+ .with([not_inherited_sacl_ace])
+ .and_return(custom_sacl)
expect(updated_target_security_object).to receive(:set_dacl).with(custom_dacl, dacl_inherits?)
expect(updated_target_security_object).to receive(:set_sacl).with(custom_sacl, sacl_inherits?)
diff --git a/spec/unit/file_content_management/tempfile_spec.rb b/spec/unit/file_content_management/tempfile_spec.rb
index a04ace5f16..82d156eb3b 100644
--- a/spec/unit/file_content_management/tempfile_spec.rb
+++ b/spec/unit/file_content_management/tempfile_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,7 +98,7 @@ describe Chef::FileContentManagement::Tempfile do
expect(tempfile.path).to match(/chef-new_file.*\.html\.erb$/)
end
- it "should pick the destdir preferrentially" do
+ it "should pick the destdir preferentially" do
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)
diff --git a/spec/unit/formatters/base_spec.rb b/spec/unit/formatters/base_spec.rb
index 30c7757e5a..74c4d3eed2 100644
--- a/spec/unit/formatters/base_spec.rb
+++ b/spec/unit/formatters/base_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)
@@ -33,7 +41,7 @@ describe Chef::Formatters::Base do
expect(formatter.output.indent).to eql(2)
end
- it "increments it and then decrements it corectly" do
+ it "increments it and then decrements it correctly" do
formatter.indent_by(2)
formatter.indent_by(-2)
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/doc_spec.rb b/spec/unit/formatters/doc_spec.rb
index b8eccc1bc9..73e756464b 100644
--- a/spec/unit/formatters/doc_spec.rb
+++ b/spec/unit/formatters/doc_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,6 +76,24 @@ describe Chef::Formatters::Base do
expect(formatter.pretty_elapsed_time).to include("10 hours 10 minutes 10 seconds")
end
+ it "shows nothing if total is nil" do
+ res = Chef::Resource::RemoteFile.new("canteloupe")
+ formatter.resource_update_progress(res, 35, nil, 10)
+ expect(out.string).to eq("")
+ end
+
+ it "shows nothing if total is 0" do
+ res = Chef::Resource::RemoteFile.new("canteloupe")
+ formatter.resource_update_progress(res, 35, 0, 10)
+ expect(out.string).to eq("")
+ end
+
+ it "shows nothing if current and total are 0" do
+ res = Chef::Resource::RemoteFile.new("canteloupe")
+ formatter.resource_update_progress(res, 0, 0, 10)
+ expect(out.string).to eq("")
+ end
+
it "shows the percentage completion of an action" do
res = Chef::Resource::RemoteFile.new("canteloupe")
formatter.resource_update_progress(res, 35, 50, 10)
diff --git a/spec/unit/formatters/error_description_spec.rb b/spec/unit/formatters/error_description_spec.rb
index 8b088308fa..50f4f66de6 100644
--- a/spec/unit/formatters/error_description_spec.rb
+++ b/spec/unit/formatters/error_description_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jordan Running (<jr@chef.io>)
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,20 +49,37 @@ 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
it "should output only the title and the Platform section" do
subject.display(out)
- expect(out.out.string).to eq <<-END
-================================================================================
-test title
-================================================================================
-
-Platform:
----------
-ruby-foo-9000
+ expect(out.out.string).to eq <<~END
+ ================================================================================
+ test title
+ ================================================================================
+
+ 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
@@ -75,18 +92,46 @@ ruby-foo-9000
it "should output the expected sections" do
subject.display(out)
- expect(out.out.string).to eq <<-END
-================================================================================
-test title
-================================================================================
-
-test heading
-------------
-test text
-
-Platform:
----------
-ruby-foo-9000
+ expect(out.out.string).to eq <<~END
+ ================================================================================
+ test title
+ ================================================================================
+
+ test heading
+ ------------
+ test text
+
+ 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 0e5104a0db..e2a28a6860 100644
--- a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
+++ b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
index 2c1da7345b..28f26cbcbb 100644
--- a/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,22 +18,22 @@
require "spec_helper"
-BAD_RECIPE = <<-E
-#
-# Cookbook Name:: syntax-err
-# Recipe:: default
-#
-# Copyright 2012-2016, YOUR_COMPANY_NAME
-#
-# All rights reserved - Do Not Redistribute
-#
-
-
-file "/tmp/explode-me" do
- mode 0655
- owner "root"
- this_is_not_a_valid_method
-end
+BAD_RECIPE = <<~E.freeze
+ #
+ # Cookbook Name:: syntax-err
+ # Recipe:: default
+ #
+ # Copyright 2012-2016, YOUR_COMPANY_NAME
+ #
+ # All rights reserved - Do Not Redistribute
+ #
+
+
+ file "/tmp/explode-me" do
+ mode 0655
+ owner "root"
+ this_is_not_a_valid_method
+ end
E
describe Chef::Formatters::ErrorInspectors::CompileErrorInspector do
diff --git a/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb
index 07643385b4..bf81a9da93 100644
--- a/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,7 +35,7 @@ describe Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector do
@response_body = %q({"error": [{"message": "gtfo"}])
@response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) forbidden", @response)
+ @exception = Net::HTTPClientException.new("(exception) forbidden", @response)
@inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception)
@inspector.add_explanation(@description)
@@ -56,7 +56,7 @@ describe Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector do
@response_body = "{\"error\":[\"{\\\"non_existent_cookbooks\\\":[\\\"apache2\\\"],\\\"cookbooks_with_no_versions\\\":[\\\"users\\\"],\\\"message\\\":\\\"Run list contains invalid items: no such cookbook nope.\\\"}\"]}"
@response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) precondition failed", @response)
+ @exception = Net::HTTPClientException.new("(exception) precondition failed", @response)
@inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception)
@inspector.add_explanation(@description)
@@ -81,10 +81,10 @@ describe Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector do
before do
- @response_body = "{\"error\":[{\"non_existent_cookbooks\":[],\"cookbooks_with_no_versions\":[],\"message\":\"unable to solve dependencies in alotted time.\"}]}"
+ @response_body = "{\"error\":[{\"non_existent_cookbooks\":[],\"cookbooks_with_no_versions\":[],\"message\":\"unable to solve dependencies in allotted time.\"}]}"
@response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) precondition failed", @response)
+ @exception = Net::HTTPClientException.new("(exception) precondition failed", @response)
@inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception)
@inspector.add_explanation(@description)
@@ -93,7 +93,7 @@ describe Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector do
it "prints a pretty message" do
@description.display(@outputter)
@outputter_output.rewind
- expect(@outputter_output.read).to include("unable to solve dependencies in alotted time.")
+ expect(@outputter_output.read).to include("unable to solve dependencies in allotted time.")
end
end
@@ -107,7 +107,7 @@ describe Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector do
@response_body = "{\"error\":[{\"non_existent_cookbooks\":[\"apache2\"],\"cookbooks_with_no_versions\":[\"users\"],\"message\":\"Run list contains invalid items: no such cookbook nope.\"}]}"
@response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) precondition failed", @response)
+ @exception = Net::HTTPClientException.new("(exception) precondition failed", @response)
@inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception)
@inspector.add_explanation(@description)
diff --git a/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb
index 02846af24a..aae5d88567 100644
--- a/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ describe Chef::Formatters::ErrorInspectors::CookbookSyncErrorInspector do
before do
@description = Chef::Formatters::ErrorDescription.new("Error Expanding RunList:")
@outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR)
- #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
+ # @outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
end
describe "when explaining a 502 error" do
diff --git a/spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb
index 93aac417fa..500269c571 100644
--- a/spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
# spec_helper loads the shared examples already.
-#require 'support/shared/unit/api_error_inspector_spec'
+# require 'support/shared/unit/api_error_inspector_spec'
describe Chef::Formatters::ErrorInspectors::NodeLoadErrorInspector do
it_behaves_like "an api error inspector"
diff --git a/spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb
index cea93888eb..51db6c632a 100644
--- a/spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
# spec_helper loads the shared examples already.
-#require 'support/shared/unit/api_error_inspector_spec'
+# require 'support/shared/unit/api_error_inspector_spec'
describe Chef::Formatters::ErrorInspectors::RegistrationErrorInspector do
it_behaves_like "an api error inspector"
diff --git a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
index 072dcfef28..9d8fb050da 100644
--- a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,7 +40,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
@description = Chef::Formatters::ErrorDescription.new("Error Converging Resource:")
@stdout = StringIO.new
@outputter = Chef::Formatters::IndentableOutputStream.new(@stdout, STDERR)
- #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
+ # @outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
allow(Chef::Config).to receive(:cookbook_path).and_return([ "/var/chef/cache" ])
end
@@ -60,7 +60,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
@trace = [
"/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
"/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
- "/usr/local/lib/ruby/gems/chef/lib/chef/client.rb:123:in `run'" # should not display
+ "/usr/local/lib/ruby/gems/chef/lib/chef/client.rb:123:in `run'", # should not display
]
@exception = Chef::Exceptions::Package.new("No such package 'non-existing-package'")
@exception.set_backtrace(@trace)
@@ -122,7 +122,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
source_line = "C:/Users/btm/chef/chef/spec/unit/fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)"
@resource.source_line = source_line
@inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
- expect(@inspector.recipe_snippet).to match(/^# In C:\/Users\/btm/)
+ expect(@inspector.recipe_snippet).to match(%r{^# In C:/Users/btm})
end
it "parses a Windows path" do
@@ -136,7 +136,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
source_line = "/home/btm/src/chef/chef/spec/unit/fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)"
@resource.source_line = source_line
@inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
- expect(@inspector.recipe_snippet).to match(/^# In \/home\/btm/)
+ expect(@inspector.recipe_snippet).to match(%r{^# In /home/btm})
end
context "when the recipe file does not exist" do
diff --git a/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb
index 3e988c584d..93755b2f32 100644
--- a/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ describe Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector do
@description = Chef::Formatters::ErrorDescription.new("Error Expanding RunList:")
@outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR)
- #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
+ # @outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
end
describe "when explaining a missing role error" do
@@ -55,9 +55,9 @@ describe Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector do
@response_body = "forbidden"
@response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) forbidden", @response)
+ @exception = Net::HTTPClientException.new("(exception) forbidden", @response)
@inspector = Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector.new(@node, @exception)
- allow(@inspector).to receive(:config).and_return(:node_name => "unit-test.example.com")
+ allow(@inspector).to receive(:config).and_return(node_name: "unit-test.example.com")
@inspector.add_explanation(@description)
end
@@ -73,12 +73,12 @@ describe Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector do
@response_body = "check your key and node name"
@response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized")
allow(@response).to receive(:body).and_return(@response_body)
- @exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
+ @exception = Net::HTTPClientException.new("(exception) unauthorized", @response)
@inspector = Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector.new(@node, @exception)
- allow(@inspector).to receive(:config).and_return(:node_name => "unit-test.example.com",
- :client_key => "/etc/chef/client.pem",
- :chef_server_url => "http://chef.example.com")
+ allow(@inspector).to receive(:config).and_return(node_name: "unit-test.example.com",
+ client_key: "/etc/chef/client.pem",
+ chef_server_url: "http://chef.example.com")
@inspector.add_explanation(@description)
end
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
index 746b343e9c..0760bcee1d 100644
--- a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
let(:node) do
node = Chef::Node.new
- node.default["kernel"] = Hash.new
+ node.default["kernel"] = {}
node.default["kernel"][:machine] = :x86_64.to_s
node.automatic[:os] = "windows"
node
@@ -93,8 +93,8 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
end
describe "script command opts switch" do
- let(:command_opts) { {} }
- let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "exit 0", command_opts) }
+ let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "exit 0", {}) }
+ let(:resource) { guard_interpreter.instance_variable_get("@resource") }
context "resource is a Script" do
context "and guard_interpreter is a :script" do
@@ -106,7 +106,7 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
end
let(:shell_out) do
- instance_double(Mixlib::ShellOut, :live_stream => true, :run_command => true, :error! => nil)
+ instance_double(Mixlib::ShellOut, live_stream: true, run_command: true, error!: nil)
end
before do
@@ -117,9 +117,9 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
end
end
- it "merges to :code" do
- expect(command_opts).to receive(:merge).with({ :code => "exit 0" }).and_call_original
- expect(guard_interpreter.evaluate).to eq(true)
+ it "assigns the comand to the resource's code property" do
+ guard_interpreter.evaluate
+ expect(resource.code).to eq("exit 0")
end
end
@@ -130,9 +130,9 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
parent_resource
end
- it "merges to :code" do
- expect(command_opts).to receive(:merge).with({ :command => "exit 0" }).and_call_original
- expect(guard_interpreter.evaluate).to eq(true)
+ it "assigns the comand to the resource's command property" do
+ guard_interpreter.evaluate
+ expect(resource.command).to eq("exit 0")
end
end
end
@@ -144,9 +144,9 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
parent_resource
end
- it "merges to :command" do
- expect(command_opts).to receive(:merge).with({ :command => "exit 0" }).and_call_original
- expect(guard_interpreter.evaluate).to eq(true)
+ it "assigns the comand to the resource's command property" do
+ guard_interpreter.evaluate
+ expect(resource.command).to eq("exit 0")
end
end
diff --git a/spec/unit/guard_interpreter_spec.rb b/spec/unit/guard_interpreter_spec.rb
index 1bfc831177..82bdb58483 100644
--- a/spec/unit/guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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::GuardInterpreter do
describe "#for_resource" do
- let (:resource) { Chef::Resource.new("foo") }
+ let(:resource) { Chef::Resource.new("foo") }
it "returns a DefaultGuardInterpreter if the resource has guard_interpreter set to :default" do
resource.guard_interpreter :default
diff --git a/spec/unit/handler/json_file_spec.rb b/spec/unit/handler/json_file_spec.rb
index 4be448690a..404acbaf64 100644
--- a/spec/unit/handler/json_file_spec.rb
+++ b/spec/unit/handler/json_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::Handler::JsonFile do
before(:each) do
- @handler = Chef::Handler::JsonFile.new(:the_sun => "will rise", :path => "/tmp/foobarbazqux")
+ @handler = Chef::Handler::JsonFile.new(the_sun: "will rise", path: "/tmp/foobarbazqux")
end
it "accepts arbitrary config options" do
diff --git a/spec/unit/handler_spec.rb b/spec/unit/handler_spec.rb
index a56645fa78..a9820e8a70 100644
--- a/spec/unit/handler_spec.rb
+++ b/spec/unit/handler_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
diff --git a/spec/unit/http/api_versions_spec.rb b/spec/unit/http/api_versions_spec.rb
new file mode 100644
index 0000000000..d856668af8
--- /dev/null
+++ b/spec/unit/http/api_versions_spec.rb
@@ -0,0 +1,100 @@
+#
+# Copyright:: Copyright (c) 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(:version_class) do
+ Class.new do
+ extend Chef::Mixin::VersionedAPIFactory
+
+ version_class_v0 = Class.new do
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 0
+ end
+ add_versioned_api_class version_class_v0
+
+ version_class_v2 = Class.new do
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 2
+ end
+ add_versioned_api_class version_class_v2
+ end
+ end
+
+ let(:client) do
+ TestVersionClient.new(url, { version_class: version_class })
+ 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..4f43c19520 100644
--- a/spec/unit/http/authenticator_spec.rb
+++ b/spec/unit/http/authenticator_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,30 +20,50 @@ require "spec_helper"
require "chef/http/authenticator"
describe Chef::HTTP::Authenticator do
- let(:class_instance) { Chef::HTTP::Authenticator.new }
- let(:method) { double("method") }
- let(:url) { double("url") }
- let(:headers) { Hash.new }
- let(:data) { double("data") }
-
- before do
- allow(class_instance).to receive(:authentication_headers).and_return({})
- end
+ let(:class_instance) { Chef::HTTP::Authenticator.new(client_name: "test") }
+ let(:method) { "GET" }
+ let(:url) { URI("https://chef.example.com/organizations/test") }
+ let(:headers) { {} }
+ let(:data) { "" }
context "when handle_request is called" do
shared_examples_for "merging the server API version into the headers" do
+ before do
+ allow(class_instance).to receive(:authentication_headers).and_return({})
+ end
+
it "merges the default version of X-Ops-Server-API-Version into the headers" do
# headers returned
- expect(class_instance.handle_request(method, url, headers, data)[2]).
- to include({ "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION })
+ expect(class_instance.handle_request(method, url, headers, data)[2])
+ .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" }) }
+ let(:class_instance) { Chef::HTTP::Authenticator.new({ api_version: "-10" }) }
it "merges the requested version of X-Ops-Server-API-Version into the headers" do
- expect(class_instance.handle_request(method, url, headers, data)[2]).
- to include({ "X-Ops-Server-API-Version" => "-10" })
+ expect(class_instance.handle_request(method, url, headers, data)[2])
+ .to include({ "X-Ops-Server-API-Version" => "-10" })
end
end
end
@@ -72,7 +92,33 @@ describe Chef::HTTP::Authenticator do
it "calls authentication_headers with the proper input" do
expect(class_instance).to receive(:authentication_headers).with(
method, url, data,
- { "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION }).and_return({})
+ { "X-Ops-Server-API-Version" => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION }
+ ).and_return({})
+ class_instance.handle_request(method, url, headers, data)
+ end
+ end
+
+ context "when ssh_agent_signing" do
+ let(:public_key) { <<~EOH }
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA49TA0y81ps0zxkOpmf5V
+ 4/c4IeR5yVyQFpX3JpxO4TquwnRh8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE
+ 6915kXSuk/cWIcNozujLK7tkuPEyYVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXr
+ WqYCnhxgOjxocenREYNhZ/OETIeiPbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42
+ BqAi1jglIdeq8lAWZNF9TbNBU21AO1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZa
+ wxpGV4dGtdcahwXNE4601aXPra+xPcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1Q
+ YwIDAQAB
+ -----END PUBLIC KEY-----
+ EOH
+
+ let(:class_instance) { Chef::HTTP::Authenticator.new(client_name: "test", raw_key: public_key, ssh_agent_signing: true) }
+
+ it "sets use_ssh_agent if needed" do
+ expect(Mixlib::Authentication::SignedHeaderAuth).to receive(:signing_object).and_wrap_original { |m, *args|
+ m.call(*args).tap do |signing_obj|
+ expect(signing_obj).to receive(:sign).with(instance_of(OpenSSL::PKey::RSA), use_ssh_agent: true).and_return({})
+ end
+ }
class_instance.handle_request(method, url, headers, data)
end
end
diff --git a/spec/unit/http/http_request_spec.rb b/spec/unit/http/http_request_spec.rb
index 29562de021..8c1b73fe12 100644
--- a/spec/unit/http/http_request_spec.rb
+++ b/spec/unit/http/http_request_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Klaas Jan Wierenga (<k.j.wierenga@gmail.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -52,6 +52,12 @@ describe Chef::HTTP::HTTPRequest do
expect(request.headers["Host"]).to eql("yourhost.com:8888")
end
+ it "should not mutate the URI when it contains parameters" do
+ # buggy constructor code mutated strings owned by the URI parameter
+ uri = URI("http://dummy.com/foo?bar=baz")
+ request = Chef::HTTP::HTTPRequest.new(:GET, uri, "")
+ expect(uri).to eql(URI("http://dummy.com/foo?bar=baz"))
+ end
end
context "with HTTPS url scheme" do
diff --git a/spec/unit/http/json_input_spec.rb b/spec/unit/http/json_input_spec.rb
index a76c8d1dc7..b71a91f16a 100644
--- a/spec/unit/http/json_input_spec.rb
+++ b/spec/unit/http/json_input_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "chef/http/json_input"
describe Chef::HTTP::JSONInput do
- let(:json_encoder) { described_class.new() }
+ let(:json_encoder) { described_class.new }
let(:url) { URI.parse("http://example.com") }
let(:headers) { {} }
diff --git a/spec/unit/http/simple_spec.rb b/spec/unit/http/simple_spec.rb
index 0170266bb5..6276bd1344 100644
--- a/spec/unit/http/simple_spec.rb
+++ b/spec/unit/http/simple_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/http/socketless_chef_zero_client_spec.rb b/spec/unit/http/socketless_chef_zero_client_spec.rb
index 637e562799..4ed3f0732f 100644
--- a/spec/unit/http/socketless_chef_zero_client_spec.rb
+++ b/spec/unit/http/socketless_chef_zero_client_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,13 +38,13 @@ describe Chef::HTTP::SocketlessChefZeroClient do
let(:expected_rack_req) do
{
- "SCRIPT_NAME" => "",
- "SERVER_NAME" => "localhost",
- "REQUEST_METHOD" => method.to_s.upcase,
- "PATH_INFO" => uri.path,
- "QUERY_STRING" => uri.query,
- "SERVER_PORT" => uri.port,
- "HTTP_HOST" => "localhost:#{uri.port}",
+ "SCRIPT_NAME" => "",
+ "SERVER_NAME" => "localhost",
+ "REQUEST_METHOD" => method.to_s.upcase,
+ "PATH_INFO" => uri.path,
+ "QUERY_STRING" => uri.query,
+ "SERVER_PORT" => uri.port,
+ "HTTP_HOST" => "localhost:#{uri.port}",
"rack.url_scheme" => "chefzero",
}
end
@@ -132,20 +132,21 @@ 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
{
- "SCRIPT_NAME" => "",
- "SERVER_NAME" => "localhost",
- "REQUEST_METHOD" => method.to_s.upcase,
- "PATH_INFO" => uri.path,
- "QUERY_STRING" => uri.query,
- "SERVER_PORT" => uri.port,
- "HTTP_HOST" => "localhost:#{uri.port}",
+ "SCRIPT_NAME" => "",
+ "SERVER_NAME" => "localhost",
+ "REQUEST_METHOD" => method.to_s.upcase,
+ "PATH_INFO" => uri.path,
+ "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),
+ "rack.input" => an_instance_of(StringIO),
}
end
diff --git a/spec/unit/http/ssl_policies_spec.rb b/spec/unit/http/ssl_policies_spec.rb
index df6dee1198..6fc00b5fd9 100644
--- a/spec/unit/http/ssl_policies_spec.rb
+++ b/spec/unit/http/ssl_policies_spec.rb
@@ -1,6 +1,6 @@
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,83 +26,86 @@ describe "HTTP SSL Policy" do
Chef::Config[:ssl_client_key] = nil
Chef::Config[:ssl_ca_path] = nil
Chef::Config[:ssl_ca_file] = nil
+ ENV["SSL_CERT_FILE"] = nil
end
- let(:unconfigured_http_client) { Net::HTTP.new("example.com", 443) }
let(:http_client) do
- unconfigured_http_client.use_ssl = true
- ssl_policy.apply
- unconfigured_http_client
+ ssl_policy_class.apply_to(Net::HTTP.new("example.com"))
end
describe Chef::HTTP::DefaultSSLPolicy do
- let(:ssl_policy) { Chef::HTTP::DefaultSSLPolicy.new(unconfigured_http_client) }
+ let(:ssl_policy_class) { Chef::HTTP::DefaultSSLPolicy }
- describe "when configured with :ssl_verify_mode set to :verify peer" do
- before do
- Chef::Config[:ssl_verify_mode] = :verify_peer
- end
-
- it "configures the HTTP client to use SSL when given a URL with the https protocol" do
- expect(http_client.use_ssl?).to be_truthy
- end
+ it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do
+ Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here"
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
- it "sets the OpenSSL verify mode to verify_peer" do
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
- end
+ it "should set the CA path if that is set in the configuration" do
+ Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl")
+ expect(http_client.ca_path).to eq(File.join(CHEF_SPEC_DATA, "ssl"))
+ end
- it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do
- Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here"
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
- end
+ it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do
+ Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here"
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
+ end
- it "should set the CA path if that is set in the configuration" do
- Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl")
- expect(http_client.ca_path).to eq(File.join(CHEF_SPEC_DATA, "ssl"))
- end
+ it "should set the CA file if that is set in the configuration" do
+ Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + "/ssl/5e707473.0"
+ expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/ssl/5e707473.0")
+ end
- it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do
- Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here"
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
- end
+ it "should set the custom CA file if SSL_CERT_FILE environment variable is set" do
+ ENV["SSL_CERT_FILE"] = CHEF_SPEC_DATA + "/trusted_certs/intermediate.pem"
+ expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/trusted_certs/intermediate.pem")
+ end
- it "should set the CA file if that is set in the configuration" do
- Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + "/ssl/5e707473.0"
- expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/ssl/5e707473.0")
- end
+ it "raises a ConfigurationError if SSL_CERT_FILE environment variable is set to a file that does not exist" do
+ ENV["SSL_CERT_FILE"] = "/dev/null/nothing_here"
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
end
- describe "when configured with :ssl_verify_mode set to :verify peer" do
- before do
- @url = URI.parse("https://chef.example.com:4443/")
- Chef::Config[:ssl_verify_mode] = :verify_none
- end
+ it "sets the OpenSSL verify mode to verify_peer when configured with :ssl_verify_mode set to :verify_peer" do
+ Chef::Config[:ssl_verify_mode] = :verify_peer
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
+ end
- it "sets the OpenSSL verify mode to :verify_none" do
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
- end
+ it "sets the OpenSSL verify mode to :verify_none when configured with :ssl_verify_mode set to :verify_none" do
+ Chef::Config[:ssl_verify_mode] = :verify_none
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
end
describe "when configured with a client certificate" do
- before { @url = URI.parse("https://chef.example.com:4443/") }
-
it "raises ConfigurationError if the certificate file doesn't exist" do
Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /ssl_client_cert .* does not exist/)
end
- it "raises ConfigurationError if the certificate file doesn't exist" do
+ it "raises ConfigurationError if the private key file doesn't exist" do
Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
Chef::Config[:ssl_client_key] = "/dev/null/nothing_here"
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /ssl_client_key .* does not exist/)
end
it "raises a ConfigurationError if one of :ssl_client_cert and :ssl_client_key is set but not both" do
Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
Chef::Config[:ssl_client_key] = nil
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /configure ssl_client_cert and ssl_client_key together/)
+ end
+
+ it "raises a ConfigurationError with a bad cert file" do
+ Chef::Config[:ssl_client_cert] = __FILE__
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading cert file '#{__FILE__}'/)
+ end
+
+ it "raises a ConfigurationError with a bad key file" do
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
+ Chef::Config[:ssl_client_key] = __FILE__
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading key file '#{__FILE__}'/)
end
it "configures the HTTP client's cert and private key" do
@@ -111,20 +114,31 @@ describe "HTTP SSL Policy" do
expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
end
- end
- context "when additional certs are located in the trusted_certs dir" do
- let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt") }
- let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) }
+ it "configures the HTTP client's cert and private key with a DER encoded cert" do
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/binary/chef-rspec-der.cert"
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
+ expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
+ expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
+ end
- let(:additional_pem_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "opscode.pem") }
- let(:additional_pem) { OpenSSL::X509::Certificate.new(File.read(additional_pem_path)) }
+ it "configures the HTTP client's cert and private key with a DER encoded key" do
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/binary/chef-rspec-der.key"
+ expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
+ expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
+ end
+ end
+ context "when additional certs are located in the trusted_certs dir" do
before do
Chef::Config.trusted_certs_dir = File.join(CHEF_SPEC_DATA, "trusted_certs")
end
it "enables verification of self-signed certificates" do
+ path = File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt")
+ self_signed_crt = OpenSSL::X509::Certificate.new(File.binread(path))
+
expect(http_client.cert_store.verify(self_signed_crt)).to be_truthy
end
@@ -137,32 +151,77 @@ describe "HTTP SSL Policy" do
# If the machine running the test doesn't have ruby SSL configured correctly,
# then the root cert also has to be loaded for the test to succeed.
# The system under test **SHOULD** do both of these things.
+ path = File.join(CHEF_SPEC_DATA, "trusted_certs", "opscode.pem")
+ additional_pem = OpenSSL::X509::Certificate.new(File.binread(path))
+
expect(http_client.cert_store.verify(additional_pem)).to be_truthy
end
- context "and some certs are duplicates" do
- it "skips duplicate certs" do
- # For whatever reason, OpenSSL errors out when adding a
- # cert you already have to the certificate store.
- ssl_policy.set_custom_certs
- ssl_policy.set_custom_certs #should not raise an error
+ it "skips duplicate certs" do
+ # For whatever reason, OpenSSL errors out when adding a
+ # cert you already have to the certificate store.
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
+ ssl_policy.set_custom_certs
+ ssl_policy.set_custom_certs # should not raise an error
+ end
+
+ it "raises ConfigurationError with a bad cert file in the trusted_certs dir" do
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
+
+ Dir.mktmpdir do |dir|
+ bad_cert_file = File.join(dir, "bad_cert_file.crt")
+ File.write(bad_cert_file, File.read(__FILE__))
+
+ Chef::Config.trusted_certs_dir = dir
+ expect { ssl_policy.set_custom_certs }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading cert file/)
end
end
+
+ it "works with binary certs" do
+ Chef::Config.trusted_certs_dir = File.join(CHEF_SPEC_DATA, "ssl", "binary")
+
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
+ ssl_policy.set_custom_certs
+ end
end
end
describe Chef::HTTP::APISSLPolicy do
- let(:ssl_policy) { Chef::HTTP::APISSLPolicy.new(unconfigured_http_client) }
+ let(:ssl_policy_class) { Chef::HTTP::APISSLPolicy }
- context "when verify_api_cert is set" do
- before do
- Chef::Config[:verify_api_cert] = true
- end
+ it "sets the OpenSSL verify mode to verify_peer when configured with :ssl_verify_mode set to :verify_peer" do
+ Chef::Config[:ssl_verify_mode] = :verify_peer
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
+ end
- it "sets the OpenSSL verify mode to verify_peer" do
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
- end
+ it "sets the OpenSSL verify mode to :verify_none when configured with :ssl_verify_mode set to :verify_none" do
+ Chef::Config[:ssl_verify_mode] = :verify_none
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
+ end
+
+ it "sets the OpenSSL verify mode to verify_peer when verify_api_cert is set" do
+ Chef::Config[:verify_api_cert] = true
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
+ end
+ end
+
+ describe Chef::HTTP::VerifyPeerSSLPolicy do
+
+ let(:ssl_policy_class) { Chef::HTTP::VerifyPeerSSLPolicy }
+
+ it "sets the OpenSSL verify mode to verify_peer" do
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
+ end
+
+ end
+
+ describe Chef::HTTP::VerifyNoneSSLPolicy do
+
+ let(:ssl_policy_class) { Chef::HTTP::VerifyNoneSSLPolicy }
+
+ it "sets the OpenSSL verify mode to verify_peer" do
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
end
end
diff --git a/spec/unit/http/validate_content_length_spec.rb b/spec/unit/http/validate_content_length_spec.rb
index 5067d36d38..520a7674af 100644
--- a/spec/unit/http/validate_content_length_spec.rb
+++ b/spec/unit/http/validate_content_length_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,7 +44,7 @@ describe Chef::HTTP::ValidateContentLength do
end
let(:response) do
- m = double("HttpResponse", :body => response_body)
+ m = double("HttpResponse", body: response_body)
allow(m).to receive(:[]) do |key|
response_headers[key]
end
@@ -67,7 +67,7 @@ describe Chef::HTTP::ValidateContentLength do
data_length = streaming_length
while data_length > 0
chunk_size = data_length > 10 ? 10 : data_length
- stream_handler.handle_chunk(double("Chunk", :bytesize => chunk_size))
+ stream_handler.handle_chunk(double("Chunk", bytesize: chunk_size))
data_length -= chunk_size
end
@@ -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
@@ -180,7 +180,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("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 d99f03aac9..bb52db60fd 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -43,7 +43,7 @@ describe Chef::HTTP do
end
- describe "#intialize" do
+ describe "#initialize" 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
@@ -74,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
@@ -93,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
@@ -156,7 +165,7 @@ describe Chef::HTTP do
end
it "raises the error without retrying or sleeping" do
- # We modify the strings to give addtional context, but the exception class should be the same
+ # We modify the strings to give additional context, but the exception class should be the same
expect { http.get("/") }.to raise_error(exception.class)
end
end
diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb
index 4da29fe4ec..9a3ce2121a 100644
--- a/spec/unit/json_compat_spec.rb
+++ b/spec/unit/json_compat_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Juanje Ojeda (<juanje.ojeda@gmail.com>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,34 +16,12 @@
# limitations under the License.
#
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
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/key_spec.rb b/spec/unit/key_spec.rb
index 4af506d227..ca98e9dfbd 100644
--- a/spec/unit/key_spec.rb
+++ b/spec/unit/key_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,20 +21,20 @@ require "spec_helper"
require "chef/key"
describe Chef::Key do
- # whether user or client irrelevent to these tests
+ # whether user or client irrelevant to these tests
let(:key) { Chef::Key.new("original_actor", "user") }
let(:public_key_string) do
- <<EOS
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
-KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
-WrlqpI3yv/5DOP8HTMCxnFuMJQtDwMcevlqebX4bCxcByuBpNYDcAHjjfLGSfMjn
-E5lZpgYWwnpic4kSjYcL9ORK9nYvlWV9P/kCYmRhIjB4AhtpWRiOfY/TKi3P2LxT
-IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
-Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
-0wIDAQAB
------END PUBLIC KEY-----
-EOS
+ <<~EOS
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
+ KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
+ WrlqpI3yv/5DOP8HTMCxnFuMJQtDwMcevlqebX4bCxcByuBpNYDcAHjjfLGSfMjn
+ E5lZpgYWwnpic4kSjYcL9ORK9nYvlWV9P/kCYmRhIjB4AhtpWRiOfY/TKi3P2LxT
+ IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
+ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
+ 0wIDAQAB
+ -----END PUBLIC KEY-----
+ EOS
end
shared_examples_for "fields with username type validation" do
@@ -66,7 +66,7 @@ EOS
context "when you feed it anything but a string" do
it "should raise an ArgumentError" do
- expect { key.send(field, Hash.new) }.to raise_error(ArgumentError)
+ expect { key.send(field, {}) }.to raise_error(ArgumentError)
end
end
end
@@ -406,9 +406,9 @@ EOS
it "creates a new key via the API with the fingerprint as the name" do
expect(rest).to receive(:post).with(url,
- { "name" => "12:3e:33:73:0b:f4:ec:72:dc:f0:4c:51:62:27:08:76:96:24:f4:4a",
- "public_key" => key.public_key,
- "expiration_date" => key.expiration_date }).and_return({})
+ { "name" => "12:3e:33:73:0b:f4:ec:72:dc:f0:4c:51:62:27:08:76:96:24:f4:4a",
+ "public_key" => key.public_key,
+ "expiration_date" => key.expiration_date }).and_return({})
key.create
end
end
@@ -424,9 +424,9 @@ EOS
context "when create_key is false" do
it "creates a new key via the API" do
expect(rest).to receive(:post).with(url,
- { "name" => key.name,
- "public_key" => key.public_key,
- "expiration_date" => key.expiration_date }).and_return({})
+ { "name" => key.name,
+ "public_key" => key.public_key,
+ "expiration_date" => key.expiration_date }).and_return({})
key.create
end
end
@@ -564,7 +564,7 @@ EOS
end
end
- end #update
+ end # update
describe "load" do
shared_examples_for "load" do
@@ -592,7 +592,7 @@ EOS
end
end
- end #load
+ end # load
describe "destroy" do
shared_examples_for "destroy key" do
diff --git a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
index d822fe8f98..4d36208be0 100644
--- a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
+++ b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist <lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,12 +25,12 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
let(:stdin) { StringIO.new }
let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) }
- let(:knife_config) { {} }
+ let(:config) { {} }
let(:client) { Chef::ApiClient.new }
let(:chef_vault_handler) do
- chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(knife_config: knife_config, ui: ui)
+ chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(config: config, ui: ui)
chef_vault_handler
end
@@ -55,28 +55,28 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
expect(bootstrap_vault_item).to receive(:save).at_least(:once)
end
- context "from knife_config[:bootstrap_vault_item]" do
+ context "from config[:bootstrap_vault_item]" do
it "sets a single item as a scalar" do
- knife_config[:bootstrap_vault_item] = { "vault" => "item1" }
+ config[:bootstrap_vault_item] = { "vault" => "item1" }
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets a single item as an array" do
- knife_config[:bootstrap_vault_item] = { "vault" => [ "item1" ] }
+ config[:bootstrap_vault_item] = { "vault" => [ "item1" ] }
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets two items as an array" do
- knife_config[:bootstrap_vault_item] = { "vault" => %w{item1 item2} }
+ config[:bootstrap_vault_item] = { "vault" => %w{item1 item2} }
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item2").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets two vaults from different hash keys" do
- knife_config[:bootstrap_vault_item] = { "vault" => %w{item1 item2}, "vault2" => [ "item3" ] }
+ config[:bootstrap_vault_item] = { "vault" => %w{item1 item2}, "vault2" => [ "item3" ] }
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item2").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault2", "item3").and_return(bootstrap_vault_item)
@@ -84,28 +84,28 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
end
end
- context "from knife_config[:bootstrap_vault_json]" do
+ context "from config[:bootstrap_vault_json]" do
it "sets a single item as a scalar" do
- knife_config[:bootstrap_vault_json] = '{ "vault": "item1" }'
+ config[:bootstrap_vault_json] = '{ "vault": "item1" }'
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets a single item as an array" do
- knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1" ] }'
+ config[:bootstrap_vault_json] = '{ "vault": [ "item1" ] }'
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets two items as an array" do
- knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ] }'
+ config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ] }'
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item2").and_return(bootstrap_vault_item)
chef_vault_handler.run(client)
end
it "sets two vaults from different hash keys" do
- knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ], "vault2": [ "item3" ] }'
+ config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ], "vault2": [ "item3" ] }'
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item1").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault", "item2").and_return(bootstrap_vault_item)
expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with("vault2", "item3").and_return(bootstrap_vault_item)
@@ -113,12 +113,12 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
end
end
- context "from knife_config[:bootstrap_vault_file]" do
+ context "from config[:bootstrap_vault_file]" do
def setup_file_contents(json)
stringio = StringIO.new(json)
- knife_config[:bootstrap_vault_file] = "/foo/bar/baz"
- expect(File).to receive(:read).with(knife_config[:bootstrap_vault_file]).and_return(stringio)
+ config[:bootstrap_vault_file] = "/foo/bar/baz"
+ expect(File).to receive(:read).with(config[:bootstrap_vault_file]).and_return(stringio)
end
it "sets a single item as a scalar" do
diff --git a/spec/unit/knife/bootstrap/client_builder_spec.rb b/spec/unit/knife/bootstrap/client_builder_spec.rb
index 97ba0fc48e..10edd13882 100644
--- a/spec/unit/knife/bootstrap/client_builder_spec.rb
+++ b/spec/unit/knife/bootstrap/client_builder_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist <lamont@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
let(:stdin) { StringIO.new }
let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) }
- let(:knife_config) { {} }
+ let(:config) { {} }
let(:chef_config) { {} }
@@ -34,15 +34,15 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
let(:rest) { double("Chef::ServerAPI") }
let(:client_builder) do
- client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(knife_config: knife_config, chef_config: chef_config, ui: ui)
+ client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(config: 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") }
- let(:exception_404) { Net::HTTPServerException.new("404 not found", response_404) }
+ let(:response_404) { OpenStruct.new(code: "404") }
+ let(:exception_404) { Net::HTTPClientException.new("404 not found", response_404) }
context "in cases where the prompting fails" do
before do
@@ -160,7 +160,7 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
it "adds tags to the node when given" do
tag_receiver = []
- knife_config[:tags] = %w{foo bar}
+ config[:tags] = %w{foo bar}
allow(node).to receive(:run_list).with([])
allow(node).to receive(:tags).and_return(tag_receiver)
client_builder.run
@@ -168,34 +168,34 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
end
it "builds a node when the run_list is a string" do
- knife_config[:run_list] = "role[base],role[app]"
+ config[:run_list] = "role[base],role[app]"
expect(node).to receive(:run_list).with(["role[base]", "role[app]"])
client_builder.run
end
it "builds a node when the run_list is an Array" do
- knife_config[:run_list] = ["role[base]", "role[app]"]
+ config[:run_list] = ["role[base]", "role[app]"]
expect(node).to receive(:run_list).with(["role[base]", "role[app]"])
client_builder.run
end
it "builds a node with first_boot_attributes if they're given" do
- knife_config[:first_boot_attributes] = { :baz => :quux }
- expect(node).to receive(:normal_attrs=).with({ :baz => :quux })
+ config[:first_boot_attributes] = { baz: :quux }
+ expect(node).to receive(:normal_attrs=).with({ baz: :quux })
expect(node).to receive(:run_list).with([])
client_builder.run
end
it "builds a node with an environment if its given" do
- knife_config[:environment] = "production"
+ config[:environment] = "production"
expect(node).to receive(:environment).with("production")
expect(node).to receive(:run_list).with([])
client_builder.run
end
it "builds a node with policy_name and policy_group when given" do
- knife_config[:policy_name] = "my-app"
- knife_config[:policy_group] = "staging"
+ config[:policy_name] = "my-app"
+ config[:policy_group] = "staging"
expect(node).to receive(:run_list).with([])
expect(node).to receive(:policy_name=).with("my-app")
diff --git a/spec/unit/knife/bootstrap/train_connector_spec.rb b/spec/unit/knife/bootstrap/train_connector_spec.rb
new file mode 100644
index 0000000000..4c384100fa
--- /dev/null
+++ b/spec/unit/knife/bootstrap/train_connector_spec.rb
@@ -0,0 +1,244 @@
+#
+# Copyright:: Copyright (c) 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 "ostruct"
+require "chef/knife/bootstrap/train_connector"
+
+describe Chef::Knife::Bootstrap::TrainConnector do
+ let(:protocol) { "mock" }
+ let(:family) { "unknown" }
+ let(:release) { "unknown" } # version
+ let(:name) { "unknown" }
+ let(:arch) { "x86_64" }
+ let(:connection_opts) { {} } # connection opts
+ let(:host_url) { "mock://user1@example.com" }
+ let(:mock_connection) { true }
+
+ subject do
+ # Example groups can still override by setting explicitly it in 'connection_opts'
+ tc = Chef::Knife::Bootstrap::TrainConnector.new(host_url, protocol, connection_opts)
+ tc
+ end
+
+ before(:each) do
+ if mock_connection
+ subject.connect!
+ subject.connection.mock_os(
+ family: family,
+ name: name,
+ release: release,
+ arch: arch
+ )
+ end
+ end
+
+ describe "platform helpers" do
+ context "on linux" do
+ let(:family) { "debian" }
+ let(:name) { "ubuntu" }
+ it "reports that it is linux and unix, because that is how train classifies it" do
+ expect(subject.unix?).to eq true
+ expect(subject.linux?).to eq true
+ expect(subject.windows?).to eq false
+ end
+ end
+ context "on unix" do
+ let(:family) { "os" }
+ let(:name) { "mac_os_x" }
+ it "reports only a unix OS" do
+ expect(subject.unix?).to eq true
+ expect(subject.linux?).to eq false
+ expect(subject.windows?).to eq false
+ end
+ end
+ context "on windows" do
+ let(:family) { "windows" }
+ let(:name) { "windows" }
+ it "reports only a windows OS" do
+ expect(subject.unix?).to eq false
+ expect(subject.linux?).to eq false
+ expect(subject.windows?).to eq true
+ end
+ end
+ end
+
+ describe "#connect!" do
+ it "establishes the connection to the remote host by waiting for it" do
+ expect(subject.connection).to receive(:wait_until_ready)
+ subject.connect!
+ end
+ end
+
+ describe "#initialize" do
+ let(:mock_connection) { false }
+
+ context "when provided target is a proper URL" do
+ let(:protocol) { "ssh" }
+ let(:host_url) { "mock://user1@localhost:2200" }
+ it "correctly configures the instance from the URL" do
+ expect(subject.config[:backend]).to eq "mock"
+ expect(subject.config[:port]).to eq 2200
+ expect(subject.config[:host]).to eq "localhost"
+ expect(subject.config[:user]).to eq "user1"
+ end
+
+ context "and conflicting options are given" do
+ let(:connection_opts) { { user: "user2", host: "example.com", port: 15 } }
+ it "resolves them from the URI" do
+ expect(subject.config[:backend]).to eq "mock"
+ expect(subject.config[:port]).to eq 2200
+ expect(subject.config[:host]).to eq "localhost"
+ expect(subject.config[:user]).to eq "user1"
+ end
+ end
+ end
+
+ context "when provided target is just a hostname" do
+ let(:host_url) { "localhost" }
+ let(:protocol) { "mock" }
+ it "correctly sets backend protocol from the default" do
+ expect(subject.config[:backend]).to eq "mock"
+ end
+
+ context "and options have been provided that are supported by the transport" do
+ let(:protocol) { "ssh" }
+ let(:connection_opts) { { port: 15, user: "user2" } }
+
+ it "sets hostname and transport from arguments and provided fields from options" do
+ expect(subject.config[:backend]).to eq "ssh"
+ expect(subject.config[:host]).to eq "localhost"
+ expect(subject.config[:user]).to eq "user2"
+ expect(subject.config[:port]).to eq 15
+ end
+
+ end
+
+ end
+
+ context "when provided target is just a an IP address" do
+ let(:host_url) { "127.0.0.1" }
+ let(:protocol) { "mock" }
+ it "correctly sets backend protocol from the default" do
+ expect(subject.config[:backend]).to eq "mock"
+ end
+ end
+ end
+
+ describe "#temp_dir" do
+ context "under windows" do
+ let(:family) { "windows" }
+ let(:name) { "windows" }
+
+ it "uses the windows command to create the temp dir" do
+ expected_command = Chef::Knife::Bootstrap::TrainConnector::MKTEMP_WIN_COMMAND
+ expect(subject).to receive(:run_command!).with(expected_command)
+ .and_return double("result", stdout: "C:/a/path")
+ expect(subject.temp_dir).to eq "C:/a/path"
+ end
+
+ end
+ context "under linux and unix-like" do
+ let(:family) { "debian" }
+ let(:name) { "ubuntu" }
+ let(:random) { "wScHX6" }
+ let(:dir) { "/tmp/chef_#{random}" }
+
+ before do
+ allow(SecureRandom).to receive(:alphanumeric).with(6).and_return(random)
+ end
+
+ context "uses the *nix command to create the temp dir and sets ownership to logged-in" do
+ it "with sudo privilege" do
+ subject.config[:sudo] = true
+ expected_command1 = "mkdir -p '#{dir}'"
+ expected_command2 = "chown user1 '#{dir}'"
+ expect(subject).to receive(:run_command!).with(expected_command1)
+ .and_return double("result", stdout: "\r\n")
+ expect(subject).to receive(:run_command!).with(expected_command2)
+ .and_return double("result", stdout: "\r\n")
+ expect(subject.temp_dir).to eq(dir)
+ end
+
+ it "without sudo privilege" do
+ expected_command = "mkdir -p '#{dir}'"
+ expect(subject).to receive(:run_command!).with(expected_command)
+ .and_return double("result", stdout: "\r\n")
+ expect(subject.temp_dir).to eq(dir)
+ end
+ end
+
+ context "with noise in stderr" do
+ it "uses the *nix command to create the temp dir" do
+ expected_command = "mkdir -p '#{dir}'"
+ expect(subject).to receive(:run_command!).with(expected_command)
+ .and_return double("result", stdout: "sudo: unable to resolve host hostname.localhost\r\n" + "#{dir}\r\n")
+ expect(subject.temp_dir).to eq(dir)
+ end
+ end
+ end
+ end
+ context "#upload_file_content!" do
+ it "creates a local file with expected content and uploads it" do
+ expect(subject).to receive(:upload_file!) do |local_path, remote_path|
+ expect(File.read(local_path)).to eq "test data"
+ expect(remote_path).to eq "/target/path"
+ end
+ expect_any_instance_of(Tempfile).to receive(:binmode)
+ subject.upload_file_content!("test data", "/target/path")
+ end
+ end
+
+ context "del_file" do
+ context "on windows" do
+ let(:family) { "windows" }
+ let(:name) { "windows" }
+ it "deletes the file with a windows command" do
+ expect(subject).to receive(:run_command!) do |cmd, &_handler|
+ expect(cmd).to match(/Test-Path "deleteme\.txt".*/)
+ end
+ subject.del_file!("deleteme.txt")
+ end
+ end
+ context "on unix-like" do
+ let(:family) { "debian" }
+ let(:name) { "ubuntu" }
+ it "deletes the file with a windows command" do
+ expect(subject).to receive(:run_command!) do |cmd, &_handler|
+ expect(cmd).to match(/rm -f "deleteme\.txt".*/)
+ end
+ subject.del_file!("deleteme.txt")
+ end
+ end
+ end
+
+ context "#run_command!" do
+ it "raises a RemoteExecutionFailed when the remote execution failed" do
+ command_result = double("results", stdout: "", stderr: "failed", exit_status: 1)
+ expect(subject).to receive(:run_command).and_return command_result
+
+ expect { subject.run_command!("test") }.to raise_error do |e|
+ expect(e.hostname).to eq subject.hostname
+ expect(e.class).to eq Chef::Knife::Bootstrap::RemoteExecutionFailed
+ expect(e.stderr).to eq "failed"
+ expect(e.stdout).to eq ""
+ expect(e.exit_status).to eq 1
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 9f944b82d9..64a59f2ddb 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -1,6 +1,7 @@
#
# Author:: Ian Meyer (<ianmmeyer@gmail.com>)
# Copyright:: Copyright 2010-2016, Ian Meyer
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,33 +20,97 @@
require "spec_helper"
Chef::Knife::Bootstrap.load_deps
-require "net/ssh"
describe Chef::Knife::Bootstrap do
- before do
- allow(ChefConfig).to receive(:windows?) { false }
+ let(:bootstrap_template) { nil }
+ let(:stderr) { StringIO.new }
+ let(:bootstrap_cli_options) { [ ] }
+ let(:linux_test) { true }
+ let(:windows_test) { false }
+ let(:linux_test) { false }
+ let(:unix_test) { false }
+ let(:ssh_test) { false }
+
+ let(:connection) do
+ double("TrainConnector",
+ windows?: windows_test,
+ linux?: linux_test,
+ unix?: unix_test)
end
+
let(:knife) do
Chef::Log.logger = Logger.new(StringIO.new)
Chef::Config[:knife][:bootstrap_template] = bootstrap_template unless bootstrap_template.nil?
k = Chef::Knife::Bootstrap.new(bootstrap_cli_options)
- k.merge_configs
-
allow(k.ui).to receive(:stderr).and_return(stderr)
allow(k).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false)
+ allow(k).to receive(:connection).and_return connection
+ k.merge_configs
k
end
- let(:stderr) { StringIO.new }
+ context "#check_license" do
+ let(:acceptor) { instance_double(LicenseAcceptance::Acceptor) }
- let(:bootstrap_template) { nil }
+ before do
+ expect(LicenseAcceptance::Acceptor).to receive(:new).and_return(acceptor)
+ end
- let(:bootstrap_cli_options) { [ ] }
+ describe "when a license is not required" do
+ it "does not set the chef_license" do
+ expect(acceptor).to receive(:license_required?).and_return(false)
+ knife.check_license
+ expect(Chef::Config[:chef_license]).to eq(nil)
+ end
+ end
- it "should use chef-full as default template" do
- expect(knife.bootstrap_template).to be_a_kind_of(String)
- expect(File.basename(knife.bootstrap_template)).to eq("chef-full")
+ describe "when a license is required" do
+ it "sets the chef_license" do
+ expect(acceptor).to receive(:license_required?).and_return(true)
+ expect(acceptor).to receive(:id_from_mixlib).and_return("id")
+ expect(acceptor).to receive(:check_and_persist)
+ expect(acceptor).to receive(:acceptance_value).and_return("accept-no-persist")
+ knife.check_license
+ expect(Chef::Config[:chef_license]).to eq("accept-no-persist")
+ end
+ end
+ end
+
+ context "#bootstrap_template" do
+ it "should default to chef-full" do
+ expect(knife.bootstrap_template).to be_a_kind_of(String)
+ expect(File.basename(knife.bootstrap_template)).to eq("chef-full")
+ end
+ end
+
+ context "#render_template - 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(/secrets/)
+ end
+ end
end
context "with --bootstrap-vault-item" do
@@ -55,24 +120,42 @@ 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
+ it "configures the preinstall command in the bootstrap template correctly" do
+ expect(rendered_template).to match(/command/)
end
end
- context "with :distro and :template_file cli options" do
- let(:bootstrap_cli_options) { [ "--distro", "my-template", "--template-file", "other-template" ] }
+ context "with --bootstrap-proxy" do
+ let(:bootstrap_cli_options) { [ "--bootstrap-proxy", "1.1.1.1" ] }
+ let(:rendered_template) do
+ knife.merge_configs
+ knife.render_template
+ end
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
+ expect(rendered_template).to match(/https_proxy="1.1.1.1" export https_proxy/)
+ end
+ end
- it "should select bootstrap template" do
- expect(File.basename(knife.bootstrap_template)).to eq("other-template")
+ context "with --bootstrap-no-proxy" do
+ let(:bootstrap_cli_options) { [ "--bootstrap-no-proxy", "localserver" ] }
+ let(:rendered_template) do
+ knife.merge_configs
+ knife.render_template
+ end
+ it "configures the https_proxy environment variable in the bootstrap template correctly" do
+ expect(rendered_template).to match(/no_proxy="localserver" export no_proxy/)
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 +176,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
@@ -102,7 +185,7 @@ describe Chef::Knife::Bootstrap do
context "when :bootstrap_template config is set to a template name" do
let(:bootstrap_template) { "example" }
- let(:builtin_template_path) { File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/chef/knife/bootstrap/templates", "example.erb")) }
+ let(:builtin_template_path) { File.expand_path(File.join(__dir__, "../../../lib/chef/knife/bootstrap/templates", "example.erb")) }
let(:chef_config_dir_template_path) { "/knife/chef/config/bootstrap/example.erb" }
@@ -123,7 +206,7 @@ describe Chef::Knife::Bootstrap do
end
before(:each) do
- expect(File).to receive(:exists?).with(bootstrap_template).and_return(false)
+ expect(File).to receive(:exist?).with(bootstrap_template).and_return(false)
end
context "when file is available everywhere" do
@@ -132,7 +215,7 @@ describe Chef::Knife::Bootstrap do
configure_env_home
configure_gem_files
- expect(File).to receive(:exists?).with(builtin_template_path).and_return(true)
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(true)
end
it "should load the template from built-in templates" do
@@ -146,8 +229,8 @@ describe Chef::Knife::Bootstrap do
configure_env_home
configure_gem_files
- expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
- expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(true)
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(true)
it "should load the template from chef_config_dir" do
knife.find_template.should eq(chef_config_dir_template_path)
@@ -161,9 +244,9 @@ describe Chef::Knife::Bootstrap do
configure_env_home
configure_gem_files
- expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
- expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
- expect(File).to receive(:exists?).with(env_home_template_path).and_return(true)
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(env_home_template_path).and_return(true)
end
it "should load the template from chef_config_dir" do
@@ -177,10 +260,10 @@ describe Chef::Knife::Bootstrap do
configure_env_home
configure_gem_files
- expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
- expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
- expect(File).to receive(:exists?).with(env_home_template_path).and_return(false)
- expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true)
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(env_home_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
end
it "should load the template from Gem files" do
@@ -194,9 +277,9 @@ describe Chef::Knife::Bootstrap do
configure_gem_files
allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil)
- expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
- expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
- expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true)
+ expect(File).to receive(:exist?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(chef_config_dir_template_path).and_return(false)
+ expect(File).to receive(:exist?).with(gem_files_template_path).and_return(true)
end
it "should load the template from Gem files" do
@@ -206,7 +289,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"])
@@ -237,7 +320,7 @@ describe Chef::Knife::Bootstrap do
context "with bootstrap_attribute options" do
let(:jsonfile) do
- file = Tempfile.new (["node", ".json"])
+ file = Tempfile.new(["node", ".json"])
File.open(file.path, "w") { |f| f.puts '{"foo":{"bar":"baz"}}' }
file
end
@@ -259,14 +342,15 @@ describe Chef::Knife::Bootstrap do
jsonfile.close
end
- context "when --json-attributes and --json-attribute-file were both passed" do
- it "raises a Chef::Exceptions::BootstrapCommandInputError with the proper error message" do
- knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
- knife.parse_options(["--json-attribute-file", jsonfile.path])
- knife.merge_configs
- expect { knife.run }.to raise_error(Chef::Exceptions::BootstrapCommandInputError)
- jsonfile.close
- end
+ it "raises a Chef::Exceptions::BootstrapCommandInputError with the proper error message" do
+ knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
+ knife.parse_options(["--json-attribute-file", jsonfile.path])
+ knife.merge_configs
+ allow(knife).to receive(:validate_name_args!)
+ expect(knife).to receive(:check_license)
+
+ expect { knife.run }.to raise_error(Chef::Exceptions::BootstrapCommandInputError)
+ jsonfile.close
end
end
end
@@ -277,7 +361,7 @@ describe Chef::Knife::Bootstrap do
it "should create a hint file when told to" do
knife.parse_options(["--hint", "openstack"])
knife.merge_configs
- expect(knife.render_template).to match(/\/etc\/chef\/ohai\/hints\/openstack.json/)
+ expect(knife.render_template).to match(%r{/etc/chef/ohai/hints/openstack.json})
end
it "should populate a hint file with JSON when given a file to read" do
@@ -292,13 +376,16 @@ describe Chef::Knife::Bootstrap do
subject(:knife) do
k = described_class.new
Chef::Config[:knife][:bootstrap_template] = template_file
+ allow(k).to receive(:connection).and_return connection
k.parse_options(options)
k.merge_configs
k
end
- let(:options) { ["--bootstrap-no-proxy", setting, "-s", "foo"] }
+ let(:options) { ["--bootstrap-no-proxy", setting] }
+
let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
+
let(:rendered_template) do
knife.render_template
end
@@ -307,7 +394,7 @@ describe Chef::Knife::Bootstrap do
let(:setting) { "api.opscode.com" }
it "renders the client.rb with a single FQDN no_proxy entry" do
- expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com".*})
+ expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com".*/)
end
end
@@ -315,7 +402,7 @@ describe Chef::Knife::Bootstrap do
let(:setting) { "api.opscode.com,172.16.10.*" }
it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
- expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com,172.16.10.\*".*})
+ expect(rendered_template).to match(/.*no_proxy\s*"api.opscode.com,172.16.10.\*".*/)
end
end
@@ -373,7 +460,7 @@ describe Chef::Knife::Bootstrap do
it "creates a secret file" do
expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
expect(knife).to receive(:read_secret).and_return(secret)
- expect(rendered_template).to match(%r{#{secret}})
+ expect(rendered_template).to match(/#{secret}/)
end
it "renders the client.rb with an encrypted_data_bag_secret entry" do
@@ -385,21 +472,13 @@ describe Chef::Knife::Bootstrap do
end
describe "when transferring trusted certificates" do
- let(:trusted_certs_dir) { Chef::Util::PathHelper.cleanpath(File.join(File.dirname(__FILE__), "../../data/trusted_certs")) }
-
let(:rendered_template) do
knife.merge_configs
knife.render_template
end
before do
- Chef::Config[:trusted_certs_dir] = trusted_certs_dir
- allow(IO).to receive(:read).and_call_original
- allow(IO).to receive(:read).with(File.expand_path(Chef::Config[:validation_key])).and_return("")
- end
-
- def certificates
- Dir[File.join(trusted_certs_dir, "*.{crt,pem}")]
+ Chef::Config[:trusted_certs_dir] = Chef::Util::PathHelper.cleanpath(File.join(CHEF_SPEC_DATA, "trusted_certs"))
end
it "creates /etc/chef/trusted_certs" do
@@ -407,9 +486,7 @@ describe Chef::Knife::Bootstrap do
end
it "copies the certificates in the directory" do
- certificates.each do |cert|
- expect(IO).to receive(:read).with(File.expand_path(cert))
- end
+ certificates = Dir[File.join(Chef::Config[:trusted_certs_dir], "*.{crt,pem}")]
certificates.each do |cert|
expect(rendered_template).to match(%r{cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'})
@@ -417,17 +494,19 @@ describe Chef::Knife::Bootstrap do
end
it "doesn't create /etc/chef/trusted_certs if :trusted_certs_dir is empty" do
- expect(Dir).to receive(:glob).with(File.join(trusted_certs_dir, "*.{crt,pem}")).and_return([])
- expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/trusted_certs})
+ Dir.mktmpdir do |dir|
+ Chef::Config[:trusted_certs_dir] = dir
+ expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/trusted_certs})
+ end
end
end
context "when doing fips things" do
let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
- let(:trusted_certs_dir) { Chef::Util::PathHelper.cleanpath(File.join(File.dirname(__FILE__), "../../data/trusted_certs")) }
before do
Chef::Config[:knife][:bootstrap_template] = template_file
+ knife.merge_configs
end
let(:rendered_template) do
@@ -440,7 +519,6 @@ describe Chef::Knife::Bootstrap do
end
it "renders 'fips true'" do
- Chef::Config[:fips] = true
expect(rendered_template).to match("fips")
end
end
@@ -480,13 +558,20 @@ describe Chef::Knife::Bootstrap do
context "when client_d_dir is set" do
let(:client_d_dir) do
Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_00")) end
+ File.join(__dir__, "../../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")
@@ -500,7 +585,9 @@ describe Chef::Knife::Bootstrap do
context "a nested directory structure" do
let(:client_d_dir) do
Chef::Util::PathHelper.cleanpath(
- File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
+ File.join(__dir__, "../../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")
@@ -509,14 +596,117 @@ describe Chef::Knife::Bootstrap do
end
end
- describe "handling policyfile options" do
+ describe "#connection_protocol" do
+ let(:host_descriptor) { "example.com" }
+ let(:config) { {} }
+ let(:knife_connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:config).and_return config
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
+ if knife_connection_protocol
+ Chef::Config[:knife][:connection_protocol] = knife_connection_protocol
+ knife.merge_configs
+ end
+ end
+
+ context "when protocol is part of the host argument" do
+ let(:host_descriptor) { "winrm://myhost" }
+
+ it "returns the value provided by the host argument" do
+ expect(knife.connection_protocol).to eq "winrm"
+ end
+ end
+
+ context "when protocol is provided via the CLI flag" do
+ let(:config) { { connection_protocol: "winrm" } }
+ it "returns that value" do
+ expect(knife.connection_protocol).to eq "winrm"
+ end
+
+ end
+ context "when protocol is provided via the host argument and the CLI flag" do
+ let(:host_descriptor) { "ssh://example.com" }
+ let(:config) { { connection_protocol: "winrm" } }
+
+ it "returns the value provided by the host argument" do
+ expect(knife.connection_protocol).to eq "ssh"
+ end
+ end
+
+ context "when no explicit protocol is provided" do
+ let(:config) { {} }
+ let(:host_descriptor) { "example.com" }
+ let(:knife_connection_protocol) { "winrm" }
+ it "falls back to knife config" do
+ expect(knife.connection_protocol).to eq "winrm"
+ end
+ context "and there is no knife bootstrap_protocol" do
+ let(:knife_connection_protocol) { nil }
+ it "falls back to 'ssh'" do
+ expect(knife.connection_protocol).to eq "ssh"
+ end
+ end
+ end
+
+ end
+
+ describe "#validate_protocol!" do
+ let(:host_descriptor) { "example.com" }
+ let(:config) { {} }
+ let(:connection_protocol) { "ssh" }
+ before do
+ allow(knife).to receive(:config).and_return config
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
+ end
+
+ context "when protocol is provided both in the URL and via --protocol" do
+
+ context "and they do not match" do
+ let(:connection_protocol) { "ssh" }
+ let(:config) { { connection_protocol: "winrm" } }
+ it "outputs an error and exits" do
+ expect(knife.ui).to receive(:error)
+ expect { knife.validate_protocol! }.to raise_error SystemExit
+ end
+ end
+
+ context "and they do match" do
+ let(:connection_protocol) { "winrm" }
+ let(:config) { { connection_protocol: "winrm" } }
+ it "returns true" do
+ expect(knife.validate_protocol!).to eq true
+ end
+ end
+ end
+
+ context "and the protocol is supported" do
+
+ Chef::Knife::Bootstrap::SUPPORTED_CONNECTION_PROTOCOLS.each do |proto|
+ let(:connection_protocol) { proto }
+ it "returns true for #{proto}" do
+ expect(knife.validate_protocol!).to eq true
+ end
+ end
+ end
+
+ context "and the protocol is not supported" do
+ let(:connection_protocol) { "invalid" }
+ it "outputs an error and exits" do
+ expect(knife.ui).to receive(:error).with(/Unsupported protocol '#{connection_protocol}'/)
+ expect { knife.validate_protocol! }.to raise_error SystemExit
+ end
+ end
+ end
+
+ describe "#validate_policy_options!" do
context "when only policy_name is given" do
let(:bootstrap_cli_options) { %w{ --policy-name my-app-server } }
it "returns an error stating that policy_name and policy_group must be given together" do
- expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
end
@@ -527,7 +717,7 @@ describe Chef::Knife::Bootstrap do
let(:bootstrap_cli_options) { %w{ --policy-group staging } }
it "returns an error stating that policy_name and policy_group must be given together" do
- expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
end
@@ -538,7 +728,7 @@ describe Chef::Knife::Bootstrap do
let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging --run-list cookbook } }
it "returns an error stating that policyfile and run_list are exclusive" do
- expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect { knife.validate_policy_options! }.to raise_error(SystemExit)
expect(stderr.string).to include("ERROR: Policyfile options and --run-list are exclusive")
end
@@ -549,7 +739,7 @@ describe Chef::Knife::Bootstrap do
let(:bootstrap_cli_options) { %w{ --policy-name my-app --policy-group staging } }
it "passes options validation" do
- expect { knife.validate_options! }.to_not raise_error
+ expect { knife.validate_policy_options! }.to_not raise_error
end
it "passes them into the bootstrap context" do
@@ -557,268 +747,1435 @@ 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
# Arguably a bug in the plugin: it shouldn't be setting this to nil, but it
# worked before, so make it work now.
context "when a plugin sets the run list option to nil" do
-
before do
knife.config[:run_list] = nil
end
it "passes options validation" do
- expect { knife.validate_options! }.to_not raise_error
+ expect { knife.validate_policy_options! }.to_not raise_error
end
+ end
+ end
+ # TODO - this is the only cli option we validate the _option_ itself -
+ # so we'll know if someone accidentally deletes or renames use_sudo_password
+ # Is this worht keeping? If so, then it seems we should expand it
+ # to cover all options.
+ context "validating use_sudo_password option" do
+ it "use_sudo_password contains description and long params for help" do
+ expect(knife.options).to(have_key(:use_sudo_password)) \
+ && expect(knife.options[:use_sudo_password][:description].to_s).not_to(eq(""))\
+ && expect(knife.options[:use_sudo_password][:long].to_s).not_to(eq(""))
end
+ end
+ context "#connection_opts" do
+ let(:connection_protocol) { "ssh" }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+ context "behavioral test: " do
+ let(:expected_connection_opts) do
+ { base_opts: true,
+ ssh_identity_opts: true,
+ ssh_opts: true,
+ gateway_opts: true,
+ host_verify_opts: true,
+ sudo_opts: true,
+ winrm_opts: true }
+ end
+
+ it "queries and merges only expected configurations" do
+ expect(knife).to receive(:base_opts).and_return({ base_opts: true })
+ expect(knife).to receive(:host_verify_opts).and_return({ host_verify_opts: true })
+ expect(knife).to receive(:gateway_opts).and_return({ gateway_opts: true })
+ expect(knife).to receive(:sudo_opts).and_return({ sudo_opts: true })
+ expect(knife).to receive(:winrm_opts).and_return({ winrm_opts: true })
+ expect(knife).to receive(:ssh_opts).and_return({ ssh_opts: true })
+ expect(knife).to receive(:ssh_identity_opts).and_return({ ssh_identity_opts: true })
+ expect(knife.connection_opts).to match expected_connection_opts
+ end
+ end
+
+ context "functional test: " do
+ context "when protocol is winrm" do
+ let(:connection_protocol) { "winrm" }
+ # context "and neither CLI nor Chef::Config config entries have been provided"
+ # end
+ context "and all supported values are provided as Chef::Config entries" do
+ before do
+ # Set everything to easily identifiable and obviously fake values
+ # to verify that Chef::Config is being sourced instead of knife.config
+ knife.config = {}
+ Chef::Config[:knife][:max_wait] = 9999
+ Chef::Config[:knife][:winrm_user] = "winbob"
+ Chef::Config[:knife][:winrm_port] = 9999
+ Chef::Config[:knife][:ca_trust_file] = "trust.me"
+ Chef::Config[:knife][:kerberos_realm] = "realm"
+ Chef::Config[:knife][:kerberos_service] = "service"
+ Chef::Config[:knife][:winrm_auth_method] = "kerberos" # default is negotiate
+ Chef::Config[:knife][:winrm_basic_auth_only] = true
+ Chef::Config[:knife][:winrm_no_verify_cert] = true
+ Chef::Config[:knife][:session_timeout] = 9999
+ Chef::Config[:knife][:winrm_ssl] = true
+ Chef::Config[:knife][:winrm_ssl_peer_fingerprint] = "ABCDEF"
+ end
+
+ context "and no CLI options have been given" do
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ ca_trust_path: "trust.me",
+ max_wait_until_ready: 9999,
+ operation_timeout: 9999,
+ ssl_peer_fingerprint: "ABCDEF",
+ winrm_transport: "kerberos",
+ winrm_basic_auth_only: true,
+ user: "winbob",
+ port: 9999,
+ self_signed: true,
+ ssl: true,
+ kerberos_realm: "realm",
+ kerberos_service: "service",
+ }
+ end
+
+ it "generates a config hash using the Chef::Config values" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+
+ end
+
+ context "and some CLI options have been given" do
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ ca_trust_path: "no trust",
+ max_wait_until_ready: 9999,
+ operation_timeout: 9999,
+ ssl_peer_fingerprint: "ABCDEF",
+ winrm_transport: "kerberos",
+ winrm_basic_auth_only: true,
+ user: "microsoftbob",
+ port: 12,
+ self_signed: true,
+ ssl: true,
+ kerberos_realm: "realm",
+ kerberos_service: "service",
+ password: "lobster",
+ }
+ end
+
+ before do
+ knife.config[:ca_trust_file] = "no trust"
+ knife.config[:connection_user] = "microsoftbob"
+ knife.config[:connection_port] = 12
+ knife.config[:winrm_port] = "13" # indirectly verify we're not looking for the wrong CLI flag
+ knife.config[:connection_password] = "lobster"
+ end
+
+ it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+
+ context "and all CLI options have been given" do
+ before do
+ # We'll force kerberos vi knife.config because it
+ # causes additional options to populate - make sure
+ # Chef::Config is different so we can be sure that we didn't
+ # pull in the Chef::Config value
+ Chef::Config[:knife][:winrm_auth_method] = "negotiate"
+ knife.config[:connection_password] = "blue"
+ knife.config[:max_wait] = 1000
+ knife.config[:connection_user] = "clippy"
+ knife.config[:connection_port] = 1000
+ knife.config[:winrm_port] = 1001 # We should not see this value get used
+
+ knife.config[:ca_trust_file] = "trust.the.internet"
+ knife.config[:kerberos_realm] = "otherrealm"
+ knife.config[:kerberos_service] = "otherservice"
+ knife.config[:winrm_auth_method] = "kerberos" # default is negotiate
+ knife.config[:winrm_basic_auth_only] = false
+ knife.config[:winrm_no_verify_cert] = false
+ knife.config[:session_timeout] = 1000
+ knife.config[:winrm_ssl] = false
+ knife.config[:winrm_ssl_peer_fingerprint] = "FEDCBA"
+ end
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ ca_trust_path: "trust.the.internet",
+ max_wait_until_ready: 1000,
+ operation_timeout: 1000,
+ ssl_peer_fingerprint: "FEDCBA",
+ winrm_transport: "kerberos",
+ winrm_basic_auth_only: false,
+ user: "clippy",
+ port: 1000,
+ self_signed: false,
+ ssl: false,
+ kerberos_realm: "otherrealm",
+ kerberos_service: "otherservice",
+ password: "blue",
+ }
+ end
+ it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+ end # with underlying Chef::Config values
+
+ context "and no values are provided from Chef::Config or CLI" do
+ before do
+ # We will use knife's actual config since these tests
+ # have assumptions based on CLI default values
+ end
+ let(:expected_result) do
+ {
+ logger: Chef::Log,
+ operation_timeout: 60,
+ self_signed: false,
+ ssl: false,
+ ssl_peer_fingerprint: nil,
+ winrm_basic_auth_only: false,
+ winrm_transport: "negotiate",
+ }
+ end
+ it "populates appropriate defaults" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+ end # winrm
+
+ context "when protocol is ssh" do
+ let(:connection_protocol) { "ssh" }
+ # context "and neither CLI nor Chef::Config config entries have been provided"
+ # end
+ context "and all supported values are provided as Chef::Config entries" do
+ before do
+ # Set everything to easily identifiable and obviously fake values
+ # to verify that Chef::Config is being sourced instead of knife.config
+ knife.config = {}
+ Chef::Config[:knife][:max_wait] = 9999
+ Chef::Config[:knife][:session_timeout] = 9999
+ Chef::Config[:knife][:ssh_user] = "sshbob"
+ Chef::Config[:knife][:ssh_port] = 9999
+ Chef::Config[:knife][:host_key_verify] = false
+ Chef::Config[:knife][:ssh_gateway_identity] = "/gateway.pem"
+ Chef::Config[:knife][:ssh_gateway] = "admin@mygateway.local:1234"
+ Chef::Config[:knife][:ssh_identity_file] = "/identity.pem"
+ Chef::Config[:knife][:use_sudo_password] = false # We have no password.
+ end
+
+ context "and no CLI options have been given" do
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ max_wait_until_ready: 9999.0,
+ connection_timeout: 9999,
+ user: "sshbob",
+ bastion_host: "mygateway.local",
+ bastion_port: 1234,
+ bastion_user: "admin",
+ forward_agent: false,
+ keys_only: true,
+ key_files: ["/identity.pem", "/gateway.pem"],
+ sudo: false,
+ verify_host_key: "always",
+ port: 9999,
+ non_interactive: true,
+ }
+ end
+
+ it "generates a correct config hash using the Chef::Config values" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+
+ context "and unsupported Chef::Config options are given in Chef::Config, not in CLI" do
+ before do
+ Chef::Config[:knife][:password] = "blah"
+ Chef::Config[:knife][:ssh_password] = "blah"
+ Chef::Config[:knife][:preserve_home] = true
+ Chef::Config[:knife][:use_sudo] = true
+ Chef::Config[:knife][:ssh_forward_agent] = "blah"
+ end
+ it "does not include the corresponding option in the connection options" do
+ knife.merge_configs
+ expect(knife.connection_opts.key?(:password)).to eq false
+ expect(knife.connection_opts.key?(:ssh_forward_agent)).to eq false
+ expect(knife.connection_opts.key?(:use_sudo)).to eq false
+ expect(knife.connection_opts.key?(:preserve_home)).to eq false
+ end
+ end
+
+ context "and some CLI options have been given" do
+ before do
+ knife.config = {}
+ knife.config[:connection_user] = "sshalice"
+ knife.config[:connection_port] = 12
+ knife.config[:ssh_port] = "13" # canary to indirectly verify we're not looking for the wrong CLI flag
+ knife.config[:connection_password] = "feta cheese"
+ knife.config[:max_wait] = 150
+ knife.config[:session_timeout] = 120
+ knife.config[:use_sudo] = true
+ knife.config[:use_sudo_pasword] = true
+ knife.config[:ssh_forward_agent] = true
+ end
+
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ max_wait_until_ready: 150.0, # cli
+ connection_timeout: 120, # cli
+ user: "sshalice", # cli
+ password: "feta cheese", # cli
+ bastion_host: "mygateway.local", # Config
+ bastion_port: 1234, # Config
+ bastion_user: "admin", # Config
+ forward_agent: true, # cli
+ keys_only: false, # implied false from config password present
+ key_files: ["/identity.pem", "/gateway.pem"], # Config
+ sudo: true, # ccli
+ verify_host_key: "always", # Config
+ port: 12, # cli
+ non_interactive: true,
+ }
+ end
+
+ it "generates a config hash using the CLI options when available and falling back to Chef::Config values" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+
+ context "and all CLI options have been given" do
+ before do
+ knife.config = {}
+ knife.config[:max_wait] = 150
+ knife.config[:session_timeout] = 120
+ knife.config[:connection_user] = "sshroot"
+ knife.config[:connection_port] = 1000
+ knife.config[:connection_password] = "blah"
+ knife.config[:forward_agent] = true
+ knife.config[:use_sudo] = true
+ knife.config[:use_sudo_password] = true
+ knife.config[:preserve_home] = true
+ knife.config[:use_sudo_pasword] = true
+ knife.config[:ssh_forward_agent] = true
+ knife.config[:ssh_verify_host_key] = true
+ knife.config[:ssh_gateway_identity] = "/gateway-identity.pem"
+ knife.config[:ssh_gateway] = "me@example.com:10"
+ knife.config[:ssh_identity_file] = "/my-identity.pem"
+
+ # We'll set these as canaries - if one of these values shows up
+ # in a failed test, then the behavior of not pulling from these keys
+ # out of knife.config is broken:
+ knife.config[:ssh_user] = "do not use"
+ knife.config[:ssh_port] = 1001
+ end
+ let(:expected_result) do
+ {
+ logger: Chef::Log, # not configurable
+ max_wait_until_ready: 150,
+ connection_timeout: 120,
+ user: "sshroot",
+ password: "blah",
+ port: 1000,
+ bastion_host: "example.com",
+ bastion_port: 10,
+ bastion_user: "me",
+ forward_agent: true,
+ keys_only: false,
+ key_files: ["/my-identity.pem", "/gateway-identity.pem"],
+ sudo: true,
+ sudo_options: "-H",
+ sudo_password: "blah",
+ verify_host_key: true,
+ non_interactive: true,
+ }
+ end
+ it "generates a config hash using the CLI options and pulling nothing from Chef::Config" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+ end
+ context "and no values are provided from Chef::Config or CLI" do
+ before do
+ # We will use knife's actual config since these tests
+ # have assumptions based on CLI default values
+ config = {}
+ end
+
+ let(:expected_result) do
+ {
+ forward_agent: false,
+ key_files: [],
+ logger: Chef::Log,
+ keys_only: false,
+ sudo: false,
+ verify_host_key: "always",
+ non_interactive: true,
+ connection_timeout: 60,
+ }
+ end
+ it "populates appropriate defaults" do
+ knife.merge_configs
+ expect(knife.connection_opts).to match expected_result
+ end
+ end
+
+ end # ssh
+ end # functional tests
+
+ end # connection_opts
+
+ context "#base_opts" do
+ let(:connection_protocol) { nil }
+
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for all protocols" do
+ context "when password is provided" do
+ before do
+ knife.config[:connection_port] = 250
+ knife.config[:connection_user] = "test"
+ knife.config[:connection_password] = "opscode"
+ end
+
+ let(:expected_opts) do
+ {
+ port: 250,
+ user: "test",
+ logger: Chef::Log,
+ password: "opscode",
+ }
+ end
+ it "generates the correct options" do
+ expect(knife.base_opts).to eq expected_opts
+ end
+
+ end
+
+ context "when password is not provided" do
+ before do
+ knife.config[:connection_port] = 250
+ knife.config[:connection_user] = "test"
+ end
+
+ let(:expected_opts) do
+ {
+ port: 250,
+ user: "test",
+ logger: Chef::Log,
+ }
+ end
+ it "generates the correct options" do
+ expect(knife.base_opts).to eq expected_opts
+ end
+ end
+ end
+ end
+
+ context "#host_verify_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ it "returns the expected configuration" do
+ knife.config[:winrm_no_verify_cert] = true
+ expect(knife.host_verify_opts).to eq( { self_signed: true } )
+ end
+ it "provides a correct default when no option given" do
+ expect(knife.host_verify_opts).to eq( { self_signed: false } )
+ end
+ end
+
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ it "returns the expected configuration" do
+ knife.config[:ssh_verify_host_key] = false
+ expect(knife.host_verify_opts).to eq( { verify_host_key: false } )
+ end
+ it "provides a correct default when no option given" do
+ expect(knife.host_verify_opts).to eq( { verify_host_key: "always" } )
+ end
+ end
end
- describe "when configuring the underlying knife ssh command" do
- context "from the command line" do
- let(:knife_ssh) do
- knife.name_args = ["foo.example.com"]
- knife.config[:ssh_user] = "rooty"
- knife.config[:ssh_port] = "4001"
- knife.config[:ssh_password] = "open_sesame"
- Chef::Config[:knife][:ssh_user] = nil
- Chef::Config[:knife][:ssh_port] = nil
- knife.config[:forward_agent] = true
- knife.config[:ssh_identity_file] = "~/.ssh/me.rsa"
- allow(knife).to receive(:render_template).and_return("")
- knife.knife_ssh
+ # TODO - test keys_only, password, config source behavior
+ context "#ssh_identity_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ it "returns an empty hash" do
+ expect(knife.ssh_identity_opts).to eq({})
end
+ end
+
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ context "when an identity file is specified" do
+ before do
+ knife.config[:ssh_identity_file] = "/identity.pem"
+ end
+ it "generates the expected configuration" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: [ "/identity.pem" ],
+ keys_only: true,
+ })
+ end
+ context "and a password is also specified" do
+ before do
+ knife.config[:connection_password] = "blah"
+ end
+ it "generates the expected configuration (key, keys_only false)" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: [ "/identity.pem" ],
+ keys_only: false,
+ })
+ end
+ end
- it "configures the hostname" do
- expect(knife_ssh.name_args.first).to eq("foo.example.com")
+ context "and a gateway is not specified" do
+ context "but a gateway identity file is specified" do
+ it "does not include the gateway identity file in keys" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: ["/identity.pem"],
+ keys_only: true,
+ })
+ end
+
+ end
+
+ end
+
+ context "and a gatway is specified" do
+ before do
+ knife.config[:ssh_gateway] = "example.com"
+ end
+ context "and a gateway identity file is not specified" do
+ it "config includes only identity file and not gateway identity" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: [ "/identity.pem" ],
+ keys_only: true,
+ })
+ end
+ end
+
+ context "and a gateway identity file is also specified" do
+ before do
+ knife.config[:ssh_gateway_identity] = "/gateway.pem"
+ end
+
+ it "generates the expected configuration (both keys, keys_only true)" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: [ "/identity.pem", "/gateway.pem" ],
+ keys_only: true,
+ })
+ end
+ end
+ end
end
- it "configures the ssh user" do
- expect(knife_ssh.config[:ssh_user]).to eq("rooty")
+ context "when no identity file is specified" do
+ it "generates the expected configuration (no keys, keys_only false)" do
+ expect(knife.ssh_identity_opts).to eq( {
+ key_files: [ ],
+ keys_only: false,
+ })
+ end
+ context "and a gateway with gateway identity file is specified" do
+ before do
+ knife.config[:ssh_gateway] = "host"
+ knife.config[:ssh_gateway_identity] = "/gateway.pem"
+ end
+
+ it "generates the expected configuration (gateway key, keys_only false)" do
+ expect(knife.ssh_identity_opts).to eq({
+ key_files: [ "/gateway.pem" ],
+ keys_only: false,
+ })
+ end
+ end
end
+ end
+ end
+
+ context "#gateway_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
- it "configures the ssh password" do
- expect(knife_ssh.config[:ssh_password]).to eq("open_sesame")
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ it "returns an empty hash" do
+ expect(knife.gateway_opts).to eq({})
end
+ end
- it "configures the ssh port" do
- expect(knife_ssh.config[:ssh_port]).to eq("4001")
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ context "and ssh_gateway with hostname, user and port provided" do
+ before do
+ knife.config[:ssh_gateway] = "testuser@gateway:9021"
+ end
+ it "returns a proper bastion host config subset" do
+ expect(knife.gateway_opts).to eq({
+ bastion_user: "testuser",
+ bastion_host: "gateway",
+ bastion_port: 9021,
+ })
+ end
+ end
+ context "and ssh_gateway with only hostname is given" do
+ before do
+ knife.config[:ssh_gateway] = "gateway"
+ end
+ it "returns a proper bastion host config subset" do
+ expect(knife.gateway_opts).to eq({
+ bastion_user: nil,
+ bastion_host: "gateway",
+ bastion_port: nil,
+ })
+ end
+ end
+ context "and ssh_gateway with hostname and user is is given" do
+ before do
+ knife.config[:ssh_gateway] = "testuser@gateway"
+ end
+ it "returns a proper bastion host config subset" do
+ expect(knife.gateway_opts).to eq({
+ bastion_user: "testuser",
+ bastion_host: "gateway",
+ bastion_port: nil,
+ })
+ end
end
- it "configures the ssh agent forwarding" do
- expect(knife_ssh.config[:forward_agent]).to eq(true)
+ context "and ssh_gateway with hostname and port is is given" do
+ before do
+ knife.config[:ssh_gateway] = "gateway:11234"
+ end
+ it "returns a proper bastion host config subset" do
+ expect(knife.gateway_opts).to eq({
+ bastion_user: nil,
+ bastion_host: "gateway",
+ bastion_port: 11234,
+ })
+ end
end
- it "configures the ssh identity file" do
- expect(knife_ssh.config[:ssh_identity_file]).to eq("~/.ssh/me.rsa")
+ context "and ssh_gateway is not provided" do
+ it "returns an empty hash" do
+ expect(knife.gateway_opts).to eq({})
+ end
end
end
+ end
- context "validating use_sudo_password" do
- before do
- knife.config[:ssh_password] = "password"
- allow(knife).to receive(:render_template).and_return("")
+ context "#sudo_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ it "returns an empty hash" do
+ expect(knife.sudo_opts).to eq({})
end
+ end
+
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ context "when use_sudo is set" do
+ before do
+ knife.config[:use_sudo] = true
+ end
- it "use_sudo_password contains description and long params for help" do
- expect(knife.options).to(have_key(:use_sudo_password)) \
- && expect(knife.options[:use_sudo_password][:description].to_s).not_to(eq(""))\
- && expect(knife.options[:use_sudo_password][:long].to_s).not_to(eq(""))
+ it "returns a config that enables sudo" do
+ expect(knife.sudo_opts).to eq( { sudo: true } )
+ end
+
+ context "when use_sudo_password is also set" do
+ before do
+ knife.config[:use_sudo_password] = true
+ knife.config[:connection_password] = "opscode"
+ end
+ it "includes :connection_password value in a sudo-enabled configuration" do
+ expect(knife.sudo_opts).to eq({
+ sudo: true,
+ sudo_password: "opscode",
+ })
+ end
+ end
+
+ context "when preserve_home is set" do
+ before do
+ knife.config[:preserve_home] = true
+ end
+ it "enables sudo with sudo_option to preserve home" do
+ expect(knife.sudo_opts).to eq({
+ sudo_options: "-H",
+ sudo: true,
+ })
+ end
+ end
+ end
+
+ context "when use_sudo is not set" do
+ before do
+ knife.config[:use_sudo_password] = true
+ knife.config[:preserve_home] = true
+ end
+ it "returns configuration for sudo off, ignoring other related options" do
+ expect(knife.sudo_opts).to eq( { sudo: false } )
+ end
+ end
+ end
+ end
+
+ context "#ssh_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ let(:default_opts) do
+ {
+ non_interactive: true,
+ forward_agent: false,
+ connection_timeout: 60,
+ }
end
- it "uses the password from --ssh-password for sudo when --use-sudo-password is set" do
- knife.config[:use_sudo] = true
- knife.config[:use_sudo_password] = true
- expect(knife.ssh_command).to include("echo \'#{knife.config[:ssh_password]}\' | sudo -S")
+ context "by default" do
+ it "returns a configuration hash with appropriate defaults" do
+ expect(knife.ssh_opts).to eq default_opts
+ end
+ end
+
+ context "when ssh_forward_agent has a value" do
+ before do
+ knife.config[:ssh_forward_agent] = true
+ end
+ it "returns a default configuration hash with forward_agent set to true" do
+ expect(knife.ssh_opts).to eq(default_opts.merge(forward_agent: true))
+ end
+ end
+ context "when session_timeout has a value" do
+ before do
+ knife.config[:session_timeout] = 120
+ end
+ it "returns a default configuration hash with updated timeout value." do
+ expect(knife.ssh_opts).to eq(default_opts.merge(connection_timeout: 120))
+ end
end
- it "should not honor --use-sudo-password when --use-sudo is not set" do
- knife.config[:use_sudo] = false
- knife.config[:use_sudo_password] = true
- expect(knife.ssh_command).not_to include("echo #{knife.config[:ssh_password]} | sudo -S")
+ end
+
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ it "returns an empty has because ssh is not winrm" do
+ expect(knife.ssh_opts).to eq({})
end
end
- context "from the knife config file" do
- let(:knife_ssh) do
- knife.name_args = ["config.example.com"]
- Chef::Config[:knife][:ssh_user] = "curiosity"
- Chef::Config[:knife][:ssh_port] = "2430"
- 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][:host_key_verify] = true
- allow(knife).to receive(:render_template).and_return("")
- knife.config = {}
- knife.merge_configs
- knife.knife_ssh
+ end
+
+ context "#winrm_opts" do
+ let(:connection_protocol) { nil }
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "for winrm" do
+ let(:connection_protocol) { "winrm" }
+ let(:expected) do
+ {
+ winrm_transport: "negotiate",
+ winrm_basic_auth_only: false,
+ ssl: false,
+ ssl_peer_fingerprint: nil,
+ operation_timeout: 60,
+ }
+ end
+
+ it "generates a correct configuration hash with expected defaults" do
+ expect(knife.winrm_opts).to eq expected
+ end
+
+ context "with ssl_peer_fingerprint" do
+ let(:ssl_peer_fingerprint_expected) do
+ expected.merge({ ssl_peer_fingerprint: "ABCD" })
+ end
+
+ before do
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
+ end
+
+ it "generates a correct options hash with ssl_peer_fingerprint from the config provided" do
+ expect(knife.winrm_opts).to eq ssl_peer_fingerprint_expected
+ end
+ end
+
+ context "with winrm_ssl" do
+ let(:ssl_expected) do
+ expected.merge({ ssl: true })
+ end
+ before do
+ knife.config[:winrm_ssl] = true
+ end
+
+ it "generates a correct options hash with ssl from the config provided" do
+ expect(knife.winrm_opts).to eq ssl_expected
+ end
+ end
+
+ context "with winrm_auth_method" do
+ let(:winrm_auth_method_expected) do
+ expected.merge({ winrm_transport: "freeaccess" })
+ end
+
+ before do
+ knife.config[:winrm_auth_method] = "freeaccess"
+ end
+
+ it "generates a correct options hash with winrm_transport from the config provided" do
+ expect(knife.winrm_opts).to eq winrm_auth_method_expected
+ end
+ end
+
+ context "with ca_trust_file" do
+ let(:ca_trust_expected) do
+ expected.merge({ ca_trust_path: "/trust.me" })
+ end
+ before do
+ knife.config[:ca_trust_file] = "/trust.me"
+ end
+
+ it "generates a correct options hash with ca_trust_file from the config provided" do
+ expect(knife.winrm_opts).to eq ca_trust_expected
+ end
+ end
+
+ context "with kerberos auth" do
+ let(:kerberos_expected) do
+ expected.merge({
+ kerberos_service: "testsvc",
+ kerberos_realm: "TESTREALM",
+ winrm_transport: "kerberos",
+ })
+ end
+
+ before do
+ knife.config[:winrm_auth_method] = "kerberos"
+ knife.config[:kerberos_service] = "testsvc"
+ knife.config[:kerberos_realm] = "TESTREALM"
+ end
+
+ it "generates a correct options hash containing kerberos auth configuration from the config provided" do
+ expect(knife.winrm_opts).to eq kerberos_expected
+ end
+ end
+
+ context "with winrm_basic_auth_only" do
+ before do
+ knife.config[:winrm_basic_auth_only] = true
+ end
+ let(:basic_auth_expected) do
+ expected.merge( { winrm_basic_auth_only: true } )
+ end
+ it "generates a correct options hash containing winrm_basic_auth_only from the config provided" do
+ expect(knife.winrm_opts).to eq basic_auth_expected
+ end
+ end
+ end
+
+ context "for ssh" do
+ let(:connection_protocol) { "ssh" }
+ it "returns an empty hash because ssh is not winrm" do
+ expect(knife.winrm_opts).to eq({})
+ end
+ end
+ end
+ describe "#run" do
+ it "performs the steps we expect to run a bootstrap" do
+ expect(knife).to receive(:check_license)
+ expect(knife).to receive(:validate_name_args!).ordered
+ expect(knife).to receive(:validate_protocol!).ordered
+ expect(knife).to receive(:validate_first_boot_attributes!).ordered
+ expect(knife).to receive(:validate_winrm_transport_opts!).ordered
+ expect(knife).to receive(:validate_policy_options!).ordered
+ expect(knife).to receive(:winrm_warn_no_ssl_verification).ordered
+ expect(knife).to receive(:warn_on_short_session_timeout).ordered
+ expect(knife).to receive(:connect!).ordered
+ expect(knife).to receive(:register_client).ordered
+ expect(knife).to receive(:render_template).and_return "content"
+ expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh"
+ expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh")
+ expect(connection).to receive(:del_file!) # Make sure cleanup happens
+
+ knife.run
+
+ # Post-run verify expected state changes (not many directly in #run)
+ expect($stdout.sync).to eq true
+ end
+ end
+
+ describe "#register_client" do
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
+ let(:client_builder_mock) { double("ClientBuilder") }
+ let(:node_name) { nil }
+ before do
+ allow(knife).to receive(:chef_vault_handler).and_return vault_handler_mock
+ allow(knife).to receive(:client_builder).and_return client_builder_mock
+ knife.config[:chef_node_name] = node_name
+ end
+
+ shared_examples_for "creating the client locally" do
+ context "when a valid node name is present" do
+ let(:node_name) { "test" }
+ before do
+ allow(client_builder_mock).to receive(:client).and_return "client"
+ allow(client_builder_mock).to receive(:client_path).and_return "/key.pem"
+ end
+
+ it "runs client_builder and vault_handler" do
+ expect(client_builder_mock).to receive(:run)
+ expect(vault_handler_mock).to receive(:run).with("client")
+ knife.register_client
+ end
+
+ it "sets the path to the client key in the bootstrap context" do
+ allow(client_builder_mock).to receive(:run)
+ allow(vault_handler_mock).to receive(:run).with("client")
+ knife.register_client
+ expect(knife.bootstrap_context.client_pem).to eq "/key.pem"
+ end
+ end
+
+ context "when no valid node name is present" do
+ let(:node_name) { nil }
+ it "shows an error and exits" do
+ expect(knife.ui).to receive(:error)
+ expect { knife.register_client }.to raise_error(SystemExit)
+ end
+ end
+ end
+ context "when chef_vault_handler says we're using vault" do
+ let(:vault_handler_mock) { double("ChefVaultHandler") }
+ before do
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return true
end
+ it_behaves_like "creating the client locally"
+ end
- it "configures the ssh user" do
- expect(knife_ssh.config[:ssh_user]).to eq("curiosity")
+ context "when an non-existant validation key is specified in chef config" do
+ before do
+ Chef::Config[:validation_key] = "/blah"
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return false
end
+ it_behaves_like "creating the client locally"
+ end
- it "configures the ssh port" do
- expect(knife_ssh.config[:ssh_port]).to eq("2430")
+ context "when a valid validation key is given and we're doing old-style client creation" do
+ before do
+ Chef::Config[:validation_key] = "/blah"
+ allow(File).to receive(:exist?).with(%r{/blah}).and_return true
+ allow(vault_handler_mock).to receive(:doing_chef_vault?).and_return false
end
- it "configures the ssh agent forwarding" do
- expect(knife_ssh.config[:forward_agent]).to eq(true)
+ it "shows a warning message" do
+ expect(knife.ui).to receive(:warn).twice
+ knife.register_client
+ end
+ end
+ end
+
+ describe "#perform_bootstrap" do
+ let(:exit_status) { 0 }
+ let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message") }
+
+ before do
+ allow(connection).to receive(:hostname).and_return "testhost"
+ end
+ it "runs the remote script and logs the output" do
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
+ expect(knife).to receive(:bootstrap_command)
+ .with("/path.sh")
+ .and_return("sh /path.sh")
+ expect(connection)
+ .to receive(:run_command)
+ .with("sh /path.sh")
+ .and_yield("output here")
+ .and_return result_mock
+
+ expect(knife.ui).to receive(:msg).with(/testhost/)
+ knife.perform_bootstrap("/path.sh")
+ end
+ context "when the remote command fails" do
+ let(:exit_status) { 1 }
+ it "shows an error and exits" do
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
+ expect(knife).to receive(:bootstrap_command)
+ .with("/path.sh")
+ .and_return("sh /path.sh")
+ expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
end
+ end
+ end
+
+ describe "#connect!" do
+ before do
+ # These are not required at run-time because train will handle its own
+ # protocol loading. In this case, we're simulating train failures and have to load
+ # them ourselves.
+ require "net/ssh"
+ require "train/transports/ssh"
+ end
- it "configures the ssh identity file" do
- expect(knife_ssh.config[:ssh_identity_file]).to eq("~/.ssh/you.rsa")
+ context "in the normal case" do
+ it "connects using the connection_opts and notifies the operator of progress" do
+ expect(knife.ui).to receive(:info).with(/Connecting to.*/)
+ expect(knife).to receive(:connection_opts).and_return( { opts: "here" })
+ expect(knife).to receive(:do_connect).with( { opts: "here" } )
+ knife.connect!
end
+ end
- it "configures the ssh gateway" do
- expect(knife_ssh.config[:ssh_gateway]).to eq("towel.blinkenlights.nl")
+ context "when a general non-auth-failure occurs" do
+ let(:expected_error) { RuntimeError.new }
+ before do
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
end
+ it "re-raises the exception" do
+ expect { knife.connect! }.to raise_error(expected_error)
+ end
+ end
- it "configures the host key verify mode" do
- expect(knife_ssh.config[:host_key_verify]).to eq(true)
+ context "when ssh fingerprint is invalid" do
+ let(:expected_error) { Train::Error.new("fingerprint AA:BB is unknown for \"blah,127.0.0.1\"") }
+ before do
+ allow(knife).to receive(:do_connect).and_raise(expected_error)
+ end
+ it "warns, prompts to accept, then connects with verify_host_key of accept_new" do
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
+ expect(knife.ui).to receive(:confirm)
+ .with(/.*host 'blah \(127.0.0.1\)'.*AA:BB.*Are you sure you want to continue.*/m)
+ .and_return(true)
+ expect(knife).to receive(:do_connect) do |opts|
+ expect(opts[:verify_host_key]).to eq :accept_new
+ end
+ knife.connect!
end
end
- describe "when falling back to password auth when host key auth fails" do
- let(:knife_ssh_with_password_auth) do
- knife.name_args = ["foo.example.com"]
- knife.config[:ssh_user] = "rooty"
- knife.config[:ssh_identity_file] = "~/.ssh/me.rsa"
- allow(knife).to receive(:render_template).and_return("")
- k = knife.knife_ssh
- allow(k).to receive(:get_password).and_return("typed_in_password")
- allow(knife).to receive(:knife_ssh).and_return(k)
- knife.knife_ssh_with_password_auth
+ context "when an auth failure occurs" do
+ let(:expected_error) do
+ e = Train::Error.new
+ actual = Net::SSH::AuthenticationFailed.new
+ # Simulate train's nested error - they wrap
+ # ssh/network errors in a TrainError.
+ allow(e).to receive(:cause).and_return(actual)
+ e
+ end
+
+ let(:expected_error_password_prompt) do
+ e = Train::ClientError.new
+ reason = :no_ssh_password_or_key_available
+ allow(e).to receive(:reason).and_return(reason)
+ e
+ end
+
+ let(:expected_error_password_prompt_winrm) do
+ e = RuntimeError.new
+ message = "password is a required option"
+ allow(e).to receive(:message).and_return(message)
+ e
end
- it "prompts the user for a password " do
- expect(knife_ssh_with_password_auth.config[:ssh_password]).to eq("typed_in_password")
+ context "and password auth was used" do
+ before do
+ allow(connection).to receive(:password_auth?).and_return true
+ end
+
+ it "re-raises the error so as not to resubmit the same failing password" do
+ expect(knife).to receive(:do_connect).and_raise(expected_error)
+ expect { knife.connect! }.to raise_error(expected_error)
+ end
end
- it "configures knife not to use the identity file that didn't work previously" do
- expect(knife_ssh_with_password_auth.config[:ssh_identity_file]).to be_nil
+ context "and password auth was not used" do
+ before do
+ allow(connection).to receive(:password_auth?).and_return false
+ allow(connection).to receive(:user).and_return "testuser"
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "when using ssh" do
+ let(:connection_protocol) { "ssh" }
+
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
+ # Ensure that we set echo off to prevent showing password on the screen
+ expect(knife).to receive(:do_connect) do |opts|
+ expect(opts[:password]).to eq "newpassword"
+ end
+ knife.connect!
+ end
+ end
+
+ context "when using winrm" do
+ let(:connection_protocol) { "winrm" }
+
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
+ expect(knife.ui).to receive(:ask).and_return("newpassword")
+ # Ensure that we set echo off to prevent showing password on the screen
+ expect(knife).to receive(:do_connect) do |opts|
+ expect(opts[:password]).to eq "newpassword"
+ end
+ knife.connect!
+ end
+ end
end
end
end
it "verifies that a server to bootstrap was given as a command line arg" do
knife.name_args = nil
+ expect(knife).to receive(:check_license)
expect { knife.run }.to raise_error(SystemExit)
expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
end
- describe "when running the bootstrap" do
- let(:knife_ssh) do
- knife.name_args = ["foo.example.com"]
- knife.config[:chef_node_name] = "foo.example.com"
- knife.config[:ssh_user] = "rooty"
- knife.config[:ssh_identity_file] = "~/.ssh/me.rsa"
- allow(knife).to receive(:render_template).and_return("")
- knife_ssh = knife.knife_ssh
- allow(knife).to receive(:knife_ssh).and_return(knife_ssh)
- knife_ssh
+ describe "#bootstrap_context" do
+ context "under Windows" do
+ let(:windows_test) { true }
+ it "creates a WindowsBootstrapContext" do
+ require "chef/knife/core/windows_bootstrap_context"
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext
+ end
end
- let(:client) { Chef::ApiClient.new }
- context "when running with a configured and present validation key" do
- before do
- # this tests runs the old code path where we have a validation key, so we need to pass that check
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
+ context "under linux" do
+ let(:linux_test) { true }
+ it "creates a BootstrapContext" do
+ require "chef/knife/core/bootstrap_context"
+ expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext
end
+ end
+ end
- it "configures the underlying ssh command and then runs it" do
- expect(knife_ssh).to receive(:run)
- knife.run
- end
+ describe "#config_value" do
+ before do
+ knife.config[:test_key_a] = "a from cli"
+ knife.config[:test_key_b] = "b from cli"
+ Chef::Config[:knife][:test_key_a] = "a from Chef::Config"
+ Chef::Config[:knife][:test_key_c] = "c from Chef::Config"
+ Chef::Config[:knife][:alt_test_key_c] = "alt c from Chef::Config"
+ knife.merge_configs
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
- it "falls back to password based auth when auth fails the first time" do
- allow(knife).to receive(:puts)
+ it "returns the Chef::Config value from the cli when the CLI key is set" do
+ expect(knife.config_value(:test_key_a, :alt_test_key_c)).to eq "a from cli"
+ end
- fallback_knife_ssh = knife_ssh.dup
- expect(knife_ssh).to receive(:run).and_raise(Net::SSH::AuthenticationFailed.new("no ssh for you"))
- allow(knife).to receive(:knife_ssh_with_password_auth).and_return(fallback_knife_ssh)
- expect(fallback_knife_ssh).to receive(:run)
- knife.run
- end
+ it "returns the Chef::Config value from the alternative key when the CLI key is not set" do
+ expect(knife.config_value(:test_key_d, :alt_test_key_c)).to eq "alt c from Chef::Config"
+ end
+
+ it "returns the default value when the key is not provided by CLI or Chef::Config" do
+ expect(knife.config_value(:missing_key, :missing_key, "found")).to eq "found"
+ end
+ end
+
+ describe "#upload_bootstrap" do
+ before do
+ allow(connection).to receive(:temp_dir).and_return(temp_dir)
+ allow(connection).to receive(:normalize_path) { |a| a }
+ end
- it "raises the exception if config[:ssh_password] is set and an authentication exception is raised" do
- knife.config[:ssh_password] = "password"
- expect(knife_ssh).to receive(:run).and_raise(Net::SSH::AuthenticationFailed)
- expect { knife.run }.to raise_error(Net::SSH::AuthenticationFailed)
+ let(:content) { "bootstrap script content" }
+ context "under Windows" do
+ let(:windows_test) { true }
+ let(:temp_dir) { "C:/Temp/bootstrap" }
+ it "creates a bat file in the temp dir provided by connection, using given content" do
+ expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat")
+ expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat"
end
+ end
- it "creates the client and adds chef-vault items if vault_list is set" do
- knife.config[:bootstrap_vault_file] = "/not/our/responsibility/to/check/if/this/exists"
- expect(knife_ssh).to receive(:run)
- expect(knife.client_builder).to receive(:run)
- expect(knife.client_builder).to receive(:client).and_return(client)
- expect(knife.chef_vault_handler).to receive(:run).with(client)
- knife.run
+ context "under Linux" do
+ let(:linux_test) { true }
+ let(:temp_dir) { "/tmp/bootstrap" }
+ it "creates a 'sh file in the temp dir provided by connection, using given content" do
+ expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh")
+ expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh"
end
+ end
+ end
- it "creates the client and adds chef-vault items if vault_items is set" do
- knife.config[:bootstrap_vault_json] = '{ "vault" => "item" }'
- expect(knife_ssh).to receive(:run)
- expect(knife.client_builder).to receive(:run)
- expect(knife.client_builder).to receive(:client).and_return(client)
- expect(knife.chef_vault_handler).to receive(:run).with(client)
- knife.run
+ describe "#bootstrap_command" do
+ context "under Windows" do
+ let(:windows_test) { true }
+ it "prefixes the command to run under cmd.exe" do
+ expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat"
end
- it "does old-style validation without creating a client key if vault_list+vault_items is not set" do
- expect(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
- expect(knife_ssh).to receive(:run)
- expect(knife.client_builder).not_to receive(:run)
- expect(knife.chef_vault_handler).not_to receive(:run)
- knife.run
+ end
+ context "under Linux" do
+ let(:linux_test) { true }
+ it "prefixes the command to run under sh" do
+ expect(knife.bootstrap_command("bootstrap")).to eq "sh bootstrap"
end
+ end
+ end
- it "raises an exception if the config[:chef_node_name] is not present" do
- knife.config[:chef_node_name] = nil
+ describe "#default_bootstrap_template" do
+ context "under Windows" do
+ let(:windows_test) { true }
+ it "is windows-chef-client-msi" do
+ expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi"
+ end
- expect { knife.run }.to raise_error(SystemExit)
+ end
+ context "under Linux" do
+ let(:linux_test) { true }
+ it "is chef-full" do
+ expect(knife.default_bootstrap_template).to eq "chef-full"
end
end
+ end
+
+ describe "#do_connect" do
+ let(:host_descriptor) { "example.com" }
+ let(:connection) { double("TrainConnector") }
+ let(:connector_mock) { double("TargetResolver", targets: [ connection ]) }
+ before do
+ allow(knife).to receive(:host_descriptor).and_return host_descriptor
+ end
- context "when the validation key is not present" do
+ it "creates a TrainConnector and connects it" do
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection
+ expect(connection).to receive(:connect!)
+ knife.do_connect({})
+ end
+
+ context "when sshd configured with requiretty" do
+ let(:pty_err_msg) { "Sudo requires a TTY. Please see the README on how to configure sudo to allow for non-interactive usage." }
+ let(:expected_error) { Train::UserError.new(pty_err_msg, :sudo_no_tty) }
before do
- # this tests runs the old code path where we have a validation key, so we need to pass that check
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
+ allow(connection).to receive(:connect!).and_raise(expected_error)
+ end
+ it "retry with pty true request option" do
+ expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return(connection).exactly(2).times
+ expect(knife.ui).to receive(:warn).with("#{pty_err_msg} - trying with pty request")
+ expect { knife.do_connect({}) }.to raise_error(expected_error)
end
+ end
+ end
+
+ describe "validate_winrm_transport_opts!" do
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "when using ssh" do
+ let(:connection_protocol) { "ssh" }
+ it "returns true" do
+ expect(knife.validate_winrm_transport_opts!).to eq true
+ end
+ end
+ context "when using winrm" do
+ let(:connection_protocol) { "winrm" }
+ context "with plaintext auth" do
+ before do
+ knife.config[:winrm_auth_method] = "plaintext"
+ end
+ context "with ssl" do
+ before do
+ knife.config[:winrm_ssl] = true
+ end
+ it "will not error because we won't send anything in plaintext regardless" do
+ expect(knife.validate_winrm_transport_opts!).to eq true
+ end
+ end
+ context "without ssl" do
+ before do
+ knife.config[:winrm_ssl] = false
+ end
+ context "and no validation key exists" do
+ before do
+ Chef::Config[:validation_key] = "validation_key.pem"
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
+ end
+
+ it "will error because we will generate and send a client key over the wire in plaintext" do
+ expect { knife.validate_winrm_transport_opts! }.to raise_error(SystemExit)
+ end
- it "creates the client (and possibly adds chef-vault items)" do
- expect(knife_ssh).to receive(:run)
- expect(knife.client_builder).to receive(:run)
- expect(knife.client_builder).to receive(:client).and_return(client)
- expect(knife.chef_vault_handler).to receive(:run).with(client)
- knife.run
+ end
+ context "and a validation key exists" do
+ before do
+ Chef::Config[:validation_key] = "validation_key.pem"
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
+ end
+ # TODO - don't we still send validation key?
+ it "will not error because we don not send client key over the wire" do
+ expect(knife.validate_winrm_transport_opts!).to eq true
+ end
+ end
+ end
end
- it "raises an exception if the config[:chef_node_name] is not present" do
- knife.config[:chef_node_name] = nil
+ context "with other auth" do
+ before do
+ knife.config[:winrm_auth_method] = "kerberos"
+ end
+
+ context "and no validation key exists" do
+ before do
+
+ Chef::Config[:validation_key] = "validation_key.pem"
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return false
+ end
+
+ it "will not error because we're not using plaintext auth" do
+ expect(knife.validate_winrm_transport_opts!).to eq true
+ end
+ end
+ context "and a validation key exists" do
+ before do
+ Chef::Config[:validation_key] = "validation_key.pem"
+ allow(File).to receive(:exist?).with(/.*validation_key.pem/).and_return true
+ end
+
+ it "will not error because a client key won't be sent over the wire in plaintext when a validation key is present" do
+ expect(knife.validate_winrm_transport_opts!).to eq true
+ end
+ end
- expect { knife.run }.to raise_error(SystemExit)
end
+
end
- context "when the validation_key is nil" do
- before do
- # this tests runs the old code path where we have a validation key, so we need to pass that check for some plugins
- Chef::Config[:validation_key] = nil
+ end
+
+ describe "#winrm_warn_no_ssl_verification" do
+ before do
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
+ end
+
+ context "when using ssh" do
+ let(:connection_protocol) { "ssh" }
+ it "does not issue a warning" do
+ expect(knife.ui).to_not receive(:warn)
+ knife.winrm_warn_no_ssl_verification
end
+ end
+ context "when using winrm" do
+ let(:connection_protocol) { "winrm" }
+ context "winrm_no_verify_cert is set" do
+ before do
+ knife.config[:winrm_no_verify_cert] = true
+ end
+
+ context "and ca_trust_file is present" do
+ before do
+ knife.config[:ca_trust_file] = "file"
+ end
+
+ it "does not issue a warning" do
+ expect(knife.ui).to_not receive(:warn)
+ knife.winrm_warn_no_ssl_verification
+ end
+ end
- it "creates the client and does not run client_builder or the chef_vault_handler" do
- expect(knife_ssh).to receive(:run)
- expect(knife.client_builder).not_to receive(:run)
- expect(knife.chef_vault_handler).not_to receive(:run)
- knife.run
+ context "and winrm_ssl_peer_fingerprint is present" do
+ before do
+ knife.config[:winrm_ssl_peer_fingerprint] = "ABCD"
+ end
+ it "does not issue a warning" do
+ expect(knife.ui).to_not receive(:warn)
+ knife.winrm_warn_no_ssl_verification
+ end
+ end
+ context "and neither ca_trust_file nor winrm_ssl_peer_fingerprint is present" do
+ it "issues a warning" do
+ expect(knife.ui).to receive(:warn)
+ knife.winrm_warn_no_ssl_verification
+ end
+ end
end
end
end
- describe "specifying ssl verification" do
+ describe "#warn_on_short_session_timeout" do
+ let(:session_timeout) { 60 }
+
+ before do
+ allow(knife).to receive(:session_timeout).and_return(session_timeout)
+ end
+ context "timeout is not set at all" do
+ let(:session_timeout) { nil }
+ it "does not issue a warning" do
+ expect(knife.ui).to_not receive(:warn)
+ knife.warn_on_short_session_timeout
+ end
+ end
+
+ context "timeout is more than 15" do
+ let(:session_timeout) { 16 }
+ it "does not issue a warning" do
+ expect(knife.ui).to_not receive(:warn)
+ knife.warn_on_short_session_timeout
+ end
+ end
+ context "timeout is 15 or less" do
+ let(:session_timeout) { 15 }
+ it "issues a warning" do
+ expect(knife.ui).to receive(:warn)
+ knife.warn_on_short_session_timeout
+ end
+ end
end
end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index 994f4d33a4..435eb888aa 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,10 +42,10 @@ describe Chef::Knife::ClientBulkDelete do
let(:nonvalidator_client_names) { %w{tim dan stephen} }
let(:nonvalidator_clients) do
- clients = Hash.new
+ clients = {}
nonvalidator_client_names.each do |client_name|
- client = Chef::ApiClientV1.new()
+ client = Chef::ApiClientV1.new
client.name(client_name)
allow(client).to receive(:destroy).and_return(true)
clients[client_name] = client
@@ -56,10 +56,10 @@ describe Chef::Knife::ClientBulkDelete do
let(:validator_client_names) { %w{myorg-validator} }
let(:validator_clients) do
- clients = Hash.new
+ clients = {}
validator_client_names.each do |validator_client_name|
- validator_client = Chef::ApiClientV1.new()
+ validator_client = Chef::ApiClientV1.new
validator_client.name(validator_client_name)
allow(validator_client).to receive(:validator).and_return(true)
allow(validator_client).to receive(:destroy).and_return(true)
@@ -128,7 +128,7 @@ describe Chef::Knife::ClientBulkDelete do
end
describe "with --delete-validators" do
- let(:option_args) { { :delete_validators => true } }
+ let(:option_args) { { delete_validators: true } }
it "should mention that validator clients will be deleted" do
knife.run
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
index 17d18d084e..d8b67de101 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,6 @@ describe Chef::Knife::ClientCreate do
{
"name" => "adam",
"validator" => false,
- "admin" => false,
}
end
@@ -82,7 +81,7 @@ describe Chef::Knife::ClientCreate do
it "prints a relevant error message" do
expect { knife.run }.to raise_error(SystemExit)
- expect(stderr.string).to match /You cannot pass --public-key and --prevent-keygen/
+ expect(stderr.string).to match(/You cannot pass --public-key and --prevent-keygen/)
end
end
@@ -94,7 +93,7 @@ describe Chef::Knife::ClientCreate do
it "should print a message upon creation" do
expect(knife).to receive(:create_client)
knife.run
- expect(stderr.string).to match /Created client.*adam/i
+ expect(stderr.string).to match(/Created client.*adam/i)
end
it "should set the Client name" do
@@ -102,14 +101,9 @@ describe Chef::Knife::ClientCreate do
expect(client.name).to eq("adam")
end
- it "by default it is not an admin" do
- knife.run
- expect(client.admin).to be_falsey
- end
-
it "by default it is not a validator" do
knife.run
- expect(client.admin).to be_falsey
+ expect(client.validator).to be_falsey
end
it "by default it should set create_key to true" do
@@ -136,17 +130,6 @@ describe Chef::Knife::ClientCreate do
end
end
- describe "with -a or --admin" do
- before do
- knife.config[:admin] = true
- end
-
- it "should create an admin client" do
- knife.run
- expect(client.admin).to be_truthy
- end
- end
-
describe "with -p or --public-key" do
before do
knife.config[:public_key] = "some_key"
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 82ef902e09..41a83b05e4 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -23,7 +23,7 @@ describe Chef::Knife::ClientDelete do
@knife = Chef::Knife::ClientDelete.new
# defaults
@knife.config = {
- :delete_validators => false,
+ delete_validators: false,
}
@knife.name_args = [ "adam" ]
end
@@ -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_reregister_spec.rb b/spec/unit/knife/client_reregister_spec.rb
index 6776cafa0a..6b6519d44f 100644
--- a/spec/unit/knife/client_reregister_spec.rb
+++ b/spec/unit/knife/client_reregister_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Knife::ClientReregister do
before(:each) do
@knife = Chef::Knife::ClientReregister.new
@knife.name_args = [ "adam" ]
- @client_mock = double("client_mock", :private_key => "foo_key")
+ @client_mock = double("client_mock", private_key: "foo_key")
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
diff --git a/spec/unit/knife/configure_client_spec.rb b/spec/unit/knife/configure_client_spec.rb
index 3ecb89f827..b104718c89 100644
--- a/spec/unit/knife/configure_client_spec.rb
+++ b/spec/unit/knife/configure_client_spec.rb
@@ -43,10 +43,10 @@ describe Chef::Knife::ConfigureClient do
@knife.name_args = ["/home/bob/.chef"]
@client_file = StringIO.new
@validation_file = StringIO.new
- expect(File).to receive(:open).with("/home/bob/.chef/client.rb", "w").
- and_yield(@client_file)
- expect(File).to receive(:open).with("/home/bob/.chef/validation.pem", "w").
- and_yield(@validation_file)
+ expect(File).to receive(:open).with("/home/bob/.chef/client.rb", "w")
+ .and_yield(@client_file)
+ expect(File).to receive(:open).with("/home/bob/.chef/validation.pem", "w")
+ .and_yield(@validation_file)
expect(IO).to receive(:read).and_return("foo_bar_baz")
end
@@ -58,24 +58,22 @@ 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'/
+ expect(@client_file.string).to match %r{chef_server_url\s+'https\://chef\.example\.com'}
+ expect(@client_file.string).to match(/validation_client_name\s+'chef-validator'/)
end
it "should write out the validation.pem file" do
allow(FileUtils).to receive(:mkdir_p)
@knife.run
- expect(@validation_file.string).to match /foo_bar_baz/
+ expect(@validation_file.string).to match(/foo_bar_baz/)
end
it "should print information on what is being configured" do
allow(FileUtils).to receive(:mkdir_p)
@knife.run
- expect(@stderr.string).to match /creating client configuration/i
- expect(@stderr.string).to match /writing client\.rb/i
- expect(@stderr.string).to match /writing validation\.pem/i
+ expect(@stderr.string).to match(/creating client configuration/i)
+ expect(@stderr.string).to match(/writing client\.rb/i)
+ expect(@stderr.string).to match(/writing validation\.pem/i)
end
end
end
diff --git a/spec/unit/knife/configure_spec.rb b/spec/unit/knife/configure_spec.rb
index e96115c056..7d6c840d1f 100644
--- a/spec/unit/knife/configure_spec.rb
+++ b/spec/unit/knife/configure_spec.rb
@@ -6,7 +6,7 @@ describe Chef::Knife::Configure do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::Configure.new
- @rest_client = double("null rest client", :post => { :result => :true })
+ @rest_client = double("null rest client", post: { result: :true })
allow(@knife).to receive(:rest).and_return(@rest_client)
@out = StringIO.new
@@ -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(/^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_bulk_delete_spec.rb b/spec/unit/knife/cookbook_bulk_delete_spec.rb
index 62b8c9fe51..3527d39bd8 100644
--- a/spec/unit/knife/cookbook_bulk_delete_spec.rb
+++ b/spec/unit/knife/cookbook_bulk_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,14 +24,14 @@ describe Chef::Knife::CookbookBulkDelete do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::CookbookBulkDelete.new
- @knife.config = { :print_after => nil }
+ @knife.config = { print_after: nil }
@knife.name_args = ["."]
@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(:confirm).and_return(true)
- @cookbooks = Hash.new
+ @cookbooks = {}
%w{cheezburger pizza lasagna}.each do |cookbook_name|
cookbook = Chef::CookbookVersion.new(cookbook_name)
@cookbooks[cookbook_name] = cookbook
diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb
deleted file mode 100644
index f860a8bce8..0000000000
--- a/spec/unit/knife/cookbook_create_spec.rb
+++ /dev/null
@@ -1,261 +0,0 @@
-#
-# Author:: Nuo Yan (<nuo@chef.io>)
-# 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 "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"]
- @stdout = StringIO.new
- allow(@knife).to receive(:stdout).and_return(@stdout)
- end
-
- describe "run" 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)
- @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_delete_spec.rb b/spec/unit/knife/cookbook_delete_spec.rb
index 9bf4c81a73..f2aa7e1be0 100644
--- a/spec/unit/knife/cookbook_delete_spec.rb
+++ b/spec/unit/knife/cookbook_delete_spec.rb
@@ -62,8 +62,8 @@ describe Chef::Knife::CookbookDelete do
describe "with -p or --purge" do
it "should prompt to purge the files" do
@knife.config[:purge] = true
- expect(@knife).to receive(:confirm).
- with(/.+Are you sure you want to purge files.+/)
+ expect(@knife).to receive(:confirm)
+ .with(/.+Are you sure you want to purge files.+/)
expect(@knife).to receive(:delete_without_explicit_version)
@knife.run
end
@@ -76,8 +76,8 @@ describe Chef::Knife::CookbookDelete do
@knife.cookbook_name = "foobar"
@knife.version = "1.0.0"
expect(@knife).to receive(:delete_object).with(Chef::CookbookVersion,
- "foobar version 1.0.0",
- "cookbook").and_yield()
+ "foobar version 1.0.0",
+ "cookbook").and_yield
expect(@knife).to receive(:delete_request).with("cookbooks/foobar/1.0.0")
@knife.delete_explicit_version
end
@@ -139,20 +139,20 @@ describe Chef::Knife::CookbookDelete do
end
it "should raise if an error other than HTTP 404 is returned" do
- exception = Net::HTTPServerException.new("500 Internal Server Error", "500")
+ exception = Net::HTTPClientException.new("500 Internal Server Error", "500")
expect(@rest_mock).to receive(:get).and_raise(exception)
- expect { @knife.available_versions }.to raise_error Net::HTTPServerException
+ expect { @knife.available_versions }.to raise_error Net::HTTPClientException
end
describe "if the cookbook can't be found" do
before(:each) do
- expect(@rest_mock).to receive(:get).
- and_raise(Net::HTTPServerException.new("404 Not Found", "404"))
+ expect(@rest_mock).to receive(:get)
+ .and_raise(Net::HTTPClientException.new("404 Not Found", "404"))
end
it "should print an error" do
@knife.available_versions
- expect(@stderr.string).to match /error.+cannot find a cookbook named foobar/i
+ expect(@stderr.string).to match(/error.+cannot find a cookbook named foobar/i)
end
it "should return nil" do
@@ -204,7 +204,7 @@ describe Chef::Knife::CookbookDelete do
it "should output that the cookbook was deleted" do
allow(@knife).to receive(:delete_request)
@knife.delete_version_without_confirmation("1.0.0")
- expect(@stderr.string).to match /deleted cookbook\[foobar\]\[1.0.0\]/im
+ expect(@stderr.string).to match(/deleted cookbook\[foobar\]\[1.0.0\]/im)
end
describe "with --print-after" do
diff --git a/spec/unit/knife/cookbook_download_spec.rb b/spec/unit/knife/cookbook_download_spec.rb
index 38a4974774..c8903dea5b 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(:exist?).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")
@@ -93,43 +111,43 @@ describe Chef::Knife::CookbookDownload do
end
it "should print an error and exit if the cookbook download directory already exists" do
- expect(File).to receive(:exists?).with("/var/tmp/chef/foobar-1.0.0").and_return(true)
- expect(@knife.ui).to receive(:fatal).with(/\/var\/tmp\/chef\/foobar-1\.0\.0 exists/i)
+ expect(File).to receive(:exist?).with("/var/tmp/chef/foobar-1.0.0").and_return(true)
+ expect(@knife.ui).to receive(:fatal).with(%r{/var/tmp/chef/foobar-1\.0\.0 exists}i)
expect { @knife.run }.to raise_error(SystemExit)
end
describe "when downloading the cookbook" do
before(:each) do
@files.map { |f| File.dirname(f) }.flatten.uniq.each do |dir|
- expect(FileUtils).to receive(:mkdir_p).with("/var/tmp/chef/foobar-1.0.0/#{dir}").
- at_least(:once)
+ expect(FileUtils).to receive(:mkdir_p).with("/var/tmp/chef/foobar-1.0.0/#{dir}")
+ .at_least(:once)
end
@files_mocks.each_pair do |file, mock|
- expect(@rest_mock).to receive(:streaming_request).with("http://example.org/files/#{file}").
- and_return(mock)
+ expect(@rest_mock).to receive(:streaming_request).with("http://example.org/files/#{file}")
+ .and_return(mock)
end
@files.each do |f|
- expect(FileUtils).to receive(:mv).
- with("/var/tmp/#{File.basename(f)}", "/var/tmp/chef/foobar-1.0.0/#{f}")
+ expect(FileUtils).to receive(:mv)
+ .with("/var/tmp/#{File.basename(f)}", "/var/tmp/chef/foobar-1.0.0/#{f}")
end
end
it "should download the cookbook when the cookbook download directory doesn't exist" do
- expect(File).to receive(:exists?).with("/var/tmp/chef/foobar-1.0.0").and_return(false)
+ expect(File).to receive(:exist?).with("/var/tmp/chef/foobar-1.0.0").and_return(false)
@knife.run
%w{attributes recipes templates}.each do |segment|
- expect(@stderr.string).to match /downloading #{segment}/im
+ expect(@stderr.string).to match(/downloading #{segment}/im)
end
- expect(@stderr.string).to match /downloading foobar cookbook version 1\.0\.0/im
- expect(@stderr.string).to match /cookbook downloaded to \/var\/tmp\/chef\/foobar-1\.0\.0/im
+ expect(@stderr.string).to match(/downloading foobar cookbook version 1\.0\.0/im)
+ expect(@stderr.string).to match %r{cookbook downloaded to /var/tmp/chef/foobar-1\.0\.0}im
end
describe "with -f or --force" do
it "should remove the existing the cookbook download directory if it exists" do
@knife.config[:force] = true
- expect(File).to receive(:exists?).with("/var/tmp/chef/foobar-1.0.0").and_return(true)
+ expect(File).to receive(:exist?).with("/var/tmp/chef/foobar-1.0.0").and_return(true)
expect(FileUtils).to receive(:rm_rf).with("/var/tmp/chef/foobar-1.0.0")
@knife.run
end
@@ -164,8 +182,8 @@ describe Chef::Knife::CookbookDownload do
describe "with -N or --latest" do
it "should return and set the version to the latest version" do
@knife.config[:latest] = true
- expect(@knife).to receive(:available_versions).at_least(:once).
- and_return(["1.0.0", "1.1.0", "2.0.0"])
+ expect(@knife).to receive(:available_versions).at_least(:once)
+ .and_return(["1.0.0", "1.1.0", "2.0.0"])
@knife.determine_version
expect(@knife.version.to_s).to eq("2.0.0")
end
@@ -178,26 +196,26 @@ describe Chef::Knife::CookbookDownload do
end
it "should return nil if there are no versions" do
- expect(Chef::CookbookVersion).to receive(:available_versions).
- with("foobar").
- and_return(nil)
+ expect(Chef::CookbookVersion).to receive(:available_versions)
+ .with("foobar")
+ .and_return(nil)
expect(@knife.available_versions).to eq(nil)
end
it "should return the available versions" do
- expect(Chef::CookbookVersion).to receive(:available_versions).
- with("foobar").
- and_return(["1.1.0", "2.0.0", "1.0.0"])
+ expect(Chef::CookbookVersion).to receive(:available_versions)
+ .with("foobar")
+ .and_return(["1.1.0", "2.0.0", "1.0.0"])
expect(@knife.available_versions).to eq([Chef::Version.new("1.0.0"),
Chef::Version.new("1.1.0"),
Chef::Version.new("2.0.0")])
end
it "should avoid multiple API calls to the server" do
- expect(Chef::CookbookVersion).to receive(:available_versions).
- once.
- with("foobar").
- and_return(["1.1.0", "2.0.0", "1.0.0"])
+ expect(Chef::CookbookVersion).to receive(:available_versions)
+ .once
+ .with("foobar")
+ .and_return(["1.1.0", "2.0.0", "1.0.0"])
@knife.available_versions
@knife.available_versions
end
diff --git a/spec/unit/knife/cookbook_list_spec.rb b/spec/unit/knife/cookbook_list_spec.rb
index fce6bc9593..4cf806c6f0 100644
--- a/spec/unit/knife/cookbook_list_spec.rb
+++ b/spec/unit/knife/cookbook_list_spec.rb
@@ -37,19 +37,19 @@ describe Chef::Knife::CookbookList do
describe "run" do
it "should display the latest version of the cookbooks" do
- expect(@rest_mock).to receive(:get).with("/cookbooks?num_versions=1").
- and_return(@cookbook_data)
+ expect(@rest_mock).to receive(:get).with("/cookbooks?num_versions=1")
+ .and_return(@cookbook_data)
@knife.run
@cookbook_names.each do |item|
- expect(@stdout.string).to match /#{item}\s+1\.0\.1/
+ expect(@stdout.string).to match(/#{item}\s+1\.0\.1/)
end
end
it "should query cookbooks for the configured environment" do
@knife.config[:environment] = "production"
- expect(@rest_mock).to receive(:get).
- with("/environments/production/cookbooks?num_versions=1").
- and_return(@cookbook_data)
+ expect(@rest_mock).to receive(:get)
+ .with("/environments/production/cookbooks?num_versions=1")
+ .and_return(@cookbook_data)
@knife.run
end
@@ -75,11 +75,11 @@ describe Chef::Knife::CookbookList do
it "should display all versions of the cookbooks" do
@knife.config[:all_versions] = true
- expect(@rest_mock).to receive(:get).with("/cookbooks?num_versions=all").
- and_return(@cookbook_data)
+ expect(@rest_mock).to receive(:get).with("/cookbooks?num_versions=all")
+ .and_return(@cookbook_data)
@knife.run
@cookbook_names.each do |item|
- expect(@stdout.string).to match /#{item}\s+1\.0\.1\s+1\.0\.0/
+ expect(@stdout.string).to match(/#{item}\s+1\.0\.1\s+1\.0\.0/)
end
end
end
diff --git a/spec/unit/knife/cookbook_metadata_from_file_spec.rb b/spec/unit/knife/cookbook_metadata_from_file_spec.rb
index 274eb5e167..f9bbffae2d 100644
--- a/spec/unit/knife/cookbook_metadata_from_file_spec.rb
+++ b/spec/unit/knife/cookbook_metadata_from_file_spec.rb
@@ -1,8 +1,8 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,12 +34,19 @@ describe Chef::Knife::CookbookMetadataFromFile do
end
after do
- if File.exists?(@tgt)
+ if File.exist?(@tgt)
File.unlink(@tgt)
end
end
describe "run" do
+ it "should print usage and exit when a FILE is not provided" do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal).with(/You must specify the FILE./)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
it "should determine cookbook name from path" do
expect(@md).to receive(:name).with(no_args)
expect(@md).to receive(:name).with("quick_start")
diff --git a/spec/unit/knife/cookbook_metadata_spec.rb b/spec/unit/knife/cookbook_metadata_spec.rb
index 4b405d0842..732cf78421 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 %r{generating metadata for foo from #{cookbook_dir}/foo/metadata\.rb}im
+ expect(stderr.string).to match %r{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 %r{generating metadata for foo from #{cookbook_dir}/foo/metadata\.rb}im
+ expect(stderr.string).to match %r{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 do
- @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
- end.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 do
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
- end.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 749e50c647..defc243de3 100644
--- a/spec/unit/knife/cookbook_show_spec.rb
+++ b/spec/unit/knife/cookbook_show_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, eersion 2.0
+# Copyright:: Copyright (c) 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.
@@ -16,7 +16,6 @@
# limitations under the License.
#
-# rename to cookbook not coookbook
require "spec_helper"
describe Chef::Knife::CookbookShow do
@@ -28,31 +27,31 @@ describe Chef::Knife::CookbookShow do
allow(Chef::CookbookVersion).to receive(:load).and_return(cb)
end
- let (:knife) do
+ let(:knife) do
knife = Chef::Knife::CookbookShow.new
knife.config = {}
knife.name_args = [ "cookbook_name" ]
knife
end
- let (:cb) do
+ let(:cb) do
cb = Chef::CookbookVersion.new("cookbook_name")
cb.manifest = manifest
cb
end
- let (:rest) { double(Chef::ServerAPI) }
+ let(:rest) { double(Chef::ServerAPI) }
- let (:content) { "Example recipe text" }
+ let(:content) { "Example recipe text" }
- let (:manifest) do
+ let(:manifest) do
{
- "recipes" => [
+ "all_files" => [
{
- :name => "default.rb",
- :path => "recipes/default.rb",
- :checksum => "1234",
- :url => "http://example.org/files/default.rb",
+ name: "recipes/default.rb",
+ path: "recipes/default.rb",
+ checksum: "1234",
+ url: "http://example.org/files/default.rb",
},
],
}
@@ -69,7 +68,7 @@ describe Chef::Knife::CookbookShow do
end
describe "with 1 argument: versions" do
- let (:response) do
+ let(:response) do
{
"cookbook_name" => {
"url" => "http://url/cookbooks/cookbook_name",
@@ -101,9 +100,42 @@ describe Chef::Knife::CookbookShow 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" => "",
+ "eager_load_libraries" => true,
+ "long_description" => "",
+ "maintainer" => "",
+ "maintainer_email" => "",
+ "license" => "All rights reserved",
+ "platforms" => {},
+ "dependencies" => {},
+ "providing" => {},
+ "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(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
- expect(knife).to receive(:output).with(cb)
+ expect(knife).to receive(:output).with(output)
knife.run
end
end
@@ -115,7 +147,7 @@ describe Chef::Knife::CookbookShow do
it "should print the json of the part" do
expect(Chef::CookbookVersion).to receive(:load).with("cookbook_name", "0.1.0").and_return(cb)
- expect(knife).to receive(:output).with(cb.manifest["recipes"])
+ expect(knife).to receive(:output).with(cb.files_for("recipes"))
knife.run
end
end
@@ -137,34 +169,34 @@ describe Chef::Knife::CookbookShow do
before(:each) do
knife.name_args = [ "cookbook_name", "0.1.0", "files", "afile.rb" ]
cb.manifest = {
- "files" => [
+ "all_files" => [
{
- :name => "afile.rb",
- :path => "files/host-examplehost.example.org/afile.rb",
- :checksum => "1111",
- :specificity => "host-examplehost.example.org",
- :url => "http://example.org/files/1111",
+ 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",
- :path => "files/ubuntu-9.10/afile.rb",
- :checksum => "2222",
- :specificity => "ubuntu-9.10",
- :url => "http://example.org/files/2222",
+ 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",
- :path => "files/ubuntu/afile.rb",
- :checksum => "3333",
- :specificity => "ubuntu",
- :url => "http://example.org/files/3333",
+ name: "files/afile.rb",
+ path: "files/ubuntu/afile.rb",
+ checksum: "3333",
+ specificity: "ubuntu",
+ url: "http://example.org/files/3333",
},
{
- :name => "afile.rb",
- :path => "files/default/afile.rb",
- :checksum => "4444",
- :specificity => "default",
- :url => "http://example.org/files/4444",
+ name: "files/afile.rb",
+ path: "files/default/afile.rb",
+ checksum: "4444",
+ specificity: "default",
+ url: "http://example.org/files/4444",
},
],
}
diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb
deleted file mode 100644
index 0ab6a8a9b4..0000000000
--- a/spec/unit/knife/cookbook_site_download_spec.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-#
-# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
-# Copyright:: Copyright 2012-2016, Thomas Bishop
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
-
-describe Chef::Knife::CookbookSiteDownload do
-
- describe "run" do
- before do
- @knife = Chef::Knife::CookbookSiteDownload.new
- @knife.name_args = ["apache2"]
- @noauth_rest = double("no auth rest")
- @stderr = StringIO.new
- @cookbook_api_url = "https://supermarket.chef.io/api/v1/cookbooks"
- @version = "1.0.2"
- @version_us = @version.tr ".", "_"
- @current_data = { "deprecated" => false,
- "latest_version" => "#{@cookbook_api_url}/apache2/versions/#{@version_us}",
- "replacement" => "other_apache2" }
-
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
- allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest)
- 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
- before do
- @current_data["deprecated"] = true
- end
-
- it "should warn with info about the replacement" do
- expect(@knife.ui).to receive(:warn).
- with(/.+deprecated.+replaced by other_apache2.+/i)
- expect(@knife.ui).to receive(:warn).
- with(/use --force.+download.+/i)
- @knife.run
- end
- end
-
- context "when" do
- before do
- @cookbook_data = { "version" => @version,
- "file" => "http://example.com/apache2_#{@version_us}.tgz" }
- @temp_file = double( :path => "/tmp/apache2_#{@version_us}.tgz" )
- @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
- end
-
- context "downloading the latest version" do
- before do
- expect(@noauth_rest).to receive(:get).
- with(@current_data["latest_version"]).
- and_return(@cookbook_data)
- expect(@noauth_rest).to receive(:streaming_request).
- with(@cookbook_data["file"]).
- and_return(@temp_file)
- end
-
- context "and it is deprecated and with --force" do
- before do
- @current_data["deprecated"] = true
- @knife.config[:force] = true
- end
-
- it "should download the latest version" do
- expect(@knife.ui).to receive(:warn).
- with(/.+deprecated.+replaced by other_apache2.+/i)
- expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
- @knife.run
- expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
- expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i
- end
-
- end
-
- it "should download the latest version" do
- expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
- @knife.run
- expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
- expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i
- end
-
- context "with -f or --file" do
- before do
- @file = "/opt/chef/cookbooks/apache2.tar.gz"
- @knife.config[:file] = @file
- expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
- end
-
- it "should download the cookbook to the desired file" do
- @knife.run
- expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
- expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i
- end
- end
-
- it "should provide an accessor to the version" do
- allow(FileUtils).to receive(:cp).and_return(true)
- expect(@knife.version).to eq(@version)
- @knife.run
- end
- end
-
- context "downloading a cookbook of a specific version" do
- before do
- @version = "1.0.1"
- @version_us = @version.tr ".", "_"
- @cookbook_data = { "version" => @version,
- "file" => "http://example.com/apache2_#{@version_us}.tgz" }
- @temp_file = double(:path => "/tmp/apache2_#{@version_us}.tgz")
- @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
- @knife.name_args << @version
- end
-
- it "should download the desired version" do
- expect(@noauth_rest).to receive(:get).
- with("#{@cookbook_api_url}/apache2/versions/#{@version_us}").
- and_return(@cookbook_data)
- expect(@noauth_rest).to receive(:streaming_request).
- with(@cookbook_data["file"]).
- and_return(@temp_file)
- expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
- @knife.run
- expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i
- expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i
- end
- end
-
- end
-
- end
-
-end
diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb
deleted file mode 100644
index d93af10761..0000000000
--- a/spec/unit/knife/cookbook_site_install_spec.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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.
-#
-
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
-
-describe Chef::Knife::CookbookSiteInstall do
- let(:knife) { Chef::Knife::CookbookSiteInstall.new }
- let(:stdout) { StringIO.new }
- let(:stderr) { StringIO.new }
- let(:downloader) { Hash.new }
- 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"
-
- allow(knife.ui).to receive(:stdout).and_return(stdout)
- knife.config = {}
- knife.config[:cookbook_path] = [ install_path ]
-
- allow(knife).to receive(:stderr).and_return(stderr)
- allow(knife).to receive(:stdout).and_return(stdout)
-
- # Assume all external commands would have succeed. :(
- 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)
- allow(downloader).to receive(:version) do
- if knife.name_args.size == 2
- knife.name_args[1]
- else
- "0.3.0"
- end
- end
-
- # Stubs for CookbookSCMRepo
- allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(repo)
- end
-
- describe "run" do
- it "raises an error if a cookbook name is not provided" do
- knife.name_args = []
- expect(knife.ui).to receive(:error).with("Please specify a cookbook to download and install.")
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "raises an error if more than two arguments are given" do
- knife.name_args = %w{foo bar baz}
- expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "raises an error if the second argument is not a version" do
- knife.name_args = ["getting-started", "1pass"]
- expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "raises an error if the second argument is a four-digit version" do
- knife.name_args = ["getting-started", "0.0.0.1"]
- expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "raises an error if the second argument is a one-digit version" do
- knife.name_args = ["getting-started", "1"]
- expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "installs the specified version if second argument is a three-digit version" do
- knife.name_args = ["getting-started", "0.1.0"]
- knife.config[:no_deps] = true
- upstream_file = File.join(install_path, "getting-started.tar.gz")
- expect(knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0")
- expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
- expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1.0")
- knife.run
- end
-
- it "installs the specified version if second argument is a two-digit version" do
- knife.name_args = ["getting-started", "0.1"]
- knife.config[:no_deps] = true
- upstream_file = File.join(install_path, "getting-started.tar.gz")
- expect(knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1")
- expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
- expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1")
- knife.run
- end
-
- it "installs the latest version if only a cookbook name is given" do
- knife.name_args = ["getting-started"]
- knife.config[:no_deps] = true
- upstream_file = File.join(install_path, "getting-started.tar.gz")
- expect(knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
- expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
- expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
- knife.run
- end
-
- it "does not create/reset git branches if use_current_branch is set" do
- knife.name_args = ["getting-started"]
- knife.config[:use_current_branch] = true
- knife.config[:no_deps] = true
- upstream_file = File.join(install_path, "getting-started.tar.gz")
- expect(repo).not_to receive(:prepare_to_import)
- expect(repo).not_to receive(:reset_to_default_state)
- knife.run
- end
-
- it "does not raise an error if cookbook_path is a string" do
- knife.config[:cookbook_path] = install_path
- knife.config[:no_deps] = true
- knife.name_args = ["getting-started"]
- upstream_file = File.join(install_path, "getting-started.tar.gz")
- expect(knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
- expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
- expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
- expect { knife.run }.not_to raise_error
- end
- end # end of run
-
- let(:metadata) { Chef::Cookbook::Metadata.new }
- let(:rb_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.rb") }
- let(:json_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.json") }
-
- describe "preferred_metadata" do
- before do
- allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata)
- allow(File).to receive(:exist?).and_return(false)
- knife.instance_variable_set(:@cookbook_name, "post-punk-kitchen")
- knife.instance_variable_set(:@install_path, install_path)
- end
-
- it "returns a populated Metadata object if metadata.rb exists" do
- allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
- expect(metadata).to receive(:from_file).with(rb_metadata_path)
- knife.preferred_metadata
- end
-
- it "returns a populated Metadata object if metadata.json exists" do
- allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
- #expect(IO).to receive(:read).with(json_metadata_path)
- allow(IO).to receive(:read)
- expect(metadata).to receive(:from_json)
- knife.preferred_metadata
- end
-
- it "prefers metadata.rb over metadata.json" do
- allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
- allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
- allow(IO).to receive(:read)
- expect(metadata).to receive(:from_file).with(rb_metadata_path)
- expect(metadata).not_to receive(:from_json)
- knife.preferred_metadata
- end
-
- it "rasies an error if it finds no metadata file" do
- expect { knife.preferred_metadata }.to raise_error { |error|
- expect(error).to be_a(Chef::Exceptions::MetadataNotFound)
- expect(error.cookbook_name).to eq("post-punk-kitchen")
- expect(error.install_path).to eq(install_path)
- }
- end
-
- end
-end
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
deleted file mode 100644
index 823eff8b04..0000000000
--- a/spec/unit/knife/cookbook_site_share_spec.rb
+++ /dev/null
@@ -1,209 +0,0 @@
-#
-# Author:: Stephen Delano (<stephen@chef.io>)
-# 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 "chef/cookbook_uploader"
-require "chef/cookbook_site_streaming_uploader"
-
-describe Chef::Knife::CookbookSiteShare do
-
- before(:each) do
- @knife = Chef::Knife::CookbookSiteShare.new
- # Merge default settings in.
- @knife.merge_configs
- @knife.name_args = %w{cookbook_name AwesomeSausage}
-
- @cookbook = Chef::CookbookVersion.new("cookbook_name")
-
- @cookbook_loader = double("Chef::CookbookLoader")
- allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(true)
- allow(@cookbook_loader).to receive(:[]).and_return(@cookbook)
- allow(Chef::CookbookLoader).to receive(:new).and_return(@cookbook_loader)
-
- @noauth_rest = double(Chef::ServerAPI)
- allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest)
-
- @cookbook_uploader = Chef::CookbookUploader.new("herpderp", :rest => "norest")
- allow(Chef::CookbookUploader).to receive(:new).and_return(@cookbook_uploader)
- allow(@cookbook_uploader).to receive(:validate_cookbooks).and_return(true)
- allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return(Dir.mktmpdir)
-
- allow(@knife).to receive(:shell_out!).and_return(true)
- @stdout = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- end
-
- describe "run" do
-
- before(:each) do
- allow(@knife).to receive(:do_upload).and_return(true)
- @category_response = {
- "name" => "cookbook_name",
- "category" => "Testing Category",
- }
- @bad_category_response = {
- "error_code" => "NOT_FOUND",
- "error_messages" => [
- "Resource does not exist.",
- ],
- }
- end
-
- it "should set true to config[:dry_run] as default" do
- expect(@knife.config[:dry_run]).to be_falsey
- end
-
- 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)
- end
-
- it "should not fail when given only 1 argument and can 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(@category_response)
- expect(@knife).to receive(:do_upload)
- @knife.run
- end
-
- 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") { 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
- @knife.name_args = ["cookbook_name"]
- expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" }
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should check if the cookbook exists" do
- expect(@cookbook_loader).to receive(:cookbook_exists?)
- @knife.run
- end
-
- it "should exit and log to error if the cookbook doesn't exist" do
- allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(false)
- expect(@knife.ui).to receive(:error)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- if File.exists?("/usr/bin/gnutar") || File.exists?("/bin/gnutar")
- it "should use gnutar to make a tarball of the cookbook" do
- expect(@knife).to receive(:shell_out!) do |args|
- expect(args.to_s).to match(/gnutar -czf/)
- end
- @knife.run
- end
- else
- it "should make a tarball of the cookbook" do
- expect(@knife).to receive(:shell_out!) do |args|
- expect(args.to_s).to match(/tar -czf/)
- end
- @knife.run
- end
- end
-
- it "should exit and log to error when the tarball creation fails" do
- allow(@knife).to receive(:shell_out!).and_raise(Chef::Exceptions::Exec)
- expect(@knife.ui).to receive(:error)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should upload the cookbook and clean up the tarball" do
- expect(@knife).to receive(:do_upload)
- expect(FileUtils).to receive(:rm_rf)
- @knife.run
- end
-
- context "when the --dry-run flag is specified" do
- before do
- allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return("/var/tmp/dummy")
- @knife.config = { :dry_run => true }
- allow(@knife).to receive_message_chain(:shell_out!, :stdout).and_return("file")
- end
-
- it "should list files in the tarball" do
- allow(@knife).to receive(:tar_cmd).and_return("footar")
- expect(@knife).to receive(:shell_out!).with("footar -czf #{@cookbook.name}.tgz #{@cookbook.name}", { :cwd => "/var/tmp/dummy" })
- expect(@knife).to receive(:shell_out!).with("footar -tzf #{@cookbook.name}.tgz", { :cwd => "/var/tmp/dummy" })
- @knife.run
- end
-
- it "does not upload the cookbook" do
- allow(@knife).to receive(:shell_out!).and_return(true)
- expect(@knife).not_to receive(:do_upload)
- @knife.run
- end
- end
- end
-
- describe "do_upload" do
-
- before(:each) do
- @upload_response = double("Net::HTTPResponse")
- allow(Chef::CookbookSiteStreamingUploader).to receive(:post).and_return(@upload_response)
-
- @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(File).to receive(:open).and_return(true)
- end
-
- it 'should post the cookbook to "https://supermarket.chef.io"' do
- response_text = Chef::JSONCompat.to_json({ :uri => "https://supermarket.chef.io/cookbooks/cookbook_name" })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(201)
- expect(Chef::CookbookSiteStreamingUploader).to receive(:post).with(/supermarket\.chef\.io/, anything(), anything(), anything())
- @knife.run
- end
-
- it "should alert the user when a version already exists" do
- response_text = Chef::JSONCompat.to_json({ :error_messages => ["Version already exists"] })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(409)
- expect { @knife.run }.to raise_error(SystemExit)
- expect(@stderr.string).to match(/ERROR(.+)cookbook already exists/)
- end
-
- it "should pass any errors on to the user" do
- response_text = Chef::JSONCompat.to_json({ :error_messages => ["You're holding it wrong"] })
- allow(@upload_response).to receive(:body).and_return(response_text)
- allow(@upload_response).to receive(:code).and_return(403)
- expect { @knife.run }.to raise_error(SystemExit)
- expect(@stderr.string).to match("ERROR(.*)You're holding it wrong")
- end
-
- it "should print the body if no errors are exposed on failure" 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.run }.to raise_error(SystemExit)
- end
-
- end
-
-end
diff --git a/spec/unit/knife/cookbook_site_unshare_spec.rb b/spec/unit/knife/cookbook_site_unshare_spec.rb
deleted file mode 100644
index 8e4358226d..0000000000
--- a/spec/unit/knife/cookbook_site_unshare_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# Author:: Stephen Delano (<stephen@chef.io>)
-# Author:: Tim Hinderliter (<tim@chef.io>)
-# 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"
-
-describe Chef::Knife::CookbookSiteUnshare do
-
- before(:each) do
- @knife = Chef::Knife::CookbookSiteUnshare.new
- @knife.name_args = ["cookbook_name"]
- allow(@knife).to receive(:confirm).and_return(true)
-
- @rest = double("Chef::ServerAPI")
- allow(@rest).to receive(:delete).and_return(true)
- allow(@knife).to receive(:rest).and_return(@rest)
- @stdout = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- end
-
- describe "run" do
-
- describe "with no cookbook argument" do
- it "should print the usage and exit" do
- @knife.name_args = []
- expect(@knife.ui).to receive(:fatal)
- expect(@knife).to receive(:show_usage)
- expect { @knife.run }.to raise_error(SystemExit)
- end
- end
-
- it "should confirm you want to unshare the cookbook" do
- expect(@knife).to receive(:confirm)
- @knife.run
- end
-
- it "should send a delete request to the cookbook site" do
- expect(@rest).to receive(:delete)
- @knife.run
- end
-
- it "should log an error and exit when forbidden" do
- exception = double('403 "Forbidden"', :code => "403")
- allow(@rest).to receive(:delete).and_raise(Net::HTTPServerException.new('403 "Forbidden"', exception))
- expect(@knife.ui).to receive(:error)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should re-raise any non-forbidden errors on delete" do
- exception = double('500 "Application Error"', :code => "500")
- allow(@rest).to receive(:delete).and_raise(Net::HTTPServerException.new('500 "Application Error"', exception))
- expect { @knife.run }.to raise_error(Net::HTTPServerException)
- end
-
- it "should log a success message" do
- expect(@knife.ui).to receive(:info)
- @knife.run
- end
-
- end
-
-end
diff --git a/spec/unit/knife/cookbook_test_spec.rb b/spec/unit/knife/cookbook_test_spec.rb
deleted file mode 100644
index f8b212e271..0000000000
--- a/spec/unit/knife/cookbook_test_spec.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# Author:: Stephen Delano (<stephen@chef.io>)$
-# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.$
-# Copyright:: Copyright 2010-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.
-#
-
-require "spec_helper"
-Chef::Knife::CookbookTest.load_deps
-
-describe Chef::Knife::CookbookTest do
- before(:each) do
- Chef::Config[:node_name] = "webmonkey.example.com"
- @knife = Chef::Knife::CookbookTest.new
- @knife.config[:cookbook_path] = File.join(CHEF_SPEC_DATA, "cookbooks")
- allow(@knife.cookbook_loader).to receive(:cookbook_exists?).and_return(true)
- @cookbooks = []
- %w{tats central_market jimmy_johns pho}.each do |cookbook_name|
- @cookbooks << Chef::CookbookVersion.new(cookbook_name)
- end
- @stdout = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- end
-
- describe "run" do
- it "should test the cookbook" do
- allow(@knife).to receive(:test_cookbook).and_return(true)
- @knife.name_args = ["italian"]
- expect(@knife).to receive(:test_cookbook).with("italian")
- @knife.run
- end
-
- it "should test multiple cookbooks when provided" do
- allow(@knife).to receive(:test_cookbook).and_return(true)
- @knife.name_args = %w{tats jimmy_johns}
- expect(@knife).to receive(:test_cookbook).with("tats")
- expect(@knife).to receive(:test_cookbook).with("jimmy_johns")
- expect(@knife).not_to receive(:test_cookbook).with("central_market")
- expect(@knife).not_to receive(:test_cookbook).with("pho")
- @knife.run
- end
-
- it "should test both ruby and templates" do
- @knife.name_args = ["example"]
- expect(@knife.config[:cookbook_path]).not_to be_empty
- Array(@knife.config[:cookbook_path]).reverse_each do |path|
- expect(@knife).to receive(:test_ruby).with(an_instance_of(Chef::Cookbook::SyntaxCheck))
- expect(@knife).to receive(:test_templates).with(an_instance_of(Chef::Cookbook::SyntaxCheck))
- end
- @knife.run
- end
-
- describe "with -a or --all" do
- it "should test all of the cookbooks" do
- allow(@knife).to receive(:test_cookbook).and_return(true)
- @knife.config[:all] = true
- @loader = {}
- allow(@loader).to receive(:load_cookbooks).and_return(@loader)
- @cookbooks.each do |cookbook|
- @loader[cookbook.name] = cookbook
- end
- allow(@knife).to receive(:cookbook_loader).and_return(@loader)
- @loader.each do |key, cookbook|
- expect(@knife).to receive(:test_cookbook).with(cookbook.name)
- end
- @knife.run
- end
- end
-
- end
-end
diff --git a/spec/unit/knife/cookbook_upload_spec.rb b/spec/unit/knife/cookbook_upload_spec.rb
index 9e07497c57..dbed8b8a67 100644
--- a/spec/unit/knife/cookbook_upload_spec.rb
+++ b/spec/unit/knife/cookbook_upload_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Matthew Kent (<mkent@magoazul.com>)
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,18 @@
# limitations under the License.
#
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+require "spec_helper"
require "chef/cookbook_uploader"
require "timeout"
describe Chef::Knife::CookbookUpload do
- let(:cookbook) { Chef::CookbookVersion.new("test_cookbook", "/tmp/blah.txt") }
+ let(:cookbook) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(cookbook.name)
+ cookbook
+ end
let(:cookbooks_by_name) do
{ cookbook.name => cookbook }
@@ -32,11 +37,13 @@ describe Chef::Knife::CookbookUpload do
let(:cookbook_loader) do
cookbook_loader = cookbooks_by_name.dup
allow(cookbook_loader).to receive(:merged_cookbooks).and_return([])
- allow(cookbook_loader).to receive(:load_cookbooks_without_shadow_warning).and_return(cookbook_loader)
+ allow(cookbook_loader).to receive(:load_cookbooks).and_return(cookbook_loader)
+ allow(cookbook_loader).to receive(:compile_metadata).and_return(nil)
+ allow(cookbook_loader).to receive(:freeze_versions).and_return(nil)
cookbook_loader
end
- let(:cookbook_uploader) { double(:upload_cookbooks => nil) }
+ let(:cookbook_uploader) { double(upload_cookbooks: nil) }
let(:output) { StringIO.new }
@@ -52,6 +59,7 @@ describe Chef::Knife::CookbookUpload do
before(:each) do
allow(Chef::CookbookLoader).to receive(:new).and_return(cookbook_loader)
+ allow(Chef::CookbookLoader).to receive(:copy_to_tmp_dir_from_array).and_yield(cookbook_loader)
end
describe "with --concurrency" do
@@ -61,16 +69,16 @@ describe Chef::Knife::CookbookUpload do
test_cookbook = Chef::CookbookVersion.new("test_cookbook", "/tmp/blah")
allow(cookbook_loader).to receive(:each).and_yield("test_cookbook", test_cookbook)
allow(cookbook_loader).to receive(:cookbook_names).and_return(["test_cookbook"])
- expect(Chef::CookbookUploader).to receive(:new).
- with( kind_of(Array), { :force => nil, :concurrency => 3 }).
- and_return(double("Chef::CookbookUploader", :upload_cookbooks => true))
+ expect(Chef::CookbookUploader).to receive(:new)
+ .with( kind_of(Array), { force: nil, concurrency: 3 })
+ .and_return(double("Chef::CookbookUploader", upload_cookbooks: true))
knife.run
end
end
describe "run" do
before(:each) do
- allow(Chef::CookbookUploader).to receive_messages(:new => cookbook_uploader)
+ allow(Chef::CookbookUploader).to receive_messages(new: cookbook_uploader)
allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({})
end
@@ -81,6 +89,34 @@ describe Chef::Knife::CookbookUpload do
expect { knife.run }.to raise_error(SystemExit)
end
+ describe "when specifying cookbook without metadata.rb or metadata.json" do
+ let(:name_args) { ["test_cookbook1"] }
+ let(:cookbook) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(false)
+ cookbook
+ end
+
+ it "should upload the cookbook" do
+ expect { knife.run }.to raise_error(Chef::Exceptions::MetadataNotFound)
+ end
+ end
+
+ describe "when name attribute in metadata not set" do
+ let(:name_args) { ["test_cookbook1"] }
+
+ let(:cookbook) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(nil)
+ cookbook
+ end
+
+ it "should upload the cookbook" do
+ expect { knife.run }.to raise_error(Chef::Exceptions::MetadataNotValid)
+ end
+ end
+
describe "when specifying a cookbook name" do
it "should upload the cookbook" do
expect(knife).to receive(:upload).once
@@ -102,40 +138,18 @@ describe Chef::Knife::CookbookUpload do
end
end
- context "when uploading a cookbook that uses deprecated overlays" do
-
- before do
- allow(cookbook_loader).to receive(:merged_cookbooks).and_return(["test_cookbook"])
- allow(cookbook_loader).to receive(:merged_cookbook_paths).
- and_return({ "test_cookbook" => %w{/path/one/test_cookbook /path/two/test_cookbook} })
- end
-
- it "emits a warning" do
- knife.run
- expected_message = <<-E
-WARNING: The cookbooks: test_cookbook exist in multiple places in your cookbook_path.
-A composite version of these cookbooks has been compiled for uploading.
-
-IMPORTANT: In a future version of Chef, this behavior will be removed and you will no longer
-be able to have the same version of a cookbook in multiple places in your cookbook_path.
-WARNING: The affected cookbooks are located:
-test_cookbook:
- /path/one/test_cookbook
- /path/two/test_cookbook
-E
- expect(output.string).to include(expected_message)
- end
- end
-
describe "when specifying a cookbook name among many" do
let(:name_args) { ["test_cookbook1"] }
+ let(:cookbook) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(cookbook.name)
+ cookbook
+ end
+
let(:cookbooks_by_name) do
- {
- "test_cookbook1" => Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah"),
- "test_cookbook2" => Chef::CookbookVersion.new("test_cookbook2", "/tmp/blah"),
- "test_cookbook3" => Chef::CookbookVersion.new("test_cookbook3", "/tmp/blah"),
- }
+ { cookbook.name => cookbook }
end
it "should read only one cookbook" do
@@ -144,8 +158,7 @@ E
end
it "should not read all cookbooks" do
- expect(cookbook_loader).not_to receive(:load_cookbooks)
- expect(cookbook_loader).not_to receive(:load_cookbooks_without_shadow_warning)
+ expect(cookbook_loader).to receive(:load_cookbooks)
knife.run
end
@@ -159,17 +172,18 @@ E
describe "when specifying a cookbook name with dependencies" do
let(:name_args) { ["test_cookbook2"] }
- let(:cookbooks_by_name) do
- { "test_cookbook1" => test_cookbook1,
- "test_cookbook2" => test_cookbook2,
- "test_cookbook3" => test_cookbook3 }
+ let(:test_cookbook1) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(cookbook.name)
+ cookbook
end
- let(:test_cookbook1) { Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah") }
-
let(:test_cookbook2) do
c = Chef::CookbookVersion.new("test_cookbook2")
c.metadata.depends("test_cookbook3")
+ allow(c).to receive(:has_metadata_file?).and_return(true)
+ allow(c.metadata).to receive(:name).and_return(c.name)
c
end
@@ -177,9 +191,17 @@ E
c = Chef::CookbookVersion.new("test_cookbook3")
c.metadata.depends("test_cookbook1")
c.metadata.depends("test_cookbook2")
+ allow(c).to receive(:has_metadata_file?).and_return(true)
+ allow(c.metadata).to receive(:name).and_return(c.name)
c
end
+ let(:cookbooks_by_name) do
+ { "test_cookbook1" => test_cookbook1,
+ "test_cookbook2" => test_cookbook2,
+ "test_cookbook3" => test_cookbook3 }
+ end
+
it "should upload all dependencies once" do
knife.config[:depends] = true
allow(knife).to receive(:cookbook_names).and_return(%w{test_cookbook1 test_cookbook2 test_cookbook3})
@@ -198,7 +220,7 @@ E
before(:each) do
cookbook.metadata.depends("dependency")
allow(cookbook_loader).to receive(:[]) do |ckbk|
- { "test_cookbook" => cookbook,
+ { "test_cookbook" => cookbook,
"dependency" => cookbook_dependency }[ckbk]
end
allow(knife).to receive(:cookbook_names).and_return(%w{cookbook_dependency test_cookbook})
@@ -208,8 +230,6 @@ E
it "should exit and not upload the cookbook" do
expect(cookbook_loader).to receive(:[]).once.with("test_cookbook")
- expect(cookbook_loader).not_to receive(:load_cookbooks)
- expect(cookbook_loader).not_to receive(:load_cookbooks_without_shadow_warning)
expect(cookbook_uploader).not_to receive(:upload_cookbooks)
expect { knife.run }.to raise_error(SystemExit)
end
@@ -225,7 +245,7 @@ E
cookbook_dependency2 = Chef::CookbookVersion.new("dependency2")
cookbook.metadata.depends("dependency2")
allow(cookbook_loader).to receive(:[]) do |ckbk|
- { "test_cookbook" => cookbook,
+ { "test_cookbook" => cookbook,
"dependency" => cookbook_dependency,
"dependency2" => cookbook_dependency2 }[ckbk]
end
@@ -241,7 +261,7 @@ E
it "should freeze the version of the cookbooks if --freeze is specified" do
knife.config[:freeze] = true
- expect(cookbook).to receive(:freeze_version).once
+ expect(cookbook_loader).to receive(:freeze_versions).once
knife.run
end
@@ -251,10 +271,22 @@ E
end
context "when cookbooks exist in the cookbook path" do
+ let(:test_cookbook1) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(cookbook.name)
+ cookbook
+ end
+
+ let(:test_cookbook2) do
+ cookbook = Chef::CookbookVersion.new("test_cookbook2", "/tmp/blah")
+ allow(cookbook).to receive(:has_metadata_file?).and_return(true)
+ allow(cookbook.metadata).to receive(:name).and_return(cookbook.name)
+ cookbook
+ end
+
before(:each) do
- @test_cookbook1 = Chef::CookbookVersion.new("test_cookbook1", "/tmp/blah")
- @test_cookbook2 = Chef::CookbookVersion.new("test_cookbook2", "/tmp/blah")
- allow(cookbook_loader).to receive(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
+ allow(cookbook_loader).to receive(:each).and_yield("test_cookbook1", test_cookbook1).and_yield("test_cookbook2", test_cookbook2)
allow(cookbook_loader).to receive(:cookbook_names).and_return(%w{test_cookbook1 test_cookbook2})
end
@@ -289,18 +321,19 @@ E
context "when cookbook path is an array" do
it "should warn users that no cookbooks exist" do
- knife.config[:cookbook_path] = ["/chef-repo/cookbooks", "/home/user/cookbooks"]
- expect(knife.ui).to receive(:warn).with(
- /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path].join(', ')}\. Use --cookbook-path to specify the desired path\./)
+ cookbook_path = windows? ? "C:/chef-repo/cookbooks" : "/chef-repo/cookbooks"
+ knife.config[:cookbook_path] = [cookbook_path, "/home/user/cookbooks"]
+ expect(knife.ui).to receive(:warn).with("Could not find any cookbooks in your cookbook path: '#{knife.config[:cookbook_path].join(", ")}'. Use --cookbook-path to specify the desired path.")
knife.run
end
end
context "when cookbook path is a string" do
it "should warn users that no cookbooks exist" do
- knife.config[:cookbook_path] = "/chef-repo/cookbooks"
+ knife.config[:cookbook_path] = windows? ? "C:/chef-repo/cookbooks" : "/chef-repo/cookbooks"
expect(knife.ui).to receive(:warn).with(
- /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path]}\. Use --cookbook-path to specify the desired path\./)
+ "Could not find any cookbooks in your cookbook path: '#{knife.config[:cookbook_path]}'. Use --cookbook-path to specify the desired path."
+ )
knife.run
end
end
@@ -310,8 +343,8 @@ E
describe "when a frozen cookbook exists on the server" do
it "should fail to replace it" do
exception = Chef::Exceptions::CookbookFrozen.new
- expect(cookbook_uploader).to receive(:upload_cookbooks).
- and_raise(exception)
+ expect(cookbook_uploader).to receive(:upload_cookbooks)
+ .and_raise(exception)
allow(knife.ui).to receive(:error)
expect(knife.ui).to receive(:error).with(exception)
expect { knife.run }.to raise_error(SystemExit)
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 6465e18ac9..a55047a739 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,19 +20,15 @@ require "spec_helper"
require "chef/knife/core/bootstrap_context"
describe Chef::Knife::Core::BootstrapContext do
- before do
- # This is required because the chef-fips pipeline does
- # has a default value of true for fips
- Chef::Config[:fips] = false
- end
-
- let(:config) { { :foo => :bar, :color => true } }
+ let(:config) { { foo: :bar, color: true } }
let(:run_list) { Chef::RunList.new("recipe[tmux]", "role[base]") }
let(:chef_config) do
{
- :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",
+ 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",
}
end
@@ -44,19 +40,19 @@ describe Chef::Knife::Core::BootstrapContext do
expect { described_class.new(config, run_list, chef_config) }.not_to raise_error
end
- it "runs chef with the first-boot.json with no environment specified" do
+ it "runs chef with the first-boot.json with no environment" do
expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json"
end
describe "when in verbosity mode" do
- let(:config) { { :verbosity => 2, :color => true } }
+ let(:config) { { verbosity: 2, color: true } }
it "adds '-l debug' when verbosity is >= 2" do
expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -l debug"
end
end
describe "when no color value has been set in config" do
- let(:config) { { :color => false } }
+ let(:config) { { color: false } }
it "adds '--no-color' when color is false" do
expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json --no-color"
end
@@ -67,28 +63,46 @@ describe Chef::Knife::Core::BootstrapContext do
end
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"
-# Using default node name (fqdn)
-EXPECTED
+ expected = <<~EXPECTED
+ 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/)
+ describe "when chef_license is set" do
+ let(:chef_config) { { chef_license: "accept-no-persist" } }
+ it "sets chef_license in the generated config file" do
+ expect(bootstrap_context.config_content).to include("chef_license \"accept-no-persist\"")
+ end
+ end
+
+ describe "when file_cache_path is set" do
+ let(:chef_config) { { file_cache_path: "/home/opscode/cache" } }
+ it "sets file_cache_path in the generated config file" do
+ expect(bootstrap_context.config_content).to include("file_cache_path \"/home/opscode/cache\"")
+ end
+ end
+
+ describe "when file_backup_path is set" do
+ let(:chef_config) { { file_backup_path: "/home/opscode/backup" } }
+ it "sets file_backup_path in the generated config file" do
+ expect(bootstrap_context.config_content).to include("file_backup_path \"/home/opscode/backup\"")
+ end
end
describe "alternate chef-client path" do
- let(:chef_config) { { :chef_client_path => "/usr/local/bin/chef-client" } }
+ let(:chef_config) { { chef_client_path: "/usr/local/bin/chef-client" } }
it "runs chef-client from another path when specified" do
expect(bootstrap_context.start_chef).to eq "/usr/local/bin/chef-client -j /etc/chef/first-boot.json"
end
end
describe "validation key path that contains a ~" do
- let(:chef_config) { { :validation_key => "~/my.key" } }
+ let(:chef_config) { { validation_key: "~/my.key" } }
it "reads the validation key when it contains a ~" do
expect(File).to receive(:exist?).with(File.expand_path("my.key", ENV["HOME"])).and_return(true)
expect(IO).to receive(:read).with(File.expand_path("my.key", ENV["HOME"]))
@@ -97,36 +111,36 @@ EXPECTED
end
describe "when an explicit node name is given" do
- let(:config) { { :chef_node_name => "foobar.example.com" } }
+ let(:config) { { chef_node_name: "foobar.example.com" } }
it "sets the node name in the client.rb" do
expect(bootstrap_context.config_content).to match(/node_name "foobar\.example\.com"/)
end
end
describe "when bootstrapping into a specific environment" do
- let(:config) { { :environment => "prodtastic", :color => true } }
+ let(:config) { { environment: "prodtastic", color: true } }
it "starts chef in the configured environment" do
expect(bootstrap_context.start_chef).to eq("chef-client -j /etc/chef/first-boot.json -E prodtastic")
end
end
describe "when tags are given" do
- let(:config) { { :tags => [ "unicorn" ] } }
+ let(:config) { { tags: [ "unicorn" ] } }
it "adds the attributes to first_boot" do
- expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ :run_list => run_list, :tags => ["unicorn"] }))
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ run_list: run_list, tags: ["unicorn"] }))
end
end
describe "when JSON attributes are given" do
- let(:config) { { :first_boot_attributes => { :baz => :quux } } }
+ let(:config) { { first_boot_attributes: { baz: :quux } } }
it "adds the attributes to first_boot" do
- expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ :baz => :quux, :run_list => run_list }))
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ baz: :quux, run_list: run_list }))
end
end
describe "when JSON attributes are NOT given" do
it "sets first_boot equal to run_list" do
- expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ :run_list => run_list }))
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ run_list: run_list }))
end
end
@@ -135,7 +149,7 @@ EXPECTED
let(:config) { { policy_name: "my_app_server", policy_group: "staging" } }
it "includes them in the first_boot data and excludes run_list" do
- expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json(config))
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({ policy_name: "my_app_server", policy_group: "staging" }))
end
end
@@ -157,101 +171,117 @@ EXPECTED
end
end
- describe "when a bootstrap_version is specified" do
- let(:chef_config) do
- {
- :knife => { :bootstrap_version => "11.12.4" },
- }
+ describe "ssl_verify_mode" do
+ it "isn't set in the config_content by default" do
+ expect(bootstrap_context.config_content).not_to include("ssl_verify_mode")
end
- it "should send the full version to the installer" do
- expect(bootstrap_context.latest_current_chef_version_string).to eq("-v 11.12.4")
- end
- end
+ describe "when configured via the config hash" do
+ let(:config) { { node_ssl_verify_mode: "none" } }
- describe "when a pre-release bootstrap_version is specified" do
- let(:chef_config) do
- {
- :knife => { :bootstrap_version => "11.12.4.rc.0" },
- }
+ it "uses the config value" do
+ expect(bootstrap_context.config_content).to include("ssl_verify_mode :verify_none")
+ end
end
+ end
- it "should send the full version to the installer and set the pre-release flag" do
- expect(bootstrap_context.latest_current_chef_version_string).to eq("-v 11.12.4.rc.0 -p")
+ describe "fips mode" do
+ before do
+ chef_config[:fips] = true
end
- end
- describe "when a bootstrap_version is not specified" do
- it "should send the latest current to the installer" do
- # Intentionally hard coded in order not to replicate the logic.
- expect(bootstrap_context.latest_current_chef_version_string).to eq("-v #{Chef::VERSION.to_i}")
+ it "sets fips mode in the client.rb" do
+ expect(bootstrap_context.config_content).to match(/fips true/)
end
end
- describe "ssl_verify_mode" do
+ describe "verify_api_cert" do
it "isn't set in the config_content by default" do
- expect(bootstrap_context.config_content).not_to include("ssl_verify_mode")
+ expect(bootstrap_context.config_content).not_to include("verify_api_cert")
end
- describe "when configured in config" do
- let(:chef_config) do
- {
- :knife => { :ssl_verify_mode => :verify_peer },
- }
- end
+ describe "when configured via the config hash" do
+ let(:config) { { node_verify_api_cert: true } }
- it "uses the config value" do
- expect(bootstrap_context.config_content).to include("ssl_verify_mode :verify_peer")
+ it "uses config value" do
+ expect(bootstrap_context.config_content).to include("verify_api_cert true")
end
+ end
+ end
- describe "when configured via CLI" do
- let(:config) { { :node_ssl_verify_mode => "none" } }
+ 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
- it "uses CLI value" do
- expect(bootstrap_context.config_content).to include("ssl_verify_mode :verify_none")
- 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
- 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")
+ 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
- describe "when configured in config" do
- let(:chef_config) do
- {
- :knife => { :verify_api_cert => :false },
- }
+ 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
- it "uses the config value" do
- expect(bootstrap_context.config_content).to include("verify_api_cert false")
+ 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
- describe "when configured via CLI" do
- let(:config) { { :node_verify_api_cert => true } }
+ 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
- it "uses CLI value" do
- expect(bootstrap_context.config_content).to include("verify_api_cert true")
- 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
- describe "prerelease" do
- it "isn't set in the config_content by default" do
- expect(bootstrap_context.config_content).not_to include("prerelease")
+ describe "#version_to_install" do
+ context "when bootstrap_version is provided" do
+ let(:config) { { bootstrap_version: "awesome" } }
+
+ it "returns bootstrap_version" do
+ expect(bootstrap_context.version_to_install).to eq "awesome"
+ end
end
- describe "when configured via cli" do
- let(:config) { { :prerelease => true } }
+ context "when bootstrap_version is not provided" do
+ let(:config) { { channel: "stable" } }
+ it "returns the currently running major version out of Chef::VERSION" do
+ expect(bootstrap_context.version_to_install).to eq Chef::VERSION.split(".").first
+ end
+ end
- it "uses CLI value" do
- expect(bootstrap_context.latest_current_chef_version_string).to eq("-p")
+ context "and channel is other than stable" do
+ let(:config) { { channel: "unstable" } }
+ it "returns the version string 'latest'" do
+ expect(bootstrap_context.version_to_install).to eq "latest"
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..316cbdfaa6 100644
--- a/spec/unit/knife/core/cookbook_scm_repo_spec.rb
+++ b/spec/unit/knife/core/cookbook_scm_repo_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::Knife::CookbookSCMRepo do
@repo_path = File.join(CHEF_SPEC_DATA, "cookbooks")
@stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
@ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
- @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => "master")
+ @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, default_branch: "master")
@branch_list = Mixlib::ShellOut.new
@branch_list.stdout.replace(<<-BRANCHES)
@@ -35,7 +35,7 @@ describe Chef::Knife::CookbookSCMRepo do
chef-vendor-graphite
chef-vendor-python
chef-vendor-absent-new
-BRANCHES
+ BRANCHES
end
it "has a path to the cookbook repo" do
@@ -69,7 +69,7 @@ BRANCHES
it "exits when the default branch doesn't exist" do
@nobranches = Mixlib::ShellOut.new.tap { |s| s.stdout.replace "\n" }
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@nobranches)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@nobranches)
expect { @cookbook_repo.sanity_check }.to raise_error(SystemExit)
end
@@ -77,22 +77,22 @@ BRANCHES
before do
@master_branch = Mixlib::ShellOut.new
@master_branch.stdout.replace "* master\n"
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@master_branch)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@master_branch)
end
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
-DIRTY
- expect(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain", :cwd => @repo_path).and_return(@dirty_status)
+ 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)
end
describe "and the repo is clean" do
before do
@clean_status = Mixlib::ShellOut.new.tap { |s| s.stdout.replace("\n") }
- allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain", :cwd => @repo_path).and_return(@clean_status)
+ allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain", cwd: @repo_path).and_return(@clean_status)
end
it "passes the sanity check" do
@@ -106,35 +106,35 @@ DIRTY
end
it "resets to default state by checking out the default branch" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git checkout master", :cwd => @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git checkout master", cwd: @repo_path)
@cookbook_repo.reset_to_default_state
end
it "determines if a the pristine copy branch exists" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@branch_list)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@branch_list)
expect(@cookbook_repo.branch_exists?("chef-vendor-apache2")).to be_truthy
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@branch_list)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@branch_list)
expect(@cookbook_repo.branch_exists?("chef-vendor-nginx")).to be_falsey
end
it "determines if a the branch not exists correctly without substring search" do
- expect(@cookbook_repo).to receive(:shell_out!).twice.with("git branch --no-color", :cwd => @repo_path).and_return(@branch_list)
+ expect(@cookbook_repo).to receive(:shell_out!).twice.with("git branch --no-color", cwd: @repo_path).and_return(@branch_list)
expect(@cookbook_repo).not_to be_branch_exists("chef-vendor-absent")
expect(@cookbook_repo).to be_branch_exists("chef-vendor-absent-new")
end
describe "when the pristine copy branch does not exist" do
it "prepares for import by creating the pristine copy branch" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@branch_list)
- expect(@cookbook_repo).to receive(:shell_out!).with("git checkout -b chef-vendor-nginx", :cwd => @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@branch_list)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git checkout -b chef-vendor-nginx", cwd: @repo_path)
@cookbook_repo.prepare_to_import("nginx")
end
end
describe "when the pristine copy branch does exist" do
it "prepares for import by checking out the pristine copy branch" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@branch_list)
- expect(@cookbook_repo).to receive(:shell_out!).with("git checkout chef-vendor-apache2", :cwd => @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", cwd: @repo_path).and_return(@branch_list)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git checkout chef-vendor-apache2", cwd: @repo_path)
@cookbook_repo.prepare_to_import("apache2")
end
end
@@ -143,7 +143,7 @@ DIRTY
before do
@updates = Mixlib::ShellOut.new
@updates.stdout.replace("\n")
- allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain -- apache2", :cwd => @repo_path).and_return(@updates)
+ allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain -- apache2", cwd: @repo_path).and_return(@updates)
end
it "shows no changes in the pristine copy" do
@@ -159,7 +159,7 @@ DIRTY
before do
@updates = Mixlib::ShellOut.new
@updates.stdout.replace(" M cookbooks/apache2/recipes/default.rb\n")
- allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain -- apache2", :cwd => @repo_path).and_return(@updates)
+ allow(@cookbook_repo).to receive(:shell_out!).with("git status --porcelain -- apache2", cwd: @repo_path).and_return(@updates)
end
it "shows changes in the pristine copy" do
@@ -167,20 +167,20 @@ DIRTY
end
it "commits the changes to the repo and tags the commit" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git add apache2", :cwd => @repo_path)
- expect(@cookbook_repo).to receive(:shell_out!).with("git commit -m \"Import apache2 version 1.2.3\" -- apache2", :cwd => @repo_path)
- expect(@cookbook_repo).to receive(:shell_out!).with("git tag -f cookbook-site-imported-apache2-1.2.3", :cwd => @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git add apache2", cwd: @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git commit -m \"Import apache2 version 1.2.3\" -- apache2", cwd: @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git tag -f cookbook-site-imported-apache2-1.2.3", cwd: @repo_path)
expect(@cookbook_repo.finalize_updates_to("apache2", "1.2.3")).to be_truthy
end
end
describe "when a custom default branch is specified" do
before do
- @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => "develop")
+ @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, default_branch: "develop")
end
it "resets to default state by checking out the default branch" do
- expect(@cookbook_repo).to receive(:shell_out!).with("git checkout develop", :cwd => @repo_path)
+ expect(@cookbook_repo).to receive(:shell_out!).with("git checkout develop", cwd: @repo_path)
@cookbook_repo.reset_to_default_state
end
end
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..f0c29b86a0 100644
--- a/spec/unit/knife/core/gem_glob_loader_spec.rb
+++ b/spec/unit/knife/core/gem_glob_loader_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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::Knife::SubcommandLoader::GemGlobLoader do
let(:plugin_dir) { File.join(home, ".chef", "plugins", "knife") }
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
Chef::Util::PathHelper.class_variable_set(:@@home_dir, home)
end
@@ -34,13 +34,13 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
it "builds a list of the core subcommand file require paths" do
expect(loader.subcommand_files).not_to be_empty
loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ expect(require_path).to match(%r{chef/knife/.*|plugins/knife/.*})
end
end
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
@@ -52,7 +52,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect($LOAD_PATH).to receive(:map).and_return([])
if Gem::Specification.respond_to? :latest_specs
expect(Gem::Specification).to receive(:latest_specs).with(true).and_return(gems)
- expect(gems[0]).to receive(:matches_for_glob).with(/chef\/knife\/\*\.rb\{(.*),\.rb,(.*)\}/).and_return(gem_files)
+ expect(gems[0]).to receive(:matches_for_glob).with(%r{chef/knife/\*\.rb\{(.*),\.rb,(.*)\}}).and_return(gem_files)
else
expect(Gem.source_index).to receive(:latest_specs).with(true).and_return(gems)
expect(gems[0]).to receive(:require_paths).twice.and_return(["lib"])
@@ -60,12 +60,12 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect(Dir).to receive(:[]).with("/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb").and_return(gem_files)
end
expect(loader).to receive(:find_subcommands_via_dirglob).and_return({})
- expect(loader.subcommand_files.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files)
+ expect(loader.subcommand_files.select { |file| file.include?("knife-ec2") }.sort).to eq(gem_files)
end
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
@@ -185,7 +185,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect(Gem.source_index).to receive(:latest_specs).and_call_original
end
loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ expect(require_path).to match(%r{chef/knife/.*|plugins/knife/.*})
end
end
@@ -201,7 +201,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do
expect(Gem.source_index).to receive(:latest_specs).and_call_original
end
loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ expect(require_path).to match(%r{chef/knife/.*|plugins/knife/.*})
end
end
end
diff --git a/spec/unit/knife/core/hashed_command_loader_spec.rb b/spec/unit/knife/core/hashed_command_loader_spec.rb
index 53bd81f4f7..c88656945b 100644
--- a/spec/unit/knife/core/hashed_command_loader_spec.rb
+++ b/spec/unit/knife/core/hashed_command_loader_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@ require "spec_helper"
describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
end
let(:plugin_manifest) do
@@ -43,12 +43,14 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
let(:loader) do
Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
- File.join(CHEF_SPEC_DATA, "knife-site-subcommands"),
- plugin_manifest) end
+ File.join(CHEF_SPEC_DATA, "knife-site-subcommands"),
+ plugin_manifest
+ )
+ end
describe "#list_commands" do
before do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
end
it "lists all commands by category when no argument is given" do
@@ -61,11 +63,11 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
context "when the plugin path is invalid" do
before do
- expect(File).to receive(:exists?).with("/file/for/plugin/b").and_return(false)
+ expect(File).to receive(:exist?).with("/file/for/plugin/b").and_return(false)
end
it "lists all commands by category when no argument is given" do
- expect(Chef::Log).to receive(:error).with(/There are files specified in the manifest that are missing/)
+ expect(Chef::Log).to receive(:error).with(/There are plugin files specified in the knife cache that cannot be found/)
expect(Chef::Log).to receive(:error).with("Missing files:\n\t/file/for/plugin/b")
expect(loader.list_commands).to eq({})
end
@@ -88,7 +90,7 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
end
it "loads the correct file and returns true if the command exists" do
- allow(File).to receive(:exists?).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
expect(Kernel).to receive(:load).with("/file/for/plugin/a").and_return(true)
expect(loader.load_command(["cool_a"])).to eq(true)
end
diff --git a/spec/unit/knife/core/node_editor_spec.rb b/spec/unit/knife/core/node_editor_spec.rb
index ce169a77dd..d8e5c86d2c 100644
--- a/spec/unit/knife/core/node_editor_spec.rb
+++ b/spec/unit/knife/core/node_editor_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jordan Running (<jr@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,10 +24,10 @@ describe Chef::Knife::NodeEditor do
{ "name" => "test_node",
"chef_environment" => "production",
"automatic" => { "foo" => "bar" },
- "default" => { "alpha" => { "bravo" => "charlie", "delta" => "echo" } },
- "normal" => { "alpha" => { "bravo" => "hotel" }, "tags" => [] },
- "override" => { "alpha" => { "bravo" => "foxtrot", "delta" => "golf" } },
- "policy_name" => nil,
+ "default" => { "alpha" => { "bravo" => "charlie", "delta" => "echo" } },
+ "normal" => { "alpha" => { "bravo" => "hotel" }, "tags" => [] },
+ "override" => { "alpha" => { "bravo" => "foxtrot", "delta" => "golf" } },
+ "policy_name" => nil,
"policy_group" => nil,
"run_list" => %w{role[comedy] role[drama] recipe[mystery]},
}
@@ -44,18 +44,18 @@ describe Chef::Knife::NodeEditor 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,|
- %w{ name chef_environment normal
- policy_name policy_group run_list }.include?(key)
- end
+ expected = node_data.select do |key,|
+ %w{ name chef_environment normal
+ policy_name policy_group run_list }.include?(key)
+ end
- expect(subject.view).to eq(expected)
- end
+ expect(subject.view).to eq(expected)
+ end
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
@@ -74,7 +74,7 @@ describe Chef::Knife::NodeEditor do
expect(ui).to have_received(:warn)
.with "Changing the name of a node results in a new node being " +
- "created, test_node will not be modified or removed."
+ "created, test_node will not be modified or removed."
expect(ui).to have_received(:confirm)
.with("Proceed with creation of new node")
@@ -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"])
@@ -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,10 +188,10 @@ 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 }
- end
+ 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/object_loader_spec.rb b/spec/unit/knife/core/object_loader_spec.rb
index 9cabf2ba63..0dcabff46d 100644
--- a/spec/unit/knife/core/object_loader_spec.rb
+++ b/spec/unit/knife/core/object_loader_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Juanje Ojeda (<juanje.ojeda@gmail.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -57,7 +57,7 @@ describe Chef::Knife::Core::ObjectLoader do
it_behaves_like "Chef object", chef_class
end
- #NOTE: This is check for the bug described at CHEF-2352
+ # NOTE: This is check for the bug described at CHEF-2352
describe "when the file is a JSON" do
describe "and it has defined 'json_class'" do
before do
diff --git a/spec/unit/knife/core/status_presenter_spec.rb b/spec/unit/knife/core/status_presenter_spec.rb
new file mode 100644
index 0000000000..377c581bfc
--- /dev/null
+++ b/spec/unit/knife/core/status_presenter_spec.rb
@@ -0,0 +1,54 @@
+# Copyright:: Copyright (c) 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::Core::StatusPresenter do
+ describe "#summarize_json" do
+ let(:presenter) { Chef::Knife::Core::StatusPresenter.new(double(:ui), double(:config, :[] => "")) }
+
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.automatic_attrs["name"] = "my_node"
+ n.automatic_attrs["ipaddress"] = "127.0.0.1"
+ end
+ end
+
+ let(:result) { JSON.parse(presenter.summarize_json([node])).first }
+
+ it "uses the first of public_ipv4_addrs when present" do
+ node.automatic_attrs["cloud"] = { "public_ipv4_addrs" => ["2.2.2.2"] }
+
+ expect(result["ip"]).to eq("2.2.2.2")
+ end
+
+ it "falls back to ipaddress when public_ipv4_addrs is empty" do
+ node.automatic_attrs["cloud"] = { "public_ipv4_addrs" => [] }
+
+ expect(result["ip"]).to eq("127.0.0.1")
+ end
+
+ it "falls back to ipaddress when cloud attributes are empty" do
+ node.automatic_attrs["cloud"] = {}
+
+ expect(result["ip"]).to eq("127.0.0.1")
+ end
+
+ it "falls back to ipaddress when cloud attributes is not present" do
+ expect(result["ip"]).to eq("127.0.0.1")
+ 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..e8bc045946 100644
--- a/spec/unit/knife/core/subcommand_loader_spec.rb
+++ b/spec/unit/knife/core/subcommand_loader_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc
+# Copyright:: Copyright (c) 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::Knife::SubcommandLoader do
let(:plugin_dir) { File.join(home, ".chef", "plugins", "knife") }
before do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
Chef::Util::PathHelper.class_variable_set(:@@home_dir, home)
end
@@ -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 9f525f22f0..3bbe799267 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,24 +25,32 @@ describe Chef::Knife::UI do
before do
@out, @err, @in = StringIO.new, StringIO.new, StringIO.new
@config = {
- :verbosity => 0,
- :yes => nil,
- :format => "summary",
+ 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
@@ -160,12 +171,12 @@ describe Chef::Knife::UI do
describe "format_list_for_display" do
it "should print the full hash if --with-uri is true" do
@ui.config[:with_uri] = true
- expect(@ui.format_list_for_display({ :marcy => :playground })).to eq({ :marcy => :playground })
+ expect(@ui.format_list_for_display({ marcy: :playground })).to eq({ marcy: :playground })
end
it "should print only the keys if --with-uri is false" do
@ui.config[:with_uri] = false
- expect(@ui.format_list_for_display({ :marcy => :playground })).to eq([ :marcy ])
+ expect(@ui.format_list_for_display({ marcy: :playground })).to eq([ :marcy ])
end
end
@@ -200,10 +211,10 @@ describe Chef::Knife::UI do
it "formats hashes appropriately" do
@ui.output({ "hi" => "a", "lo" => "b" })
- expect(@out.string).to eq <<EOM
-hi: a
-lo: b
-EOM
+ expect(@out.string).to eq <<~EOM
+ hi: a
+ lo: b
+ EOM
end
it "formats empty hashes appropriately" do
@@ -213,10 +224,10 @@ EOM
it "formats arrays appropriately" do
@ui.output(%w{a b})
- expect(@out.string).to eq <<EOM
-a
-b
-EOM
+ expect(@out.string).to eq <<~EOM
+ a
+ b
+ EOM
end
it "formats empty arrays appropriately" do
@@ -236,75 +247,75 @@ EOM
it "formats nested arrays appropriately" do
@ui.output([ %w{a b}, %w{c d}])
- expect(@out.string).to eq <<EOM
-a
-b
+ expect(@out.string).to eq <<~EOM
+ a
+ b
-c
-d
-EOM
+ c
+ d
+ EOM
end
it "formats nested arrays with single- and empty subarrays appropriately" do
@ui.output([ %w{a b}, [ "c" ], [], %w{d e}])
- expect(@out.string).to eq <<EOM
-a
-b
+ expect(@out.string).to eq <<~EOM
+ a
+ b
-c
+ c
-d
-e
-EOM
+ d
+ e
+ EOM
end
it "formats arrays of hashes with extra lines in between for readability" do
@ui.output([ { "a" => "b", "c" => "d" }, { "x" => "y" }, { "m" => "n", "o" => "p" }])
- expect(@out.string).to eq <<EOM
-a: b
-c: d
+ expect(@out.string).to eq <<~EOM
+ a: b
+ c: d
-x: y
+ x: y
-m: n
-o: p
-EOM
+ m: n
+ o: p
+ EOM
end
it "formats hashes with empty array members appropriately" do
@ui.output({ "a" => [], "b" => "c" })
- expect(@out.string).to eq <<EOM
-a:
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a:
+ b: c
+ EOM
end
it "formats hashes with single-member array values appropriately" do
@ui.output({ "a" => [ "foo" ], "b" => "c" })
- expect(@out.string).to eq <<EOM
-a: foo
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a: foo
+ b: c
+ EOM
end
it "formats hashes with array members appropriately" do
@ui.output({ "a" => %w{foo bar}, "b" => "c" })
- expect(@out.string).to eq <<EOM
-a:
- foo
- bar
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a:
+ foo
+ bar
+ b: c
+ EOM
end
it "formats hashes with single-member nested array values appropriately" do
@ui.output({ "a" => [ [ "foo" ] ], "b" => "c" })
- expect(@out.string).to eq <<EOM
-a:
- foo
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a:
+ foo
+ b: c
+ EOM
end
it "formats hashes with nested array values appropriately" do
@@ -316,20 +327,20 @@ EOM
it "formats hashes with hash values appropriately" do
@ui.output({ "a" => { "aa" => "bb", "cc" => "dd" }, "b" => "c" })
- expect(@out.string).to eq <<EOM
-a:
- aa: bb
- cc: dd
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a:
+ aa: bb
+ cc: dd
+ b: c
+ EOM
end
it "formats hashes with empty hash values appropriately" do
@ui.output({ "a" => {}, "b" => "c" })
- expect(@out.string).to eq <<EOM
-a:
-b: c
-EOM
+ expect(@out.string).to eq <<~EOM
+ a:
+ b: c
+ EOM
end
end
@@ -347,7 +358,7 @@ EOM
describe "format_for_display" do
it "should return the raw data" do
- input = { :gi => :go }
+ input = { gi: :go }
expect(@ui.format_for_display(input)).to eq(input)
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
@@ -439,9 +480,9 @@ EOM
context "when running on Windows" do
before(:each) do
- stdout = double("StringIO", :tty? => true)
+ stdout = double("StringIO", tty?: true)
allow(@ui).to receive(:stdout).and_return(stdout)
- allow(ChefConfig).to receive(:windows?) { true }
+ allow(ChefUtils).to receive(:windows?) { true }
Chef::Config.reset
end
@@ -466,6 +507,23 @@ EOM
end
end
+ describe "color" do
+ context "when ui.color? => true" do
+ it "returns colored output" do
+ skip "doesn't work on systems that don't correctly have terminals setup for color"
+ expect(@ui).to receive(:color?).and_return(true)
+ expect(@ui.color("a_bus_is", :yellow)).to eql("\e[33ma_bus_is\e[0m")
+ end
+ end
+
+ context "when ui.color? => false" do
+ it "returns plain output" do
+ expect(@ui).to receive(:color?).and_return(false)
+ expect(@ui.color("a_bus_is", :yellow)).to eql("a_bus_is")
+ end
+ end
+ end
+
describe "confirm" do
let(:stdout) { StringIO.new }
let(:output) { stdout.string }
@@ -590,7 +648,7 @@ EOM
out = StringIO.new
allow(@ui).to receive(:stdout).and_return(out)
allow(@ui).to receive(:stdin).and_return(StringIO.new(" \n"))
- expect(@ui.ask_question("your chef server URL? ", :default => "http://localhost:4000")).to eq("http://localhost:4000")
+ expect(@ui.ask_question("your chef server URL? ", default: "http://localhost:4000")).to eq("http://localhost:4000")
expect(out.string).to eq("your chef server URL? [http://localhost:4000] ")
end
end
diff --git a/spec/unit/knife/core/windows_bootstrap_context_spec.rb b/spec/unit/knife/core/windows_bootstrap_context_spec.rb
new file mode 100644
index 0000000000..76b90c955e
--- /dev/null
+++ b/spec/unit/knife/core/windows_bootstrap_context_spec.rb
@@ -0,0 +1,238 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 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/knife/core/windows_bootstrap_context"
+describe Chef::Knife::Core::WindowsBootstrapContext do
+ let(:config) { {} }
+ let(:chef_config) { Chef::Config.save } # "dup" to a hash
+ let(:bootstrap_context) { Chef::Knife::Core::WindowsBootstrapContext.new(config, nil, chef_config, nil) }
+
+ describe "fips" do
+ context "when fips is set" do
+ before do
+ chef_config[:fips] = true
+ end
+
+ it "sets fips mode in the client.rb" do
+ expect(bootstrap_context.config_content).to match(/fips true/)
+ end
+ end
+
+ context "when fips is not set" do
+ before do
+ chef_config[:fips] = false
+ end
+
+ it "sets fips mode in the client.rb" do
+ expect(bootstrap_context.config_content).not_to match(/fips true/)
+ end
+ end
+ end
+
+ describe "trusted_certs_script" do
+ let(:mock_cert_dir) { ::File.absolute_path(::File.join("spec", "assets", "fake_trusted_certs")) }
+ let(:script_output) { bootstrap_context.trusted_certs_script }
+ let(:crt_files) { ::Dir.glob(::File.join(mock_cert_dir, "*.crt")) }
+ let(:pem_files) { ::Dir.glob(::File.join(mock_cert_dir, "*.pem")) }
+ let(:other_files) { ::Dir.glob(::File.join(mock_cert_dir, "*")) - crt_files - pem_files }
+
+ before do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(trusted_certs_dir: mock_cert_dir))
+ end
+
+ it "should echo every .crt file in the trusted_certs directory" do
+ crt_files.each do |f|
+ echo_file = ::File.read(f).gsub(/^/, "echo.")
+ expect(script_output).to include(::File.join("trusted_certs", ::File.basename(f)))
+ expect(script_output).to include(echo_file)
+ end
+ end
+
+ it "should echo every .pem file in the trusted_certs directory" do
+ pem_files.each do |f|
+ echo_file = ::File.read(f).gsub(/^/, "echo.")
+ expect(script_output).to include(::File.join("trusted_certs", ::File.basename(f)))
+ expect(script_output).to include(echo_file)
+ end
+ end
+
+ it "should not echo files which aren't .crt or .pem files" do
+ other_files.each do |f|
+ echo_file = ::File.read(f).gsub(/^/, "echo.")
+ expect(script_output).to_not include(::File.join("trusted_certs", ::File.basename(f)))
+ expect(script_output).to_not include(echo_file)
+ end
+ end
+ end
+
+ describe "validation_key" do
+ before do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(validation_key: "C:\\chef\\key.pem"))
+ end
+
+ it "should return false if validation_key does not exist" do
+ allow(::File).to receive(:expand_path).with("C:\\chef\\key.pem").and_call_original
+ allow(::File).to receive(:exist?).and_return(false)
+ expect(bootstrap_context.validation_key).to eq(false)
+ end
+ end
+
+ describe "#get_log_location" do
+
+ context "when config_log_location value is nil" do
+ it "sets STDOUT in client.rb as default" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: nil))
+ expect(bootstrap_context.get_log_location).to eq("STDOUT\n")
+ end
+ end
+
+ context "when config_log_location value is empty" do
+ it "sets STDOUT in client.rb as default" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: ""))
+ expect(bootstrap_context.get_log_location).to eq("STDOUT\n")
+ end
+ end
+
+ context "when config_log_location value is STDOUT" do
+ it "sets STDOUT in client.rb" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: STDOUT))
+ expect(bootstrap_context.get_log_location).to eq("STDOUT\n")
+ end
+ end
+
+ context "when config_log_location value is STDERR" do
+ it "sets STDERR in client.rb" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: STDERR))
+ expect(bootstrap_context.get_log_location).to eq("STDERR\n")
+ end
+ end
+
+ context "when config_log_location value is path to a file" do
+ it "sets file path in client.rb" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: "C:\\chef\\chef.log"))
+ expect(bootstrap_context.get_log_location).to eq("\"C:\\chef\\chef.log\"\n")
+ end
+ end
+
+ context "when config_log_location value is :win_evt" do
+ it "sets :win_evt in client.rb" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: :win_evt))
+ expect(bootstrap_context.get_log_location).to eq(":win_evt\n")
+ end
+ end
+
+ context "when config_log_location value is :syslog" do
+ it "raise error with message and exit" do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(config_log_location: :syslog))
+ expect { bootstrap_context.get_log_location }.to raise_error("syslog is not supported for log_location on Windows OS\n")
+ end
+ end
+
+ end
+
+ describe "#config_content" do
+ before do
+ bootstrap_context.instance_variable_set(
+ :@chef_config, Mash.new(
+ config_log_level: :info,
+ config_log_location: STDOUT,
+ chef_server_url: "http://chef.example.com:4444",
+ validation_client_name: "chef-validator-testing",
+ file_cache_path: "c:/chef/cache",
+ file_backup_path: "c:/chef/backup",
+ cache_options: ({ path: "c:/chef/cache/checksums", skip_expires: true })
+ )
+ )
+ end
+
+ it "generates the config file data" do
+ expected = <<~EXPECTED
+ echo.chef_server_url "http://chef.example.com:4444"
+ echo.validation_client_name "chef-validator-testing"
+ echo.file_cache_path "C:\\\\chef\\\\cache"
+ echo.file_backup_path "C:\\\\chef\\\\backup"
+ echo.cache_options ^({:path =^> "C:\\\\chef\\\\cache\\\\checksums", :skip_expires =^> true}^)
+ echo.# Using default node name ^(fqdn^)
+ echo.log_level :auto
+ echo.log_location STDOUT
+ EXPECTED
+ expect(bootstrap_context.config_content).to eq expected
+ end
+
+ describe "when chef_license is set" do
+ before do
+ bootstrap_context.instance_variable_set(:@chef_config, Mash.new(chef_license: "accept-no-persist"))
+ end
+ it "sets chef_license in the generated config file" do
+ expect(bootstrap_context.config_content).to include("chef_license \"accept-no-persist\"")
+ end
+ end
+ end
+
+ describe "#start_chef" do
+ it "returns the expected string" do
+ expect(bootstrap_context.start_chef).to eq(
+ <<~EOH
+ SET "PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin;%PATH%"
+ chef-client -c C:\\chef\\client.rb -j C:\\chef\\first-boot.json
+ EOH
+ )
+ end
+ end
+
+ describe "msi_url" do
+ context "when msi_url config option is not set" do
+ let(:config) { { channel: "stable" } }
+ before do
+ expect(bootstrap_context).to receive(:version_to_install).and_return("something")
+ end
+
+ it "returns a chef.io msi url with minimal url parameters" do
+ reference_url = "https://www.chef.io/chef/download?p=windows&channel=stable&v=something"
+ expect(bootstrap_context.msi_url).to eq(reference_url)
+ end
+
+ it "returns a chef.io msi url with provided url parameters substituted" do
+ reference_url = "https://www.chef.io/chef/download?p=windows&pv=machine&m=arch&DownloadContext=ctx&channel=stable&v=something"
+ expect(bootstrap_context.msi_url("machine", "arch", "ctx")).to eq(reference_url)
+ end
+
+ context "when a channel is provided in config" do
+ let(:config) { { channel: "current" } }
+ it "returns a chef.io msi url with the requested channel" do
+ reference_url = "https://www.chef.io/chef/download?p=windows&channel=current&v=something"
+ expect(bootstrap_context.msi_url).to eq(reference_url)
+ end
+ end
+ end
+
+ context "when msi_url config option is set" do
+ let(:custom_url) { "file://something" }
+ let(:config) { { msi_url: custom_url, install: true } }
+
+ it "returns the overridden url" do
+ expect(bootstrap_context.msi_url).to eq(custom_url)
+ end
+
+ it "doesn't introduce any unnecessary query parameters if provided by the template" do
+ expect(bootstrap_context.msi_url("machine", "arch", "ctx")).to eq(custom_url)
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/data_bag_create_spec.rb b/spec/unit/knife/data_bag_create_spec.rb
index b852c30401..93082c190e 100644
--- a/spec/unit/knife/data_bag_create_spec.rb
+++ b/spec/unit/knife/data_bag_create_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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::HTTPClientException.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::HTTPClientException.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_edit_spec.rb b/spec/unit/knife/data_bag_edit_spec.rb
index 635dc63489..6ebcaf4945 100644
--- a/spec/unit/knife/data_bag_edit_spec.rb
+++ b/spec/unit/knife/data_bag_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,8 +49,7 @@ describe Chef::Knife::DataBagEdit do
let(:is_encrypted?) { false }
let(:transmitted_hash) { raw_edited_hash }
- let(:data_to_edit) { db }
-
+ let(:data_to_edit) { db.raw_data }
shared_examples_for "editing a data bag" do
it "correctly edits then uploads the data bag" do
expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(db)
@@ -74,7 +73,7 @@ describe Chef::Knife::DataBagEdit do
end
context "when config[:print_after] is set" do
- let(:config) { { :print_after => true } }
+ let(:config) { { print_after: true } }
before do
expect(knife.ui).to receive(:output).with(raw_edited_hash)
end
@@ -96,7 +95,7 @@ describe Chef::Knife::DataBagEdit do
let(:is_encrypted?) { true }
let(:db) { Chef::DataBagItem.from_hash(enc_raw_hash) }
# If the data bag is encrypted, it gets passed to `edit` as a hash. Otherwise, it gets passed as a DataBag
- let (:data_to_edit) { raw_hash }
+ let(:data_to_edit) { raw_hash }
before(:each) do
expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true)
diff --git a/spec/unit/knife/data_bag_from_file_spec.rb b/spec/unit/knife/data_bag_from_file_spec.rb
index 3bea392ae2..12211eede3 100644
--- a/spec/unit/knife/data_bag_from_file_spec.rb
+++ b/spec/unit/knife/data_bag_from_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,7 @@ Chef::Knife::DataBagFromFile.load_deps
describe Chef::Knife::DataBagFromFile do
before :each do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
Chef::Config[:node_name] = "webmonkey.example.com"
FileUtils.mkdir_p([db_folder, db_folder2])
db_file.write(Chef::JSONCompat.to_json(plain_data))
@@ -77,7 +77,8 @@ describe Chef::Knife::DataBagFromFile do
"id" => "item_name",
"greeting" => "hello",
"nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
- } end
+ }
+ 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_secret_options_spec.rb b/spec/unit/knife/data_bag_secret_options_spec.rb
index 2f4d8c8a88..e8f99c3f79 100644
--- a/spec/unit/knife/data_bag_secret_options_spec.rb
+++ b/spec/unit/knife/data_bag_secret_options_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -50,8 +50,8 @@ describe Chef::Knife::DataBagSecretOptions do
describe "#validate_secrets" do
it "throws an error when provided with both --secret and --secret-file on the CL" do
- Chef::Config[:knife][:cl_secret_file] = secret_file.path
- Chef::Config[:knife][:cl_secret] = secret
+ example_db.config[:cl_secret_file] = secret_file.path
+ example_db.config[:cl_secret] = secret
expect(example_db).to receive(:exit).with(1)
expect(example_db.ui).to receive(:fatal).with("Please specify only one of --secret, --secret-file")
@@ -61,6 +61,7 @@ describe Chef::Knife::DataBagSecretOptions do
it "throws an error when provided with `secret` and `secret_file` in knife.rb" do
Chef::Config[:knife][:secret_file] = secret_file.path
Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db).to receive(:exit).with(1)
expect(example_db.ui).to receive(:fatal).with("Please specify only one of 'secret' or 'secret_file' in your config file")
@@ -72,14 +73,16 @@ describe Chef::Knife::DataBagSecretOptions do
describe "#read_secret" do
it "returns the secret first" do
- Chef::Config[:knife][:cl_secret] = secret
- expect(example_db).to receive(:config).and_return({ :secret => secret })
+ example_db.config[:cl_secret] = secret
+ Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db.read_secret).to eq(secret)
end
it "returns the secret_file only if secret does not exist" do
- Chef::Config[:knife][:cl_secret_file] = secret_file.path
- expect(example_db).to receive(:config).and_return({ :secret_file => secret_file.path })
+ example_db.config[:cl_secret_file] = secret_file.path
+ Chef::Config[:knife][:secret_file] = secret_file.path
+ example_db.merge_configs
expect(Chef::EncryptedDataBagItem).to receive(:load_secret).with(secret_file.path).and_return("secret file contents")
expect(example_db.read_secret).to eq("secret file contents")
end
@@ -87,11 +90,13 @@ describe Chef::Knife::DataBagSecretOptions do
it "returns the secret from the knife.rb config" do
Chef::Config[:knife][:secret_file] = secret_file.path
Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db.read_secret).to eq(secret)
end
it "returns the secret_file from the knife.rb config only if the secret does not exist" do
Chef::Config[:knife][:secret_file] = secret_file.path
+ example_db.merge_configs
expect(Chef::EncryptedDataBagItem).to receive(:load_secret).with(secret_file.path).and_return("secret file contents")
expect(example_db.read_secret).to eq("secret file contents")
end
@@ -101,58 +106,61 @@ describe Chef::Knife::DataBagSecretOptions do
describe "#encryption_secret_provided?" do
it "returns true if the secret is passed on the CL" do
- Chef::Config[:knife][:cl_secret] = secret
+ example_db.config[:cl_secret] = secret
expect(example_db.encryption_secret_provided?).to eq(true)
end
it "returns true if the secret_file is passed on the CL" do
- Chef::Config[:knife][:cl_secret_file] = secret_file.path
+ example_db.config[:cl_secret_file] = secret_file.path
expect(example_db.encryption_secret_provided?).to eq(true)
end
it "returns true if --encrypt is passed on the CL and :secret is in config" do
- expect(example_db).to receive(:config).and_return({ :encrypt => true })
+ example_db.config[:encrypt] = true
Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db.encryption_secret_provided?).to eq(true)
end
it "returns true if --encrypt is passed on the CL and :secret_file is in config" do
- expect(example_db).to receive(:config).and_return({ :encrypt => true })
+ example_db.config[:encrypt] = true
Chef::Config[:knife][:secret_file] = secret_file.path
+ example_db.merge_configs
expect(example_db.encryption_secret_provided?).to eq(true)
end
it "throws an error if --encrypt is passed and there is not :secret or :secret_file in the config" do
- expect(example_db).to receive(:config).and_return({ :encrypt => true })
+ example_db.config[:encrypt] = true
expect(example_db).to receive(:exit).with(1)
expect(example_db.ui).to receive(:fatal).with("No secret or secret_file specified in config, unable to encrypt item.")
example_db.encryption_secret_provided?
end
it "returns false if no secret is passed" do
- expect(example_db).to receive(:config).and_return({})
expect(example_db.encryption_secret_provided?).to eq(false)
end
it "returns false if --encrypt is not provided and :secret is in the config" do
- expect(example_db).to receive(:config).and_return({})
Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db.encryption_secret_provided?).to eq(false)
end
it "returns false if --encrypt is not provided and :secret_file is in the config" do
- expect(example_db).to receive(:config).and_return({})
Chef::Config[:knife][:secret_file] = secret_file.path
+ example_db.merge_configs
expect(example_db.encryption_secret_provided?).to eq(false)
end
it "returns true if --encrypt is not provided, :secret is in the config and need_encrypt_flag is false" do
Chef::Config[:knife][:secret] = secret
+ example_db.merge_configs
expect(example_db.encryption_secret_provided_ignore_encrypt_flag?).to eq(true)
end
it "returns true if --encrypt is not provided, :secret_file is in the config and need_encrypt_flag is false" do
Chef::Config[:knife][:secret_file] = secret_file.path
+ example_db.merge_configs
expect(example_db.encryption_secret_provided_ignore_encrypt_flag?).to eq(true)
end
diff --git a/spec/unit/knife/data_bag_show_spec.rb b/spec/unit/knife/data_bag_show_spec.rb
index ece7f5bf78..2b806b8a65 100644
--- a/spec/unit/knife/data_bag_show_spec.rb
+++ b/spec/unit/knife/data_bag_show_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,7 +47,8 @@ describe Chef::Knife::DataBagShow do
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
+ "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) }
@@ -91,12 +92,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
@@ -104,6 +104,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..bfaeed0c82 100644
--- a/spec/unit/knife/environment_compare_spec.rb
+++ b/spec/unit/knife/environment_compare_spec.rb
@@ -61,8 +61,8 @@ 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|
- expect(@stdout.string).to(match /#{item}/) && expect(@stdout.string.lines.count).to(be 4)
+ @environments.each_key do |item|
+ expect(@stdout.string).to(match(/#{item}/)) && expect(@stdout.string.lines.count).to(be 4)
end
end
@@ -78,8 +78,8 @@ describe Chef::Knife::EnvironmentCompare do
@knife.config[:format] = "summary"
@knife.config[:mismatch] = true
@knife.run
- @constraints.each do |item, ver|
- expect(@stdout.string).to match /#{ver[1]}/
+ @constraints.each_value do |ver|
+ expect(@stdout.string).to match(/#{ver[1]}/)
end
end
@@ -96,8 +96,8 @@ describe Chef::Knife::EnvironmentCompare do
@knife.config[:format] = "summary"
@knife.config[:all] = true
@knife.run
- @constraints.each do |item, ver|
- expect(@stdout.string).to match /#{ver[1]}/
+ @constraints.each_value do |ver|
+ expect(@stdout.string).to match(/#{ver[1]}/)
end
end
diff --git a/spec/unit/knife/environment_create_spec.rb b/spec/unit/knife/environment_create_spec.rb
index a80626657c..d54cab8dc9 100644
--- a/spec/unit/knife/environment_create_spec.rb
+++ b/spec/unit/knife/environment_create_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/environment_delete_spec.rb b/spec/unit/knife/environment_delete_spec.rb
index 307a6196c0..643bf1cc13 100644
--- a/spec/unit/knife/environment_delete_spec.rb
+++ b/spec/unit/knife/environment_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/environment_edit_spec.rb b/spec/unit/knife/environment_edit_spec.rb
index 1f2ff27ce8..1feb1c05fd 100644
--- a/spec/unit/knife/environment_edit_spec.rb
+++ b/spec/unit/knife/environment_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -63,7 +63,7 @@ describe Chef::Knife::EnvironmentEdit do
@knife.run
end
- it "shoud show usage and exit when no environment name is provided" do
+ it "should show usage and exit when no environment name is provided" do
@knife.name_args = []
expect(@knife).to receive(:show_usage)
expect { @knife.run }.to raise_error(SystemExit)
diff --git a/spec/unit/knife/environment_from_file_spec.rb b/spec/unit/knife/environment_from_file_spec.rb
index 4505da3637..2090ec7bbd 100644
--- a/spec/unit/knife/environment_from_file_spec.rb
+++ b/spec/unit/knife/environment_from_file_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
# Author:: Seth Falcon (<seth@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,7 @@ Chef::Knife::EnvironmentFromFile.load_deps
describe Chef::Knife::EnvironmentFromFile do
before(:each) do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
@knife = Chef::Knife::EnvironmentFromFile.new
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
@@ -61,7 +61,7 @@ describe Chef::Knife::EnvironmentFromFile do
allow(File).to receive(:expand_path).with("./environments/").and_return("/tmp/environments")
allow(Dir).to receive(:glob).with("/tmp/environments/*.{json,rb}").and_return(["spec.rb", "apple.rb"])
@knife.name_args = []
- allow(@knife).to receive(:config).and_return({ :all => true })
+ allow(@knife).to receive(:config).and_return({ all: true })
expect(@environment).to receive(:save).twice
@knife.run
end
diff --git a/spec/unit/knife/environment_list_spec.rb b/spec/unit/knife/environment_list_spec.rb
index 636b32273e..7bb0e723aa 100644
--- a/spec/unit/knife/environment_list_spec.rb
+++ b/spec/unit/knife/environment_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/environment_show_spec.rb b/spec/unit/knife/environment_show_spec.rb
index bd4148e451..8f67e593bc 100644
--- a/spec/unit/knife/environment_show_spec.rb
+++ b/spec/unit/knife/environment_show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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 5b00c6ea31..12826ae7e2 100644
--- a/spec/unit/knife/key_create_spec.rb
+++ b/spec/unit/knife/key_create_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,9 +44,9 @@ describe "key create commands that inherit knife" do
context "when the service object is called" do
it "creates a new instance of Chef::Knife::KeyCreate with the correct args" do
- expect(Chef::Knife::KeyCreate).to receive(:new).
- with("charmander", command.actor_field_name, command.ui, command.config).
- and_return(service_object)
+ expect(Chef::Knife::KeyCreate).to receive(:new)
+ .with("charmander", command.actor_field_name, command.ui, command.config)
+ .and_return(service_object)
command.service_object
end
end # when the service object is called
@@ -84,7 +84,7 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
0wIDAQAB
-----END PUBLIC KEY-----"
end
- let(:config) { Hash.new }
+ let(:config) { {} }
let(:actor) { "charmander" }
let(:ui) { instance_double("Chef::Knife::UI") }
@@ -207,7 +207,7 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
end
end # when the server returns a private key
end # when the command is run
- end #key create run command"
+ end # key create run command"
context "when actor_field_name is 'user'" do
it_should_behave_like "key create run command" do
diff --git a/spec/unit/knife/key_delete_spec.rb b/spec/unit/knife/key_delete_spec.rb
index 0176f3c71e..fd39c7381a 100644
--- a/spec/unit/knife/key_delete_spec.rb
+++ b/spec/unit/knife/key_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,16 +37,16 @@ describe "key delete commands that inherit knife" do
end
context "after apply_params! is called with valid args" do
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
before do
command.apply_params!(params)
end
context "when the service object is called" do
it "creates a new instance of Chef::Knife::KeyDelete with the correct args" do
- expect(Chef::Knife::KeyDelete).to receive(:new).
- with("charmander-key", "charmander", command.actor_field_name, command.ui).
- and_return(service_object)
+ expect(Chef::Knife::KeyDelete).to receive(:new)
+ .with("charmander-key", "charmander", command.actor_field_name, command.ui)
+ .and_return(service_object)
command.service_object
end
end # when the service object is called
@@ -59,7 +59,7 @@ describe "key delete commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyDelete) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
@@ -69,7 +69,7 @@ describe "key delete commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyDelete) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
end
diff --git a/spec/unit/knife/key_edit_spec.rb b/spec/unit/knife/key_edit_spec.rb
index 244d8bdcc7..b42503af59 100644
--- a/spec/unit/knife/key_edit_spec.rb
+++ b/spec/unit/knife/key_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,16 +37,16 @@ describe "key edit commands that inherit knife" do
end
context "after apply_params! is called with valid args" do
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
before do
command.apply_params!(params)
end
context "when the service object is called" do
it "creates a new instance of Chef::Knife::KeyEdit with the correct args" do
- expect(Chef::Knife::KeyEdit).to receive(:new).
- with("charmander-key", "charmander", command.actor_field_name, command.ui, command.config).
- and_return(service_object)
+ expect(Chef::Knife::KeyEdit).to receive(:new)
+ .with("charmander-key", "charmander", command.actor_field_name, command.ui, command.config)
+ .and_return(service_object)
command.service_object
end
end # when the service object is called
@@ -59,7 +59,7 @@ describe "key edit commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyEdit) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
@@ -69,7 +69,7 @@ describe "key edit commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyEdit) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
end
@@ -86,7 +86,7 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
0wIDAQAB
-----END PUBLIC KEY-----"
end
- let(:config) { Hash.new }
+ let(:config) { {} }
let(:actor) { "charmander" }
let(:keyname) { "charmander-key" }
let(:ui) { instance_double("Chef::Knife::UI") }
diff --git a/spec/unit/knife/key_helper.rb b/spec/unit/knife/key_helper.rb
index af81812b93..6dbfb567f4 100644
--- a/spec/unit/knife/key_helper.rb
+++ b/spec/unit/knife/key_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/key_list_spec.rb b/spec/unit/knife/key_list_spec.rb
index 82fd1e4a09..51ed73b64f 100644
--- a/spec/unit/knife/key_list_spec.rb
+++ b/spec/unit/knife/key_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,9 +44,9 @@ describe "key list commands that inherit knife" do
context "when the service object is called" do
it "creates a new instance of Chef::Knife::KeyList with the correct args" do
- expect(Chef::Knife::KeyList).to receive(:new).
- with("charmander", command.list_method, command.ui, command.config).
- and_return(service_object)
+ expect(Chef::Knife::KeyList).to receive(:new)
+ .with("charmander", command.list_method, command.ui, command.config)
+ .and_return(service_object)
command.service_object
end
end # when the service object is called
@@ -73,7 +73,7 @@ describe "key list commands that inherit knife" do
end
describe Chef::Knife::KeyList do
- let(:config) { Hash.new }
+ let(:config) { {} }
let(:actor) { "charmander" }
let(:ui) { instance_double("Chef::Knife::UI") }
diff --git a/spec/unit/knife/key_show_spec.rb b/spec/unit/knife/key_show_spec.rb
index 139d4f91a2..6d1ca2ccc7 100644
--- a/spec/unit/knife/key_show_spec.rb
+++ b/spec/unit/knife/key_show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,16 +37,16 @@ describe "key show commands that inherit knife" do
end
context "after apply_params! is called with valid args" do
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
before do
command.apply_params!(params)
end
context "when the service object is called" do
it "creates a new instance of Chef::Knife::KeyShow with the correct args" do
- expect(Chef::Knife::KeyShow).to receive(:new).
- with("charmander-key", "charmander", command.load_method, command.ui).
- and_return(service_object)
+ expect(Chef::Knife::KeyShow).to receive(:new)
+ .with("charmander-key", "charmander", command.load_method, command.ui)
+ .and_return(service_object)
command.service_object
end
end # when the service object is called
@@ -59,7 +59,7 @@ describe "key show commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyShow) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
@@ -69,7 +69,7 @@ describe "key show commands that inherit knife" do
it_should_behave_like "a knife key command with a keyname as the second arg"
it_should_behave_like "a knife key command" do
let(:service_object) { instance_double(Chef::Knife::KeyShow) }
- let(:params) { ["charmander", "charmander-key"] }
+ let(:params) { %w{charmander charmander-key} }
end
end
end
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_bulk_delete_spec.rb b/spec/unit/knife/node_bulk_delete_spec.rb
index 2a3563e563..e23f286999 100644
--- a/spec/unit/knife/node_bulk_delete_spec.rb
+++ b/spec/unit/knife/node_bulk_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,7 @@ describe Chef::Knife::NodeBulkDelete do
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
allow(@knife.ui).to receive(:confirm).and_return(true)
- @nodes = Hash.new
+ @nodes = {}
%w{adam brent jacob}.each do |node_name|
@nodes[node_name] = "http://localhost:4000/nodes/#{node_name}"
end
@@ -44,14 +44,14 @@ describe Chef::Knife::NodeBulkDelete do
# I hate not having == defined for anything :(
actual = @knife.all_nodes
expect(actual.keys).to match_array(expected.keys)
- expect(actual.values.map { |n| n.name }).to match_array(%w{adam brent jacob})
+ expect(actual.values.map(&:name)).to match_array(%w{adam brent jacob})
end
end
describe "run" do
before do
@inflatedish_list = @nodes.keys.inject({}) do |nodes_by_name, name|
- node = Chef::Node.new()
+ node = Chef::Node.new
node.name(name)
allow(node).to receive(:destroy).and_return(true)
nodes_by_name[name] = node
diff --git a/spec/unit/knife/node_delete_spec.rb b/spec/unit/knife/node_delete_spec.rb
index d4ef32bccf..e6c677c041 100644
--- a/spec/unit/knife/node_delete_spec.rb
+++ b/spec/unit/knife/node_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,14 +23,19 @@ describe Chef::Knife::NodeDelete do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::NodeDelete.new
@knife.config = {
- :print_after => nil,
+ 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..7b2ebb5b2c 100644
--- a/spec/unit/knife/node_edit_spec.rb
+++ b/spec/unit/knife/node_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,19 +23,19 @@ 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
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::NodeEdit.new
@knife.config = {
- :editor => "cat",
- :attribute => nil,
- :print_after => nil,
+ editor: "cat",
+ attribute: nil,
+ print_after: nil,
}
@knife.name_args = [ "adam" ]
- @node = Chef::Node.new()
+ @node = Chef::Node.new
end
it "should load the node" do
@@ -45,11 +45,13 @@ 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 }
- @node.override_attrs = { :dont => :show }
- @node.normal_attrs = { :do_show => :these }
+ @node.automatic_attrs = { go: :away }
+ @node.default_attrs = { hide: :me }
+ @node.override_attrs = { dont: :show }
+ @node.normal_attrs = { do_show: :these }
@node.chef_environment("prod")
@node.run_list("recipe[foo]")
end
diff --git a/spec/unit/knife/node_environment_set_spec.rb b/spec/unit/knife/node_environment_set_spec.rb
index 7ceafdad78..6a6d48cc2f 100644
--- a/spec/unit/knife/node_environment_set_spec.rb
+++ b/spec/unit/knife/node_environment_set_spec.rb
@@ -24,7 +24,7 @@ describe Chef::Knife::NodeEnvironmentSet do
@knife = Chef::Knife::NodeEnvironmentSet.new
@knife.name_args = %w{adam bar}
allow(@knife).to receive(:output).and_return(true)
- @node = Chef::Node.new()
+ @node = Chef::Node.new
@node.name("knifetest-node")
@node.chef_environment << "foo"
allow(@node).to receive(:save).and_return(true)
@@ -47,6 +47,11 @@ describe Chef::Knife::NodeEnvironmentSet do
@knife.run
end
+ it "sets the environment to config for display" do
+ @knife.run
+ expect(@knife.config[:environment]).to eq("bar")
+ end
+
it "should print the environment" do
expect(@knife).to receive(:output).and_return(true)
@knife.run
diff --git a/spec/unit/knife/node_from_file_spec.rb b/spec/unit/knife/node_from_file_spec.rb
index 61c63c150e..00d6dd5d1a 100644
--- a/spec/unit/knife/node_from_file_spec.rb
+++ b/spec/unit/knife/node_from_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,12 +25,12 @@ describe Chef::Knife::NodeFromFile do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::NodeFromFile.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "adam.rb" ]
allow(@knife).to receive(:output).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @node = Chef::Node.new()
+ @node = Chef::Node.new
allow(@node).to receive(:save)
allow(@knife.loader).to receive(:load_from).and_return(@node)
@stdout = StringIO.new
diff --git a/spec/unit/knife/node_list_spec.rb b/spec/unit/knife/node_list_spec.rb
index b94101ef7a..d594fffc14 100644
--- a/spec/unit/knife/node_list_spec.rb
+++ b/spec/unit/knife/node_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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..40b1d2617d
--- /dev/null
+++ b/spec/unit/knife/node_policy_set_spec.rb
@@ -0,0 +1,122 @@
+#
+# Author:: Piyush Awasthi (<piyush.awasthi@chef.io>)
+# Copyright:: Copyright (c) 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_add_spec.rb b/spec/unit/knife/node_run_list_add_spec.rb
index e11bf78029..0148711fac 100644
--- a/spec/unit/knife/node_run_list_add_spec.rb
+++ b/spec/unit/knife/node_run_list_add_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,11 +23,11 @@ describe Chef::Knife::NodeRunListAdd do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::NodeRunListAdd.new
@knife.config = {
- :after => nil,
+ after: nil,
}
@knife.name_args = [ "adam", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @node = Chef::Node.new()
+ @node = Chef::Node.new
allow(@node).to receive(:save).and_return(true)
allow(Chef::Node).to receive(:load).and_return(@node)
end
diff --git a/spec/unit/knife/node_run_list_remove_spec.rb b/spec/unit/knife/node_run_list_remove_spec.rb
index 4f753d7991..1974821728 100644
--- a/spec/unit/knife/node_run_list_remove_spec.rb
+++ b/spec/unit/knife/node_run_list_remove_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::Knife::NodeRunListRemove do
@knife = Chef::Knife::NodeRunListRemove.new
@knife.config[:print_after] = nil
@knife.name_args = [ "adam", "role[monkey]" ]
- @node = Chef::Node.new()
+ @node = Chef::Node.new
@node.name("knifetest-node")
@node.run_list << "role[monkey]"
allow(@node).to receive(:save).and_return(true)
diff --git a/spec/unit/knife/node_run_list_set_spec.rb b/spec/unit/knife/node_run_list_set_spec.rb
index bd55edb997..6246dfce6a 100644
--- a/spec/unit/knife/node_run_list_set_spec.rb
+++ b/spec/unit/knife/node_run_list_set_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeRunListSet do
@knife.config = {}
@knife.name_args = [ "adam", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @node = Chef::Node.new()
+ @node = Chef::Node.new
allow(@node).to receive(:save).and_return(true)
allow(Chef::Node).to receive(:load).and_return(@node)
end
diff --git a/spec/unit/knife/node_show_spec.rb b/spec/unit/knife/node_show_spec.rb
index 2f684b27f4..037672501e 100644
--- a/spec/unit/knife/node_show_spec.rb
+++ b/spec/unit/knife/node_show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Knife::NodeShow do
let(:node) do
- node = Chef::Node.new()
+ node = Chef::Node.new
node.name("adam")
node.run_list = ["role[base]"]
node
diff --git a/spec/unit/knife/osc_user_create_spec.rb b/spec/unit/knife/osc_user_create_spec.rb
deleted file mode 100644
index 0413d46f78..0000000000
--- a/spec/unit/knife/osc_user_create_spec.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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"
-
-Chef::Knife::OscUserCreate.load_deps
-
-# DEPRECATION NOTE
-# 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_create_spec.rb.
-
-describe Chef::Knife::OscUserCreate do
- before(:each) do
- @knife = Chef::Knife::OscUserCreate.new
-
- @stdout = StringIO.new
- @stderr = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
-
- @knife.name_args = [ "a_user" ]
- @knife.config[:user_password] = "foobar"
- @user = Chef::User.new
- @user.name "a_user"
- @user_with_private_key = Chef::User.new
- @user_with_private_key.name "a_user"
- @user_with_private_key.private_key "private_key"
- allow(@user).to receive(:create).and_return(@user_with_private_key)
- allow(Chef::User).to receive(:new).and_return(@user)
- allow(Chef::User).to receive(:from_hash).and_return(@user)
- allow(@knife).to receive(:edit_hash).and_return(@user.to_hash)
- end
-
- it "creates a new user" do
- expect(Chef::User).to receive(:new).and_return(@user)
- expect(@user).to receive(:create)
- @knife.run
- expect(@stderr.string).to match /created user.+a_user/i
- end
-
- it "sets the password" do
- @knife.config[:user_password] = "a_password"
- expect(@user).to receive(:password).with("a_password")
- @knife.run
- end
-
- it "exits with an error if password is blank" do
- @knife.config[:user_password] = ""
- expect { @knife.run }.to raise_error SystemExit
- expect(@stderr.string).to match /You must specify a non-blank password/
- end
-
- it "sets the user name" do
- expect(@user).to receive(:name).with("a_user")
- @knife.run
- end
-
- it "sets the public key if given" do
- @knife.config[:user_key] = "/a/filename"
- allow(File).to receive(:read).with(File.expand_path("/a/filename")).and_return("a_key")
- expect(@user).to receive(:public_key).with("a_key")
- @knife.run
- end
-
- it "allows you to edit the data" do
- expect(@knife).to receive(:edit_hash).with(@user)
- @knife.run
- end
-
- it "writes the private key to a file when --file is specified" do
- @knife.config[:file] = "/tmp/a_file"
- filehandle = double("filehandle")
- expect(filehandle).to receive(:print).with("private_key")
- expect(File).to receive(:open).with("/tmp/a_file", "w").and_yield(filehandle)
- @knife.run
- end
-end
diff --git a/spec/unit/knife/osc_user_delete_spec.rb b/spec/unit/knife/osc_user_delete_spec.rb
deleted file mode 100644
index 6e90988156..0000000000
--- a/spec/unit/knife/osc_user_delete_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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"
-
-# DEPRECATION NOTE
-# 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_delete_spec.rb.
-
-describe Chef::Knife::OscUserDelete do
- before(:each) do
- Chef::Knife::OscUserDelete.load_deps
- @knife = Chef::Knife::OscUserDelete.new
- @knife.name_args = [ "my_user" ]
- end
-
- it "deletes the user" do
- expect(@knife).to receive(:delete_object).with(Chef::User, "my_user")
- @knife.run
- end
-
- it "prints usage and exits when a user name is not provided" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-end
diff --git a/spec/unit/knife/osc_user_edit_spec.rb b/spec/unit/knife/osc_user_edit_spec.rb
deleted file mode 100644
index 59fb152b5b..0000000000
--- a/spec/unit/knife/osc_user_edit_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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"
-
-# DEPRECATION NOTE
-# 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_edit_spec.rb.
-
-describe Chef::Knife::OscUserEdit do
- before(:each) do
- @stderr = StringIO.new
- @stdout = StringIO.new
-
- Chef::Knife::OscUserEdit.load_deps
- @knife = Chef::Knife::OscUserEdit.new
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- @knife.name_args = [ "my_user" ]
- @knife.config[:disable_editing] = true
- end
-
- it "loads and edits the user" do
- data = { :name => "my_user" }
- allow(Chef::User).to receive(:load).with("my_user").and_return(data)
- expect(@knife).to receive(:edit_hash).with(data).and_return(data)
- @knife.run
- end
-
- it "prints usage and exits when a user name is not provided" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-end
diff --git a/spec/unit/knife/osc_user_list_spec.rb b/spec/unit/knife/osc_user_list_spec.rb
deleted file mode 100644
index 10682eb96f..0000000000
--- a/spec/unit/knife/osc_user_list_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Author:: Steven Danna
-# 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"
-
-# DEPRECATION NOTE
-# 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_list_spec.rb.
-
-describe Chef::Knife::OscUserList do
- before(:each) do
- Chef::Knife::OscUserList.load_deps
- @knife = Chef::Knife::OscUserList.new
- end
-
- it "lists the users" do
- expect(Chef::User).to receive(:list)
- expect(@knife).to receive(:format_list_for_display)
- @knife.run
- end
-end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
deleted file mode 100644
index ae6b4be9a8..0000000000
--- a/spec/unit/knife/osc_user_reregister_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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"
-
-# DEPRECATION NOTE
-# 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.
-
-describe Chef::Knife::OscUserReregister do
- before(:each) do
- Chef::Knife::OscUserReregister.load_deps
- @knife = Chef::Knife::OscUserReregister.new
- @knife.name_args = [ "a_user" ]
- @user_mock = double("user_mock", :private_key => "private_key")
- allow(Chef::User).to receive(:load).and_return(@user_mock)
- @stdout = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- end
-
- it "prints usage and exits when a user name is not provided" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "reregisters the user and prints the key" do
- expect(@user_mock).to receive(:reregister).and_return(@user_mock)
- @knife.run
- expect(@stdout.string).to match( /private_key/ )
- end
-
- it "writes the private key to a file when --file is specified" do
- expect(@user_mock).to receive(:reregister).and_return(@user_mock)
- @knife.config[:file] = "/tmp/a_file"
- filehandle = StringIO.new
- expect(File).to receive(:open).with("/tmp/a_file", "w").and_yield(filehandle)
- @knife.run
- expect(filehandle.string).to eq("private_key")
- end
-end
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
deleted file mode 100644
index d6efbef870..0000000000
--- a/spec/unit/knife/osc_user_show_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Author:: Steven Danna (<steve@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"
-
-# DEPRECATION NOTE
-# 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.
-
-describe Chef::Knife::OscUserShow do
- before(:each) do
- Chef::Knife::OscUserShow.load_deps
- @knife = Chef::Knife::OscUserShow.new
- @knife.name_args = [ "my_user" ]
- @user_mock = double("user_mock")
- end
-
- it "loads and displays the user" do
- expect(Chef::User).to receive(:load).with("my_user").and_return(@user_mock)
- expect(@knife).to receive(:format_for_display).with(@user_mock)
- @knife.run
- end
-
- it "prints usage and exits when a user name is not provided" do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
- end
-end
diff --git a/spec/unit/knife/raw_spec.rb b/spec/unit/knife/raw_spec.rb
index 9202998fb9..1f88195e65 100644
--- a/spec/unit/knife/raw_spec.rb
+++ b/spec/unit/knife/raw_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,8 +35,8 @@ describe Chef::Knife::Raw do
it "should set the x-ops-request-source header when --proxy-auth is set" do
knife.config[:proxy_auth] = true
expect(rest).to receive(:request).with(:GET, "/nodes",
- { "Content-Type" => "application/json",
- "x-ops-request-source" => "web" }, false)
+ { "Content-Type" => "application/json",
+ "x-ops-request-source" => "web" }, false)
knife.run
end
end
diff --git a/spec/unit/knife/role_bulk_delete_spec.rb b/spec/unit/knife/role_bulk_delete_spec.rb
index e9054c1d00..5af7c51584 100644
--- a/spec/unit/knife/role_bulk_delete_spec.rb
+++ b/spec/unit/knife/role_bulk_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Delano (<stephen@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,15 +23,15 @@ describe Chef::Knife::RoleBulkDelete do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::RoleBulkDelete.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = ["."]
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
allow(@knife.ui).to receive(:confirm).and_return(true)
- @roles = Hash.new
+ @roles = {}
%w{dev staging production}.each do |role_name|
- role = Chef::Role.new()
+ role = Chef::Role.new
role.name(role_name)
allow(role).to receive(:destroy).and_return(true)
@roles[role_name] = role
diff --git a/spec/unit/knife/role_create_spec.rb b/spec/unit/knife/role_create_spec.rb
index 9466d9642c..0d563e40dd 100644
--- a/spec/unit/knife/role_create_spec.rb
+++ b/spec/unit/knife/role_create_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,11 +23,11 @@ describe Chef::Knife::RoleCreate do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::RoleCreate.new
@knife.config = {
- :description => nil,
+ description: nil,
}
@knife.name_args = [ "adam" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:save)
allow(Chef::Role).to receive(:new).and_return(@role)
allow(@knife).to receive(:edit_data).and_return(@role)
diff --git a/spec/unit/knife/role_delete_spec.rb b/spec/unit/knife/role_delete_spec.rb
index f095e5ba2d..d43f99689d 100644
--- a/spec/unit/knife/role_delete_spec.rb
+++ b/spec/unit/knife/role_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,12 +23,12 @@ describe Chef::Knife::RoleDelete do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::RoleDelete.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "adam" ]
allow(@knife).to receive(:output).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:destroy).and_return(true)
allow(Chef::Role).to receive(:load).and_return(@role)
@stdout = StringIO.new
diff --git a/spec/unit/knife/role_edit_spec.rb b/spec/unit/knife/role_edit_spec.rb
index 5e03b7aef3..faf9cf7d84 100644
--- a/spec/unit/knife/role_edit_spec.rb
+++ b/spec/unit/knife/role_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleEdit do
@knife.config[:print_after] = nil
@knife.name_args = [ "adam" ]
allow(@knife.ui).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:save)
allow(Chef::Role).to receive(:load).and_return(@role)
allow(@knife.ui).to receive(:edit_data).and_return(@role)
diff --git a/spec/unit/knife/role_env_run_list_add_spec.rb b/spec/unit/knife/role_env_run_list_add_spec.rb
index 4738101f90..13a05db33e 100644
--- a/spec/unit/knife/role_env_run_list_add_spec.rb
+++ b/spec/unit/knife/role_env_run_list_add_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,25 +21,25 @@ require "spec_helper"
describe Chef::Knife::RoleEnvRunListAdd do
before(:each) do
-# Chef::Config[:role_name] = "websimian"
-# Chef::Config[:env_name] = "QA"
+ # Chef::Config[:role_name] = "websimian"
+ # Chef::Config[:env_name] = "QA"
@knife = Chef::Knife::RoleEnvRunListAdd.new
@knife.config = {
- :after => nil,
+ after: nil,
}
@knife.name_args = [ "will", "QA", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:save).and_return(true)
allow(Chef::Role).to receive(:load).and_return(@role)
end
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should have an empty default run list" do
@knife.run
@@ -85,17 +85,17 @@ describe Chef::Knife::RoleEnvRunListAdd do
end
it "should add to the run list after the specified entries in the QA run list" do
- #Setup
+ # Setup
@role.run_list_for("_default") << "role[acorns]"
@role.run_list_for("_default") << "role[barn]"
@knife.run
@role.run_list_for("QA") << "role[pencil]"
@role.run_list_for("QA") << "role[pen]"
- #Configuration we are testing
+ # Configuration we are testing
@knife.config[:after] = "role[pencil]"
@knife.name_args = [ "will", "QA", "role[pad]", "role[whackadoo]" ]
@knife.run
- #The actual tests
+ # The actual tests
expect(@role.run_list_for("QA")[0]).to eq("role[monkey]")
expect(@role.run_list_for("QA")[1]).to eq("role[pencil]")
expect(@role.run_list_for("QA")[2]).to eq("role[pad]")
diff --git a/spec/unit/knife/role_env_run_list_clear_spec.rb b/spec/unit/knife/role_env_run_list_clear_spec.rb
index c9c24858c0..d4b9625550 100644
--- a/spec/unit/knife/role_env_run_list_clear_spec.rb
+++ b/spec/unit/knife/role_env_run_list_clear_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,12 +28,12 @@ describe Chef::Knife::RoleEnvRunListClear do
@knife = Chef::Knife::RoleEnvRunListClear.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = %w{will QA}
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -44,10 +44,10 @@ describe Chef::Knife::RoleEnvRunListClear do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_env_run_list_remove_spec.rb b/spec/unit/knife/role_env_run_list_remove_spec.rb
index 8a077f1de3..7f9b41475c 100644
--- a/spec/unit/knife/role_env_run_list_remove_spec.rb
+++ b/spec/unit/knife/role_env_run_list_remove_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,12 +28,12 @@ describe Chef::Knife::RoleEnvRunListRemove do
@knife = Chef::Knife::RoleEnvRunListRemove.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "QA", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -44,10 +44,10 @@ describe Chef::Knife::RoleEnvRunListRemove do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_env_run_list_replace_spec.rb b/spec/unit/knife/role_env_run_list_replace_spec.rb
index 08dcdd90e2..93b233efdc 100644
--- a/spec/unit/knife/role_env_run_list_replace_spec.rb
+++ b/spec/unit/knife/role_env_run_list_replace_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,12 +28,12 @@ describe Chef::Knife::RoleEnvRunListReplace do
@knife = Chef::Knife::RoleEnvRunListReplace.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "QA", "role[dude]", "role[person]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -44,10 +44,10 @@ describe Chef::Knife::RoleEnvRunListReplace do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_env_run_list_set_spec.rb b/spec/unit/knife/role_env_run_list_set_spec.rb
index aed1c9fe1e..d35e4dbb17 100644
--- a/spec/unit/knife/role_env_run_list_set_spec.rb
+++ b/spec/unit/knife/role_env_run_list_set_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,12 +28,12 @@ describe Chef::Knife::RoleEnvRunListSet do
@knife = Chef::Knife::RoleEnvRunListSet.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "QA", "role[owen]", "role[mauntel]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -44,10 +44,10 @@ describe Chef::Knife::RoleEnvRunListSet do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_from_file_spec.rb b/spec/unit/knife/role_from_file_spec.rb
index 104894df17..51e94d31e3 100644
--- a/spec/unit/knife/role_from_file_spec.rb
+++ b/spec/unit/knife/role_from_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,12 +25,12 @@ describe Chef::Knife::RoleFromFile do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::RoleFromFile.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "adam.rb" ]
allow(@knife).to receive(:output).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:save)
allow(@knife.loader).to receive(:load_from).and_return(@role)
@stdout = StringIO.new
diff --git a/spec/unit/knife/role_list_spec.rb b/spec/unit/knife/role_list_spec.rb
index 30743fdc02..dea2e874a4 100644
--- a/spec/unit/knife/role_list_spec.rb
+++ b/spec/unit/knife/role_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/role_run_list_add_spec.rb b/spec/unit/knife/role_run_list_add_spec.rb
index fe7318c040..6f222ee80a 100644
--- a/spec/unit/knife/role_run_list_add_spec.rb
+++ b/spec/unit/knife/role_run_list_add_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,25 +21,25 @@ require "spec_helper"
describe Chef::Knife::RoleRunListAdd do
before(:each) do
-# Chef::Config[:role_name] = "websimian"
-# Chef::Config[:env_name] = "QA"
+ # Chef::Config[:role_name] = "websimian"
+ # Chef::Config[:env_name] = "QA"
@knife = Chef::Knife::RoleRunListAdd.new
@knife.config = {
- :after => nil,
+ after: nil,
}
@knife.name_args = [ "will", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
allow(@role).to receive(:save).and_return(true)
allow(Chef::Role).to receive(:load).and_return(@role)
end
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should have a run list with the monkey role" do
@knife.run
@@ -74,14 +74,14 @@ describe Chef::Knife::RoleRunListAdd do
end
it "should add to the run list after the specified entries in the default run list" do
- #Setup
+ # Setup
@role.run_list_for("_default") << "role[acorns]"
@role.run_list_for("_default") << "role[barn]"
- #Configuration we are testing
+ # Configuration we are testing
@knife.config[:after] = "role[acorns]"
@knife.name_args = [ "will", "role[pad]", "role[whackadoo]" ]
@knife.run
- #The actual tests
+ # The actual tests
expect(@role.run_list[0]).to eq("role[acorns]")
expect(@role.run_list[1]).to eq("role[pad]")
expect(@role.run_list[2]).to eq("role[whackadoo]")
diff --git a/spec/unit/knife/role_run_list_clear_spec.rb b/spec/unit/knife/role_run_list_clear_spec.rb
index 4ed1f312b0..327a9979b0 100644
--- a/spec/unit/knife/role_run_list_clear_spec.rb
+++ b/spec/unit/knife/role_run_list_clear_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,12 +27,12 @@ describe Chef::Knife::RoleRunListClear do
@knife = Chef::Knife::RoleRunListClear.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -43,10 +43,10 @@ describe Chef::Knife::RoleRunListClear do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_run_list_remove_spec.rb b/spec/unit/knife/role_run_list_remove_spec.rb
index 087bc2c6ee..200a559c08 100644
--- a/spec/unit/knife/role_run_list_remove_spec.rb
+++ b/spec/unit/knife/role_run_list_remove_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,12 +27,12 @@ describe Chef::Knife::RoleRunListRemove do
@knife = Chef::Knife::RoleRunListRemove.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "role[monkey]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -43,10 +43,10 @@ describe Chef::Knife::RoleRunListRemove do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_run_list_replace_spec.rb b/spec/unit/knife/role_run_list_replace_spec.rb
index 2bc060ae2d..1957403fb1 100644
--- a/spec/unit/knife/role_run_list_replace_spec.rb
+++ b/spec/unit/knife/role_run_list_replace_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,12 +27,12 @@ describe Chef::Knife::RoleRunListReplace do
@knife = Chef::Knife::RoleRunListReplace.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "role[dude]", "role[person]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -43,10 +43,10 @@ describe Chef::Knife::RoleRunListReplace do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/role_run_list_set_spec.rb b/spec/unit/knife/role_run_list_set_spec.rb
index 27b4d9fea1..06098c585e 100644
--- a/spec/unit/knife/role_run_list_set_spec.rb
+++ b/spec/unit/knife/role_run_list_set_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Will Albenzi (<walbenzi@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,12 +27,12 @@ describe Chef::Knife::RoleRunListSet do
@knife = Chef::Knife::RoleRunListSet.new
@knife.config = {
- :print_after => nil,
+ print_after: nil,
}
@knife.name_args = [ "will", "role[owen]", "role[mauntel]" ]
allow(@knife).to receive(:output).and_return(true)
- @role = Chef::Role.new()
+ @role = Chef::Role.new
@role.name("will")
allow(@role).to receive(:save).and_return(true)
@@ -43,10 +43,10 @@ describe Chef::Knife::RoleRunListSet do
describe "run" do
-# it "should display all the things" do
-# @knife.run
-# @role.to_json.should == 'show all the things'
-# end
+ # it "should display all the things" do
+ # @knife.run
+ # @role.to_json.should == 'show all the things'
+ # end
it "should load the node" do
expect(Chef::Role).to receive(:load).with("will").and_return(@role)
diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb
index 44a133d858..8606045e8c 100644
--- a/spec/unit/knife/ssh_spec.rb
+++ b/spec/unit/knife/ssh_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@chef.io>
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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,118 +170,152 @@ 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]])
+ it "uses the timeout from the CLI" do
+ @knife.config = {}
+ Chef::Config[:knife][:ssh_timeout] = nil
+ @knife.config[:ssh_timeout] = 5
+ @knife.session_from_list([["the.b.org", nil, nil]])
+ @knife.merge_configs
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.config = {}
+ Chef::Config[:knife][:ssh_timeout] = 6
+ @knife.merge_configs
+ @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
- let(:execution_channel) { double(:execution_channel, :on_data => nil) }
- let(:session_channel) { double(:session_channel, :request_pty => nil) }
+ let(:execution_channel) { double(:execution_channel, on_data: nil, on_extended_data: nil) }
+ let(:session_channel) { double(:session_channel, request_pty: nil) }
- let(:execution_channel2) { double(:execution_channel, :on_data => nil) }
- let(:session_channel2) { double(:session_channel, :request_pty => nil) }
+ let(:execution_channel2) { double(:execution_channel, on_data: nil, on_extended_data: nil) }
+ let(:session_channel2) { double(:session_channel, request_pty: nil) }
- let(:session) { double(:session, :loop => nil) }
+ let(:session) { double(:session, loop: nil) }
let(:command) { "false" }
before do
- expect(execution_channel).
- to receive(:on_request).
- and_yield(nil, double(:data_stream, :read_long => exit_status))
-
- expect(session_channel).
- to receive(:exec).
- with(command).
- and_yield(execution_channel, true)
-
- expect(execution_channel2).
- to receive(:on_request).
- and_yield(nil, double(:data_stream, :read_long => exit_status2))
-
- expect(session_channel2).
- to receive(:exec).
- with(command).
- and_yield(execution_channel2, true)
-
- expect(session).
- to receive(:open_channel).
- and_yield(session_channel).
- and_yield(session_channel2)
+ expect(execution_channel)
+ .to receive(:on_request)
+ .and_yield(nil, double(:data_stream, read_long: exit_status))
+
+ expect(session_channel)
+ .to receive(:exec)
+ .with(command)
+ .and_yield(execution_channel, true)
+
+ expect(execution_channel2)
+ .to receive(:on_request)
+ .and_yield(nil, double(:data_stream, read_long: exit_status2))
+
+ expect(session_channel2)
+ .to receive(:exec)
+ .with(command)
+ .and_yield(execution_channel2, true)
+
+ expect(session)
+ .to receive(:open_channel)
+ .and_yield(session_channel)
+ .and_yield(session_channel2)
end
context "both connections return 0" do
@@ -291,136 +346,56 @@ describe Chef::Knife::Ssh do
end
end
- describe "#run" do
+ 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_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"]
- end
-
- context "with an error" do
- let(:exit_code) { 1 }
-
- it "should exit with a non-zero exit code" do
- expect(@knife).to receive(:exit).with(exit_code)
- @knife.run
- end
+ allow(@knife).to receive(:exec).and_return(0)
end
- context "with no error" do
- let(:exit_code) { 0 }
-
- it "should not exit" do
- expect(@knife).not_to receive(:exit)
- @knife.run
- 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 "#configure_password" do
- before do
- @knife.config.delete(:ssh_password_ng)
- @knife.config.delete(:ssh_password)
- end
-
- context "when setting ssh_password_ng from knife ssh" do
- # in this case ssh_password_ng exists, but ssh_password does not
- it "should prompt for a password when ssh_passsword_ng is nil" do
- @knife.config[:ssh_password_ng] = nil
- expect(@knife).to receive(:get_password).and_return("mysekretpassw0rd")
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
- end
-
- it "should set ssh_password to false if ssh_password_ng is false" do
- @knife.config[:ssh_password_ng] = false
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to be_falsey
- end
+ describe "#run" do
- it "should set ssh_password to ssh_password_ng if we set a password" do
- @knife.config[:ssh_password_ng] = "mysekretpassw0rd"
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
- end
+ it "should print usage and exit when a SEARCH QUERY is not provided" do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal).with(/You must specify the SEARCH QUERY./)
+ expect { @knife.run }.to raise_error(SystemExit)
end
- context "when setting ssh_password from knife bootstrap / knife * server create" do
- # in this case ssh_password exists, but ssh_password_ng does not
- it "should set ssh_password to nil when ssh_password is nil" do
- @knife.config[:ssh_password] = nil
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to be_nil
- end
-
- it "should set ssh_password to false when ssh_password is false" do
- @knife.config[:ssh_password] = false
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to be_falsey
- end
-
- it "should set ssh_password to ssh_password if we set a password" do
- @knife.config[:ssh_password] = "mysekretpassw0rd"
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
- end
- end
- context "when setting ssh_password in the config variable" do
- before(:each) do
- Chef::Config[:knife][:ssh_password] = "my_knife_passw0rd"
+ context "exit" do
+ before do
+ @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(:ssh_command).and_return(exit_code)
+ @knife.name_args = ["*:*", "false"]
end
- context "when setting ssh_password_ng from knife ssh" do
- # in this case ssh_password_ng exists, but ssh_password does not
- it "should prompt for a password when ssh_passsword_ng is nil" do
- @knife.config[:ssh_password_ng] = nil
- expect(@knife).to receive(:get_password).and_return("mysekretpassw0rd")
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
- end
- it "should set ssh_password to the configured knife.rb value if ssh_password_ng is false" do
- @knife.config[:ssh_password_ng] = false
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd")
- end
+ context "with an error" do
+ let(:exit_code) { 1 }
- it "should set ssh_password to ssh_password_ng if we set a password" do
- @knife.config[:ssh_password_ng] = "mysekretpassw0rd"
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
+ it "should exit with a non-zero exit code" do
+ expect(@knife).to receive(:exit).with(exit_code)
+ @knife.run
end
end
- context "when setting ssh_password from knife bootstrap / knife * server create" do
- # in this case ssh_password exists, but ssh_password_ng does not
- it "should set ssh_password to the configured knife.rb value when ssh_password is nil" do
- @knife.config[:ssh_password] = nil
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd")
- end
-
- it "should set ssh_password to the configured knife.rb value when ssh_password is false" do
- @knife.config[:ssh_password] = false
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd")
- end
+ context "with no error" do
+ let(:exit_code) { 0 }
- it "should set ssh_password to ssh_password if we set a password" do
- @knife.config[:ssh_password] = "mysekretpassw0rd"
- expect(@knife).not_to receive(:get_password)
- @knife.configure_password
- expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd")
+ it "should not exit" do
+ expect(@knife).not_to receive(:exit)
+ @knife.run
end
end
end
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index 8aa18c3abc..1165da4539 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -67,12 +67,12 @@ describe Chef::Knife::SslCheck do
it "prints an error and exits" do
expect { ssl_check.run }.to raise_error(SystemExit)
- expected_stdout = <<-E
-USAGE: knife ssl check [URL] (options)
-E
- expected_stderr = <<-E
-ERROR: Given URI: `foo.test' is invalid
-E
+ expected_stdout = <<~E
+ USAGE: knife ssl check [URL] (options)
+ E
+ expected_stderr = <<~E
+ ERROR: Given URI: `foo.test' is invalid
+ E
expect(stdout_io.string).to eq(expected_stdout)
expect(stderr_io.string).to eq(expected_stderr)
end
@@ -83,12 +83,12 @@ E
it "prints an error and exits" do
expect { ssl_check.run }.to raise_error(SystemExit)
- expected_stdout = <<-E
-USAGE: knife ssl check [URL] (options)
-E
- expected_stderr = <<-E
-ERROR: Given URI: `#{name_args[0]}' is invalid
-E
+ expected_stdout = <<~E
+ USAGE: knife ssl check [URL] (options)
+ E
+ expected_stderr = <<~E
+ ERROR: Given URI: `#{name_args[0]}' is invalid
+ E
expect(stdout_io.string).to eq(expected_stdout)
expect(stderr_io.string).to eq(expected_stderr)
end
@@ -168,8 +168,8 @@ E
def run
ssl_check.run
rescue Exception
- #puts "OUT: #{stdout_io.string}"
- #puts "ERR: #{stderr_io.string}"
+ # puts "OUT: #{stdout_io.string}"
+ # puts "ERR: #{stderr_io.string}"
raise
end
@@ -199,12 +199,12 @@ E
before do
@old_signal = trap(:INT, "DEFAULT")
- expect(ssl_check).to receive(:proxified_socket).
- with("foo.example.com", 8443).
- and_return(tcp_socket_for_debug)
- expect(OpenSSL::SSL::SSLSocket).to receive(:new).
- with(tcp_socket_for_debug, ssl_check.noverify_peer_ssl_context).
- and_return(ssl_socket_for_debug)
+ expect(ssl_check).to receive(:proxified_socket)
+ .with("foo.example.com", 8443)
+ .and_return(tcp_socket_for_debug)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new)
+ .with(tcp_socket_for_debug, ssl_check.noverify_peer_ssl_context)
+ .and_return(ssl_socket_for_debug)
end
after do
@@ -215,9 +215,9 @@ E
before do
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
expect(ssl_socket).to receive(:connect) # no error
- expect(ssl_socket).to receive(:post_connection_check).
- with("foo.example.com").
- and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:post_connection_check)
+ .with("foo.example.com")
+ .and_raise(OpenSSL::SSL::SSLError)
expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
@@ -235,10 +235,10 @@ E
context "when the cert is not signed by any trusted authority" do
before do
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
- expect(ssl_socket).to receive(:connect).
- and_raise(OpenSSL::SSL::SSLError)
- expect(ssl_socket).to receive(:hostname=).
- with("foo.example.com") # no error
+ expect(ssl_socket).to receive(:connect)
+ .and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:hostname=)
+ .with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
end
diff --git a/spec/unit/knife/ssl_fetch_spec.rb b/spec/unit/knife/ssl_fetch_spec.rb
index 8bb4810b88..2184994dc0 100644
--- a/spec/unit/knife/ssl_fetch_spec.rb
+++ b/spec/unit/knife/ssl_fetch_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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::Knife::SslFetch do
context "when a specific URI is given" do
let(:name_args) { %w{https://example.test:10443/foo} }
- it "fetchs the SSL configuration against the given host" do
+ it "fetches the SSL configuration against the given host" do
expect(ssl_fetch.host).to eq("example.test")
expect(ssl_fetch.port).to eq(10443)
end
@@ -68,12 +68,12 @@ describe Chef::Knife::SslFetch do
it "prints an error and exits" do
expect { ssl_fetch.run }.to raise_error(SystemExit)
- expected_stdout = <<-E
-USAGE: knife ssl fetch [URL] (options)
-E
- expected_stderr = <<-E
-ERROR: Given URI: `foo.test' is invalid
-E
+ expected_stdout = <<~E
+ USAGE: knife ssl fetch [URL] (options)
+ E
+ expected_stderr = <<~E
+ ERROR: Given URI: `foo.test' is invalid
+ E
expect(stdout_io.string).to eq(expected_stdout)
expect(stderr_io.string).to eq(expected_stderr)
end
@@ -84,12 +84,12 @@ E
it "prints an error and exits" do
expect { ssl_fetch.run }.to raise_error(SystemExit)
- expected_stdout = <<-E
-USAGE: knife ssl fetch [URL] (options)
-E
- expected_stderr = <<-E
-ERROR: Given URI: `#{name_args[0]}' is invalid
-E
+ expected_stdout = <<~E
+ USAGE: knife ssl fetch [URL] (options)
+ E
+ expected_stderr = <<~E
+ ERROR: Given URI: `#{name_args[0]}' is invalid
+ E
expect(stdout_io.string).to eq(expected_stdout)
expect(stderr_io.string).to eq(expected_stderr)
end
@@ -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} }
@@ -169,10 +187,10 @@ E
end
it "tells the user their URL is for a non-ssl service" do
- expected_error_text = <<-ERROR_TEXT
-ERROR: The service at the given URI (http://foo.example.com) does not accept SSL connections
-ERROR: Perhaps you meant to connect to 'https://foo.example.com'?
-ERROR_TEXT
+ expected_error_text = <<~ERROR_TEXT
+ ERROR: The service at the given URI (http://foo.example.com) does not accept SSL connections
+ ERROR: Perhaps you meant to connect to 'https://foo.example.com'?
+ ERROR_TEXT
run
expect(stderr).to include(expected_error_text)
@@ -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 c87ea3ad17..838e4c9600 100644
--- a/spec/unit/knife/status_spec.rb
+++ b/spec/unit/knife/status_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Sahil Muthoo (<sahil.muthoo@gmail.com>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,12 +23,14 @@ 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")
allow(@query).to receive(:search).and_yield(node)
allow(Chef::Search::Query).to receive(:new).and_return(@query)
- @knife = Chef::Knife::Status.new
+ @knife = Chef::Knife::Status.new
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
@@ -37,8 +39,9 @@ describe Chef::Knife::Status do
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"] } } end
+ cloud: ["cloud"], run_list: ["run_list"], platform: ["platform"],
+ 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/supermarket_download_spec.rb b/spec/unit/knife/supermarket_download_spec.rb
new file mode 100644
index 0000000000..5d15e74966
--- /dev/null
+++ b/spec/unit/knife/supermarket_download_spec.rb
@@ -0,0 +1,152 @@
+#
+# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
+# Copyright:: Copyright 2012-2016, Thomas Bishop
+# Copyright:: Copyright (c) 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/supermarket_download"
+require "spec_helper"
+
+describe Chef::Knife::SupermarketDownload do
+
+ describe "run" do
+ before do
+ @knife = Chef::Knife::SupermarketDownload.new
+ @knife.name_args = ["apache2"]
+ @noauth_rest = double("no auth rest")
+ @stderr = StringIO.new
+ @cookbook_api_url = "https://supermarket.chef.io/api/v1/cookbooks"
+ @version = "1.0.2"
+ @version_us = @version.tr ".", "_"
+ @current_data = { "deprecated" => false,
+ "latest_version" => "#{@cookbook_api_url}/apache2/versions/#{@version_us}",
+ "replacement" => "other_apache2" }
+
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
+ allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest)
+ 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
+ before do
+ @current_data["deprecated"] = true
+ end
+
+ it "should warn with info about the replacement" do
+ expect(@knife.ui).to receive(:warn)
+ .with(/.+deprecated.+replaced by other_apache2.+/i)
+ expect(@knife.ui).to receive(:warn)
+ .with(/use --force.+download.+/i)
+ @knife.run
+ end
+ end
+
+ context "when" do
+ before do
+ @cookbook_data = { "version" => @version,
+ "file" => "http://example.com/apache2_#{@version_us}.tgz" }
+ @temp_file = double( path: "/tmp/apache2_#{@version_us}.tgz" )
+ @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
+ end
+
+ context "downloading the latest version" do
+ before do
+ expect(@noauth_rest).to receive(:get)
+ .with(@current_data["latest_version"])
+ .and_return(@cookbook_data)
+ expect(@noauth_rest).to receive(:streaming_request)
+ .with(@cookbook_data["file"])
+ .and_return(@temp_file)
+ end
+
+ context "and it is deprecated and with --force" do
+ before do
+ @current_data["deprecated"] = true
+ @knife.config[:force] = true
+ end
+
+ it "should download the latest version" do
+ expect(@knife.ui).to receive(:warn)
+ .with(/.+deprecated.+replaced by other_apache2.+/i)
+ expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ expect(@stderr.string).to match(/downloading apache2.+version.+#{Regexp.escape(@version)}/i)
+ expect(@stderr.string).to match(/cookbook save.+#{Regexp.escape(@file)}/i)
+ end
+
+ end
+
+ it "should download the latest version" do
+ expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ expect(@stderr.string).to match(/downloading apache2.+version.+#{Regexp.escape(@version)}/i)
+ expect(@stderr.string).to match(/cookbook save.+#{Regexp.escape(@file)}/i)
+ end
+
+ context "with -f or --file" do
+ before do
+ @file = "/opt/chef/cookbooks/apache2.tar.gz"
+ @knife.config[:file] = @file
+ expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
+ end
+
+ it "should download the cookbook to the desired file" do
+ @knife.run
+ expect(@stderr.string).to match(/downloading apache2.+version.+#{Regexp.escape(@version)}/i)
+ expect(@stderr.string).to match(/cookbook save.+#{Regexp.escape(@file)}/i)
+ end
+ end
+
+ it "should provide an accessor to the version" do
+ allow(FileUtils).to receive(:cp).and_return(true)
+ expect(@knife.version).to eq(@version)
+ @knife.run
+ end
+ end
+
+ context "downloading a cookbook of a specific version" do
+ before do
+ @version = "1.0.1"
+ @version_us = @version.tr ".", "_"
+ @cookbook_data = { "version" => @version,
+ "file" => "http://example.com/apache2_#{@version_us}.tgz" }
+ @temp_file = double(path: "/tmp/apache2_#{@version_us}.tgz")
+ @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz")
+ @knife.name_args << @version
+ end
+
+ it "should download the desired version" do
+ expect(@noauth_rest).to receive(:get)
+ .with("#{@cookbook_api_url}/apache2/versions/#{@version_us}")
+ .and_return(@cookbook_data)
+ expect(@noauth_rest).to receive(:streaming_request)
+ .with(@cookbook_data["file"])
+ .and_return(@temp_file)
+ expect(FileUtils).to receive(:cp).with(@temp_file.path, @file)
+ @knife.run
+ expect(@stderr.string).to match(/downloading apache2.+version.+#{Regexp.escape(@version)}/i)
+ expect(@stderr.string).to match(/cookbook save.+#{Regexp.escape(@file)}/i)
+ end
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/supermarket_install_spec.rb b/spec/unit/knife/supermarket_install_spec.rb
new file mode 100644
index 0000000000..03cc5d1992
--- /dev/null
+++ b/spec/unit/knife/supermarket_install_spec.rb
@@ -0,0 +1,202 @@
+#
+# Author:: Steven Danna (<steve@chef.io>)
+# Copyright:: Copyright (c) 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/knife/supermarket_install"
+
+describe Chef::Knife::SupermarketInstall do
+ let(:knife) { Chef::Knife::SupermarketInstall.new }
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+ let(:downloader) { {} }
+ 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 ChefUtils.windows?
+ "C:/tmp/chef"
+ else
+ "/var/tmp/chef"
+ end
+ end
+
+ before(:each) do
+ require "chef/knife/core/cookbook_scm_repo"
+
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ knife.config = {}
+ knife.config[:cookbook_path] = [ install_path ]
+
+ allow(knife).to receive(:stderr).and_return(stderr)
+ allow(knife).to receive(:stdout).and_return(stdout)
+
+ # Assume all external commands would have succeed. :(
+ 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)
+
+ # SupermarketDownload Setup
+ allow(knife).to receive(:download_cookbook_to).and_return(downloader)
+ allow(downloader).to receive(:version) do
+ if knife.name_args.size == 2
+ knife.name_args[1]
+ else
+ "0.3.0"
+ end
+ end
+
+ # Stubs for CookbookSCMRepo
+ allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(repo)
+ end
+
+ describe "run" do
+ it "raises an error if a cookbook name is not provided" do
+ knife.name_args = []
+ expect(knife.ui).to receive(:error).with("Please specify a cookbook to download and install.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if more than two arguments are given" do
+ knife.name_args = %w{foo bar baz}
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is not a version" do
+ knife.name_args = %w{getting-started 1pass}
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is a four-digit version" do
+ knife.name_args = ["getting-started", "0.0.0.1"]
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is a one-digit version" do
+ knife.name_args = %w{getting-started 1}
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "installs the specified version if second argument is a three-digit version" do
+ knife.name_args = ["getting-started", "0.1.0"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1.0")
+ knife.run
+ end
+
+ it "installs the specified version if second argument is a two-digit version" do
+ knife.name_args = ["getting-started", "0.1"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1")
+ knife.run
+ end
+
+ it "installs the latest version if only a cookbook name is given" do
+ knife.name_args = ["getting-started"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
+ knife.run
+ end
+
+ it "does not create/reset git branches if use_current_branch is set" do
+ knife.name_args = ["getting-started"]
+ knife.config[:use_current_branch] = true
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(repo).not_to receive(:prepare_to_import)
+ expect(repo).not_to receive(:reset_to_default_state)
+ knife.run
+ end
+
+ it "does not raise an error if cookbook_path is a string" do
+ knife.config[:cookbook_path] = install_path
+ knife.config[:no_deps] = true
+ knife.name_args = ["getting-started"]
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
+ expect { knife.run }.not_to raise_error
+ end
+ end # end of run
+
+ let(:metadata) { Chef::Cookbook::Metadata.new }
+ let(:rb_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.rb") }
+ let(:json_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.json") }
+
+ describe "preferred_metadata" do
+ before do
+ allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata)
+ allow(File).to receive(:exist?).and_return(false)
+ knife.instance_variable_set(:@cookbook_name, "post-punk-kitchen")
+ knife.instance_variable_set(:@install_path, install_path)
+ end
+
+ it "returns a populated Metadata object if metadata.rb exists" do
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
+ knife.preferred_metadata
+ end
+
+ it "returns a populated Metadata object if metadata.json exists" do
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
+ # expect(IO).to receive(:read).with(json_metadata_path)
+ allow(IO).to receive(:read)
+ expect(metadata).to receive(:from_json)
+ knife.preferred_metadata
+ end
+
+ it "prefers metadata.rb over metadata.json" do
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
+ allow(IO).to receive(:read)
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
+ expect(metadata).not_to receive(:from_json)
+ knife.preferred_metadata
+ end
+
+ it "rasies an error if it finds no metadata file" do
+ expect { knife.preferred_metadata }.to raise_error { |error|
+ expect(error).to be_a(Chef::Exceptions::MetadataNotFound)
+ expect(error.cookbook_name).to eq("post-punk-kitchen")
+ expect(error.install_path).to eq(install_path)
+ }
+ end
+
+ end
+end
diff --git a/spec/unit/knife/supermarket_list_spec.rb b/spec/unit/knife/supermarket_list_spec.rb
new file mode 100644
index 0000000000..a1acccaaaa
--- /dev/null
+++ b/spec/unit/knife/supermarket_list_spec.rb
@@ -0,0 +1,70 @@
+#
+# Author:: Vivek Singh (<vivek.singh@msystechnologies.com>)
+# Copyright:: Copyright (c) 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/supermarket_list"
+require "spec_helper"
+
+describe Chef::Knife::SupermarketList do
+ let(:knife) { described_class.new }
+ let(:noauth_rest) { double("no auth rest") }
+ let(:stdout) { StringIO.new }
+ let(:cookbooks_data) {
+ [
+ { "cookbook_name" => "1password", "cookbook_maintainer" => "jtimberman", "cookbook_description" => "Installs 1password", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/1password" },
+ { "cookbook_name" => "301", "cookbook_maintainer" => "markhuge", "cookbook_description" => "Installs/Configures 301", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/301" },
+ { "cookbook_name" => "3cx", "cookbook_maintainer" => "obay", "cookbook_description" => "Installs/Configures 3cx", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/3cx" },
+ { "cookbook_name" => "7dtd", "cookbook_maintainer" => "gregf", "cookbook_description" => "Installs/Configures the 7 Days To Die server", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/7dtd" },
+ { "cookbook_name" => "7-zip", "cookbook_maintainer" => "sneal", "cookbook_description" => "Installs/Configures the 7-zip file archiver", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/7-zip" },
+ ]
+ }
+
+ let(:response_text) {
+ {
+ "start" => 0,
+ "total" => 5,
+ "items" => cookbooks_data,
+ }
+ }
+
+ describe "run" do
+ before do
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife).to receive(:noauth_rest).and_return(noauth_rest)
+ expect(noauth_rest).to receive(:get).and_return(response_text)
+ knife.configure_chef
+ end
+
+ it "should display all supermarket cookbooks" do
+ knife.run
+ cookbooks_data.each do |item|
+ expect(stdout.string).to match(/#{item["cookbook_name"]}\s/)
+ end
+ end
+
+ describe "with -w or --with-uri" do
+ it "should display the cookbook uris" do
+ knife.config[:with_uri] = true
+ knife.run
+ cookbooks_data.each do |item|
+ expect(stdout.string).to match(/#{item["cookbook_name"]}\s/)
+ expect(stdout.string).to match(/#{item["cookbook"]}\s/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/supermarket_search_spec.rb b/spec/unit/knife/supermarket_search_spec.rb
new file mode 100644
index 0000000000..cba2f615aa
--- /dev/null
+++ b/spec/unit/knife/supermarket_search_spec.rb
@@ -0,0 +1,85 @@
+#
+# Author:: Vivek Singh (<vivek.singh@msystechnologies.com>)
+# Copyright:: Copyright (c) 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/supermarket_search"
+require "spec_helper"
+
+describe Chef::Knife::SupermarketSearch do
+ let(:knife) { described_class.new }
+ let(:noauth_rest) { double("no auth rest") }
+ let(:stdout) { StringIO.new }
+ let(:cookbooks_data) {
+ [
+ { "cookbook_name" => "mysql", "cookbook_maintainer" => "sous-chefs", "cookbook_description" => "Provides mysql_service, mysql_config, and mysql_client resources", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mysql" },
+ { "cookbook_name" => "mw_mysql", "cookbook_maintainer" => "car", "cookbook_description" => "Installs/Configures mw_mysql", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mw_mysql" },
+ { "cookbook_name" => "L7-mysql", "cookbook_maintainer" => "szelcsanyi", "cookbook_description" => "Installs/Configures MySQL server", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/l7-mysql" },
+ { "cookbook_name" => "mysql-sys", "cookbook_maintainer" => "ovaistariq", "cookbook_description" => "Installs the mysql-sys tool. Description of the tool is available here https://github.com/MarkLeith/mysql-sys", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mysql-sys" },
+ { "cookbook_name" => "cg_mysql", "cookbook_maintainer" => "phai", "cookbook_description" => "Installs/Configures mysql with master and slave", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/cg_mysql" },
+ ]
+ }
+
+ let(:response_text) {
+ {
+ "start" => 0,
+ "total" => 5,
+ "items" => cookbooks_data,
+ }
+ }
+
+ let(:empty_response_text) {
+ {
+ "start" => 0,
+ "total" => 0,
+ "items" => [],
+ }
+ }
+
+ describe "run" do
+ before do
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife).to receive(:noauth_rest).and_return(noauth_rest)
+ knife.configure_chef
+ end
+
+ context "when name_args is present" do
+ before do
+ expect(noauth_rest).to receive(:get).and_return(response_text)
+ end
+
+ it "should display cookbooks with given name value" do
+ knife.name_args = ["mysql"]
+ knife.run
+ cookbooks_data.each do |item|
+ expect(stdout.string).to match(/#{item["cookbook_name"]}\s/)
+ end
+ end
+ end
+
+ context "when name_args is empty string" do
+ before do
+ expect(noauth_rest).to receive(:get).and_return(empty_response_text)
+ end
+
+ it "display nothing with name arg empty string" do
+ knife.name_args = [""]
+ knife.run
+ expect(stdout.string).to eq("\n")
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/supermarket_share_spec.rb b/spec/unit/knife/supermarket_share_spec.rb
new file mode 100644
index 0000000000..f6c44f4cd8
--- /dev/null
+++ b/spec/unit/knife/supermarket_share_spec.rb
@@ -0,0 +1,209 @@
+#
+# Author:: Stephen Delano (<stephen@chef.io>)
+# Copyright:: Copyright (c) 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/knife/supermarket_share"
+require "chef/cookbook_uploader"
+require "chef/cookbook_site_streaming_uploader"
+
+describe Chef::Knife::SupermarketShare do
+
+ before(:each) do
+ @knife = Chef::Knife::SupermarketShare.new
+ # Merge default settings in.
+ @knife.merge_configs
+ @knife.name_args = %w{cookbook_name AwesomeSausage}
+
+ @cookbook = Chef::CookbookVersion.new("cookbook_name")
+
+ @cookbook_loader = double("Chef::CookbookLoader")
+ allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(true)
+ allow(@cookbook_loader).to receive(:[]).and_return(@cookbook)
+ allow(Chef::CookbookLoader).to receive(:new).and_return(@cookbook_loader)
+
+ @noauth_rest = double(Chef::ServerAPI)
+ allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest)
+
+ @cookbook_uploader = Chef::CookbookUploader.new("herpderp", rest: "norest")
+ allow(Chef::CookbookUploader).to receive(:new).and_return(@cookbook_uploader)
+ allow(@cookbook_uploader).to receive(:validate_cookbooks).and_return(true)
+ allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return(Dir.mktmpdir)
+
+ allow(@knife).to receive(:shell_out!).and_return(true)
+ @stdout = StringIO.new
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+
+ before(:each) do
+ allow(@knife).to receive(:do_upload).and_return(true)
+ @category_response = {
+ "name" => "cookbook_name",
+ "category" => "Testing Category",
+ }
+ @bad_category_response = {
+ "error_code" => "NOT_FOUND",
+ "error_messages" => [
+ "Resource does not exist.",
+ ],
+ }
+ end
+
+ it "should set true to config[:dry_run] as default" do
+ expect(@knife.config[:dry_run]).to be_falsey
+ end
+
+ 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)
+ end
+
+ it "should not fail when given only 1 argument and can 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(@category_response)
+ expect(@knife).to receive(:do_upload)
+ @knife.run
+ end
+
+ 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") { raise Net::HTTPClientException.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
+ @knife.name_args = ["cookbook_name"]
+ expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" }
+ expect(@knife.ui).to receive(:fatal)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
+ it "should check if the cookbook exists" do
+ expect(@cookbook_loader).to receive(:cookbook_exists?)
+ @knife.run
+ end
+
+ it "should exit and log to error if the cookbook doesn't exist" do
+ allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(false)
+ expect(@knife.ui).to receive(:error)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
+ if File.exist?("/usr/bin/gnutar") || File.exist?("/bin/gnutar")
+ it "should use gnutar to make a tarball of the cookbook" do
+ expect(@knife).to receive(:shell_out!) do |args|
+ expect(args.to_s).to match(/gnutar -czf/)
+ end
+ @knife.run
+ end
+ else
+ it "should make a tarball of the cookbook" do
+ expect(@knife).to receive(:shell_out!) do |args|
+ expect(args.to_s).to match(/tar -czf/)
+ end
+ @knife.run
+ end
+ end
+
+ it "should exit and log to error when the tarball creation fails" do
+ allow(@knife).to receive(:shell_out!).and_raise(Chef::Exceptions::Exec)
+ expect(@knife.ui).to receive(:error)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
+ it "should upload the cookbook and clean up the tarball" do
+ expect(@knife).to receive(:do_upload)
+ expect(FileUtils).to receive(:rm_rf)
+ @knife.run
+ end
+
+ context "when the --dry-run flag is specified" do
+ before do
+ allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return("/var/tmp/dummy")
+ @knife.config = { dry_run: true }
+ allow(@knife).to receive_message_chain(:shell_out!, :stdout).and_return("file")
+ end
+
+ it "should list files in the tarball" do
+ allow(@knife).to receive(:tar_cmd).and_return("footar")
+ expect(@knife).to receive(:shell_out!).with("footar -czf #{@cookbook.name}.tgz #{@cookbook.name}", { cwd: "/var/tmp/dummy" })
+ expect(@knife).to receive(:shell_out!).with("footar -tzf #{@cookbook.name}.tgz", { cwd: "/var/tmp/dummy" })
+ @knife.run
+ end
+
+ it "does not upload the cookbook" do
+ allow(@knife).to receive(:shell_out!).and_return(true)
+ expect(@knife).not_to receive(:do_upload)
+ @knife.run
+ end
+ end
+ end
+
+ describe "do_upload" do
+
+ before(:each) do
+ @upload_response = double("Net::HTTPResponse")
+ allow(Chef::CookbookSiteStreamingUploader).to receive(:post).and_return(@upload_response)
+
+ @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(File).to receive(:open).and_return(true)
+ end
+
+ it 'should post the cookbook to "https://supermarket.chef.io"' do
+ response_text = Chef::JSONCompat.to_json({ uri: "https://supermarket.chef.io/cookbooks/cookbook_name" })
+ allow(@upload_response).to receive(:body).and_return(response_text)
+ allow(@upload_response).to receive(:code).and_return(201)
+ expect(Chef::CookbookSiteStreamingUploader).to receive(:post).with(/supermarket\.chef\.io/, anything, anything, anything)
+ @knife.run
+ end
+
+ it "should alert the user when a version already exists" do
+ response_text = Chef::JSONCompat.to_json({ error_messages: ["Version already exists"] })
+ allow(@upload_response).to receive(:body).and_return(response_text)
+ allow(@upload_response).to receive(:code).and_return(409)
+ expect { @knife.run }.to raise_error(SystemExit)
+ expect(@stderr.string).to match(/ERROR(.+)cookbook already exists/)
+ end
+
+ it "should pass any errors on to the user" do
+ response_text = Chef::JSONCompat.to_json({ error_messages: ["You're holding it wrong"] })
+ allow(@upload_response).to receive(:body).and_return(response_text)
+ allow(@upload_response).to receive(:code).and_return(403)
+ expect { @knife.run }.to raise_error(SystemExit)
+ expect(@stderr.string).to match("ERROR(.*)You're holding it wrong")
+ end
+
+ it "should print the body if no errors are exposed on failure" 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.run }.to raise_error(SystemExit)
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/supermarket_unshare_spec.rb b/spec/unit/knife/supermarket_unshare_spec.rb
new file mode 100644
index 0000000000..8ae4d03cb5
--- /dev/null
+++ b/spec/unit/knife/supermarket_unshare_spec.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Stephen Delano (<stephen@chef.io>)
+# Author:: Tim Hinderliter (<tim@chef.io>)
+# Copyright:: Copyright (c) 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/knife/supermarket_unshare"
+
+describe Chef::Knife::SupermarketUnshare do
+
+ before(:each) do
+ @knife = Chef::Knife::SupermarketUnshare.new
+ @knife.name_args = ["cookbook_name"]
+ allow(@knife).to receive(:confirm).and_return(true)
+
+ @rest = double("Chef::ServerAPI")
+ allow(@rest).to receive(:delete).and_return(true)
+ allow(@knife).to receive(:rest).and_return(@rest)
+ @stdout = StringIO.new
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ end
+
+ describe "run" do
+
+ describe "with no cookbook argument" do
+ it "should print the usage and exit" do
+ @knife.name_args = []
+ expect(@knife.ui).to receive(:fatal)
+ expect(@knife).to receive(:show_usage)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+ end
+
+ it "should confirm you want to unshare the cookbook" do
+ expect(@knife).to receive(:confirm)
+ @knife.run
+ end
+
+ it "should send a delete request to the cookbook site" do
+ expect(@rest).to receive(:delete)
+ @knife.run
+ end
+
+ it "should log an error and exit when forbidden" do
+ exception = double('403 "Forbidden"', code: "403")
+ allow(@rest).to receive(:delete).and_raise(Net::HTTPClientException.new('403 "Forbidden"', exception))
+ expect(@knife.ui).to receive(:error)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
+ it "should re-raise any non-forbidden errors on delete" do
+ exception = double('500 "Application Error"', code: "500")
+ allow(@rest).to receive(:delete).and_raise(Net::HTTPClientException.new('500 "Application Error"', exception))
+ expect { @knife.run }.to raise_error(Net::HTTPClientException)
+ end
+
+ it "should log a success message" do
+ expect(@knife.ui).to receive(:info)
+ @knife.run
+ end
+
+ end
+
+end
diff --git a/spec/unit/knife/tag_create_spec.rb b/spec/unit/knife/tag_create_spec.rb
index 6a3ced3f5b..a1a4923871 100644
--- a/spec/unit/knife/tag_create_spec.rb
+++ b/spec/unit/knife/tag_create_spec.rb
@@ -17,7 +17,7 @@ describe Chef::Knife::TagCreate do
it "can create tags on a node" do
@knife.run
expect(@node.tags).to eq(["happytag"])
- expect(@stderr.string).to match /created tags happytag.+node webmonkey.example.com/i
+ expect(@stderr.string).to match(/created tags happytag.+node webmonkey.example.com/i)
end
end
end
diff --git a/spec/unit/knife/tag_delete_spec.rb b/spec/unit/knife/tag_delete_spec.rb
index 5c932706af..4201196de0 100644
--- a/spec/unit/knife/tag_delete_spec.rb
+++ b/spec/unit/knife/tag_delete_spec.rb
@@ -19,7 +19,7 @@ describe Chef::Knife::TagDelete do
expect(@node.tags).to eq(%w{sadtag happytag})
@knife.run
expect(@node.tags).to eq(["happytag"])
- expect(@stderr.string).to match /deleted.+sadtag/i
+ expect(@stderr.string).to match(/deleted.+sadtag/i)
end
end
end
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index 07d72fd05a..be3d2fd99c 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Steven Danna (<steve@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,25 +38,6 @@ describe Chef::Knife::UserCreate do
allow(knife.ui).to receive(:warn)
end
- # delete this once OSC11 support is gone
- context "when only one name_arg is passed" do
- before do
- knife.name_args = ["some_user"]
- allow(knife).to receive(:run_osc_11_user_create).and_raise(SystemExit)
- end
-
- it "displays the osc warning" do
- expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "calls knife osc_user create" do
- expect(knife).to receive(:run_osc_11_user_create)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- end
-
context "when USERNAME isn't specified" do
# from spec/support/shared/unit/knife_shared.rb
it_should_behave_like "mandatory field missing" do
@@ -65,17 +46,6 @@ describe Chef::Knife::UserCreate do
end
end
- # uncomment once OSC11 support is gone,
- # pending doesn't work for shared_examples_for by default
- #
- # context "when DISPLAY_NAME isn't specified" do
- # # from spec/support/shared/unit/knife_shared.rb
- # it_should_behave_like "mandatory field missing" do
- # let(:name_args) { ['some_user'] }
- # let(:fieldname) { 'display name' }
- # end
- # end
-
context "when FIRST_NAME isn't specified" do
# from spec/support/shared/unit/knife_shared.rb
it_should_behave_like "mandatory field missing" do
@@ -142,7 +112,7 @@ describe Chef::Knife::UserCreate do
it "prints a relevant error message" do
expect { knife.run }.to raise_error(SystemExit)
- expect(stderr.string).to match /You cannot pass --user-key and --prevent-keygen/
+ expect(stderr.string).to match(/You cannot pass --user-key and --prevent-keygen/)
end
end
diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb
index 0f71b39a41..959d792b9e 100644
--- a/spec/unit/knife/user_delete_spec.rb
+++ b/spec/unit/knife/user_delete_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,27 +32,8 @@ describe Chef::Knife::UserDelete do
allow(knife.ui).to receive(:stdout).and_return(stdout)
end
- # delete this once OSC11 support is gone
- context "when the username field is not supported by the server" do
- before do
- allow(knife).to receive(:run_osc_11_user_delete).and_raise(SystemExit)
- allow(user).to receive(:username).and_return(nil)
- end
-
- it "displays the osc warning" do
- expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "forwards the command to knife osc_user edit" do
- expect(knife).to receive(:run_osc_11_user_delete)
- expect { knife.run }.to raise_error(SystemExit)
- end
- end
-
it "deletes the user" do
- #expect(knife).to receive(:delete_object).with(Chef::UserV1, 'my_user')
- expect(knife).to receive(:delete_object).with("my_user")
+ expect(knife).to receive(:delete_object).with(Chef::UserV1, "my_user")
knife.run
end
diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb
index 18ade54068..54a44890e0 100644
--- a/spec/unit/knife/user_edit_spec.rb
+++ b/spec/unit/knife/user_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,24 +32,6 @@ describe Chef::Knife::UserEdit do
knife.config[:disable_editing] = true
end
- # delete this once OSC11 support is gone
- context "when the username field is not supported by the server" do
- before do
- allow(knife).to receive(:run_osc_11_user_edit).and_raise(SystemExit)
- allow(Chef::UserV1).to receive(:load).and_return({ "username" => nil })
- end
-
- it "displays the osc warning" do
- expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "forwards the command to knife osc_user edit" do
- expect(knife).to receive(:run_osc_11_user_edit)
- expect { knife.run }.to raise_error(SystemExit)
- end
- end
-
it "loads and edits the user" do
data = { "username" => "my_user" }
allow(Chef::UserV1).to receive(:load).with("my_user").and_return(data)
diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb
index bb135dbe03..21c07f3fb1 100644
--- a/spec/unit/knife/user_list_spec.rb
+++ b/spec/unit/knife/user_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb
index d650ff9fb8..481415e432 100644
--- a/spec/unit/knife/user_reregister_spec.rb
+++ b/spec/unit/knife/user_reregister_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::Knife::UserReregister do
let(:knife) { Chef::Knife::UserReregister.new }
- let(:user_mock) { double("user_mock", :private_key => "private_key") }
+ let(:user_mock) { double("user_mock", private_key: "private_key") }
let(:stdout) { StringIO.new }
before do
@@ -32,24 +32,6 @@ describe Chef::Knife::UserReregister do
allow(user_mock).to receive(:username).and_return("a_user")
end
- # delete this once OSC11 support is gone
- context "when the username field is not supported by the server" do
- before do
- allow(knife).to receive(:run_osc_11_user_reregister).and_raise(SystemExit)
- allow(user_mock).to receive(:username).and_return(nil)
- end
-
- it "displays the osc warning" do
- expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "forwards the command to knife osc_user edit" do
- expect(knife).to receive(:run_osc_11_user_reregister)
- expect { knife.run }.to raise_error(SystemExit)
- end
- end
-
it "prints usage and exits when a user name is not provided" do
knife.name_args = []
expect(knife).to receive(:show_usage)
diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb
index 3a38161b34..198b9352f3 100644
--- a/spec/unit/knife/user_show_spec.rb
+++ b/spec/unit/knife/user_show_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,25 +31,6 @@ describe Chef::Knife::UserShow do
allow(knife.ui).to receive(:stdout).and_return(stdout)
end
- # delete this once OSC11 support is gone
- context "when the username field is not supported by the server" do
- before do
- allow(knife).to receive(:run_osc_11_user_show).and_raise(SystemExit)
- allow(Chef::UserV1).to receive(:load).with("my_user").and_return(user_mock)
- allow(user_mock).to receive(:username).and_return(nil)
- end
-
- it "displays the osc warning" do
- expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
- expect { knife.run }.to raise_error(SystemExit)
- end
-
- it "forwards the command to knife osc_user edit" do
- expect(knife).to receive(:run_osc_11_user_show)
- expect { knife.run }.to raise_error(SystemExit)
- end
- end
-
it "loads and displays the user" do
expect(Chef::UserV1).to receive(:load).with("my_user").and_return(user_mock)
expect(knife).to receive(:format_for_display).with(user_mock)
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index f0ec45d59a..88f36a3973 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,9 +35,9 @@ describe Chef::Knife do
let(:config_loader) do
instance_double("WorkstationConfigLoader",
- load: nil, no_config_found?: false,
- config_location: config_location,
- :chef_config_dir => "/etc/chef")
+ load: nil, no_config_found?: false,
+ config_location: config_location,
+ chef_config_dir: "/etc/chef")
end
before(:each) do
@@ -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)
@@ -54,7 +55,7 @@ describe Chef::Knife do
allow(knife.ui).to receive(:print)
allow(Chef::Log).to receive(:init)
allow(Chef::Log).to receive(:level)
- [:debug, :info, :warn, :error, :crit].each do |level_sym|
+ %i{debug info warn error crit}.each do |level_sym|
allow(Chef::Log).to receive(level_sym)
end
allow(Chef::Knife).to receive(:puts)
@@ -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
@@ -165,7 +166,8 @@ describe Chef::Knife do
"X-Chef-Version" => Chef::VERSION,
"Host" => "api.opscode.piab",
"X-REMOTE-REQUEST-ID" => request_id,
- } end
+ }
+ end
let(:request_id) { "1234" }
@@ -204,7 +206,7 @@ describe Chef::Knife do
KnifeSpecs.send :remove_const, :TestYourself
end
Kernel.load(File.join(CHEF_SPEC_DATA, "knife_subcommand", "test_yourself.rb"))
- Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) }
+ Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.is_a?(Class) }
end
it "confirms that the headers include X-Remote-Request-Id" do
@@ -219,15 +221,15 @@ describe Chef::Knife do
KnifeSpecs.send :remove_const, :TestYourself
end
Kernel.load(File.join(CHEF_SPEC_DATA, "knife_subcommand", "test_yourself.rb"))
- Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) }
+ Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.is_a?(Class) }
end
it "merges the global knife CLI options" do
extra_opts = {}
- extra_opts[:editor] = { :long => "--editor EDITOR",
- :description => "Set the editor to use for interactive commands",
- :short => "-e EDITOR",
- :default => "/usr/bin/vim" }
+ extra_opts[:editor] = { long: "--editor EDITOR",
+ description: "Set the editor to use for interactive commands",
+ short: "-e EDITOR",
+ default: "/usr/bin/vim" }
# there is special hackery to return the subcommand instance going on here.
command = Chef::Knife.run(%w{test yourself}, extra_opts)
@@ -279,11 +281,36 @@ describe Chef::Knife do
expect(other_deps_loaded).to be_truthy
end
+ describe "working with unmerged configuration in #config_source" do
+ let(:command) { KnifeSpecs::TestYourself.new([]) }
+
+ before do
+ KnifeSpecs::TestYourself.option(:opt_with_default,
+ short: "-D VALUE",
+ default: "default-value")
+ end
+ # This supports a use case used by plugins, where the pattern
+ # seems to follow:
+ # cmd = KnifeCommand.new
+ # cmd.config[:config_key] = value
+ # cmd.run
+ #
+ # This bypasses Knife::run and the `merge_configs` call it
+ # performs - config_source should break when that happens.
+ context "when config is fed in directly without a merge" do
+ it "retains the value but returns nil as a config source" do
+ command.config[:test1] = "value"
+ expect(command.config[:test1]).to eq "value"
+ expect(command.config_source(:test1)).to eq nil
+ end
+ end
+
+ end
describe "merging configuration options" do
before do
KnifeSpecs::TestYourself.option(:opt_with_default,
- :short => "-D VALUE",
- :default => "default-value")
+ short: "-D VALUE",
+ default: "default-value")
end
it "sets the default log_location to STDERR for Chef::Log warnings" do
@@ -298,29 +325,50 @@ describe Chef::Knife do
expect(Chef::Config[:log_level]).to eql(:warn)
end
- it "prefers the default value if no config or command line value is present" do
- knife_command = KnifeSpecs::TestYourself.new([]) #empty argv
+ it "prefers the default value from option definition if no config or command line value is present and reports the source as default" do
+ knife_command = KnifeSpecs::TestYourself.new([]) # empty argv
knife_command.configure_chef
expect(knife_command.config[:opt_with_default]).to eq("default-value")
+ expect(knife_command.config_source(:opt_with_default)).to eq(:cli_default)
end
- it "prefers a value in Chef::Config[:knife] to the default" do
+ it "prefers a value in Chef::Config[:knife] to the default and reports the source as config" do
Chef::Config[:knife][:opt_with_default] = "from-knife-config"
- knife_command = KnifeSpecs::TestYourself.new([]) #empty argv
+ knife_command = KnifeSpecs::TestYourself.new([]) # empty argv
knife_command.configure_chef
expect(knife_command.config[:opt_with_default]).to eq("from-knife-config")
+ expect(knife_command.config_source(:opt_with_default)).to eq(:config)
end
- it "prefers a value from command line over Chef::Config and the default" do
- Chef::Config[:knife][:opt_with_default] = "from-knife-config"
+ it "prefers a value from command line over Chef::Config and the default and reports the source as CLI" do
knife_command = KnifeSpecs::TestYourself.new(["-D", "from-cli"])
knife_command.configure_chef
expect(knife_command.config[:opt_with_default]).to eq("from-cli")
+ expect(knife_command.config_source(:opt_with_default)).to eq(:cli)
end
it "merges `listen` config to Chef::Config" do
- Chef::Knife.run(%w{test yourself --no-listen}, Chef::Application::Knife.options)
+ knife_command = Chef::Knife.run(%w{test yourself --no-listen}, Chef::Application::Knife.options)
expect(Chef::Config[:listen]).to be(false)
+ expect(knife_command.config_source(:listen)).to eq(:cli)
+ end
+
+ it "merges Chef::Config[:knife] values into the config hash even if they have no cli keys" do
+ Chef::Config[:knife][:opt_with_no_cli_key] = "from-knife-config"
+ knife_command = KnifeSpecs::TestYourself.new([]) # empty argv
+ knife_command.configure_chef
+ expect(knife_command.config[:opt_with_no_cli_key]).to eq("from-knife-config")
+ expect(knife_command.config_source(:opt_with_no_cli_key)).to eq(:config)
+ end
+
+ it "merges Chef::Config[:knife] default values into the config hash even if they have no cli keys" do
+ Chef::Config.config_context :knife do
+ default :opt_with_no_cli_key, "from-knife-default"
+ end
+ knife_command = KnifeSpecs::TestYourself.new([]) # empty argv
+ knife_command.configure_chef
+ expect(knife_command.config[:opt_with_no_cli_key]).to eq("from-knife-default")
+ expect(knife_command.config_source(:opt_with_no_cli_key)).to eq(:config_default)
end
context "verbosity is one" do
@@ -329,8 +377,9 @@ describe Chef::Knife do
before do
knife.config[:verbosity] = 1
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)
+ 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
@@ -342,25 +391,56 @@ describe Chef::Knife do
end
end
- it "does not humanize the exception if Chef::Config[:verbosity] is two" do
- Chef::Config[:verbosity] = 2
- allow(knife).to receive(:run).and_raise(Exception)
- expect(knife).not_to receive(:humanize_exception)
- expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
+ # -VV (2) is debug, -VVV (3) is trace
+ [ 2, 3 ].each do |verbosity|
+ it "does not humanize the exception if Chef::Config[:verbosity] is #{verbosity}" do
+ Chef::Config[:verbosity] = verbosity
+ allow(knife).to receive(:run).and_raise(Exception)
+ expect(knife).not_to receive(:humanize_exception)
+ expect { knife.run_with_pretty_exceptions }.to raise_error(Exception)
+ end
end
end
- end
- describe "when first created" do
+ describe "setting arbitrary configuration with --config-option" do
- let(:knife) { KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming}) }
+ let(:stdout) { StringIO.new }
- before do
- unless KnifeSpecs.const_defined?(:TestYourself)
- Kernel.load(File.join(CHEF_SPEC_DATA, "knife_subcommand", "test_yourself.rb"))
+ 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
+
+ let(:knife) {
+ Kernel.load "spec/data/knife_subcommand/test_yourself.rb"
+ KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming})
+ }
+
it "it parses the options passed to it" do
expect(knife.config[:scro]).to eq("scrogramming")
end
@@ -370,6 +450,8 @@ describe Chef::Knife do
end
it "does not have lazy dependencies loaded" do
+ skip "unstable with randomization... prolly needs more isolation"
+
expect(knife.class.test_deps_loaded).not_to be_truthy
end
end
@@ -390,8 +472,8 @@ describe Chef::Knife do
it "formats 401s nicely" do
response = Net::HTTPUnauthorized.new("1.1", "401", "Unauthorized")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no syncronize your clock?"))
- allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "y u no syncronize your clock?"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.new("401 Unauthorized", response))
knife.run_with_pretty_exceptions
expect(stderr.string).to match(/ERROR: Failed to authenticate to/)
expect(stderr.string).to match(/Response: y u no syncronize your clock\?/)
@@ -400,32 +482,54 @@ describe Chef::Knife do
it "formats 403s nicely" do
response = Net::HTTPForbidden.new("1.1", "403", "Forbidden")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- 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(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "y u no administrator"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.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{Response: y u no administrator})
+ expect(stderr.string).to match(/ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action/)
+ expect(stderr.string).to match(/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::HTTPClientException.new("403 Forbidden", response))
+ allow(knife).to receive(:username).and_return("sadpanda")
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(/ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action/)
+ expect(stderr.string).to match(/ERROR: There are proxy servers configured, your server url may need to be added to NO_PROXY./)
+ expect(stderr.string).to match(/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.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u search wrong"))
- allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "y u search wrong"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.new("400 Bad Request", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: The data in your request was invalid})
- expect(stderr.string).to match(%r{Response: y u search wrong})
+ expect(stderr.string).to match(/ERROR: The data in your request was invalid/)
+ expect(stderr.string).to match(/Response: y u search wrong/)
end
it "formats 404s nicely" do
response = Net::HTTPNotFound.new("1.1", "404", "Not Found")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nothing to see here"))
- allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "nothing to see here"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.new("404 Not Found", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: The object you are looking for could not be found})
- expect(stderr.string).to match(%r{Response: nothing to see here})
+ expect(stderr.string).to match(/ERROR: The object you are looking for could not be found/)
+ expect(stderr.string).to match(/Response: nothing to see here/)
end
it "formats 406s (non-supported API version error) nicely" do
@@ -433,63 +537,63 @@ describe Chef::Knife do
response.instance_variable_set(:@read, true) # I hate you, net/http.
# set the header
- response["x-ops-server-api-version"] = Chef::JSONCompat.to_json(:min_version => "0", :max_version => "1", :request_version => "10000000")
+ response["x-ops-server-api-version"] = Chef::JSONCompat.to_json(min_version: "0", max_version: "1", request_version: "10000000")
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
- allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("406 Not Acceptable", response))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "sad trombone"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.new("406 Not Acceptable", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to include("The request that Knife sent was using API version 10000000")
- expect(stderr.string).to include("The Chef server you sent the request to supports a min API verson of 0 and a max API version of 1")
- expect(stderr.string).to include("Please either update your Chef client or server to be a compatible set")
+ expect(stderr.string).to match(/The request that .* sent was using API version 10000000./)
+ expect(stderr.string).to match(/The server you sent the request to supports a min API version of 0 and a max API version of 1./)
+ expect(stderr.string).to match(/Please either update your .* or the server to be a compatible set./)
end
it "formats 500s nicely" do
response = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "sad trombone"))
allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: internal server error})
- expect(stderr.string).to match(%r{Response: sad trombone})
+ expect(stderr.string).to match(/ERROR: internal server error/)
+ expect(stderr.string).to match(/Response: sad trombone/)
end
it "formats 502s nicely" do
response = Net::HTTPBadGateway.new("1.1", "502", "Bad Gateway")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sadder trombone"))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "sadder trombone"))
allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: bad gateway})
- expect(stderr.string).to match(%r{Response: sadder trombone})
+ expect(stderr.string).to match(/ERROR: bad gateway/)
+ expect(stderr.string).to match(/Response: sadder trombone/)
end
it "formats 503s nicely" do
response = Net::HTTPServiceUnavailable.new("1.1", "503", "Service Unavailable")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "saddest trombone"))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "saddest trombone"))
allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: Service temporarily unavailable})
- expect(stderr.string).to match(%r{Response: saddest trombone})
+ expect(stderr.string).to match(/ERROR: Service temporarily unavailable/)
+ expect(stderr.string).to match(/Response: saddest trombone/)
end
it "formats other HTTP errors nicely" do
response = Net::HTTPPaymentRequired.new("1.1", "402", "Payment Required")
response.instance_variable_set(:@read, true) # I hate you, net/http.
- allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nobugfixtillyoubuy"))
- allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response))
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(error: "nobugfixtillyoubuy"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPClientException.new("402 Payment Required", response))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: Payment Required})
- expect(stderr.string).to match(%r{Response: nobugfixtillyoubuy})
+ expect(stderr.string).to match(/ERROR: Payment Required/)
+ expect(stderr.string).to match(/Response: nobugfixtillyoubuy/)
end
it "formats NameError and NoMethodError nicely" do
allow(knife).to receive(:run).and_raise(NameError.new("Undefined constant FUUU"))
knife.run_with_pretty_exceptions
- expect(stderr.string).to match(%r{ERROR: knife encountered an unexpected error})
- expect(stderr.string).to match(%r{This may be a bug in the 'knife' knife command or plugin})
- expect(stderr.string).to match(%r{Exception: NameError: Undefined constant FUUU})
+ expect(stderr.string).to match(/ERROR: .* encountered an unexpected error/)
+ expect(stderr.string).to match(/This may be a bug in the 'knife' .* command or plugin/)
+ expect(stderr.string).to match(/Exception: NameError: Undefined constant FUUU/)
end
it "formats missing private key errors nicely" do
@@ -497,7 +601,7 @@ describe Chef::Knife do
allow(knife).to receive(:api_key).and_return("/home/root/.chef/no-key-here.pem")
knife.run_with_pretty_exceptions
expect(stderr.string).to match(%r{ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem})
- expect(stderr.string).to match(%r{Check your configuration file and ensure that your private key is readable})
+ expect(stderr.string).to match(/Check your configuration file and ensure that your private key is readable/)
end
it "formats connection refused errors nicely" do
@@ -506,8 +610,8 @@ describe Chef::Knife do
# Errno::ECONNREFUSED message differs by platform
# *nix = Errno::ECONNREFUSED: Connection refused
# win32: Errno::ECONNREFUSED: No connection could be made because the target machine actively refused it.
- expect(stderr.string).to match(%r{ERROR: Network Error: .* - y u no shut up})
- expect(stderr.string).to match(%r{Check your knife configuration and network settings})
+ expect(stderr.string).to match(/ERROR: Network Error: .* - y u no shut up/)
+ expect(stderr.string).to match(/Check your .* configuration and network settings/)
end
it "formats SSL errors nicely and suggests to use `knife ssl check` and `knife ssl fetch`" do
@@ -516,13 +620,13 @@ describe Chef::Knife do
knife.run_with_pretty_exceptions
- expected_message = <<-MSG
-ERROR: Could not establish a secure connection to the server.
-Use `knife ssl check` to troubleshoot your SSL configuration.
-If your Chef Server uses a self-signed certificate, you can use
-`knife ssl fetch` to make knife trust the server's certificates.
-MSG
- expect(stderr.string).to include(expected_message)
+ expected_message = <<~MSG
+ ERROR: Could not establish a secure connection to the server.
+ Use `.* ssl check` to troubleshoot your SSL configuration.
+ If your server uses a self-signed certificate, you can use
+ `.* ssl fetch` to make .* trust the server's certificates.
+ MSG
+ expect(stderr.string).to match(expected_message)
end
end
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..1daacb89da 100644
--- a/spec/unit/log/syslog_spec.rb
+++ b/spec/unit/log/syslog_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,33 +17,27 @@
#
require "spec_helper"
-require "chef"
-describe "Chef::Log::Syslog", :unix_only => true do
+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))
- @old_log_level = Chef::Log.level
Chef::Log.level = :info
- @old_loggers = Chef::Log.loggers
- Chef::Log.use_log_devices([syslog])
- end
-
- after do
- Chef::Log.level = @old_log_level
- Chef::Log.use_log_devices(@old_loggers)
end
it "should send message with severity info to syslog." do
- expect(syslog).to receive(:info).with("*** Chef 12.4.0.dev.0 ***")
- Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ expect(syslog).to receive(:add).with(1, "*** Chef 12.4.0.dev.0 ***", nil)
+ expect {
+ Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ }.not_to output.to_stderr
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.")
- Chef::Log.warn("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 instead.", nil)
+ expect {
+ Chef::Log.warn("No config file found or specified on command line. Using command line options instead.")
+ }.not_to output.to_stderr
end
it "should fallback into send message with severity info to syslog when wrong format." do
diff --git a/spec/unit/log/winevt_spec.rb b/spec/unit/log/winevt_spec.rb
index d5d452159b..1130b77575 100644
--- a/spec/unit/log/winevt_spec.rb
+++ b/spec/unit/log/winevt_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (jdm@chef.io)
# Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,33 +19,40 @@
require "spec_helper"
-describe Chef::Log::WinEvt do
+describe Chef::Log::WinEvt, :windows_only do
let(:evtlog) { instance_double("Win32::EventLog") }
let(:winevt) { Chef::Log::WinEvt.new(evtlog) }
let(:app) { Chef::Application.new }
before do
-
Chef::Log.init(MonoLogger.new(winevt))
- @old_log_level = Chef::Log.level
Chef::Log.level = :info
- @old_loggers = Chef::Log.loggers
- Chef::Log.use_log_devices([winevt])
- end
-
- after do
- Chef::Log.level = @old_log_level
- Chef::Log.use_log_devices(@old_loggers)
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 ***")
- Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ expect(evtlog).to receive(:report_event).with(
+ event_type: ::Win32::EventLog::INFO_TYPE,
+ source: Chef::Log::WinEvt::SOURCE,
+ event_id: Chef::Log::WinEvt::INFO_EVENT_ID,
+ data: ["*** Chef 12.4.0.dev.0 ***"]
+ )
+
+ expect {
+ Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ }.not_to output.to_stderr
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.")
- Chef::Log.warn("No config file found or specified on command line, using command line options.")
+ expect(evtlog).to receive(:report_event).with(
+ event_type: ::Win32::EventLog::WARN_TYPE,
+ source: Chef::Log::WinEvt::SOURCE,
+ event_id: Chef::Log::WinEvt::WARN_EVENT_ID,
+ data: ["No config file found or specified on command line. Using command line options instead."]
+ )
+
+ expect {
+ Chef::Log.warn("No config file found or specified on command line. Using command line options instead.")
+ }.not_to output.to_stderr
end
it "should fallback into send message with severity info to Windows Event Log when wrong format." do
diff --git a/spec/unit/log_spec.rb b/spec/unit/log_spec.rb
deleted file mode 100644
index 929a24a95b..0000000000
--- a/spec/unit/log_spec.rb
+++ /dev/null
@@ -1,24 +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 "tempfile"
-require "logger"
-require "spec_helper"
-
-describe Chef::Log do
-end
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 0689d99647..ac2c95d7e7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,14 +41,11 @@ describe "LWRP" do
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
@@ -143,7 +140,7 @@ describe "LWRP" do
before do
@tmpdir = Dir.mktmpdir("lwrp_test")
@lwrp_path = File.join(@tmpdir, "foo.rb")
- content = IO.read(File.expand_path("../../data/lwrp/resources/foo.rb", __FILE__))
+ content = IO.read(File.expand_path("../data/lwrp/resources/foo.rb", __dir__))
IO.write(@lwrp_path, content)
Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
@original_resource = Chef::ResourceResolver.resolve(:lwrp_foo)
@@ -155,7 +152,7 @@ describe "LWRP" do
context "And the LWRP is asked to load again, this time with different code" do
before do
- content = IO.read(File.expand_path("../../data/lwrp_override/resources/foo.rb", __FILE__))
+ content = IO.read(File.expand_path("../data/lwrp_override/resources/foo.rb", __dir__))
IO.write(@lwrp_path, content)
Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
end
@@ -172,7 +169,7 @@ describe "LWRP" do
describe "Lightweight Chef::Resource" do
before do
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
+ Dir[File.expand_path(File.join(__dir__, "..", "data", "lwrp", "resources", "*"))].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -202,7 +199,7 @@ describe "LWRP" do
end
it "should create a method for each attribute" do
- expect(get_lwrp(:lwrp_foo).new("blah").methods.map { |m| m.to_sym }).to include(:monkey)
+ expect(get_lwrp(:lwrp_foo).new("blah").methods.map(&:to_sym)).to include(:monkey)
end
it "should build attribute methods that respect validation rules" do
@@ -214,7 +211,7 @@ describe "LWRP" do
node.normal[:penguin_name] = "jackass"
run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new, @events)
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources_with_default_attributes", "*"))].each do |file|
+ Dir[File.expand_path(File.join(__dir__, "..", "data", "lwrp", "resources_with_default_attributes", "*"))].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, run_context)
end
@@ -246,8 +243,8 @@ describe "LWRP" do
let(:klass) do
Class.new(Chef::Resource::LWRPBase) do
self.resource_name = :sample_resource
- attribute :food, :default => lazy { "BACON!" * 3 }
- attribute :drink, :default => lazy { |r| "Drink after #{r.food}!" }
+ attribute :food, default: lazy { "BACON!" * 3 }
+ attribute :drink, default: lazy { |r| "Drink after #{r.food}!" }
end
end
@@ -267,12 +264,12 @@ describe "LWRP" do
let(:lwrp) do
Class.new(Chef::Resource::LWRPBase) do
actions :eat, :sleep
- default_action [:eat, :sleep]
+ default_action %i{eat sleep}
end
end
it "returns the array of default actions" do
- expect(lwrp.default_action).to eq([:eat, :sleep])
+ expect(lwrp.default_action).to eq(%i{eat sleep})
end
end
@@ -290,7 +287,7 @@ describe "LWRP" do
end
it "delegates #actions to the parent" do
- expect(child.actions).to eq([:nothing, :eat, :sleep])
+ expect(child.actions).to eq(%i{nothing eat sleep})
end
it "delegates #default_action to the parent" do
@@ -307,7 +304,7 @@ describe "LWRP" do
end
it "does not delegate #actions to the parent" do
- expect(child.actions).to eq([:nothing, :dont_eat, :dont_sleep])
+ expect(child.actions).to eq(%i{nothing dont_eat dont_sleep})
end
it "does not delegate #default_action to the parent" do
@@ -324,15 +321,8 @@ 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])
+ expect(child.actions).to eq(%i{nothing eat sleep drink})
end
end
end
@@ -340,40 +330,40 @@ describe "LWRP" do
describe "when actions is set to an array" do
let(:resource_class) do
Class.new(Chef::Resource::LWRPBase) do
- actions [ :eat, :sleep ]
+ actions %i{eat sleep}
end
end
let(:resource) do
resource_class.new("blah")
end
it "actions includes those actions" do
- expect(resource_class.actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource_class.actions).to eq %i{nothing eat sleep}
end
it "allowed_actions includes those actions" do
- expect(resource_class.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource_class.allowed_actions).to eq %i{nothing eat sleep}
end
it "resource.allowed_actions includes those actions" do
- expect(resource.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource.allowed_actions).to eq %i{nothing eat sleep}
end
end
describe "when allowed_actions is set to an array" do
let(:resource_class) do
Class.new(Chef::Resource::LWRPBase) do
- allowed_actions [ :eat, :sleep ]
+ allowed_actions %i{eat sleep}
end
end
let(:resource) do
resource_class.new("blah")
end
it "actions includes those actions" do
- expect(resource_class.actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource_class.actions).to eq %i{nothing eat sleep}
end
it "allowed_actions includes those actions" do
- expect(resource_class.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource_class.allowed_actions).to eq %i{nothing eat sleep}
end
it "resource.allowed_actions includes those actions" do
- expect(resource.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ expect(resource.allowed_actions).to eq %i{nothing eat sleep}
end
end
end
@@ -393,28 +383,27 @@ 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 = {} }
end
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)
+ Dir[File.expand_path(File.expand_path("../data/lwrp/resources/*", __dir__))].each do |file|
+ 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)
+ Dir[File.expand_path(File.expand_path("../data/lwrp/providers/*", __dir__))].each do |file|
+ 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
+ context "with a cookbook with a hyphen 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,10 +517,10 @@ 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 = @runner.build_provider(resource)
+ provider = resource.provider_for_action(:twiddle_thumbs)
+ # provider = @runner.build_provider(resource)
provider.action_twiddle_thumbs
expect(provider.enclosed_resource.monkey).to eq("bob, the monkey")
@@ -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.is_a?(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
@@ -640,77 +603,28 @@ describe "LWRP" do
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
- 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.is_a?(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")
- expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(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(lwrp.is_a?(subclass)).to be_falsey
expect(subclass === lwrp.class).to be_falsey
expect(subclass === get_lwrp(:lwrp_once)).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,14 +653,17 @@ describe "LWRP" do
end
end
- let(:recipe) do
- cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
+ let(:run_context) do
+ cookbook_repo = File.expand_path(File.join(__dir__, "..", "data", "cookbooks"))
cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
cookbook_loader.load_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cookbook_loader)
node = Chef::Node.new
events = Chef::EventDispatch::Dispatcher.new
- run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ Chef::RunContext.new(node, cookbook_collection, events)
+ end
+
+ let(:recipe) do
Chef::Recipe.new("hjk", "test", run_context)
end
diff --git a/spec/unit/mash_spec.rb b/spec/unit/mash_spec.rb
deleted file mode 100644
index e58f6b85a1..0000000000
--- a/spec/unit/mash_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Matthew Kent (<mkent@magoazul.com>)
-# 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"
-require "chef/mash"
-
-describe Mash do
- it "should duplicate a simple key/value mash to a new mash" do
- data = { :x => "one", :y => "two", :z => "three" }
- @orig = Mash.new(data)
- @copy = @orig.dup
- expect(@copy.to_hash).to eq(Mash.new(data).to_hash)
- @copy[:x] = "four"
- expect(@orig[:x]).to eq("one")
- end
-
- it "should duplicate a mash with an array to a new mash" do
- data = { :x => "one", :y => "two", :z => [1, 2, 3] }
- @orig = Mash.new(data)
- @copy = @orig.dup
- expect(@copy.to_hash).to eq(Mash.new(data).to_hash)
- @copy[:z] << 4
- expect(@orig[:z]).to eq([1, 2, 3])
- end
-
- it "should duplicate a nested mash to a new mash" do
- data = { :x => "one", :y => "two", :z => Mash.new({ :a => [1, 2, 3] }) }
- @orig = Mash.new(data)
- @copy = @orig.dup
- expect(@copy.to_hash).to eq(Mash.new(data).to_hash)
- @copy[:z][:a] << 4
- expect(@orig[:z][:a]).to eq([1, 2, 3])
- end
-
- # add more!
-end
diff --git a/spec/unit/mixin/api_version_request_handling_spec.rb b/spec/unit/mixin/api_version_request_handling_spec.rb
index 191dee643b..eb30d55845 100644
--- a/spec/unit/mixin/api_version_request_handling_spec.rb
+++ b/spec/unit/mixin/api_version_request_handling_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Cloke (tyler@chef.io)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,24 +26,24 @@ describe Chef::Mixin::ApiVersionRequestHandling do
let(:default_supported_client_versions) { [0, 1, 2] }
context "when the response code is not 406" do
- let(:response) { OpenStruct.new(:code => "405") }
- let(:exception) { Net::HTTPServerException.new("405 Something Else", response) }
+ let(:response) { OpenStruct.new(code: "405") }
+ let(:exception) { Net::HTTPClientException.new("405 Something Else", response) }
it "returns nil" do
- expect(object.server_client_api_version_intersection(exception, default_supported_client_versions)).
- to be_nil
+ expect(object.server_client_api_version_intersection(exception, default_supported_client_versions))
+ .to be_nil
end
end # when the response code is not 406
context "when the response code is 406" do
- let(:response) { OpenStruct.new(:code => "406") }
- let(:exception) { Net::HTTPServerException.new("406 Not Acceptable", response) }
+ let(:response) { OpenStruct.new(code: "406") }
+ let(:exception) { Net::HTTPClientException.new("406 Not Acceptable", response) }
context "when x-ops-server-api-version header does not exist" do
it "returns nil" do
- expect(object.server_client_api_version_intersection(exception, default_supported_client_versions)).
- to be_nil
+ expect(object.server_client_api_version_intersection(exception, default_supported_client_versions))
+ .to be_nil
end
end # when x-ops-server-api-version header does not exist
@@ -64,13 +64,13 @@ describe Chef::Mixin::ApiVersionRequestHandling do
context "when there is no intersection between client and server versions" do
shared_examples_for "no intersection between client and server versions" do
it "return an array" do
- expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
- to be_a_kind_of(Array)
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions))
+ .to be_a_kind_of(Array)
end
it "returns an empty array" do
- expect(object.server_client_api_version_intersection(exception, supported_client_versions).length).
- to eq(0)
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions).length)
+ .to eq(0)
end
end
@@ -94,8 +94,8 @@ describe Chef::Mixin::ApiVersionRequestHandling do
let(:supported_client_versions) { [1, 2, 3, 4, 5] }
it "includes all of the intersection" do
- expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
- to eq([2, 3, 4])
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions))
+ .to eq([2, 3, 4])
end
end # when multiple versions intersect
@@ -103,8 +103,8 @@ describe Chef::Mixin::ApiVersionRequestHandling do
let(:supported_client_versions) { [0, 1, 2] }
it "includes the intersection" do
- expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
- to eq([2])
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions))
+ .to eq([2])
end
end # when only the min client version intersects
@@ -112,8 +112,8 @@ describe Chef::Mixin::ApiVersionRequestHandling do
let(:supported_client_versions) { [4, 5, 6] }
it "includes the intersection" do
- expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
- to eq([4])
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions))
+ .to eq([4])
end
end # when only the max client version intersects
diff --git a/spec/unit/mixin/checksum_spec.rb b/spec/unit/mixin/checksum_spec.rb
index 997dcd523e..e8fb1ed4dc 100644
--- a/spec/unit/mixin/checksum_spec.rb
+++ b/spec/unit/mixin/checksum_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,7 @@ describe Chef::Mixin::Checksum do
@checksum_user = Chef::CMCCheck.new
@cache = Chef::Digester.instance
@file = CHEF_SPEC_DATA + "/checksum/random.txt"
- @stat = double("File::Stat", { :mtime => Time.at(0) })
+ @stat = double("File::Stat", { mtime: Time.at(0) })
allow(File).to receive(:stat).and_return(@stat)
end
@@ -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 e9f0dacad6..0000000000
--- a/spec/unit/mixin/command_spec.rb
+++ /dev/null
@@ -1,107 +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 do
- 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
- 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 do
- 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 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 do
- Timeout.timeout(10) do
- evil_forker = "exit if fork; 10.times { sleep 1}"
- run_command(:command => "ruby -e '#{evil_forker}'")
- end end.not_to raise_error
- end
-
- end
- end
- end
-end
diff --git a/spec/unit/mixin/deep_merge_spec.rb b/spec/unit/mixin/deep_merge_spec.rb
index 2122008616..c6681b3d16 100644
--- a/spec/unit/mixin/deep_merge_spec.rb
+++ b/spec/unit/mixin/deep_merge_spec.rb
@@ -236,6 +236,14 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
@dm.deep_merge!(hash_src, hash_dst)
expect(hash_dst).to eq({ "item" => "orange" })
end
+
+ it "should overwrite a string with a nil when merging nil values" do
+ hash_src = { "item" => nil }
+ hash_dst = { "item" => "orange" }
+ @dm.deep_merge!(hash_src, hash_dst)
+ expect(hash_dst).to eq({ "item" => nil })
+ end
+
end # deep_merge!
# Chef specific
@@ -338,5 +346,12 @@ describe Chef::Mixin::DeepMerge do
merge_with_hash = { "top_level_a" => 2, "top_level_b" => true }
@dm.hash_only_merge(merge_ee_hash, merge_with_hash)
end
+
+ it "should overwrite a string with a nil when merging nil values" do
+ hash_src = { "item" => nil }
+ hash_dst = { "item" => "orange" }
+ merged_result = @dm.hash_only_merge(hash_dst, hash_src)
+ expect(merged_result).to eq({ "item" => nil })
+ end
end
end
diff --git a/spec/unit/mixin/default_paths_spec.rb b/spec/unit/mixin/default_paths_spec.rb
new file mode 100644
index 0000000000..0224b8f4ce
--- /dev/null
+++ b/spec/unit/mixin/default_paths_spec.rb
@@ -0,0 +1,92 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+# Copyright:: Copyright (c) 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 DefaultPathsTestHarness
+ include Chef::Mixin::DefaultPaths
+end
+
+describe Chef::Mixin::DefaultPaths do
+
+ before do
+ @default_paths = DefaultPathsTestHarness.new
+ end
+
+ describe "when enforcing default paths" do
+ before do
+ Chef::Config[:enforce_default_paths] = true
+ @ruby_bindir = "/some/ruby/bin"
+ @gem_bindir = "/some/gem/bin"
+ allow(Gem).to receive(:bindir).and_return(@gem_bindir)
+ allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(@ruby_bindir)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ end
+
+ it "adds all useful PATHs even if environment is an empty hash" do
+ env = {}
+ @default_paths.enforce_default_paths(env)
+ 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" => "" }
+ @default_paths.enforce_default_paths(env)
+ 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" }
+ @default_paths.enforce_default_paths(env)
+ 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
+ env = { "PATH" => "/usr/bin:/sbin:/bin:/b#{0x81.chr}t".force_encoding("ISO-8859-1") }
+ @default_paths.enforce_default_paths(env)
+ expect(env["PATH"].encoding.to_s).to eq("UTF-8")
+ end
+
+ it "adds the current executing Ruby's bindir and Gem bindir to the PATH" do
+ env = { "PATH" => "" }
+ @default_paths.enforce_default_paths(env)
+ 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 PATH" do
+ ruby_bindir = "/usr/bin"
+ gem_bindir = "/yo/gabba/gabba"
+ allow(Gem).to receive(:bindir).and_return(gem_bindir)
+ allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(ruby_bindir)
+ env = { "PATH" => gem_bindir }
+ @default_paths.enforce_default_paths(env)
+ 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
+ ruby_bindir = 'C:\ruby\bin'
+ gem_bindir = 'C:\gems\bin'
+ allow(Gem).to receive(:bindir).and_return(gem_bindir)
+ allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(ruby_bindir)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ env = { "PATH" => 'C:\Windows\system32;C:\mr\softie' }
+ @default_paths.enforce_default_paths(env)
+ expect(env["PATH"]).to eq("#{gem_bindir};#{ruby_bindir};C:\\Windows\\system32;C:\\mr\\softie")
+ end
+ end
+end
diff --git a/spec/unit/mixin/deprecation_spec.rb b/spec/unit/mixin/deprecation_spec.rb
index 8707c6476e..f299f789db 100644
--- a/spec/unit/mixin/deprecation_spec.rb
+++ b/spec/unit/mixin/deprecation_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb b/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
index 248de0ba95..7d40759513 100644
--- a/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
+++ b/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Mark Mzyk (<mmzyk@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,11 +51,11 @@ describe Chef::Mixin::EnforceOwnershipAndPermissions do
allow_any_instance_of(Chef::FileAccessControl).to receive(:define_resource_requirements)
allow_any_instance_of(Chef::FileAccessControl).to receive(:describe_changes)
- passwd_struct = OpenStruct.new(:name => "root", :passwd => "x",
- :uid => 0, :gid => 0, :dir => "/root",
- :shell => "/bin/bash")
+ passwd_struct = OpenStruct.new(name: "root", passwd: "x",
+ uid: 0, gid: 0, dir: "/root",
+ shell: "/bin/bash")
- group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0)
+ group_struct = OpenStruct.new(name: "root", passwd: "x", gid: 0)
allow(Etc).to receive(:getpwuid).and_return(passwd_struct)
allow(Etc).to receive(:getgrgid).and_return(group_struct)
end
@@ -75,11 +75,11 @@ describe Chef::Mixin::EnforceOwnershipAndPermissions do
allow_any_instance_of(Chef::FileAccessControl).to receive(:uid_from_resource).and_return(0)
allow_any_instance_of(Chef::FileAccessControl).to receive(:describe_changes)
- passwd_struct = OpenStruct.new(:name => "root", :passwd => "x",
- :uid => 0, :gid => 0, :dir => "/root",
- :shell => "/bin/bash")
+ passwd_struct = OpenStruct.new(name: "root", passwd: "x",
+ uid: 0, gid: 0, dir: "/root",
+ shell: "/bin/bash")
- group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0)
+ group_struct = OpenStruct.new(name: "root", passwd: "x", gid: 0)
allow(Etc).to receive(:getpwuid).and_return(passwd_struct)
allow(Etc).to receive(:getgrgid).and_return(group_struct)
end
diff --git a/spec/unit/mixin/homebrew_user_spec.rb b/spec/unit/mixin/homebrew_user_spec.rb
index c9a6e6e909..92d7ff51c1 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:: Copyright (c) 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.
@@ -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 }
@@ -52,7 +48,7 @@ describe Chef::Mixin::HomebrewUser do
let(:brew_owner) { 2001 }
let(:default_brew_path) { "/usr/local/bin/brew" }
let(:stat_double) do
- d = double()
+ d = double
expect(d).to receive(:uid).and_return(brew_owner)
d
end
@@ -60,7 +56,7 @@ describe Chef::Mixin::HomebrewUser do
context "debug statement prints owner name" do
before do
- expect(Etc).to receive(:getpwuid).with(brew_owner).and_return(OpenStruct.new(:name => "name"))
+ expect(Etc).to receive(:getpwuid).with(brew_owner).and_return(OpenStruct.new(name: "name"))
end
it "returns the owner of the brew executable when it is at a default location" do
diff --git a/spec/unit/mixin/lazy_module_include.rb b/spec/unit/mixin/lazy_module_include.rb
index 542ae853ae..6f17d76a41 100644
--- a/spec/unit/mixin/lazy_module_include.rb
+++ b/spec/unit/mixin/lazy_module_include.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/mixin/openssl_helper_spec.rb b/spec/unit/mixin/openssl_helper_spec.rb
new file mode 100644
index 0000000000..7766e8f9b2
--- /dev/null
+++ b/spec/unit/mixin/openssl_helper_spec.rb
@@ -0,0 +1,892 @@
+#
+# Copyright:: Copyright (c) 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 "spec_helper"
+require "chef/mixin/openssl_helper"
+
+describe Chef::Mixin::OpenSSLHelper do
+ let(:instance) do
+ Class.new { include Chef::Mixin::OpenSSLHelper }.new
+ 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(256).to_pem) # this is 256 to speed up specs
+ @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/ec private key" do
+ it "Throws an OpenSSL::PKey::PKeyError 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 rsa 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 the ec key file does exist, and does contain a vaild ec private key" do
+ it "returns true" do
+ @keyfile.write(OpenSSL::PKey::EC.generate("prime256v1").to_pem)
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path)).to be_truthy
+ end
+ end
+
+ context "When a valid rsa 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 ec keyfile requires a passphrase, and an invalid passphrase is supplied" do
+ it "returns false" do
+ @keyfile.write(OpenSSL::PKey::EC.generate("prime256v1").to_pem(cipher, "oink"))
+ @keyfile.close
+ expect(instance.priv_key_file_valid?(@keyfile.path, "poml")).to be_falsey
+ end
+ end
+
+ context "When a valid rsa 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
+
+ context "When a valid ec keyfile requires a passphrase, and a valid passphrase is supplied" do
+ it "returns true" do
+ @keyfile.write(OpenSSL::PKey::EC.generate("prime256v1").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
+
+ describe "#crl_file_valid?" do
+ require "tempfile"
+
+ before(:each) do
+ @crlfile = Tempfile.new("crlfile")
+ end
+
+ context "When the crl file doesnt not exist" do
+ it "returns false" do
+ expect(instance.crl_file_valid?("/tmp/bad_filename")).to be_falsey
+ end
+ end
+
+ context "When the crl file does exist, but does not contain a valid CRL" do
+ it "returns false" do
+ @crlfile.write("I_am_not_a_crl_I_am_a_free_man")
+ @crlfile.close
+ expect(instance.crl_file_valid?(@crlfile.path)).to be_falsey
+ end
+ end
+
+ context "When the crl file does exist, and does contain a vaild CRL" do
+ it "returns true" do
+ @crlfile.write("-----BEGIN X509 CRL-----\nMIIBbjCB0QIBATAKBggqhkjOPQQDAjAOMQwwCgYDVQQDDANDQTIXDTE4MDgwMTE3\nMjg1NVoXDTE4MDgwOTE3Mjg1NVowNjA0AhUAx7y2YCouQlHvTignoijLUrwM6i8X\nDTE4MDgwMTE3Mjg1NVowDDAKBgNVHRUEAwoBAKBaMFgwCgYDVR0UBAMCAQQwSgYD\nVR0jBEMwQYAUCqE8XxFIFys0LTVPvsO1UtmrlyOhEqQQMA4xDDAKBgNVBAMMA0NB\nMoIVAPneTuAa1LzrK0wiZrxE8/1lSTp3MAoGCCqGSM49BAMCA4GLADCBhwJBct+Z\nZV3IZkPNevQv2S8lZ6kAMudN8R4QSzIQfM354Uk880RyQStP2S5Mb4gW3aFzwAy2\n/+rbx0bn2WmwoQv17I8CQgDtbvhf9chyPgMwAGCF7al04fve90fU1zRNH0zX1j9H\niDA2q1uBX+3TcTWcN+xgNimeRpvJFJ3uOB6w7jtwqGf1YQ==\n-----END X509 CRL-----\n")
+ @crlfile.close
+ expect(instance.crl_file_valid?(@crlfile.path)).to be_truthy
+ end
+ end
+
+ after(:each) do
+ @crlfile.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
+
+ describe "#gen_ec_priv_key" do
+ context "When given an invalid curve" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_ec_priv_key(2048)
+ end.to raise_error(TypeError)
+ end
+
+ it "Throws an ArgumentError" do
+ expect do
+ instance.gen_ec_priv_key("primeFromTheFuture")
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ context "When a proper curve is given" do
+ it "Generates an ec key object" do
+ expect(instance.gen_ec_priv_key("prime256v1")).to be_kind_of(OpenSSL::PKey::EC)
+ end
+ end
+ end
+
+ describe "#encrypt_ec_key" do
+ before(:all) do
+ @ec_key = OpenSSL::PKey::EC.generate("prime256v1")
+ end
+
+ context "When given anything other than an EC key object to encrypt" do
+ it "Raises a TypeError" do
+ expect do
+ instance.encrypt_ec_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_ec_key(@ec_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_ec_key(@ec_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_ec_key(@ec_key, "1234", "des3_bogus")
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ context "When given a valid ec key and a valid passphrase string" do
+ it "Generates a valid encrypted PEM" do
+ @encrypted_key = instance.encrypt_ec_key(@ec_key, "oink", "des3")
+ expect(@encrypted_key).to be_kind_of(String)
+ expect(OpenSSL::PKey::EC.new(@encrypted_key, "oink").private?).to be_truthy
+ end
+ end
+ end
+
+ describe "#gen_x509_request" do
+ before(:all) do
+ @subject = OpenSSL::X509::Name.new [%w{CN x509request}]
+ @ec_key = OpenSSL::PKey::EC.generate("prime256v1")
+ @rsa_key = OpenSSL::PKey::RSA.new(2048)
+ end
+
+ context "When given anything other than an RSA/EC key object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_request(@subject, "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given anything other than an X509 Name object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_request("abc", @key)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given a valid EC key and a valid subject" do
+ it "Generates a valid x509 request PEM" do
+ @x509_request = instance.gen_x509_request(@subject, @ec_key)
+ expect(@x509_request).to be_kind_of(OpenSSL::X509::Request)
+ expect(OpenSSL::X509::Request.new(@x509_request).verify(@ec_key)).to be_truthy
+ end
+ end
+
+ context "When given a valid RSA key and a valid subject" do
+ it "Generates a valid x509 request PEM" do
+ @x509_request = instance.gen_x509_request(@subject, @rsa_key)
+ expect(@x509_request).to be_kind_of(OpenSSL::X509::Request)
+ expect(OpenSSL::X509::Request.new(@x509_request).verify(@rsa_key)).to be_truthy
+ end
+ end
+ end
+
+ describe "#gen_x509_extensions" do
+ context "When given anything other than an Ruby Hash object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_extensions("abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a misformatted ruby Hash is given" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_extensions("pouet" => "plop")
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a ArgumentError" do
+ expect do
+ instance.gen_x509_extensions("pouet" => { "values" => [ "keyCertSign" ], "wrong_key" => true })
+ end.to raise_error(ArgumentError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_extensions("keyUsage" => { "values" => "keyCertSign", "critical" => true })
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_extensions("keyUsage" => { "values" => [ "keyCertSign" ], "critical" => "yes" })
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given a well formatted ruby Hash" do
+ it "Generates a valid Array of X509 Extensions" do
+ @x509_extension = instance.gen_x509_extensions("keyUsage" => { "values" => [ "keyCertSign" ], "critical" => true })
+ expect(@x509_extension).to be_kind_of(Array)
+ @x509_extension.each { |e| expect(e).to be_kind_of(OpenSSL::X509::Extension) }
+ end
+ end
+ end
+
+ describe "#gen_x509_cert" do
+ before(:all) do
+ @instance = Class.new { include Chef::Mixin::OpenSSLHelper }.new
+ @rsa_key = OpenSSL::PKey::RSA.new(2048)
+ @ec_key = OpenSSL::PKey::EC.generate("prime256v1")
+
+ @rsa_request = @instance.gen_x509_request(OpenSSL::X509::Name.new([%w{CN RSACert}]), @rsa_key)
+ @ec_request = @instance.gen_x509_request(OpenSSL::X509::Name.new([%w{CN ECCert}]), @ec_key)
+
+ @x509_extension = @instance.gen_x509_extensions("keyUsage" => { "values" => [ "keyCertSign" ], "critical" => true })
+
+ # Generating CA
+ @ca_key = OpenSSL::PKey::RSA.new(2048)
+ @ca_cert = OpenSSL::X509::Certificate.new
+ @ca_cert.version = 2
+ @ca_cert.serial = 1
+ @ca_cert.subject = OpenSSL::X509::Name.new [%w{CN TestCA}]
+ @ca_cert.issuer = @ca_cert.subject
+ @ca_cert.public_key = @ca_key.public_key
+ @ca_cert.not_before = Time.now
+ @ca_cert.not_after = @ca_cert.not_before + 365 * 24 * 60 * 60
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = @ca_cert
+ ef.issuer_certificate = @ca_cert
+ @ca_cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
+ @ca_cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
+ @ca_cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
+ @ca_cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
+ @ca_cert.sign(@ca_key, OpenSSL::Digest.new("SHA256"))
+
+ @info_with_issuer = { "validity" => 365, "issuer" => @ca_cert }
+ @info_without_issuer = { "validity" => 365 }
+ end
+
+ context "When the request given is anything other then a Ruby OpenSSL::X509::Request" do
+ it "Raises a TypeError" do
+ expect do
+ @instance.gen_x509_cert("abc", @x509_extension, @info_without_issuer, @rsa_key)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the extension given is anything other then a Ruby Array" do
+ it "Raises a TypeError" do
+ expect do
+ @instance.gen_x509_cert(@rsa_request, "abc", @info_without_issuer, @rsa_key)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the info given is anything other then a Ruby Hash" do
+ it "Raises a TypeError" do
+ expect do
+ @instance.gen_x509_cert(@rsa_request, @x509_extension, "abc", @rsa_key)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the key given is anything other then a Ruby OpenSSL::Pkey::EC or OpenSSL::Pkey::RSA object" do
+ it "Raises a TypeError" do
+ expect do
+ @instance.gen_x509_cert(@rsa_request, @x509_extension, @info_without_issuer, "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameters to generate a self signed certificate" do
+ it "Generates a valid x509 Certificate" do
+ @x509_certificate = @instance.gen_x509_cert(@rsa_request, @x509_extension, @info_without_issuer, @rsa_key)
+ expect(@x509_certificate).to be_kind_of(OpenSSL::X509::Certificate)
+ expect(OpenSSL::X509::Certificate.new(@x509_certificate).verify(@rsa_key)).to be_truthy
+ end
+ end
+
+ context "When given valid parameters to generate a CA signed certificate" do
+ it "Generates a valid x509 Certificate" do
+ @x509_certificate = @instance.gen_x509_cert(@ec_request, @x509_extension, @info_with_issuer, @ca_key)
+ expect(@x509_certificate).to be_kind_of(OpenSSL::X509::Certificate)
+ expect(OpenSSL::X509::Certificate.new(@x509_certificate).verify(@ca_key)).to be_truthy
+ end
+ end
+ end
+
+ describe "#get_next_crl_number" do
+ before(:all) do
+ @crl = OpenSSL::X509::CRL. new "-----BEGIN X509 CRL-----\nMIIBbTCB0QIBATAKBggqhkjOPQQDAjAOMQwwCgYDVQQDDANDQTIXDTE4MDgwMjA5\nMzc0OFoXDTE4MDgxMDA5Mzc0OFowNjA0AhUAx7y2YCouQlHvTignoijLUrwM6i8X\nDTE4MDgwMjA5Mzc0OFowDDAKBgNVHRUEAwoBAKBaMFgwCgYDVR0UBAMCAQQwSgYD\nVR0jBEMwQYAUxRlLNQUIOeWVaYm6HS0qFIbNCs2hEqQQMA4xDDAKBgNVBAMMA0NB\nMoIVAN1nyw8cj7IbhRLBu2CfS9Q8ILmDMAoGCCqGSM49BAMCA4GKADCBhgJBNR3o\njo/PzFwFGJKxIMa09pU+jprLG2CWehpZ4tGDjwiDCfZBztkg3H15eu+hyWmDp0U9\neAP5iJHVb12/3KZP0YUCQSgmaoLF68+Gh7ha+hcDjwFhzqdgmh/UlGPaxFBJ1BiQ\nQq9uBn0IT4o7v1Tv2WRZNDk7oiuRaZG+R9IodiZPsGKv\n-----END X509 CRL-----\n"
+ end
+
+ context "When the CRL given is anything other then a Ruby OpenSSL::X509::CRL object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.get_next_crl_number("abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameter to get the next crlNumber" do
+ it "Get 5" do
+ @next_crl = instance.get_next_crl_number(@crl)
+ expect(@next_crl).to be_kind_of(Integer)
+ expect(@next_crl == 5).to be_truthy
+ end
+ end
+ end
+
+ describe "#serial_revoked?" do
+ before(:all) do
+ @crl = OpenSSL::X509::CRL. new "-----BEGIN X509 CRL-----\nMIIBbTCB0QIBATAKBggqhkjOPQQDAjAOMQwwCgYDVQQDDANDQTIXDTE4MDgwMjA5\nMzc0OFoXDTE4MDgxMDA5Mzc0OFowNjA0AhUAx7y2YCouQlHvTignoijLUrwM6i8X\nDTE4MDgwMjA5Mzc0OFowDDAKBgNVHRUEAwoBAKBaMFgwCgYDVR0UBAMCAQQwSgYD\nVR0jBEMwQYAUxRlLNQUIOeWVaYm6HS0qFIbNCs2hEqQQMA4xDDAKBgNVBAMMA0NB\nMoIVAN1nyw8cj7IbhRLBu2CfS9Q8ILmDMAoGCCqGSM49BAMCA4GKADCBhgJBNR3o\njo/PzFwFGJKxIMa09pU+jprLG2CWehpZ4tGDjwiDCfZBztkg3H15eu+hyWmDp0U9\neAP5iJHVb12/3KZP0YUCQSgmaoLF68+Gh7ha+hcDjwFhzqdgmh/UlGPaxFBJ1BiQ\nQq9uBn0IT4o7v1Tv2WRZNDk7oiuRaZG+R9IodiZPsGKv\n-----END X509 CRL-----\n"
+ end
+
+ context "When the CRL given is anything other then a Ruby OpenSSL::X509::CRL object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.serial_revoked?("abc", "C7BCB6602A2E4251EF4E2827A228CB52BC0CEA2F")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the serial given is anything other then a Ruby String or Integer object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.serial_revoked?(@crl, [])
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameters to know if the serial is revoked" do
+ it "get true" do
+ @serial_revoked = instance.serial_revoked?(@crl, "C7BCB6602A2E4251EF4E2827A228CB52BC0CEA2F")
+ expect(@serial_revoked).to be_kind_of(TrueClass)
+ end
+ end
+ end
+
+ describe "#gen_x509_crl" do
+ before(:all) do
+ # Generating CA
+
+ @ca_key = OpenSSL::PKey::RSA.new(2048)
+ @ca_cert = OpenSSL::X509::Certificate.new
+ @ca_cert.version = 2
+ @ca_cert.serial = 1
+ @ca_cert.subject = OpenSSL::X509::Name.new [%w{CN TestCA}]
+ @ca_cert.issuer = @ca_cert.subject
+ @ca_cert.public_key = @ca_key.public_key
+ @ca_cert.not_before = Time.now
+ @ca_cert.not_after = @ca_cert.not_before + 365 * 24 * 60 * 60
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = @ca_cert
+ ef.issuer_certificate = @ca_cert
+ @ca_cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
+ @ca_cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
+ @ca_cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
+ @ca_cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
+ @ca_cert.sign(@ca_key, OpenSSL::Digest.new("SHA256"))
+
+ @info = { "validity" => 8, "issuer" => @ca_cert }
+ end
+
+ context "When the CA private key given is anything other then a Ruby OpenSSL::PKey::EC object or a OpenSSL::PKey::RSA object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_crl("abc", @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the info given is anything other then a Ruby Hash" do
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_crl(@ca_key, "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a misformatted info Ruby Hash is given" do
+ it "Raises a ArgumentError" do
+ expect do
+ instance.gen_x509_crl(@ca_key, "abc" => "def", "validity" => 8)
+ end.to raise_error(ArgumentError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_crl(@ca_key, "issuer" => "abc", "validity" => 8)
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.gen_x509_crl(@ca_key, "issuer" => @ca_cert, "validity" => "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameters to generate a CRL" do
+ it "Generates a valid x509 CRL" do
+ @x509_crl = instance.gen_x509_crl(@ca_key, @info)
+ expect(@x509_crl).to be_kind_of(OpenSSL::X509::CRL)
+ expect(OpenSSL::X509::CRL.new(@x509_crl).verify(@ca_key)).to be_truthy
+ end
+ end
+ end
+
+ describe "#renew_x509_crl" do
+ before(:all) do
+ # Generating CA
+ @instance = Class.new { include Chef::Mixin::OpenSSLHelper }.new
+ @ca_key = OpenSSL::PKey::RSA.new(2048)
+ @ca_cert = OpenSSL::X509::Certificate.new
+ @ca_cert.version = 2
+ @ca_cert.serial = 1
+ @ca_cert.subject = OpenSSL::X509::Name.new [%w{CN TestCA}]
+ @ca_cert.issuer = @ca_cert.subject
+ @ca_cert.public_key = @ca_key.public_key
+ @ca_cert.not_before = Time.now
+ @ca_cert.not_after = @ca_cert.not_before + 365 * 24 * 60 * 60
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = @ca_cert
+ ef.issuer_certificate = @ca_cert
+ @ca_cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
+ @ca_cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
+ @ca_cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
+ @ca_cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
+ @ca_cert.sign(@ca_key, OpenSSL::Digest.new("SHA256"))
+
+ @info = { "validity" => 8, "issuer" => @ca_cert }
+
+ @crl = @instance.gen_x509_crl(@ca_key, @info)
+ end
+
+ context "When the CRL given is anything other then a Ruby OpenSSL::X509::CRL object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.renew_x509_crl("abc", @ca_key, @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the CA private key given is anything other then a Ruby OpenSSL::PKey::EC object or a OpenSSL::PKey::RSA object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.renew_x509_crl(@crl, "abc", @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the info given is anything other then a Ruby Hash" do
+ it "Raises a TypeError" do
+ expect do
+ instance.renew_x509_crl(@crl, @ca_key, "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a misformatted info Ruby Hash is given" do
+ it "Raises a ArgumentError" do
+ expect do
+ instance.renew_x509_crl(@crl, @ca_key, "abc" => "def", "validity" => 8)
+ end.to raise_error(ArgumentError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.renew_x509_crl(@crl, @ca_key, "issuer" => "abc", "validity" => 8)
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.renew_x509_crl(@crl, @ca_key, "issuer" => @ca_cert, "validity" => "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameters to renew a CRL" do
+ it "Renew a valid x509 CRL" do
+ @renewed_crl = instance.renew_x509_crl(@crl, @ca_key, @info)
+ expect(@renewed_crl).to be_kind_of(OpenSSL::X509::CRL)
+ expect(OpenSSL::X509::CRL.new(@renewed_crl).verify(@ca_key)).to be_truthy
+ end
+ end
+ end
+
+ describe "#revoke_x509_crl" do
+ before(:all) do
+ # Generating CA
+
+ @instance = Class.new { include Chef::Mixin::OpenSSLHelper }.new
+ @ca_key = OpenSSL::PKey::RSA.new(2048)
+ @ca_cert = OpenSSL::X509::Certificate.new
+ @ca_cert.version = 2
+ @ca_cert.serial = 1
+ @ca_cert.subject = OpenSSL::X509::Name.new [%w{CN TestCA}]
+ @ca_cert.issuer = @ca_cert.subject
+ @ca_cert.public_key = @ca_key.public_key
+ @ca_cert.not_before = Time.now
+ @ca_cert.not_after = @ca_cert.not_before + 365 * 24 * 60 * 60
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = @ca_cert
+ ef.issuer_certificate = @ca_cert
+ @ca_cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
+ @ca_cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
+ @ca_cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
+ @ca_cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
+ @ca_cert.sign(@ca_key, OpenSSL::Digest.new("SHA256"))
+
+ @info = { "validity" => 8, "issuer" => @ca_cert }
+
+ @crl = @instance.gen_x509_crl(@ca_key, @info)
+ @revoke_info = { "serial" => 1, "reason" => 0 }
+ end
+
+ context "When the revoke_info given is anything other then a Ruby Hash" do
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl("abc", @crl, @ca_key, @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the CRL given is anything other then a Ruby OpenSSL::X509::CRL object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, "abc", @ca_key, @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the CA private key given is anything other then a Ruby OpenSSL::PKey::EC object or a OpenSSL::PKey::RSA object" do
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, @crl, "abc", @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When the info given is anything other then a Ruby Hash" do
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, @crl, @ca_key, "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a misformatted revoke_info Ruby Hash is given" do
+ it "Raises a ArgumentError" do
+ expect do
+ instance.revoke_x509_crl({ "abc" => "def", "ghi" => "jkl" }, @crl, @ca_key, @info)
+ end.to raise_error(ArgumentError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl({ "serial" => [], "reason" => 0 }, @crl, @ca_key, @info)
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl({ "serial" => 1, "reason" => "abc" }, @crl, @ca_key, @info)
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When a misformatted info Ruby Hash is given" do
+ it "Raises a ArgumentError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, @crl, @ca_key, "abc" => "def", "validity" => 8)
+ end.to raise_error(ArgumentError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, @crl, @ca_key, "issuer" => "abc", "validity" => 8)
+ end.to raise_error(TypeError)
+ end
+
+ it "Raises a TypeError" do
+ expect do
+ instance.revoke_x509_crl(@revoke_info, @crl, @ca_key, "issuer" => @ca_cert, "validity" => "abc")
+ end.to raise_error(TypeError)
+ end
+ end
+
+ context "When given valid parameters to revoke a Serial in a CRL" do
+ it "Revoke a Serial in a CRL" do
+ @crl_with_revoked_serial = instance.revoke_x509_crl(@revoke_info, @crl, @ca_key, @info)
+ expect(@crl_with_revoked_serial).to be_kind_of(OpenSSL::X509::CRL)
+ expect(OpenSSL::X509::CRL.new(@crl_with_revoked_serial).verify(@ca_key)).to be_truthy
+ expect(instance.serial_revoked?(@crl_with_revoked_serial, 1)).to be_truthy
+ end
+ end
+ end
+
+ describe "#cert_need_renewall?" do
+ require "tempfile"
+
+ before(:each) do
+ @certfile = Tempfile.new("certfile")
+ end
+
+ context "When the cert file doesn't exist" do
+ it "returns true" do
+ expect(instance.cert_need_renewall?("/tmp/bad_filename", 3650)).to be_truthy
+ end
+ end
+
+ context "When the cert file does exist, but does not contain a valid Certificate" do
+ it "returns true" do
+ @certfile.write("I_am_not_a_cert_I_am_a_free_man")
+ @certfile.close
+ expect(instance.cert_need_renewall?(@certfile.path, 3650)).to be_truthy
+ end
+ end
+
+ context "When the cert file does exist, and does not contain a soon to expire certificate" do
+ it "returns false" do
+ @certfile.write("-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEB\nCwUAMA0xCzAJBgNVBAMMAkNBMB4XDTE5MTIyNTEyNTY1NVoXDTI5MTIyMjEyNTY1\nNVowDTELMAkGA1UEAwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC9bDWs5akf85UEMj8kry4DYNuAnaL4GnMs6XukQtp3dso+FgbKprgVogyepnet\nE+GlQq32u/n4y8K228kB6NoCn+c/yP+4QlKUBt0xSzQbSUuAE/5xZoKi/kH1ZsQ/\nuKXN/tIHagApEUGn5zqc8WBvWPliRAqiklwj8WtSw1WRa5eCdaVtln3wKuvPnYR5\n/V4YBHyHNhtlfXJBMtEaXm1rRzJGun+FdcrsCfcIFXp8lWobF+EVP8fRwqFTEtT6\nRXv6RT8sHy53a0KNTm8qnbacfr1MofgUuhzLjOrbIVvXpnRLeOkv8XW5rSH+zgsC\nZFK3bJ3j6UVbFQV4jXwlAWVrAgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgGmMA8G\nA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK4S2PNu6bpjxkJxedNaxfCrwtD4MEkG\nA1UdIwRCMECAFK4S2PNu6bpjxkJxedNaxfCrwtD4oRGkDzANMQswCQYDVQQDDAJD\nQYIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEBCwUAA4IBAQBGk+u3\n9N3PLWNOwYrqK7fD4yceWnz4UsV9uN1IU5PQTgYBaGyAZvU+VJluZZeDj7QjwbUW\ngISclvW/pSWpUVW3O0sfAM97u+5UMYHz4W5Bgq8CtdOKHgdZHKhzBePhmou11zO0\nZ6uQ7bkh0/REqKO7TFKaMMnakEhFXoDrS1EiB4W69KVXyrBVzVm5LK7uvOAQAeMp\nnEk3Oz+5pmKjSCf1cEd2jzAgDbaVrIvxICPgXAlNrKukmRW/0UHqDDVBfF7PioD2\nptlQFxWIkih6s/clwhsBFBwV1yyCirYfjhzmKPPLZUmx10okudLzaKrRbkPxrzbC\nmKEZoV+Nz2CNrGm5\n-----END CERTIFICATE-----\n")
+ @certfile.close
+ expect(instance.cert_need_renewall?(@certfile.path, 5)).to be_falsey
+ end
+ end
+
+ context "When the cert file does exist, and does contain a soon to expire certificate" do
+ it "returns true" do
+ @certfile.write("-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEB\nCwUAMA0xCzAJBgNVBAMMAkNBMB4XDTE5MTIyNTEyNTY1NVoXDTI5MTIyMjEyNTY1\nNVowDTELMAkGA1UEAwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC9bDWs5akf85UEMj8kry4DYNuAnaL4GnMs6XukQtp3dso+FgbKprgVogyepnet\nE+GlQq32u/n4y8K228kB6NoCn+c/yP+4QlKUBt0xSzQbSUuAE/5xZoKi/kH1ZsQ/\nuKXN/tIHagApEUGn5zqc8WBvWPliRAqiklwj8WtSw1WRa5eCdaVtln3wKuvPnYR5\n/V4YBHyHNhtlfXJBMtEaXm1rRzJGun+FdcrsCfcIFXp8lWobF+EVP8fRwqFTEtT6\nRXv6RT8sHy53a0KNTm8qnbacfr1MofgUuhzLjOrbIVvXpnRLeOkv8XW5rSH+zgsC\nZFK3bJ3j6UVbFQV4jXwlAWVrAgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgGmMA8G\nA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK4S2PNu6bpjxkJxedNaxfCrwtD4MEkG\nA1UdIwRCMECAFK4S2PNu6bpjxkJxedNaxfCrwtD4oRGkDzANMQswCQYDVQQDDAJD\nQYIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEBCwUAA4IBAQBGk+u3\n9N3PLWNOwYrqK7fD4yceWnz4UsV9uN1IU5PQTgYBaGyAZvU+VJluZZeDj7QjwbUW\ngISclvW/pSWpUVW3O0sfAM97u+5UMYHz4W5Bgq8CtdOKHgdZHKhzBePhmou11zO0\nZ6uQ7bkh0/REqKO7TFKaMMnakEhFXoDrS1EiB4W69KVXyrBVzVm5LK7uvOAQAeMp\nnEk3Oz+5pmKjSCf1cEd2jzAgDbaVrIvxICPgXAlNrKukmRW/0UHqDDVBfF7PioD2\nptlQFxWIkih6s/clwhsBFBwV1yyCirYfjhzmKPPLZUmx10okudLzaKrRbkPxrzbC\nmKEZoV+Nz2CNrGm5\n-----END CERTIFICATE-----\n")
+ @certfile.close
+ expect(instance.cert_need_renewall?(@certfile.path, 3650)).to be_truthy
+ end
+ end
+
+ after(:each) do
+ @certfile.unlink
+ end
+ end
+end
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 0cafb925c8..459c2ce237 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,11 +30,11 @@ end
describe Chef::Mixin::ParamsValidate do
before(:each) do
- @vo = TinyClass.new()
+ @vo = TinyClass.new
end
it "should allow a hash and a hash as arguments to validate" do
- expect { @vo.validate({ :one => "two" }, {}) }.not_to raise_error
+ expect { @vo.validate({ one: "two" }, {}) }.not_to raise_error
end
it "should raise an argument error if validate is called incorrectly" do
@@ -42,26 +42,26 @@ describe Chef::Mixin::ParamsValidate do
end
it "should require validation map keys to be symbols or strings" do
- expect { @vo.validate({ :one => "two" }, { :one => true }) }.not_to raise_error
- expect { @vo.validate({ :one => "two" }, { "one" => true }) }.not_to raise_error
- expect { @vo.validate({ :one => "two" }, { Hash.new => true }) }.to raise_error(ArgumentError)
+ expect { @vo.validate({ one: "two" }, { one: true }) }.not_to raise_error
+ expect { @vo.validate({ one: "two" }, { "one" => true }) }.not_to raise_error
+ expect { @vo.validate({ one: "two" }, { {} => true }) }.to raise_error(ArgumentError)
end
it "should allow options to be required with true" do
- expect { @vo.validate({ :one => "two" }, { :one => true }) }.not_to raise_error
+ expect { @vo.validate({ one: "two" }, { one: true }) }.not_to raise_error
end
it "should allow options to be optional with false" do
- expect { @vo.validate({}, { :one => false }) }.not_to raise_error
+ expect { @vo.validate({}, { one: false }) }.not_to raise_error
end
it "should allow you to check what kind_of? thing an argument is with kind_of" do
expect do
@vo.validate(
- { :one => "string" },
+ { one: "string" },
{
- :one => {
- :kind_of => String,
+ one: {
+ kind_of: String,
},
}
)
@@ -69,10 +69,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :one => "string" },
+ { one: "string" },
{
- :one => {
- :kind_of => Array,
+ one: {
+ kind_of: Array,
},
}
)
@@ -82,10 +82,10 @@ describe Chef::Mixin::ParamsValidate do
it "should allow you to specify an argument is required with required" do
expect do
@vo.validate(
- { :one => "string" },
+ { one: "string" },
{
- :one => {
- :required => true,
+ one: {
+ required: true,
},
}
)
@@ -93,10 +93,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :two => "string" },
+ { two: "string" },
{
- :one => {
- :required => true,
+ one: {
+ required: true,
},
}
)
@@ -104,10 +104,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :two => "string" },
+ { two: "string" },
{
- :one => {
- :required => false,
+ one: {
+ required: false,
},
}
)
@@ -117,10 +117,10 @@ describe Chef::Mixin::ParamsValidate do
it "should allow you to specify whether an object has a method with respond_to" do
expect do
@vo.validate(
- { :one => @vo },
+ { one: @vo },
{
- :one => {
- :respond_to => "validate",
+ one: {
+ respond_to: "validate",
},
}
)
@@ -128,10 +128,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :one => @vo },
+ { one: @vo },
{
- :one => {
- :respond_to => "monkey",
+ one: {
+ respond_to: "monkey",
},
}
)
@@ -141,10 +141,10 @@ describe Chef::Mixin::ParamsValidate do
it "should allow you to specify whether an object has all the given methods with respond_to and an array" do
expect do
@vo.validate(
- { :one => @vo },
+ { one: @vo },
{
- :one => {
- :respond_to => %w{validate music},
+ one: {
+ respond_to: %w{validate music},
},
}
)
@@ -152,10 +152,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :one => @vo },
+ { one: @vo },
{
- :one => {
- :respond_to => %w{monkey validate},
+ one: {
+ respond_to: %w{monkey validate},
},
}
)
@@ -163,10 +163,10 @@ describe Chef::Mixin::ParamsValidate do
end
it "should let you set a default value with default => value" do
- arguments = Hash.new
+ arguments = {}
@vo.validate(arguments, {
- :one => {
- :default => "is the loneliest number",
+ one: {
+ default: "is the loneliest number",
},
})
expect(arguments[:one]).to eq("is the loneliest number")
@@ -175,10 +175,10 @@ describe Chef::Mixin::ParamsValidate do
it "should let you check regular expressions" do
expect do
@vo.validate(
- { :one => "is good" },
+ { one: "is good" },
{
- :one => {
- :regex => /^is good$/,
+ one: {
+ regex: /^is good$/,
},
}
)
@@ -186,10 +186,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :one => "is good" },
+ { one: "is good" },
{
- :one => {
- :regex => /^is bad$/,
+ one: {
+ regex: /^is bad$/,
},
}
)
@@ -199,10 +199,10 @@ describe Chef::Mixin::ParamsValidate do
it "should let you specify your own callbacks" do
expect do
@vo.validate(
- { :one => "is good" },
+ { one: "is good" },
{
- :one => {
- :callbacks => {
+ one: {
+ callbacks: {
"should be equal to is good" => lambda do |a|
a == "is good"
end,
@@ -214,10 +214,10 @@ describe Chef::Mixin::ParamsValidate do
expect do
@vo.validate(
- { :one => "is bad" },
+ { one: "is bad" },
{
- :one => {
- :callbacks => {
+ one: {
+ callbacks: {
"should be equal to 'is good'" => lambda do |a|
a == "is good"
end,
@@ -229,27 +229,27 @@ describe Chef::Mixin::ParamsValidate do
end
it "should let you combine checks" do
- args = { :one => "is good", :two => "is bad" }
+ args = { one: "is good", two: "is bad" }
expect do
@vo.validate(
args,
{
- :one => {
- :kind_of => String,
- :respond_to => [ :to_s, :upcase ],
- :regex => /^is good/,
- :callbacks => {
+ one: {
+ kind_of: String,
+ respond_to: %i{to_s upcase},
+ regex: /^is good/,
+ callbacks: {
"should be your friend" => lambda do |a|
a == "is good"
end,
},
- :required => true,
+ required: true,
},
- :two => {
- :kind_of => String,
- :required => false,
+ two: {
+ kind_of: String,
+ required: false,
},
- :three => { :default => "neato mosquito" },
+ three: { default: "neato mosquito" },
}
)
end.not_to raise_error
@@ -258,22 +258,22 @@ describe Chef::Mixin::ParamsValidate do
@vo.validate(
args,
{
- :one => {
- :kind_of => String,
- :respond_to => [ :to_s, :upcase ],
- :regex => /^is good/,
- :callbacks => {
+ one: {
+ kind_of: String,
+ respond_to: %i{to_s upcase},
+ regex: /^is good/,
+ callbacks: {
"should be your friend" => lambda do |a|
a == "is good"
end,
},
- :required => true,
+ required: true,
},
- :two => {
- :kind_of => Hash,
- :required => false,
+ two: {
+ kind_of: Hash,
+ required: false,
},
- :three => { :default => "neato mosquito" },
+ three: { default: "neato mosquito" },
}
)
end.to raise_error(ArgumentError)
@@ -282,10 +282,10 @@ describe Chef::Mixin::ParamsValidate do
it "should raise an ArgumentError if the validation map has an unknown check" do
expect do
@vo.validate(
- { :one => "two" },
+ { one: "two" },
{
- :one => {
- :busted => "check",
+ one: {
+ busted: "check",
},
}
)
@@ -294,37 +294,37 @@ describe Chef::Mixin::ParamsValidate do
it "should accept keys that are strings in the options" do
expect do
- @vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ } })
+ @vo.validate({ "one" => "two" }, { one: { regex: /^two$/ } })
end.not_to raise_error
end
it "should allow an array to kind_of" do
expect do
@vo.validate(
- { :one => "string" },
+ { one: "string" },
{
- :one => {
- :kind_of => [ String, Array ],
+ one: {
+ kind_of: [ String, Array ],
},
}
)
end.not_to raise_error
expect do
@vo.validate(
- { :one => ["string"] },
+ { one: ["string"] },
{
- :one => {
- :kind_of => [ String, Array ],
+ one: {
+ kind_of: [ String, Array ],
},
}
)
end.not_to raise_error
expect do
@vo.validate(
- { :one => Hash.new },
+ { one: {} },
{
- :one => {
- :kind_of => [ String, Array ],
+ one: {
+ kind_of: [ String, Array ],
},
}
)
@@ -333,15 +333,26 @@ describe Chef::Mixin::ParamsValidate do
it "asserts that a value returns false from a predicate method" do
expect do
- @vo.validate({ :not_blank => "should pass" },
- { :not_blank => { :cannot_be => [ :nil, :empty ] } })
+ @vo.validate({ not_blank: "should pass" },
+ { not_blank: { cannot_be: %i{nil empty} } })
end.not_to raise_error
expect do
- @vo.validate({ :not_blank => "" },
- { :not_blank => { :cannot_be => [ :nil, :empty ] } })
+ @vo.validate({ not_blank: "" },
+ { not_blank: { cannot_be: %i{nil empty} } })
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: %i{nil empty}, validation_message: "my validation message" } })
+ end.not_to raise_error
+ expect do
+ @vo.validate({ not_blank: "" },
+ { not_blank: { cannot_be: %i{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)
@@ -350,38 +361,38 @@ describe Chef::Mixin::ParamsValidate do
it "should set and return a default value when the argument is nil, then return the same value" do
value = "meow"
- expect(@vo.set_or_return(:test, nil, { :default => value }).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:test, nil, { default: value }).object_id).to eq(value.object_id)
expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
end
it "should raise an ArgumentError when argument is nil and required is true" do
expect do
- @vo.set_or_return(:test, nil, { :required => true })
+ @vo.set_or_return(:test, nil, { required: true })
end.to raise_error(ArgumentError)
end
it "should not raise an error when argument is nil and required is false" do
expect do
- @vo.set_or_return(:test, nil, { :required => false })
+ @vo.set_or_return(:test, nil, { required: false })
end.not_to raise_error
end
it "should set and return @name, then return @name for foo when argument is nil" do
value = "meow"
expect(@vo.set_or_return(:name, value, {}).object_id).to eq(value.object_id)
- expect(@vo.set_or_return(:foo, nil, { :name_attribute => true }).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:foo, nil, { name_attribute: true }).object_id).to eq(value.object_id)
end
it "should allow DelayedEvaluator instance to be set for value regardless of restriction" do
value = Chef::DelayedEvaluator.new { "test" }
- @vo.set_or_return(:test, value, { :kind_of => Numeric })
+ @vo.set_or_return(:test, value, { kind_of: Numeric })
end
it "should raise an error when delayed evaluated attribute is not valid" do
value = Chef::DelayedEvaluator.new { "test" }
- @vo.set_or_return(:test, value, { :kind_of => Numeric })
+ @vo.set_or_return(:test, value, { kind_of: Numeric })
expect do
- @vo.set_or_return(:test, nil, { :kind_of => Numeric })
+ @vo.set_or_return(:test, nil, { kind_of: Numeric })
end.to raise_error(Chef::Exceptions::ValidationFailed)
end
diff --git a/spec/unit/mixin/path_sanity_spec.rb b/spec/unit/mixin/path_sanity_spec.rb
deleted file mode 100644
index 675b5722be..0000000000
--- a/spec/unit/mixin/path_sanity_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-#
-# Author:: Seth Chisamore (<schisamo@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.
-#
-
-require "spec_helper"
-
-class PathSanityTestHarness
- include Chef::Mixin::PathSanity
-end
-
-describe Chef::Mixin::PathSanity do
-
- before do
- @sanity = PathSanityTestHarness.new
- end
-
- describe "when enforcing path sanity" do
- before do
- Chef::Config[:enforce_path_sanity] = true
- @ruby_bindir = "/some/ruby/bin"
- @gem_bindir = "/some/gem/bin"
- allow(Gem).to receive(:bindir).and_return(@gem_bindir)
- allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(@ruby_bindir)
- allow(ChefConfig).to receive(:windows?).and_return(false)
- end
-
- 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")
- 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")
- 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")
- end
-
- it "creates path with utf-8 encoding" do
- env = { "PATH" => "/usr/bin:/sbin:/bin:/b#{0x81.chr}t".force_encoding("ISO-8859-1") }
- @sanity.enforce_path_sanity(env)
- expect(env["PATH"].encoding.to_s).to eq("UTF-8")
- end
-
- 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")
- end
-
- it "does not create entries for Ruby/Gem bindirs if they exist in SANE_PATH or PATH" do
- ruby_bindir = "/usr/bin"
- gem_bindir = "/yo/gabba/gabba"
- allow(Gem).to receive(:bindir).and_return(gem_bindir)
- 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")
- end
-
- it "builds a valid windows path" do
- ruby_bindir = 'C:\ruby\bin'
- gem_bindir = 'C:\gems\bin'
- allow(Gem).to receive(:bindir).and_return(gem_bindir)
- allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(ruby_bindir)
- 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}")
- 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..92e92dc2a1
--- /dev/null
+++ b/spec/unit/mixin/powershell_exec_spec.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Stuart Preston (<stuart@chef.io>)
+# Copyright:: Copyright (c) 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
+ context "not specifying an interpreter" 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 "uses less than version 6" do
+ execution = object.powershell_exec("$PSVersionTable")
+ expect(execution.result["PSVersion"].to_s.to_i).to be < 6
+ end
+ end
+
+ context "using pwsh interpreter" do
+ it "runs a basic command and returns a Chef::PowerShell object" do
+ expect(object.powershell_exec("$PSVersionTable", :pwsh)).to be_kind_of(Chef::Pwsh)
+ end
+
+ it "uses greater than version 6" do
+ execution = object.powershell_exec("$PSVersionTable", :pwsh)
+ expect(execution.result["PSVersion"]["Major"]).to be > 6
+ end
+ end
+
+ context "using powershell interpreter" do
+ it "runs a basic command and returns a Chef::PowerShell object" do
+ expect(object.powershell_exec("$PSVersionTable", :powershell)).to be_kind_of(Chef::PowerShell)
+ end
+
+ it "uses less than version 6" do
+ execution = object.powershell_exec("$PSVersionTable", :powershell)
+ expect(execution.result["PSVersion"].to_s.to_i).to be < 6
+ end
+ 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("The term 'this-should-error' is not recognized")
+ end
+
+ it "raises an error if the interpreter is invalid" do
+ expect { object.powershell_exec("this-should-error", :powerfart) }.to raise_error(ArgumentError)
+ end
+ end
+
+ 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 "raises an error if the command fails" do
+ expect { object.powershell_exec!("this-should-error") }.to raise_error(Chef::PowerShell::CommandFailed)
+ end
+
+ it "raises an error if the interpreter is invalid" do
+ expect { object.powershell_exec!("this-should-error", :powerfart) }.to raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb
index 8e5f3588ce..14a9483758 100644
--- a/spec/unit/mixin/powershell_out_spec.rb
+++ b/spec/unit/mixin/powershell_out_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
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" }
@@ -30,8 +30,7 @@ describe Chef::Mixin::PowershellOut do
it "runs a command and returns the shell_out object" do
ret = double("Mixlib::ShellOut")
expect(object).to receive(:shell_out).with(
- "powershell.exe #{flags} -Command \"Get-Process\"",
- {}
+ "powershell.exe #{flags} -Command \"Get-Process\""
).and_return(ret)
expect(object.powershell_out("Get-Process")).to eql(ret)
end
@@ -44,14 +43,39 @@ describe Chef::Mixin::PowershellOut do
).and_return(ret)
expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret)
end
+
+ it "uses pwsh.exe when given :pwsh interpreter" do
+ ret = double("Mixlib::ShellOut")
+ expect(object).to receive(:shell_out).with(
+ "pwsh.exe #{flags} -Command \"Get-Process\"",
+ timeout: 600
+ ).and_return(ret)
+ expect(object.powershell_out("Get-Process", :pwsh, timeout: 600)).to eql(ret)
+ end
+
+ it "raises error if interpreter is invalid" do
+ ret = double("Mixlib::ShellOut")
+ expect { object.powershell_out("Get-Process", :blah, timeout: 600) }.to raise_error(ArgumentError)
+ 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
it "runs a command and returns the shell_out object" do
mixlib_shellout = double("Mixlib::ShellOut")
expect(object).to receive(:shell_out).with(
- "powershell.exe #{flags} -Command \"Get-Process\"",
- {}
+ "powershell.exe #{flags} -Command \"Get-Process\""
).and_return(mixlib_shellout)
expect(mixlib_shellout).to receive(:error!)
expect(object.powershell_out!("Get-Process")).to eql(mixlib_shellout)
@@ -66,5 +90,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 6f52abccfb..7eebfd2348 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ class Chef::PSTypeTester
end
describe Chef::Mixin::PowershellTypeCoercions do
- let (:test_class) { Chef::PSTypeTester.new }
+ let(:test_class) { Chef::PSTypeTester.new }
describe "#translate_type" do
it "single quotes a string" do
@@ -34,7 +34,7 @@ describe Chef::Mixin::PowershellTypeCoercions do
["'", '"', "#", "`"].each do |c|
it "base64 encodes a string that contains #{c}" do
- expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c))
+ expect(test_class.translate_type(c.to_s)).to match(Base64.strict_encode64(c))
end
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..25b94178e5 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,19 +31,28 @@ 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 %i{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.keys).to eq %i{b ab bc}
expect(B.properties[:b].validation_options[:is]).to eq "b"
expect(B.properties[:ab].validation_options[:is]).to be_nil
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 %i{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"
@@ -89,7 +99,7 @@ module ChefMixinPropertiesSpec
end
it "Outerest.properties.validation_options[:is] inner, outer, outerest" do
- expect(Outerest.properties.keys).to eq [:inner, :outer, :outerest]
+ expect(Outerest.properties.keys).to eq %i{inner outer outerest}
end
end
end
diff --git a/spec/unit/mixin/proxified_socket_spec.rb b/spec/unit/mixin/proxified_socket_spec.rb
index d3ba54f618..acb6a9c532 100644
--- a/spec/unit/mixin/proxified_socket_spec.rb
+++ b/spec/unit/mixin/proxified_socket_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/mixin/securable_spec.rb b/spec/unit/mixin/securable_spec.rb
index 6f50ce853f..bbbd6bab84 100644
--- a/spec/unit/mixin/securable_spec.rb
+++ b/spec/unit/mixin/securable_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Mark Mzyk (<mmzyk@chef.io>)
-# Copyright:: Copyright 2011-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -46,7 +45,7 @@ describe Chef::Mixin::Securable do
describe "unix-specific behavior" do
before(:each) do
platform_mock :unix do
- load File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "chef", "mixin", "securable.rb")
+ load File.join(__dir__, "..", "..", "..", "lib", "chef", "mixin", "securable.rb")
@securable = Object.new
@securable.send(:extend, Chef::Mixin::Securable)
@securable.send(:extend, Chef::Mixin::ParamsValidate)
@@ -117,10 +116,10 @@ describe Chef::Mixin::Securable do
it "should not accept group/owner names containing embedded carriage returns" do
skip "XXX: params_validate needs to be extended to support multi-line regex"
- #lambda { @securable.group "\ntest" }.should raise_error(ArgumentError)
- #lambda { @securable.group "te\nst" }.should raise_error(ArgumentError)
- #lambda { @securable.owner "\ntest" }.should raise_error(ArgumentError)
- #lambda { @securable.owner "te\nst" }.should raise_error(ArgumentError)
+ # lambda { @securable.group "\ntest" }.should raise_error(ArgumentError)
+ # lambda { @securable.group "te\nst" }.should raise_error(ArgumentError)
+ # lambda { @securable.owner "\ntest" }.should raise_error(ArgumentError)
+ # lambda { @securable.owner "te\nst" }.should raise_error(ArgumentError)
end
it "should accept group/owner names in UTF-8" do
@@ -177,7 +176,7 @@ describe Chef::Mixin::Securable do
describe "windows-specific behavior" do
before(:each) do
platform_mock :windows do
- load File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "chef", "mixin", "securable.rb")
+ load File.join(__dir__, "..", "..", "..", "lib", "chef", "mixin", "securable.rb")
securable_class = Class.new do
include Chef::Mixin::Securable
include Chef::Mixin::ParamsValidate
@@ -258,23 +257,23 @@ describe Chef::Mixin::Securable do
end
it "should allow you to specify whether the permissions applies_to_children with true/false/:containers_only/:objects_only" do
- expect { @securable.rights :read, "The Dude", :applies_to_children => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => :containers_only }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => :objects_only }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => "poop" }.to raise_error(ArgumentError)
+ expect { @securable.rights :read, "The Dude", applies_to_children: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: :containers_only }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: :objects_only }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: "poop" }.to raise_error(ArgumentError)
end
it "should allow you to specify whether the permissions applies_to_self with true/false" do
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_self => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_self => "poop" }.to raise_error(ArgumentError)
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, applies_to_self: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_self: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_self: "poop" }.to raise_error(ArgumentError)
end
it "should allow you to specify whether the permissions applies one_level_deep with true/false" do
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => "poop" }.to raise_error(ArgumentError)
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, one_level_deep: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, one_level_deep: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, one_level_deep: "poop" }.to raise_error(ArgumentError)
end
it "should allow multiple rights and deny_rights declarations" do
@@ -288,21 +287,21 @@ describe Chef::Mixin::Securable do
end
it "should allow you to specify whether the permission applies_to_self only if you specified applies_to_children" do
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => false, :applies_to_self => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => false, :applies_to_self => false }.to raise_error(ArgumentError)
- expect { @securable.rights :read, "The Dude", :applies_to_self => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_self => false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, applies_to_self: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, applies_to_self: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: false, applies_to_self: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: false, applies_to_self: false }.to raise_error(ArgumentError)
+ expect { @securable.rights :read, "The Dude", applies_to_self: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_self: false }.not_to raise_error
end
it "should allow you to specify whether the permission applies one_level_deep only if you specified applies_to_children" do
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :applies_to_children => false, :one_level_deep => true }.to raise_error(ArgumentError)
- expect { @securable.rights :read, "The Dude", :applies_to_children => false, :one_level_deep => false }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :one_level_deep => true }.not_to raise_error
- expect { @securable.rights :read, "The Dude", :one_level_deep => false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, one_level_deep: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: true, one_level_deep: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", applies_to_children: false, one_level_deep: true }.to raise_error(ArgumentError)
+ expect { @securable.rights :read, "The Dude", applies_to_children: false, one_level_deep: false }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", one_level_deep: true }.not_to raise_error
+ expect { @securable.rights :read, "The Dude", one_level_deep: false }.not_to raise_error
end
it "should allow you to specify whether the permissions inherit with true/false" do
diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb
index bf74ff410e..2b76a10e6c 100644
--- a/spec/unit/mixin/shell_out_spec.rb
+++ b/spec/unit/mixin/shell_out_spec.rb
@@ -21,92 +21,17 @@
#
require "spec_helper"
+require "chef/mixin/default_paths"
describe Chef::Mixin::ShellOut do
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 ChefUtils.windows?
+ "Path"
+ else
+ "PATH"
end
end
@@ -121,183 +46,263 @@ describe Chef::Mixin::ShellOut do
ENV.update(@original_env)
end
+ let(:retobj) { instance_double(Mixlib::ShellOut, "error!" => false) }
let(:cmd) { "echo '#{rand(1000)}'" }
- describe "#shell_out" 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 } }
- 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" } }
- 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 set environment language settings to the configured internal locale when they are not present" do
- options = { :environment => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :environment => {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd, options)
- end
-
- it "should not mutate the options hash when it adds language settings" do
- options = { :environment => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :environment => {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd, options)
- expect(options[:environment].has_key?("LC_ALL")).to be false
+ %i{shell_out shell_out!}.each do |method|
+ describe "##{method}" 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, env_path => nil } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
+ shell_out_obj.send(method, 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", env_path => "foo:bar:baz" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ end
+
+ it "should set environment language settings to the configured internal locale when they are not present" do
+ options = { environment: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ environment: {
+ "HOME" => "/Users/morty",
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
+ "LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ end
+
+ it "should not mutate the options hash when it adds language settings" do
+ options = { environment: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ environment: {
+ "HOME" => "/Users/morty",
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
+ "LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ expect(options[:environment].key?("LC_ALL")).to be false
+ end
end
- end
- 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 } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out(cmd, options)
+ 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, env_path => nil } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
+ shell_out_obj.send(method, 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", env_path => "foo:bar:baz" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, **options).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ end
+
+ it "should set environment language settings to the configured internal locale when they are not present" do
+ options = { env: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ env: {
+ "HOME" => "/Users/morty",
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
+ "LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ end
+
+ it "should not mutate the options hash when it adds language settings" do
+ options = { env: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ env: {
+ "HOME" => "/Users/morty",
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
+ "LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ expect(options[:env].key?("LC_ALL")).to be false
+ end
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" } }
- 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 set environment language settings to the configured internal locale when they are not present" do
- options = { :env => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :env => {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd, options)
- end
-
- it "should not mutate the options hash when it adds language settings" do
- options = { :env => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :env => {
- "HOME" => "/Users/morty",
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd, options)
- expect(options[:env].has_key?("LC_ALL")).to be false
+ describe "and no env/environment option is present" do
+ it "should set environment language settings to the configured internal locale" do
+ options = { user: "morty" }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ user: "morty",
+ environment: {
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
+ "LANGUAGE" => Chef::Config[:internal_locale],
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd, **options)
+ end
end
end
- describe "and no env/environment option is present" do
+ describe "when the last argument is not a Hash" do
it "should set environment language settings to the configured internal locale" do
- options = { :user => "morty" }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :user => "morty",
- :environment => {
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd,
+ environment: {
+ "LC_ALL" => Chef::Config[:internal_locale],
+ "LANG" => Chef::Config[:internal_locale],
"LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd, options)
+ env_path => shell_out_obj.default_paths,
+ }).and_return(retobj)
+ shell_out_obj.send(method, cmd)
end
end
end
-
- describe "when the last argument is not a Hash" do
- it "should set environment language settings to the configured internal locale" do
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
- :environment => {
- "LC_ALL" => Chef::Config[:internal_locale],
- "LANG" => Chef::Config[:internal_locale],
- "LANGUAGE" => Chef::Config[:internal_locale],
- },
- }).and_return(true)
- shell_out_obj.shell_out(cmd)
- end
- end
-
end
- describe "#shell_out_with_systems_locale" do
+ describe "#shell_out default_env: false" do
describe "when the last argument is a Hash" do
describe "and environment is an option" do
it "should not change environment['LC_ALL'] when set to nil" do
- options = { :environment => { "LC_ALL" => nil } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { environment: { "LC_ALL" => nil } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
it "should not change environment['LC_ALL'] when set to non-nil" do
- options = { :environment => { "LC_ALL" => "en_US.UTF-8" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { environment: { "LC_ALL" => "en_US.UTF-8" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
it "should no longer set environment['LC_ALL'] to nil when 'LC_ALL' not present" do
- options = { :environment => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { environment: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
end
describe "and env is an option" do
it "should not change env when set to nil" do
- options = { :env => { "LC_ALL" => nil } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { env: { "LC_ALL" => nil } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
it "should not change env when set to non-nil" do
- options = { :env => { "LC_ALL" => "en_US.UTF-8" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { env: { "LC_ALL" => "en_US.UTF-8" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
it "should no longer set env['LC_ALL'] to nil when 'LC_ALL' not present" do
- options = { :env => { "HOME" => "/Users/morty" } }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { env: { "HOME" => "/Users/morty" } }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
end
describe "and no env/environment option is present" do
it "should no longer add environment option and set environment['LC_ALL'] to nil" do
- options = { :user => "morty" }
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd, options)
+ options = { user: "morty" }
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, **options, default_env: false)
end
end
end
describe "when the last argument is not a Hash" do
it "should no longer add environment options and set environment['LC_ALL'] to nil" do
- expect(shell_out_obj).to receive(:shell_out_command).with(cmd).and_return(true)
- shell_out_obj.shell_out_with_systems_locale(cmd)
+ expect(shell_out_obj).to receive(:__shell_out_command).with(cmd).and_return(true)
+ shell_out_obj.shell_out(cmd, default_env: false)
end
end
end
+ describe "Custom Resource timeouts" do
+ class CustomResource < Chef::Resource
+ provides :whatever
+
+ property :timeout, Numeric
+
+ action :install do
+ end
+ end
+
+ let(:new_resource) { CustomResource.new("foo") }
+ let(:provider) { new_resource.provider_for_action(:install) }
+
+ %i{shell_out shell_out!}.each do |method|
+ stubbed_method = (method == :shell_out) ? :shell_out_compacted : :shell_out_compacted!
+ it "#{method} defaults to 900 seconds" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 900)
+ provider.send(method, "foo")
+ end
+ it "#{method} overrides the default timeout with its options" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
+ provider.send(method, "foo", timeout: 1)
+ end
+ it "#{method} overrides the new_resource.timeout with the timeout option" do
+ new_resource.timeout(99)
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
+ provider.send(method, "foo", timeout: 1)
+ end
+ it "#{method} defaults to 900 seconds and preserves options" do
+ expect(provider).to receive(stubbed_method).with("foo", env: nil, timeout: 900)
+ provider.send(method, "foo", env: nil)
+ end
+ it "#{method} overrides the default timeout with its options and preserves options" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
+ provider.send(method, "foo", timeout: 1, env: nil)
+ end
+ it "#{method} overrides the new_resource.timeout with the timeout option and preseves options" do
+ new_resource.timeout(99)
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
+ provider.send(method, "foo", timeout: 1, env: nil)
+ end
+ end
+ end
+
+ describe "timeouts" do
+ let(:new_resource) { Chef::Resource::Package.new("foo") }
+ let(:provider) { new_resource.provider_for_action(:install) }
+
+ %i{shell_out shell_out!}.each do |method|
+ stubbed_method = (method == :shell_out) ? :shell_out_compacted : :shell_out_compacted!
+ it "#{method} defaults to 900 seconds" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 900)
+ provider.send(method, "foo")
+ end
+ it "#{method} overrides the default timeout with its options" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
+ provider.send(method, "foo", timeout: 1)
+ end
+ it "#{method} overrides the new_resource.timeout with the timeout option" do
+ new_resource.timeout(99)
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1)
+ provider.send(method, "foo", timeout: 1)
+ end
+ it "#{method} defaults to 900 seconds and preserves options" do
+ expect(provider).to receive(stubbed_method).with("foo", env: nil, timeout: 900)
+ provider.send(method, "foo", env: nil)
+ end
+ it "#{method} overrides the default timeout with its options and preserves options" do
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
+ provider.send(method, "foo", timeout: 1, env: nil)
+ end
+ it "#{method} overrides the new_resource.timeout with the timeout option and preseves options" do
+ new_resource.timeout(99)
+ expect(provider).to receive(stubbed_method).with("foo", timeout: 1, env: nil)
+ provider.send(method, "foo", timeout: 1, env: nil)
+ end
+ end
+ end
end
end
diff --git a/spec/unit/mixin/subclass_directive_spec.rb b/spec/unit/mixin/subclass_directive_spec.rb
index ef3c566838..f7732cc51b 100644
--- a/spec/unit/mixin/subclass_directive_spec.rb
+++ b/spec/unit/mixin/subclass_directive_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,9 +31,9 @@ class ChildWithoutDirective < SubclassDirectiveParent
end
describe Chef::Mixin::Uris do
- let (:child) { SubclassDirectiveChild.new }
+ let(:child) { SubclassDirectiveChild.new }
- let (:other_child) { ChildWithoutDirective.new }
+ let(:other_child) { ChildWithoutDirective.new }
it "the child instance has the directive set" do
expect(child.behave_differently?).to be true
diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb
index ab7ed5bc5a..63522839e9 100644
--- a/spec/unit/mixin/template_spec.rb
+++ b/spec/unit/mixin/template_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
require "cgi"
describe Chef::Mixin::Template, "render_template" do
- let(:sep) { Chef::Platform.windows? ? "\r\n" : "\n" }
+ let(:sep) { ChefUtils.windows? ? "\r\n" : "\n" }
before :each do
@context = Chef::Mixin::Template::TemplateContext.new({})
@@ -39,7 +39,7 @@ describe Chef::Mixin::Template, "render_template" do
describe "when running on windows" do
before do
- allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
it "should render the templates with windows line endings" do
@@ -54,7 +54,7 @@ describe Chef::Mixin::Template, "render_template" do
describe "when running on unix" do
before do
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
end
it "should render the templates with unix line endings" do
@@ -104,16 +104,16 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should render local files" do
- begin
- tf = Tempfile.new("partial")
- tf.write "test"
- tf.rewind
-
- output = @template_context.render_template_from_string("before {<%= render '#{tf.path}', :local => true %>} after")
- expect(output).to eq("before {test} after")
- ensure
- tf.close
- end
+
+ tf = Tempfile.new("partial")
+ tf.write "test"
+ tf.rewind
+
+ output = @template_context.render_template_from_string("before {<%= render '#{tf.path}', :local => true %>} after")
+ expect(output).to eq("before {test} after")
+ ensure
+ tf.close
+
end
it "should render partials from a different cookbook" do
@@ -124,16 +124,16 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should render using the source argument if provided" do
- begin
- tf = Tempfile.new("partial")
- tf.write "test"
- tf.rewind
-
- output = @template_context.render_template_from_string("before {<%= render 'something', :local => true, :source => '#{tf.path}' %>} after")
- expect(output).to eq("before {test} after")
- ensure
- tf.close
- end
+
+ tf = Tempfile.new("partial")
+ tf.write "test"
+ tf.rewind
+
+ output = @template_context.render_template_from_string("before {<%= render 'something', :local => true, :source => '#{tf.path}' %>} after")
+ expect(output).to eq("before {test} after")
+ ensure
+ tf.close
+
end
it "should pass the node to partials" do
@@ -182,6 +182,51 @@ describe Chef::Mixin::Template, "render_template" do
expect(output).to eq("before {partial one We could be diving for pearls! calling home} after")
end
+ describe "when an exception is raised in the template" do
+ let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "templates", "failed.erb")) }
+
+ def do_raise
+ @template_context.render_template(template_file)
+ end
+
+ it "should catch and re-raise the exception as a TemplateError" do
+ expect { do_raise }.to raise_error(Chef::Mixin::Template::TemplateError)
+ end
+
+ describe "the raised TemplateError" do
+ subject(:exception) do
+
+ do_raise
+ rescue Chef::Mixin::Template::TemplateError => e
+ e
+
+ end
+
+ it "should contain template file and line numbers" do
+ expect(exception.line_number).to eq(5)
+ end
+
+ it "should provide a source listing of the template around the exception" do
+ expect(exception.source_listing).to eq(" 3: Which includes some content\n 4: \n 5: And will fail <%= nil[] %>")
+ end
+
+ it "should provide a nice source location" do
+ expect(exception.source_location).to eq("on line #5")
+ end
+
+ it "should create a pretty output for the terminal" do
+ expect(exception.to_s).to match(/Chef::Mixin::Template::TemplateError/)
+ expect(exception.to_s).to match(/undefined method `\[\]' for nil:NilClass/)
+ expect(exception.to_s).to include(" 3: Which includes some content\n 4: \n 5: And will fail <%= nil[] %>")
+ expect(exception.to_s).to include(exception.original_exception.backtrace.first)
+ end
+
+ it "should include template file on original_exception backtrace" do
+ expect(exception.original_exception.backtrace).to include(/#{Regexp.escape(template_file)}/)
+ end
+ end
+ end
+
describe "when customizing the template context" do
it "extends the context to include modules" do
@@ -197,17 +242,13 @@ describe Chef::Mixin::Template, "render_template" do
it "emits a warning when overriding 'core' methods" do
mod = Module.new do
- def render
- end
+ def render; end
- def node
- end
+ def node; end
- def render_template
- end
+ def render_template; end
- def render_template_from_string
- end
+ def render_template_from_string; end
end
%w{node render render_template render_template_from_string}.each do |method_name|
expect(Chef::Log).to receive(:warn).with(/^Core template method `#{method_name}' overridden by extension module/)
@@ -233,11 +274,11 @@ describe Chef::Mixin::Template, "render_template" do
describe "the raised TemplateError" do
before :each do
- begin
- do_raise
- rescue Chef::Mixin::Template::TemplateError => e
- @exception = e
- end
+
+ do_raise
+ rescue Chef::Mixin::Template::TemplateError => e
+ @exception = e
+
end
it "should have the original exception" do
diff --git a/spec/unit/mixin/unformatter_spec.rb b/spec/unit/mixin/unformatter_spec.rb
index b2b57c150c..a2fc69000b 100644
--- a/spec/unit/mixin/unformatter_spec.rb
+++ b/spec/unit/mixin/unformatter_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,14 +22,13 @@ require "chef/mixin/unformatter"
class Chef::UnformatterTest
include Chef::Mixin::Unformatter
- def foo
- end
+ def foo; end
end
describe Chef::Mixin::Unformatter do
- let (:unformatter) { Chef::UnformatterTest.new }
- let (:message) { "Test Message" }
+ let(:unformatter) { Chef::UnformatterTest.new }
+ let(:message) { "Test Message" }
describe "#write" do
context "with a timestamp" do
diff --git a/spec/unit/mixin/uris_spec.rb b/spec/unit/mixin/uris_spec.rb
index 9005edd7b4..b8b500cb38 100644
--- a/spec/unit/mixin/uris_spec.rb
+++ b/spec/unit/mixin/uris_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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::UrisTest
end
describe Chef::Mixin::Uris do
- let (:uris) { Chef::UrisTest.new }
+ let(:uris) { Chef::UrisTest.new }
describe "#uri_scheme?" do
it "matches 'scheme://foo.com'" do
diff --git a/spec/unit/mixin/user_context_spec.rb b/spec/unit/mixin/user_context_spec.rb
new file mode 100644
index 0000000000..6c2ea298ae
--- /dev/null
+++ b/spec/unit/mixin/user_context_spec.rb
@@ -0,0 +1,99 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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(ChefUtils).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
+ 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") { 1 / 0 } }.to raise_error(ZeroDivisionError)
+ end
+ end
+ end
+
+ it_behaves_like "a method that requires a block"
+ end
+
+ context "when not running on Windows" do
+ before do
+ allow(ChefUtils).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..e97c920361
--- /dev/null
+++ b/spec/unit/mixin/versioned_api_spec.rb
@@ -0,0 +1,128 @@
+#
+# Copyright:: Copyright (c) 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..a0030a5c51
--- /dev/null
+++ b/spec/unit/mixin/which.rb
@@ -0,0 +1,170 @@
+#
+# Copyright:: Copyright (c) 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 ["/dir2/foo1", "/dir1/foo2"].include?(f)
+
+ 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
+
+ describe "useful non-stubbed tests" do
+ it "works even when the PATH is nuked via adding default_paths", unix_only: true do
+ old_path = ENV["PATH"]
+ expect(test.which("ls")).to be_truthy
+ ENV["PATH"] = old_path
+ end
+ end
+end
diff --git a/spec/unit/mixin/windows_architecture_helper_spec.rb b/spec/unit/mixin/windows_architecture_helper_spec.rb
index 4559702e1c..b2d64f44b2 100644
--- a/spec/unit/mixin/windows_architecture_helper_spec.rb
+++ b/spec/unit/mixin/windows_architecture_helper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::WindowsArchitectureHelper do
include Chef::Mixin::WindowsArchitectureHelper
before do
- @valid_architectures = [ :i386, :x86_64 ]
+ @valid_architectures = %i{i386 x86_64}
@invalid_architectures = [ "i386", "x86_64", :x64, :x86, :arm ]
@node_i386 = Chef::Node.new
@@ -50,10 +50,10 @@ describe Chef::Mixin::WindowsArchitectureHelper do
it "raises an error if an invalid architecture is passed to assert_valid_windows_architecture!" do
@invalid_architectures.each do |architecture|
- begin
- expect(assert_valid_windows_architecture!(architecture)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
- rescue Chef::Exceptions::Win32ArchitectureIncorrect
- end
+
+ expect(assert_valid_windows_architecture!(architecture)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
+ rescue Chef::Exceptions::Win32ArchitectureIncorrect
+
end
end
@@ -67,14 +67,14 @@ describe Chef::Mixin::WindowsArchitectureHelper do
it "returns true only when forced_32bit_override_required? has 64-bit node architecture and 32-bit desired architecture" do
with_node_architecture_combinations do |node, desired_arch|
expect(forced_32bit_override_required?(node, desired_arch)).to be true if (node_windows_architecture(node) == :x86_64) && (desired_arch == :i386) && !is_i386_process_on_x86_64_windows?
- expect(forced_32bit_override_required?(node, desired_arch)).to be false if ! ((node_windows_architecture(node) == :x86_64) && (desired_arch == :i386))
+ expect(forced_32bit_override_required?(node, desired_arch)).to be false unless (node_windows_architecture(node) == :x86_64) && (desired_arch == :i386)
end
end
def with_node_architecture_combinations
@valid_architectures.each do |node_architecture|
new_node = Chef::Node.new
- new_node.default["kernel"] = Hash.new
+ new_node.default["kernel"] = {}
new_node.default["kernel"][:machine] = node_architecture.to_s
@valid_architectures.each do |architecture|
diff --git a/spec/unit/mixin/xml_escape_spec.rb b/spec/unit/mixin/xml_escape_spec.rb
index 495ad0662c..a1504578ab 100644
--- a/spec/unit/mixin/xml_escape_spec.rb
+++ b/spec/unit/mixin/xml_escape_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,6 +49,6 @@ describe Chef::Mixin::XMLEscape do
end
it "converts win 1252 characters correctly" do
- expect(@escaper.xml_escape("#{0x80.chr}")).to eq("&#8364;")
+ expect(@escaper.xml_escape((0x80.chr).to_s)).to eq("&#8364;")
end
end
diff --git a/spec/unit/monkey_patches/uri_spec.rb b/spec/unit/monkey_patches/uri_spec.rb
deleted file mode 100644
index 0679705cf4..0000000000
--- a/spec/unit/monkey_patches/uri_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-#--
-# Author:: Daniel DeLeo (<dan@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 "spec_helper"
-describe URI do
-
- describe "when a URI contains an IPv6 literal" do
-
- let(:ipv6_uri) do
- URI.parse("https://[2a00:1450:4009:809::1008]:8443")
- end
-
- it "returns the hostname without brackets" do
- expect(ipv6_uri.hostname).to eq("2a00:1450:4009:809::1008")
- end
-
- end
-
-end
diff --git a/spec/unit/monologger_spec.rb b/spec/unit/monologger_spec.rb
index e7f2dfb19a..5448de32b2 100644
--- a/spec/unit/monologger_spec.rb
+++ b/spec/unit/monologger_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index e40f454c0b..6e78725bd9 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 (c) 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" },
@@ -59,16 +63,16 @@ describe Chef::Node::Attribute do
"mtu" => "1280",
"type" => "gif",
"encapsulation" => "IPIP" },
- "vmnet8" => { "flags" => %w{UP BROADCAST SMART RUNNING SIMPLEX MULTICAST},
- "number" => "8",
- "addresses" => { "192.168.4.1" => { "broadcast" => "192.168.4.255",
- "netmask" => "255.255.255.0",
- "family" => "inet" },
- "00:50:56:c0:00:08" => { "family" => "lladdr" } },
- "mtu" => "1500",
- "type" => "vmnet",
- "arp" => { "192.168.4.255" => "ff:ff:ff:ff:ff:ff" },
- "encapsulation" => "Ethernet" },
+ "vmnet8" => { "flags" => %w{UP BROADCAST SMART RUNNING SIMPLEX MULTICAST},
+ "number" => "8",
+ "addresses" => { "192.168.4.1" => { "broadcast" => "192.168.4.255",
+ "netmask" => "255.255.255.0",
+ "family" => "inet" },
+ "00:50:56:c0:00:08" => { "family" => "lladdr" } },
+ "mtu" => "1500",
+ "type" => "vmnet",
+ "arp" => { "192.168.4.255" => "ff:ff:ff:ff:ff:ff" },
+ "encapsulation" => "Ethernet" },
"en0" => { "status" => "inactive",
"flags" => %w{UP BROADCAST SMART RUNNING SIMPLEX MULTICAST},
"number" => "0",
@@ -76,9 +80,9 @@ describe Chef::Node::Attribute do
"mtu" => "1500",
"media" => { "supported" => { "autoselect" => { "options" => [] },
"none" => { "options" => [] },
- "1000baseT" => { "options" => ["full-duplex", "flow-control", "hw-loopback"] },
- "10baseT/UTP" => { "options" => ["half-duplex", "full-duplex", "flow-control", "hw-loopback"] },
- "100baseTX" => { "options" => ["half-duplex", "full-duplex", "flow-control", "hw-loopback"] } },
+ "1000baseT" => { "options" => %w{full-duplex flow-control hw-loopback} },
+ "10baseT/UTP" => { "options" => %w{half-duplex full-duplex flow-control hw-loopback} },
+ "100baseTX" => { "options" => %w{half-duplex full-duplex flow-control hw-loopback} } },
"selected" => { "autoselect" => { "options" => [] } } },
"type" => "en",
"encapsulation" => "Ethernet" },
@@ -114,28 +118,28 @@ describe Chef::Node::Attribute do
"selected" => { "autoselect" => { "options" => [] } } },
"type" => "en",
"encapsulation" => "Ethernet" },
- "fw0" => { "status" => "inactive",
- "flags" => %w{BROADCAST SIMPLEX MULTICAST},
- "number" => "0",
- "addresses" => { "00:23:32:ff:fe:b0:32:f2" => { "family" => "lladdr" } },
- "mtu" => "4078",
- "media" => { "supported" => { "autoselect" => { "options" => ["full-duplex"] } },
- "selected" => { "autoselect" => { "options" => ["full-duplex"] } } },
- "type" => "fw",
- "encapsulation" => "1394" },
- "en3" => { "status" => "active",
- "flags" => %w{UP BROADCAST SMART RUNNING SIMPLEX MULTICAST},
- "number" => "3",
- "addresses" => { "169.254.206.152" => { "broadcast" => "169.254.255.255",
- "netmask" => "255.255.0.0",
- "family" => "inet" },
- "00:1c:42:00:00:00" => { "family" => "lladdr" },
- "fe80::21c:42ff:fe00:0" => { "scope" => "Link", "prefixlen" => "64", "family" => "inet6" } },
- "mtu" => "1500",
- "media" => { "supported" => { "autoselect" => { "options" => [] } },
- "selected" => { "autoselect" => { "options" => [] } } },
- "type" => "en",
- "encapsulation" => "Ethernet" } } },
+ "fw0" => { "status" => "inactive",
+ "flags" => %w{BROADCAST SIMPLEX MULTICAST},
+ "number" => "0",
+ "addresses" => { "00:23:32:ff:fe:b0:32:f2" => { "family" => "lladdr" } },
+ "mtu" => "4078",
+ "media" => { "supported" => { "autoselect" => { "options" => ["full-duplex"] } },
+ "selected" => { "autoselect" => { "options" => ["full-duplex"] } } },
+ "type" => "fw",
+ "encapsulation" => "1394" },
+ "en3" => { "status" => "active",
+ "flags" => %w{UP BROADCAST SMART RUNNING SIMPLEX MULTICAST},
+ "number" => "3",
+ "addresses" => { "169.254.206.152" => { "broadcast" => "169.254.255.255",
+ "netmask" => "255.255.0.0",
+ "family" => "inet" },
+ "00:1c:42:00:00:00" => { "family" => "lladdr" },
+ "fe80::21c:42ff:fe00:0" => { "scope" => "Link", "prefixlen" => "64", "family" => "inet6" } },
+ "mtu" => "1500",
+ "media" => { "supported" => { "autoselect" => { "options" => [] } },
+ "selected" => { "autoselect" => { "options" => [] } } },
+ "type" => "en",
+ "encapsulation" => "Ethernet" } } },
"fqdn" => "latte.local",
"ohai_time" => 1249065590.90391,
"domain" => "local",
@@ -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
@@ -174,13 +178,13 @@ describe Chef::Node::Attribute do
expect(@attributes).to be_a_kind_of(Chef::Node::Attribute)
end
- it "should take an Automatioc, Normal, Default and Override hash" do
+ it "should take an Automatic, Normal, Default and Override hash" do
expect { Chef::Node::Attribute.new({}, {}, {}, {}) }.not_to raise_error
end
- [ :normal, :default, :override, :automatic ].each do |accessor|
+ %i{normal default override automatic}.each do |accessor|
it "should set #{accessor}" do
- na = Chef::Node::Attribute.new({ :normal => true }, { :default => true }, { :override => true }, { :automatic => true })
+ na = Chef::Node::Attribute.new({ normal: true }, { default: true }, { override: true }, { automatic: true })
expect(na.send(accessor)).to eq({ accessor.to_s => true })
end
end
@@ -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,9 +219,7 @@ 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 = [
%w{default default},
%w{env_default env_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
@@ -361,7 +382,7 @@ describe Chef::Node::Attribute do
expect(@attributes["command"]["ps"]).to eq("ps -ef")
end
- it "should return default data if it is not overriden or in attribute data" do
+ it "should return default data if it is not overridden or in attribute data" do
expect(@attributes["music"]["mastodon"]).to eq("rocks")
end
@@ -455,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" } })
@@ -464,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
@@ -471,29 +500,42 @@ 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
it "should return true if an attribute exists" do
- expect(@attributes.has_key?("music")).to eq(true)
+ expect(@attributes.key?("music")).to eq(true)
end
it "should return false if an attribute does not exist" do
- expect(@attributes.has_key?("ninja")).to eq(false)
+ expect(@attributes.key?("ninja")).to eq(false)
end
it "should return false if an attribute does not exist using dot notation" 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
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true)
+ expect(@attributes.key?("does_not_exist_at_all")).to eq(false)
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)
+ @attributes.key?("music")
+ expect(@attributes["music"].key?("apophis")).to eq(true)
end
it "does not find keys above the current nesting level" do
@@ -504,7 +546,7 @@ describe Chef::Node::Attribute do
expect(@attributes["music"]["this"]).not_to have_key("must")
end
- [:include?, :key?, :member?].each do |method|
+ %i{include? key? member?}.each do |method|
it "should alias the method #{method} to itself" do
expect(@attributes).to respond_to(method)
end
@@ -526,25 +568,12 @@ describe Chef::Node::Attribute do
end
- describe "method_missing" do
- it "should behave like a [] lookup" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- 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
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- @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(
{
- "one" => { "two" => "three" },
- "hut" => { "two" => "three" },
+ "one" => { "two" => "three" },
+ "hut" => { "two" => "three" },
"place" => {},
},
{
@@ -560,8 +589,8 @@ describe Chef::Node::Attribute do
end
it "should yield each top level key" do
- collect = Array.new
- @attributes.keys.each do |k|
+ collect = []
+ @attributes.each_key do |k|
collect << k
end
expect(collect.include?("one")).to eq(true)
@@ -573,8 +602,8 @@ describe Chef::Node::Attribute do
end
it "should yield lower if we go deeper" do
- collect = Array.new
- @attributes["one"].keys.each do |k|
+ collect = []
+ @attributes["one"].each_key do |k|
collect << k
end
expect(collect.include?("two")).to eq(true)
@@ -592,11 +621,11 @@ describe Chef::Node::Attribute do
before(:each) do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -608,7 +637,7 @@ describe Chef::Node::Attribute do
end
it "should yield each top level key and value, post merge rules" do
- collect = Hash.new
+ collect = {}
@attributes.each do |k, v|
collect[k] = v
end
@@ -630,11 +659,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -650,7 +679,7 @@ describe Chef::Node::Attribute do
end
it "should yield each top level key, post merge rules" do
- collect = Array.new
+ collect = []
@attributes.each_key do |k|
collect << k
end
@@ -666,11 +695,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -686,7 +715,7 @@ describe Chef::Node::Attribute do
end
it "should yield each top level key and value pair, post merge rules" do
- collect = Hash.new
+ collect = {}
@attributes.each_pair do |k, v|
collect[k] = v
end
@@ -702,11 +731,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -722,7 +751,7 @@ describe Chef::Node::Attribute do
end
it "should yield each value, post merge rules" do
- collect = Array.new
+ collect = []
@attributes.each_value do |v|
collect << v
end
@@ -733,7 +762,7 @@ describe Chef::Node::Attribute do
end
it "should yield four elements" do
- collect = Array.new
+ collect = []
@attributes.each_value do |v|
collect << v
end
@@ -746,11 +775,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -780,11 +809,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -837,11 +866,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -857,11 +886,11 @@ describe Chef::Node::Attribute do
end
it "should return true if any key has the value supplied" do
- expect(@attributes.has_value?("cookies")).to eq(true)
+ expect(@attributes.value?("cookies")).to eq(true)
end
it "should return false no key has the value supplied" do
- expect(@attributes.has_value?("lololol")).to eq(false)
+ expect(@attributes.value?("lololol")).to eq(false)
end
it "should alias value?" do
@@ -882,11 +911,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -923,11 +952,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -959,11 +988,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -978,14 +1007,8 @@ describe Chef::Node::Attribute do
expect(@attributes).to respond_to(:select)
end
- if RUBY_VERSION >= "1.8.7"
- it "should not raise a LocalJumpError if no block is given" do
- expect { @attributes.select }.not_to raise_error
- end
- else
- it "should raise a LocalJumpError if no block is given" do
- expect { @attributes.select }.to raise_error(LocalJumpError)
- end
+ it "should not raise a LocalJumpError if no block is given" do
+ expect { @attributes.select }.not_to raise_error
end
it "should return an empty hash/array (ruby-version-dependent) for a block containing nil" do
@@ -1009,11 +1032,11 @@ describe Chef::Node::Attribute do
before do
@attributes = Chef::Node::Attribute.new(
{
- "one" => "two",
- "hut" => "three",
+ "one" => "two",
+ "hut" => "three",
},
{
- "one" => "four",
+ "one" => "four",
"snakes" => "on a plane",
},
{
@@ -1105,25 +1128,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
@@ -1166,12 +1189,134 @@ 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 "raises an error when using `attr=value`" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
+ 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 "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(FrozenError, /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(FrozenError, /can't modify frozen String/)
+ end
+ end
+
+ describe "deep merging with nils" do
+ it "nils when deep merging between default levels knocks out values" do
+ @attributes.default["foo"] = "bar"
+ expect(@attributes["foo"]).to eql("bar")
+ @attributes.force_default["foo"] = nil
+ expect(@attributes["foo"]).to be nil
+ end
+
+ it "nils when deep merging between override levels knocks out values" do
+ @attributes.override["foo"] = "bar"
+ expect(@attributes["foo"]).to eql("bar")
+ @attributes.force_override["foo"] = nil
+ expect(@attributes["foo"]).to be nil
+ end
+
+ it "nils when deep merging between default+override levels knocks out values" do
+ @attributes.default["foo"] = "bar"
+ expect(@attributes["foo"]).to eql("bar")
+ @attributes.override["foo"] = nil
+ expect(@attributes["foo"]).to be nil
+ end
+
+ it "nils when deep merging between normal+automatic levels knocks out values" do
+ @attributes.normal["foo"] = "bar"
+ expect(@attributes["foo"]).to eql("bar")
+ @attributes.automatic["foo"] = nil
+ expect(@attributes["foo"]).to be nil
+ end
+ end
+
+ describe "to_json" do
+ it "should convert to a valid json string" do
+ json = @attributes["hot"].to_json
+ expect { JSON.parse(json) }.not_to raise_error
+ end
+
+ it "should convert to a json based on current state" do
+ expect(@attributes["hot"].to_json).to eq("{\"day\":\"sunday\"}")
+ end
+ end
+
+ describe "to_yaml" do
+ it "should convert to a valid yaml format" do
+ json = @attributes["hot"].to_yaml
+ expect { YAML.parse(json) }.not_to raise_error
+ end
+
+ it "should convert to a yaml based on current state" do
+ expect(@attributes["hot"].to_yaml).to eq("---\nday: sunday\n")
+ end
+ end
end
diff --git a/spec/unit/node/immutable_collections_spec.rb b/spec/unit/node/immutable_collections_spec.rb
index fe4e50d1bd..836e9862e9 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,16 +19,83 @@
require "spec_helper"
require "chef/node/immutable_collections"
+shared_examples_for "ImmutableMash module" do |param|
+ let(:copy) { @immutable_mash.send(param) }
+
+ it "converts an immutable mash to a new mutable hash" do
+ expect(copy).to be_is_a(Hash)
+ end
+
+ it "converts an immutable nested mash to a new mutable hash" do
+ expect(copy["top_level_4"]["level2"]).to be_is_a(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
+
+shared_examples_for "ImmutableArray module" do |param|
+ let(:copy) { @immutable_nested_array.send(param) }
+
+ 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_is_a(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
+
+shared_examples_for "Immutable#to_yaml" do
+ it "converts an immutable array to a new valid YAML mutable string" do
+ expect { YAML.parse(copy) }.not_to raise_error
+ end
+
+ it "should create a YAML string with content" do
+ # Roundtrip the test string through YAML to compensate for some changes in libyaml-0.2.5
+ # See: https://github.com/yaml/libyaml/pull/186
+ expected = YAML.dump(YAML.load(parsed_yaml))
+
+ expect(copy).to eq(expected)
+ end
+end
+
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,52 +121,46 @@ describe Chef::Node::ImmutableMash do
expect(@immutable_mash[:top_level_4][:level2]).to be_a(Chef::Node::ImmutableMash)
end
- describe "to_hash" do
- before do
- @copy = @immutable_mash.to_hash
- 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
+ # 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
- it "should create a mash with the same content" do
- expect(@copy).to eq(@immutable_mash)
+ %w{to_h to_hash dup}.each do |immutable_meth|
+ describe immutable_meth do
+ include_examples "ImmutableMash module", description
end
+ end
- it "should allow mutation" do
- expect { @copy["m"] = "m" }.not_to raise_error
- end
+ describe "to_yaml" do
+ let(:copy) { @immutable_mash.to_yaml }
+ let(:parsed_yaml) { "---\ntop:\n second_level: some value\ntop_level_2:\n- array\n- of\n- values\ntop_level_3:\n- hash_array: 1\n hash_array_b: 2\ntop_level_4:\n level2:\n key: value\n" }
+ include_examples "Immutable#to_yaml"
end
- [
- :[]=,
- :clear,
- :default=,
- :default_proc=,
- :delete,
- :delete_if,
- :keep_if,
- :merge!,
- :update,
- :reject!,
- :replace,
- :select!,
- :shift,
- :write,
- :write!,
- :unlink,
- :unlink!,
- ].each do |mutator|
+ %i{
+ []=
+ clear
+ default=
+ default_proc=
+ delete
+ delete_if
+ keep_if
+ merge!
+ update
+ reject!
+ 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)
end
@@ -117,7 +178,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
@@ -126,42 +187,46 @@ describe Chef::Node::ImmutableArray do
# with ImmutableMash, above
###
- [
- :<<,
- :[]=,
- :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,
- ].each do |mutator|
+ %i{
+ <<
+ []=
+ clear
+ collect!
+ compact!
+ default=
+ default_proc=
+ delete
+ delete_at
+ delete_if
+ fill
+ flatten!
+ insert
+ keep_if
+ map!
+ merge!
+ pop
+ push
+ reject!
+ reverse!
+ replace
+ select!
+ shift
+ slice!
+ sort!
+ sort_by!
+ uniq!
+ unshift
+ }.each do |mutator|
it "does not allow mutation via `#{mutator}" do
expect { @immutable_array.send(mutator) }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
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
@@ -172,30 +237,22 @@ describe Chef::Node::ImmutableArray do
expect(mutable[0]).to eq(:value)
end
- describe "to_a" do
- before do
- @copy = @immutable_nested_array.to_a
- 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)
+ %w{to_a to_array dup}.each do |immutable_meth|
+ describe immutable_meth do
+ include_examples "ImmutableArray module", description
end
+ end
- it "converts an immutable nested mash to a new mutable hash" do
- expect(@copy[2]).to be_instance_of(Hash)
- end
+ describe "to_yaml" do
+ let(:copy) { @immutable_nested_array.to_yaml }
+ let(:parsed_yaml) { "---\n- level1\n- - foo\n - bar\n - baz\n - 1\n - 2\n - 3\n -\n - true\n - false\n - - el\n - 0\n -\n- m: m\n" }
- it "should create an array with the same content" do
- expect(@copy).to eq(@immutable_nested_array)
- end
+ include_examples "Immutable#to_yaml"
+ end
- it "should allow mutation" do
- expect { @copy << "m" }.not_to raise_error
+ 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
index 5319ba4a35..834b9864f8 100644
--- a/spec/unit/node/vivid_mash_spec.rb
+++ b/spec/unit/node/vivid_mash_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,202 +19,212 @@ require "spec_helper"
require "chef/node/attribute_collections"
describe Chef::Node::VividMash do
- class Root
- attr_accessor :top_level_breadcrumb
- end
-
- let(:root) { Root.new }
+ let(:root) { instance_double(Chef::Node::Attribute) }
let(:vivid) do
- expect(root).to receive(:reset_cache).at_least(:once).with(nil)
- Chef::Node::VividMash.new(root,
- { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
+ root
)
end
- def with_breadcrumb(key)
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
- expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original
+ 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
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read("one", "two", "three")).to eql("four")
end
it "does not trainwreck when hitting hash keys that do not exist" do
- with_breadcrumb("one")
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
- with_breadcrumb("array")
expect(vivid.read("array", 5, "one")).to eql(nil)
end
it "does not trainwreck when hitting an array with a string key" do
- with_breadcrumb("array")
expect(vivid.read("array", "one", "two")).to eql(nil)
end
it "does not trainwreck when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.read("nil", "one", "two")).to eql(nil)
end
end
context "#exist?" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "true if there's a hash key there" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three")).to be true
end
it "true for intermediate hashes" do
- with_breadcrumb("one")
expect(vivid.exist?("one")).to be true
end
it "true for arrays that exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 1)).to be true
end
it "true when the value of the key is nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil")).to be true
end
it "false when attributes don't exist" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "five", "six")).to be false
end
it "false when traversing a non-container" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three", "four")).to be false
end
it "false when an array index does not exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 3)).to be false
end
it "false when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil", "foo", "bar")).to be false
end
end
context "#read!" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read!("one", "two", "three")).to eql("four")
end
it "reads arrays deeply" do
- with_breadcrumb("array")
expect(vivid.read!("array", 1)).to eql(1)
end
it "throws an exception when attributes do not exist" do
- with_breadcrumb("one")
- expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute, "one.five.six")
end
it "throws an exception when traversing a non-container" do
- with_breadcrumb("one")
- expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute, "one.two.three.four")
end
it "throws an exception when an array element does not exist" do
- with_breadcrumb("array")
- expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+ expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute, "array.3")
end
end
context "#write" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
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
- with_breadcrumb("one")
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
- with_breadcrumb("array")
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
- with_breadcrumb("array")
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
- with_breadcrumb("one")
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
- with_breadcrumb("one")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -222,69 +232,55 @@ describe Chef::Node::VividMash do
end
context "#write!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
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
- with_breadcrumb("one")
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
- with_breadcrumb("array")
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
- with_breadcrumb("array")
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
- with_breadcrumb("one")
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
- with_breadcrumb("one")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -292,41 +288,31 @@ describe Chef::Node::VividMash do
end
context "#unlink" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should return nil if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
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
- with_breadcrumb("one")
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
- with_breadcrumb("array")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("nil")
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 })
@@ -334,44 +320,139 @@ describe Chef::Node::VividMash do
end
context "#unlink!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should raise an exception if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
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
- with_breadcrumb("one")
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
- with_breadcrumb("array")
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
- with_breadcrumb("nil")
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
- with_breadcrumb("nil")
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
+
+describe Chef::Node::AttrArray do
+ let(:root) { instance_double(Chef::Node::Attribute) }
+
+ let(:array) do
+ Chef::Node::AttrArray.new(
+ %w{zero one two},
+ root
+ )
+ end
+
+ context "#<<" do
+ it "converts a Hash appended with #<< to a VividMash" do
+ array << { "three" => "four" }
+ expect(array[3].class).to eql(Chef::Node::VividMash)
+ end
+
+ it "deeply converts objects appended with #<<" do
+ array << [ { "three" => [ 0, 1] } ]
+ expect(array[3].class).to eql(Chef::Node::AttrArray)
+ expect(array[3][0].class).to eql(Chef::Node::VividMash)
+ expect(array[3][0]["three"].class).to eql(Chef::Node::AttrArray)
+ end
+ end
+
+ context "#[]=" do
+ it "assigning a Hash into an array converts it to VividMash" do
+ array[0] = { "zero" => "zero2" }
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#push" do
+ it "pushing a Hash into an array converts it to VividMash" do
+ array.push({ "three" => "four" })
+ expect(array[3].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#unshift" do
+ it "unshifting a Hash into an array converts it to VividMash" do
+ array.unshift({ "zero" => "zero2" })
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#insert" do
+ it "inserting a Hash into an array converts it to VividMash" do
+ array.insert(1, { "zero" => "zero2" })
+ expect(array[1].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#collect!" do
+ it "converts Hashes" do
+ array.collect! { |x| { "zero" => "zero2" } }
+ expect(array[1].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#map!" do
+ it "converts Hashes" do
+ array.map! { |x| { "zero" => "zero2" } }
+ expect(array[1].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#compact!" do
+ it "VividMashes remain VividMashes" do
+ array = Chef::Node::AttrArray.new(
+ [ nil, { "one" => "two" }, nil ],
+ root
+ )
+ expect(array[1].class).to eql(Chef::Node::VividMash)
+ array.compact!
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#fill" do
+ it "inserts VividMashes for Hashes" do
+ array.fill({ "one" => "two" })
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#flatten!" do
+ it "flattens sub-arrays maintaining VividMashes in them" do
+ array = Chef::Node::AttrArray.new(
+ [ [ { "one" => "two" } ], [ { "one" => "two" } ] ],
+ root
+ )
+ expect(array[0][0].class).to eql(Chef::Node::VividMash)
+ array.flatten!
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+
+ context "#replace" do
+ it "replaces the array converting hashes to mashes" do
+ array.replace([ { "foo" => "bar" } ])
+ expect(array[0].class).to eql(Chef::Node::VividMash)
+ end
+ end
+end
diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb
index 0480a721af..91d203d6a2 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,15 @@
require "spec_helper"
require "chef/node_map"
+class Foo; end
+class Bar; end
+
+class FooResource < Chef::Resource; end
+class BarResource < Chef::Resource; end
+
+class FooProvider < Chef::Provider; end
+class BarProvider < Chef::Provider; end
+
describe Chef::NodeMap do
let(:node_map) { Chef::NodeMap.new }
@@ -95,12 +104,68 @@ describe Chef::NodeMap do
end
it "returns nil when the platform_family does not match" do
- allow(node).to receive(:[]).with(:os).and_return("linux")
- allow(node).to receive(:[]).with(:platform_family).and_return("debian")
+ node.automatic["os"] = "linux"
+ node.automatic["platform_family"] = "debian"
+ expect(node_map.get(node, :thing)).to eql(nil)
+ 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 include({ thing: [{ klass: Bar, target_mode: nil }] })
+ 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, target_mode: nil }] })
+ 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, target_mode: nil }], thing2: [{ klass: Bar, target_mode: nil }] })
+ 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|
@@ -109,26 +174,26 @@ describe Chef::NodeMap do
end
it "returns the value when the node matches" do
- allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
- allow(node).to receive(:[]).with(:platform_version).and_return("7.0")
+ node.automatic["platform_family"] = "rhel"
+ node.automatic["platform_version"] = "7.0"
expect(node_map.get(node, :thing)).to eql(:foo)
end
it "returns nil when the block does not match" do
- allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
- allow(node).to receive(:[]).with(:platform_version).and_return("6.4")
+ node.automatic["platform_family"] = "rhel"
+ node.automatic["platform_version"] = "6.4"
expect(node_map.get(node, :thing)).to eql(nil)
end
it "returns nil when the platform_family filter does not match" do
- allow(node).to receive(:[]).with(:platform_family).and_return("debian")
- allow(node).to receive(:[]).with(:platform_version).and_return("7.0")
+ node.automatic["platform_family"] = "debian"
+ node.automatic["platform_version"] = "7.0"
expect(node_map.get(node, :thing)).to eql(nil)
end
it "returns nil when both do not match" do
- allow(node).to receive(:[]).with(:platform_family).and_return("debian")
- allow(node).to receive(:[]).with(:platform_version).and_return("6.0")
+ node.automatic["platform_family"] = "debian"
+ node.automatic["platform_version"] = "6.0"
expect(node_map.get(node, :thing)).to eql(nil)
end
@@ -138,32 +203,73 @@ describe Chef::NodeMap do
end
it "returns the value when the node matches" do
- allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
- allow(node).to receive(:[]).with(:platform_version).and_return("7.0")
+ node.automatic["platform_family"] = "rhel"
+ node.automatic["platform_version"] = "7.0"
expect(node_map.get(node, :thing)).to eql(:foo)
end
end
end
- describe "resource back-compat testing" do
- before :each do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ # When in target mode, only match when target_mode is explicitly supported
+ context "when target mode is enabled" do
+ before do
+ allow(Chef::Config).to receive(:target_mode?).and_return(true)
+ end
+
+ it "returns the value when target_mode matches" do
+ node_map.set(:something, :network, target_mode: true)
+ expect(node_map.get(node, :something)).to eql(:network)
+ end
+
+ it "returns nil when target_mode does not match" do
+ node_map.set(:something, :local, target_mode: false)
+ expect(node_map.get(node, :something)).to eql(nil)
+ end
+ end
+
+ # When not in target mode, match regardless of target_mode filter
+ context "when target mode is not enabled" do
+ before do
+ allow(Chef::Config).to receive(:target_mode?).and_return(false)
+ end
+
+ it "returns the value if target_mode matches" do
+ node_map.set(:something, :local, target_mode: true)
+ expect(node_map.get(node, :something)).to eql(:local)
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)
+ it "returns the value if target_mode does not match" do
+ node_map.set(:something, :local, target_mode: false)
+ expect(node_map.get(node, :something)).to eql(:local)
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
+
+ describe "locked mode" do
+ context "while unlocked" do
+ it "allows setting the same key twice" do
+ expect(Chef::Log).to_not receive(:warn)
+ node_map.set(:foo, FooResource)
+ node_map.set(:foo, BarResource)
+ expect(node_map.get(node, :foo)).to eql(BarResource)
+ end
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)
+
+ context "while locked" do
+ it "warns on setting the same key twice" do
+ expect(Chef::Log).to receive(:warn).with(/Resource foo/)
+ node_map.set(:foo, FooResource)
+ node_map.lock!
+ node_map.set(:foo, BarResource)
+ expect(node_map.get(node, :foo)).to eql(BarResource)
+ end
+
+ it "warns on setting the same key twice for a provider" do
+ expect(Chef::Log).to receive(:warn).with(/Provider foo/)
+ node_map.set(:foo, FooProvider)
+ node_map.lock!
+ node_map.set(:foo, BarProvider)
+ expect(node_map.get(node, :foo)).to eql(BarProvider)
+ end
end
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 2c8fc4408b..ec7beb9a50 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "ostruct"
describe Chef::Node do
- let(:node) { Chef::Node.new() }
+ let(:node) { Chef::Node.new }
let(:platform_introspector) { node }
it_behaves_like "a platform introspector"
@@ -52,8 +52,8 @@ describe Chef::Node do
describe "when the node does not exist on the server" do
before do
- response = OpenStruct.new(:code => "404")
- exception = Net::HTTPServerException.new("404 not found", response)
+ response = OpenStruct.new(code: "404")
+ exception = Net::HTTPClientException.new("404 not found", response)
allow(Chef::Node).to receive(:load).and_raise(exception)
node.name("created-node")
end
@@ -103,7 +103,7 @@ describe Chef::Node do
end
it "should always have a string for name" do
- expect { node.name(Hash.new) }.to raise_error(ArgumentError)
+ expect { node.name({}) }.to raise_error(ArgumentError)
end
it "cannot be blank" do
@@ -126,7 +126,7 @@ describe Chef::Node do
end
it "should disallow non-strings" do
- expect { node.chef_environment(Hash.new) }.to raise_error(ArgumentError)
+ expect { node.chef_environment({}) }.to raise_error(ArgumentError)
expect { node.chef_environment(42) }.to raise_error(ArgumentError)
end
@@ -162,7 +162,7 @@ describe Chef::Node do
end
it "disallows non-strings" do
- expect { node.policy_name(Hash.new) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { node.policy_name({}) }.to raise_error(Chef::Exceptions::ValidationFailed)
expect { node.policy_name(42) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
@@ -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
@@ -198,7 +198,7 @@ describe Chef::Node do
end
it "disallows non-strings" do
- expect { node.policy_group(Hash.new) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { node.policy_group({}) }.to raise_error(Chef::Exceptions::ValidationFailed)
expect { node.policy_group(42) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
@@ -238,15 +238,25 @@ describe Chef::Node do
expect(node["battles"]["people"].attribute?("snozzberry")).to eq(false)
end
- it "does not allow you to set an attribute via method_missing" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- 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
+ it "does not allow modification of node attributes via array methods" do
Chef::Config[:treat_deprecation_warnings_as_errors] = false
- node.default.sunshine = "is bright"
- expect(node.sunshine).to eql("is bright")
+ 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
@@ -255,12 +265,6 @@ describe Chef::Node do
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
- it "should allow you to set an attribute with set_unless with method_missing but emit a deprecation warning" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = 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 an attribute with set_unless" do
node.normal_unless[:snoopy][:is_a_puppy] = false
expect(node[:snoopy][:is_a_puppy]).to eq(false)
@@ -293,30 +297,38 @@ describe Chef::Node do
expect(node[:snoopy][:is_a_puppy]).to eq(true)
end
- it "auto-vivifies attributes created via method syntax" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- node.normal.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
+ it "should let you use tag as a convince 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 "set is a deprecated alias for normal" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(Chef).to receive(:log_deprecation).with(/set is deprecated/)
- node.set[:snoopy][:is_a_puppy] = true
- expect(node[:snoopy][:is_a_puppy]).to eq(true)
+ 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 "set_unless is a deprecated alias for normal_unless" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(Chef).to receive(:log_deprecation).with(/set_unless is deprecated/)
- node.set_unless[:snoopy][:is_a_puppy] = false
- expect(node[:snoopy][:is_a_puppy]).to eq(false)
+ 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
@@ -358,12 +370,6 @@ describe Chef::Node do
expect(node["a"]["r1"]["g"]["u"]).to eql("u1")
end
- it "auto-vivifies attributes created via method syntax" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- node.default.fuu.bahrr.baz = "qux"
- expect(node.fuu.bahrr.baz).to eq("qux")
- 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"
@@ -420,12 +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
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- node.override.fuu.bahrr.baz = "qux"
- expect(node.fuu.bahrr.baz).to eq("qux")
- end
end
describe "globally deleting attributes" do
@@ -436,7 +436,7 @@ describe Chef::Node do
node.override["mysql"]["server"]["port"] = 3456
end
- it "deletes all the values and returns the value with the highest precidence" do
+ it "deletes all the values and returns the value with the highest precedence" do
expect( node.rm("mysql", "server", "port") ).to eql(3456)
expect( node["mysql"]["server"]["port"] ).to be_nil
expect( node["mysql"]["server"] ).to eql({})
@@ -527,7 +527,7 @@ describe Chef::Node do
expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
end
- it "returns nil for the combined attribues" do
+ it "returns nil for the combined attributes" do
expect( node.rm_default("mysql", "server", "port") ).to eql(3456)
expect( node["mysql"]["server"]["port"] ).to eql(nil)
end
@@ -633,7 +633,7 @@ describe Chef::Node do
expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql" })
end
- it "replaces a value at the cookbook sub-level of the atributes only" do
+ it "replaces a value at the cookbook sub-level of the attributes only" do
node.default["mysql"]["server"]["port"] = 2345
node.default["mysql"]["server"]["service_name"] = "fancypants-sql"
node.role_default["mysql"]["server"]["port"] = 1234
@@ -756,21 +756,20 @@ 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
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
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
node.force_default[:solr][:version] = "4.10.2"
- node.force_default[:solr][:data_dir] = "/opt/solr-#{node['solr'][:version]}/example/solr"
+ node.force_default[:solr][:data_dir] = "/opt/solr-#{node["solr"][:version]}/example/solr"
node.force_default[:solr][:xms] = "512M"
expect(node[:solr][:xms]).to eql("512M")
expect(node["solr"][:xms]).to eql("512M")
@@ -779,8 +778,8 @@ describe Chef::Node do
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"] = "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
@@ -792,9 +791,9 @@ describe Chef::Node do
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"
- seen_attributes = Hash.new
+ node.default["sunshine"] = "is bright"
+ node.default["canada"] = "is a nice place"
+ seen_attributes = {}
node.each_attribute do |a, v|
seen_attributes[a] = v
end
@@ -853,7 +852,7 @@ describe Chef::Node do
describe "consuming json" do
before do
- @ohai_data = { :platform => "foo", :platform_version => "bar" }
+ @ohai_data = { platform: "foo", platform_version: "bar" }
end
it "consumes the run list portion of a collection of attributes and returns the remainder" do
@@ -941,11 +940,28 @@ describe Chef::Node do
expect(node["one"].to_hash).to eq({ "two" => { "three" => "forty-two" } })
end
+ it "converts the platform_version to a Chef::VersionString" do
+ node.consume_external_attrs(@ohai_data, {})
+ expect(node["platform_version"]).to be_kind_of(Chef::VersionString)
+ end
+ end
+
+ describe "merging ohai data" do
+ before do
+ @ohai_data = { platform: "foo", platform_version: "bar" }
+ end
+
+ it "converts the platform_version to a Chef::VersionString" do
+ node.consume_external_attrs(@ohai_data, {})
+ node.consume_ohai_data({ "platform_version" => "6.3" })
+ expect(node["platform_version"]).to be_kind_of(Chef::VersionString)
+ expect(node["platform_version"] =~ "~> 6.1").to be true
+ end
end
describe "preparing for a chef client run" do
before do
- @ohai_data = { :platform => "foobuntu", :platform_version => "23.42" }
+ @ohai_data = { platform: "foobuntu", platform_version: "23.42" }
end
it "sets its platform according to platform detection" do
@@ -967,6 +983,13 @@ describe Chef::Node do
expect(node.normal_attrs).to eq({ "foo" => "bar", "tags" => [] })
end
+ it "converts the platform_version to a Chef::VersionString" do
+ node.consume_external_attrs(@ohai_data, {})
+ expect(node.automatic_attrs[:platform_version]).to be_a_kind_of(Chef::VersionString)
+ expect(node[:platform_version]).to be_a_kind_of(Chef::VersionString)
+ expect(node[:platform_version] =~ "~> 23.6").to be true
+ end
+
end
describe "when expanding its run list and merging attributes" do
@@ -1089,12 +1112,12 @@ describe Chef::Node do
before do
node.chef_environment = "rspec"
@expansion = Chef::RunList::RunListExpansion.new("rspec", [])
- @expansion.default_attrs.replace({ :default => "from role", :d_role => "role only" })
- @expansion.override_attrs.replace({ :override => "from role", :o_role => "role only" })
+ @expansion.default_attrs.replace({ default: "from role", d_role: "role only" })
+ @expansion.override_attrs.replace({ override: "from role", o_role: "role only" })
@environment = Chef::Environment.new
- @environment.default_attributes = { :default => "from env", :d_env => "env only" }
- @environment.override_attributes = { :override => "from env", :o_env => "env only" }
+ @environment.default_attributes = { default: "from env", d_env: "env only" }
+ @environment.override_attributes = { override: "from env", o_env: "env only" }
allow(Chef::Environment).to receive(:load).and_return(@environment)
node.apply_expansion_attributes(@expansion)
end
@@ -1153,16 +1176,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
@@ -1189,7 +1212,7 @@ describe Chef::Node do
expect(node.name).to eql("test.example.com-short")
expect(node["sunshine"]).to eql("in")
expect(node["something"]).to eql("else")
- expect(node.run_list).to eq(["operations-master", "operations-monitoring"])
+ expect(node.run_list).to eq(%w{operations-master operations-monitoring})
end
it "should raise an exception if the file cannot be found or read" do
@@ -1208,7 +1231,7 @@ describe Chef::Node do
node.run_list << "role[leninist]"
node.run_list << "recipe[stalinist]"
- @example = Chef::Node.new()
+ @example = Chef::Node.new
@example.name("newname")
@example.chef_environment("prod")
@example.default_attrs = { "alpha" => { "bravo" => "charlie", "delta" => "echo" } }
@@ -1262,7 +1285,7 @@ describe Chef::Node do
end
describe "converting to or from json" do
- it "should serialize itself as json", :json => true do
+ it "should serialize itself as json", json: true do
node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
json = Chef::JSONCompat.to_json(node)
expect(json).to match(/json_class/)
@@ -1274,16 +1297,16 @@ describe Chef::Node do
expect(json).to match(/run_list/)
end
- it "should serialize valid json with a run list", :json => true do
- #This test came about because activesupport mucks with Chef json serialization
- #Test should pass with and without Activesupport
+ it "should serialize valid json with a run list", json: true do
+ # This test came about because activesupport mucks with Chef json serialization
+ # Test should pass with and without Activesupport
node.run_list << { "type" => "role", "name" => "Cthulu" }
node.run_list << { "type" => "role", "name" => "Hastur" }
json = Chef::JSONCompat.to_json(node)
expect(json).to match(/\"run_list\":\[\"role\[Cthulu\]\",\"role\[Hastur\]\"\]/)
end
- it "should serialize the correct run list", :json => true do
+ it "should serialize the correct run list", json: true do
node.run_list << "role[marxist]"
node.run_list << "role[leninist]"
node.override_runlist << "role[stalinist]"
@@ -1308,7 +1331,7 @@ describe Chef::Node do
expect(node_for_json["default"]["env default"]).to eq("env default")
end
- it "should deserialize itself from json", :json => true do
+ it "should deserialize itself from json", json: true do
node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
json = Chef::JSONCompat.to_json(node)
serialized_node = Chef::Node.from_hash(Chef::JSONCompat.parse(json))
@@ -1354,6 +1377,7 @@ 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
@@ -1384,8 +1408,8 @@ describe Chef::Node do
describe "list" do
describe "inflated" do
it "should return a hash of node names and objects" do
- n1 = double("Chef::Node", :name => "one")
- allow(n1).to receive(:kind_of?).with(Chef::Node) { true }
+ n1 = double("Chef::Node", name: "one")
+ allow(n1).to receive(:is_a?).with(Chef::Node) { true }
expect(@query).to receive(:search).with(:node).and_yield(n1)
r = Chef::Node.list(true)
expect(r["one"]).to eq(n1)
@@ -1438,8 +1462,8 @@ describe Chef::Node do
it "should create if it cannot update" do
node.name("monkey")
allow(node).to receive(:data_for_save).and_return({})
- exception = double("404 error", :code => "404")
- expect(@rest).to receive(:put).and_raise(Net::HTTPServerException.new("foo", exception))
+ exception = double("404 error", code: "404")
+ expect(@rest).to receive(:put).and_raise(Net::HTTPClientException.new("foo", exception))
expect(@rest).to receive(:post).with("nodes", {})
node.save
end
@@ -1459,17 +1483,16 @@ describe Chef::Node do
end
end
- context "with whitelisted attributes configured" do
- it "should only save whitelisted attributes (and subattributes)" do
- Chef::Config[:automatic_attribute_whitelist] = [
+ context "with allowed attributes configured" do
+ it "should only save allowed attributes (and subattributes)" do
+ Chef::Config[:allowed_default_attributes] = [
["filesystem", "/dev/disk0s2"],
"network/interfaces/eth0",
]
- data = {
- "automatic" => {
+ node.default = {
"filesystem" => {
- "/dev/disk0s2" => { "size" => "10mb" },
+ "/dev/disk0s2" => { "size" => "10mb" },
"map - autohome" => { "size" => "10mb" },
},
"network" => {
@@ -1478,12 +1501,13 @@ describe Chef::Node do
"eth1" => {},
},
},
- },
- "default" => {}, "normal" => {}, "override" => {}
- }
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
selected_data = {
- "automatic" => {
+ "default" => {
"filesystem" => {
"/dev/disk0s2" => { "size" => "10mb" },
},
@@ -1493,22 +1517,20 @@ describe Chef::Node do
},
},
},
- "default" => {}, "normal" => {}, "override" => {}
+ "automatic" => {}, "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")
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
node.save
end
- it "should save false-y whitelisted attributes" do
- Chef::Config[:default_attribute_whitelist] = [
+ it "should save false-y allowed attributes" do
+ Chef::Config[:allowed_default_attributes] = [
"foo/bar/baz",
]
- data = {
- "default" => {
+ node.default = {
"foo" => {
"bar" => {
"baz" => false,
@@ -1517,8 +1539,11 @@ describe Chef::Node do
"stuff" => true,
},
},
- },
- }
+ }
+
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
selected_data = {
"default" => {
@@ -1531,31 +1556,186 @@ describe Chef::Node do
}
node.name("falsey-monkey")
- allow(node).to receive(:for_json).and_return(data)
- expect(@rest).to receive(:put).with("nodes/falsey-monkey", selected_data).and_return("foo")
+ expect(@rest).to receive(:put).with("nodes/falsey-monkey", hash_including(selected_data)).and_return("foo")
node.save
end
- it "should not save any attributes if the whitelist is empty" do
- Chef::Config[:automatic_attribute_whitelist] = []
+ it "should not save any attributes if the allowed is empty" do
+ Chef::Config[:allowed_default_attributes] = []
- data = {
- "automatic" => {
+ node.default = {
"filesystem" => {
- "/dev/disk0s2" => { "size" => "10mb" },
+ "/dev/disk0s2" => { "size" => "10mb" },
"map - autohome" => { "size" => "10mb" },
},
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
+
+ selected_data = {
+ "automatic" => {}, "default" => {}, "normal" => {}, "override" => {}
+ }
+
+ node.name("picky-monkey")
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
+ node.save
+ end
+ end
+
+ context "with deprecated whitelist attributes configured" do
+ it "should only save allowed attributes (and subattributes)" do
+ Chef::Config[:default_attribute_whitelist] = [
+ ["filesystem", "/dev/disk0s2"],
+ "network/interfaces/eth0",
+ ]
+
+ node.default = {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth0" => {},
+ "eth1" => {},
+ },
+ },
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
+
+ selected_data = {
+ "default" => {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth0" => {},
+ },
+ },
},
- "default" => {}, "normal" => {}, "override" => {}
+ "automatic" => {}, "normal" => {}, "override" => {}
}
+ node.name("picky-monkey")
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
+ node.save
+ end
+ end
+
+ context "with deprecated blacklist attributes configured" do
+ it "should only save non-blocklisted attributes (and subattributes)" do
+ Chef::Config[:default_attribute_blacklist] = [
+ ["filesystem", "/dev/disk0s2"],
+ "network/interfaces/eth0",
+ ]
+
+ node.default = {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth0" => {},
+ "eth1" => {},
+ },
+ },
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
+
selected_data = {
- "automatic" => {}, "default" => {}, "normal" => {}, "override" => {}
+ "default" => {
+ "filesystem" => {
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth1" => {},
+ },
+ },
+ },
+ "automatic" => {}, "normal" => {}, "override" => {}
}
+ node.name("picky-monkey")
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
+ node.save
+ end
+ end
+
+ context "with blocklisted attributes configured" do
+ it "should only save non-blocklisted attributes (and subattributes)" do
+ Chef::Config[:blocked_default_attributes] = [
+ ["filesystem", "/dev/disk0s2"],
+ "network/interfaces/eth0",
+ ]
+
+ node.default = {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth0" => {},
+ "eth1" => {},
+ },
+ },
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
+ selected_data = {
+ "default" => {
+ "filesystem" => {
+ "map - autohome" => { "size" => "10mb" },
+ },
+ "network" => {
+ "interfaces" => {
+ "eth1" => {},
+ },
+ },
+ },
+ "automatic" => {}, "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")
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
+ node.save
+ end
+
+ it "should save all attributes if the blocklist is empty" do
+ Chef::Config[:blocked_default_attributes] = []
+
+ node.default = {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ }
+ node.automatic = {}
+ node.normal = {}
+ node.override = {}
+
+ selected_data = {
+ "default" => {
+ "filesystem" => {
+ "/dev/disk0s2" => { "size" => "10mb" },
+ "map - autohome" => { "size" => "10mb" },
+ },
+ },
+ "automatic" => {}, "normal" => {}, "override" => {}
+ }
+
+ node.name("picky-monkey")
+ expect(@rest).to receive(:put).with("nodes/picky-monkey", hash_including(selected_data)).and_return("foo")
node.save
end
end
@@ -1593,11 +1773,11 @@ describe Chef::Node do
end
let(:http_exception) do
- begin
- response.error!
- rescue => e
- e
- end
+
+ response.error!
+ rescue => e
+ e
+
end
let(:trimmed_node) do
@@ -1608,55 +1788,9 @@ describe Chef::Node do
end
- context "on Chef Client 13 and later" do
-
- # 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
-
- context "when the node exists" do
-
- 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
-
- end
-
- context "when the node doesn't exist" do
-
- let(:response_404) do
- Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found")
- end
-
- let(:http_exception_404) do
- begin
- response_404.error!
- rescue => e
- e
- 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_raise(http_exception_404)
- expect(@rest).to receive(:post).with("nodes", trimmed_node).and_return(@node)
- node.save
- 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
+ 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
end
@@ -1681,4 +1815,140 @@ describe Chef::Node do
end
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
+
+ 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 "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 "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
+
+ # 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
+
+ # 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 update 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
+
+ describe "when abusing the deep merge cache" do
+ # https://github.com/chef/chef/issues/7738
+ it "do not corrupt VividMashes that are part of the merge set and not the merge_onto set" do
+ # need to have a merge two-deep (not at the top-level) between at least two default (or two override)
+ # levels where the lowest priority one is the one that is going to be corrupted
+ node.default["foo"]["bar"]["baz"] = "fizz"
+ node.env_default["foo"]["bar"]["quux"] = "buzz"
+ node.default["foo"]["bar"].tap do |bar|
+ bar["test"] = "wrong"
+ # this triggers a deep merge
+ node["foo"]["bar"]["test"]
+ # this should correctly write and dirty the cache so the next read does another deep merge on the correct __root__
+ bar["test"] = "right"
+ end
+ expect(node["foo"]["bar"]["test"]).to eql("right")
+ end
+
+ it "do not corrupt VividMashes that are part of the merge set and not the merge_onto set (when priorities are reversed)" do
+ # need to have a merge two-deep (not at the top-level) between at least two default (or two override)
+ # levels where the *HIGHEST* priority one is the one that is going to be corrupted
+ node.env_default["foo"]["bar"]["baz"] = "fizz"
+ node.default["foo"]["bar"]["quux"] = "buzz"
+ node.env_default["foo"]["bar"].tap do |bar|
+ bar["test"] = "wrong"
+ # this triggers a deep merge
+ node["foo"]["bar"]["test"]
+ # this should correctly write and dirty the cache so the next read does another deep merge on the correct __root__
+ bar["test"] = "right"
+ end
+ expect(node["foo"]["bar"]["test"]).to eql("right")
+ end
+ end
end
diff --git a/spec/unit/org_spec.rb b/spec/unit/org_spec.rb
index 49557417a5..0e1e72f968 100644
--- a/spec/unit/org_spec.rb
+++ b/spec/unit/org_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -52,7 +52,7 @@ describe Chef::Org do
end
it "raises an ArgumentError if you feed it anything but a string" do
- expect { org.name Hash.new }.to raise_error(ArgumentError)
+ expect { org.name({}) }.to raise_error(ArgumentError)
end
end
@@ -63,7 +63,7 @@ describe Chef::Org do
end
it "raises an ArgumentError if you feed it anything but a string" do
- expect { org.name Hash.new }.to raise_error(ArgumentError)
+ expect { org.name({}) }.to raise_error(ArgumentError)
end
end
@@ -74,7 +74,7 @@ describe Chef::Org do
end
it "raises an ArgumentError if you feed it something lame" do
- expect { org.private_key Hash.new }.to raise_error(ArgumentError)
+ expect { org.private_key({}) }.to raise_error(ArgumentError)
end
end
@@ -164,7 +164,7 @@ describe Chef::Org do
describe "create" do
it "creates a new org via the API" do
- expect(rest).to receive(:post).with("organizations", { :name => "foobar", :full_name => "foo bar bat" }).and_return({})
+ expect(rest).to receive(:post).with("organizations", { name: "foobar", full_name: "foo bar bat" }).and_return({})
org.create
end
end
@@ -181,7 +181,7 @@ describe Chef::Org do
describe "update" do
it "updates an existing org on via the API" do
- expect(rest).to receive(:put).with("organizations/foobar", { :name => "foobar", :full_name => "foo bar bat" }).and_return({})
+ expect(rest).to receive(:put).with("organizations/foobar", { name: "foobar", full_name: "foo bar bat" }).and_return({})
org.update
end
end
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index aa2b3c1f11..0b4169810e 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,162 +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"
-
- let(:key) { "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels" }
- let(:key_query_value) { 0x0001 }
- let(:access) { key_query_value | 0x0100 }
- let(:hive) { double("Win32::Registry::HKEY_LOCAL_MACHINE") }
- let(:registry) { double("Win32::Registry") }
-
- before(:all) do
- Win32::Registry = Class.new
- Win32::Registry::Error = Class.new(RuntimeError)
- end
-
- before do
- Win32::Registry::HKEY_LOCAL_MACHINE = hive
- Win32::Registry::KEY_QUERY_VALUE = key_query_value
- end
-
- after do
- Win32::Registry.send(:remove_const, "HKEY_LOCAL_MACHINE") if defined?(Win32::Registry::HKEY_LOCAL_MACHINE)
- Win32::Registry.send(:remove_const, "KEY_QUERY_VALUE") if defined?(Win32::Registry::KEY_QUERY_VALUE)
- end
-
- it "returns false early when not on windows" do
- allow(ChefConfig).to receive(:windows?).and_return(false)
- expect(Chef::Platform).to_not receive(:require)
- expect(Chef::Platform.windows_nano_server?).to be false
- end
-
- it "returns true when the registry value is 1" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_yield(registry)
- expect(registry).to receive(:[]).with("NanoServer").and_return(1)
- expect(Chef::Platform.windows_nano_server?).to be true
- end
-
- it "returns false when the registry value is not 1" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_yield(registry)
- expect(registry).to receive(:[]).with("NanoServer").and_return(0)
- expect(Chef::Platform.windows_nano_server?).to be false
- end
-
- it "returns false when the registry value does not exist" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_yield(registry)
- expect(registry).to receive(:[]).with("NanoServer").
- and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
- expect(Chef::Platform.windows_nano_server?).to be false
- end
-
- it "returns false when the registry key does not exist" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
- expect(Chef::Platform.windows_nano_server?).to be false
- end
-end
-
-describe "Chef::Platform#supports_msi?" do
- include_context "Win32" # clear and restore Win32:: namespace
-
- let(:key) { "System\\CurrentControlSet\\Services\\msiserver" }
- let(:key_query_value) { 0x0001 }
- let(:access) { key_query_value }
- let(:hive) { double("Win32::Registry::HKEY_LOCAL_MACHINE") }
- let(:registry) { double("Win32::Registry") }
-
- before(:all) do
- Win32::Registry = Class.new
- Win32::Registry::Error = Class.new(RuntimeError)
- end
-
- before do
- Win32::Registry::HKEY_LOCAL_MACHINE = hive
- Win32::Registry::KEY_QUERY_VALUE = key_query_value
- end
-
- after do
- Win32::Registry.send(:remove_const, "HKEY_LOCAL_MACHINE") if defined?(Win32::Registry::HKEY_LOCAL_MACHINE)
- Win32::Registry.send(:remove_const, "KEY_QUERY_VALUE") if defined?(Win32::Registry::KEY_QUERY_VALUE)
- end
-
- it "returns false early when not on windows" do
- allow(ChefConfig).to receive(:windows?).and_return(false)
- expect(Chef::Platform).to_not receive(:require)
- expect(Chef::Platform.supports_msi?).to be false
- end
-
- it "returns true when the registry key exists" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_yield(registry)
- expect(Chef::Platform.supports_msi?).to be true
- end
-
- it "returns false when the registry key does not exist" do
- allow(ChefConfig).to receive(:windows?).and_return(true)
- allow(Chef::Platform).to receive(:require).with("win32/registry")
- expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
- with(key, access).
- and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
- expect(Chef::Platform.supports_msi?).to be false
- end
-end
-
-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
- end
-
- ["1.0", "2.0", "3.0"].each do |version|
- it "returns false for Powershell #{version}" do
- node = Chef::Node.new
- node.automatic[:languages][:powershell][:version] = version
- expect(Chef::Platform.supports_dsc?(node)).to be_falsey
- end
- end
-
- ["4.0", "5.0"].each do |version|
- it "returns true for Powershell #{version}" do
- node = Chef::Node.new
- node.automatic[:languages][:powershell][:version] = version
- expect(Chef::Platform.supports_dsc?(node)).to be_truthy
- end
- end
-end
-
describe "Chef::Platform#supports_dsc_invoke_resource?" do
it "returns false if powershell is not present" do
node = Chef::Node.new
@@ -181,7 +25,7 @@ describe "Chef::Platform#supports_dsc_invoke_resource?" do
end
["1.0", "2.0", "3.0", "4.0", "5.0.10017.9"].each do |version|
- it "returns false for Powershell #{version}" do
+ it "returns false for PowerShell #{version}" do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = version
expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
@@ -197,24 +41,23 @@ end
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") }
+ let(:powershell) { instance_double("Chef::PowerShell") }
it "returns true when RefreshMode is Disabled" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new).
- with(node, "Get-DscLocalConfigurationManager", :object).
- and_return(cmdlet)
- expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
- expect(cmdlet_result).to receive(:return_value).and_return({ "RefreshMode" => "Disabled" })
+ expect(Chef::PowerShell).to receive(:new)
+ .with("Get-DscLocalConfigurationManager")
+ .and_return(powershell)
+ expect(powershell).to receive(:error!)
+ expect(powershell).to receive(:result).and_return({ "RefreshMode" => "Disabled" })
expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be true
end
it "returns false when RefreshMode is not Disabled" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new).
- with(node, "Get-DscLocalConfigurationManager", :object).
- and_return(cmdlet)
- expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
- expect(cmdlet_result).to receive(:return_value).and_return({ "RefreshMode" => "LaLaLa" })
+ expect(Chef::PowerShell).to receive(:new)
+ .with("Get-DscLocalConfigurationManager")
+ .and_return(powershell)
+ expect(powershell).to receive(:error!)
+ expect(powershell).to receive(:result).and_return({ "RefreshMode" => "LaLaLa" })
expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be false
end
end
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 d94b2a69a2..d61dec4bc8 100644
--- a/spec/unit/policy_builder/dynamic_spec.rb
+++ b/spec/unit/policy_builder/dynamic_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -131,18 +131,6 @@ describe Chef::PolicyBuilder::Dynamic do
end
- context "and :use_policyfile is set in Chef::Config" do
-
- before do
- Chef::Config[:use_policyfile] = true
- end
-
- it "uses the Policyfile implementation" do
- expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
- end
-
- end
-
context "and policy_name and policy_group are set on Chef::Config" do
before do
diff --git a/spec/unit/policy_builder/expand_node_object_spec.rb b/spec/unit/policy_builder/expand_node_object_spec.rb
index 420db2e855..cb4069e969 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 (c) 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
@@ -46,13 +42,13 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
expect(policy_builder).to respond_to(:finish_load_node)
end
- it "implements a build_node method" do
+ it "implements a build_node method" do
expect(policy_builder).to respond_to(:build_node)
end
it "implements a setup_run_context method that accepts a list of recipe files to run" do
expect(policy_builder).to respond_to(:setup_run_context)
- expect(policy_builder.method(:setup_run_context).arity).to eq(-1) #optional argument
+ expect(policy_builder.method(:setup_run_context).arity).to eq(-1) # optional argument
end
it "implements a run_context method" do
@@ -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
@@ -201,7 +176,7 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
let(:expansion) do
recipe_list = Chef::RunList::VersionedRecipeList.new
recipe_list.add_recipe("recipe[from_role::default", "1.0.2")
- double("RunListExpansion", :recipes => recipe_list)
+ double("RunListExpansion", recipes: recipe_list)
end
let(:node) do
@@ -249,7 +224,7 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
end
it "reports that a temporary policy is being used" do
- expect(policy_builder.temporary_policy?).to be_truthy
+ expect(policy_builder.temporary_policy?).to be true
end
end
@@ -293,13 +268,13 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
let(:chef_http) { double("Chef::ServerAPI") }
let(:cookbook_resolve_url) { "environments/#{node.chef_environment}/cookbook_versions" }
- let(:cookbook_resolve_post_data) { { :run_list => ["first::default", "second::default"] } }
+ let(:cookbook_resolve_post_data) { { run_list: ["first::default", "second::default"] } }
# cookbook_hash is just a hash, but since we're passing it between mock
# objects, we get a little better test strictness by using a double (which
# will have object equality rather than semantic equality #== semantics).
let(:cookbook_hash) { double("cookbook hash") }
- let(:expanded_cookbook_hash) { double("expanded cookbook hash", :each => nil) }
+ let(:expanded_cookbook_hash) { double("expanded cookbook hash", each: nil) }
let(:cookbook_synchronizer) { double("CookbookSynchronizer") }
diff --git a/spec/unit/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb
index 307bd45c18..6be0da8f4d 100644
--- a/spec/unit/policy_builder/policyfile_spec.rb
+++ b/spec/unit/policy_builder/policyfile_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -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
@@ -182,12 +218,12 @@ describe Chef::PolicyBuilder::Policyfile do
end
context "when the deployment group cannot be loaded" do
- let(:error404) { Net::HTTPServerException.new("404 message", :body) }
+ let(:error404) { Net::HTTPClientException.new("404 message", :body) }
before do
- expect(http_api).to receive(:get).
- with("data/policyfiles/example-policy-stage").
- and_raise(error404)
+ expect(api_service).to receive(:get)
+ .with("data/policyfiles/example-policy-stage")
+ .and_raise(error404)
end
it "raises an error" do
@@ -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
@@ -611,14 +738,14 @@ describe Chef::PolicyBuilder::Policyfile do
shared_examples "fetching cookbooks when they don't exist" do
context "and a cookbook is missing" do
- let(:error404) { Net::HTTPServerException.new("404 message", :body) }
+ let(:error404) { Net::HTTPClientException.new("404 message", :body) }
before do
policy_builder.finish_load_node(node)
policy_builder.build_node
- expect(http_api).to receive(:get).with(cookbook1_url).
- and_raise(error404)
+ expect(api_service).to receive(:get).with(cookbook1_url)
+ .and_raise(error404)
end
it "raises an error indicating which cookbook is missing" do
@@ -636,9 +763,9 @@ describe Chef::PolicyBuilder::Policyfile do
policy_builder.finish_load_node(node)
policy_builder.build_node
- allow(Chef::CookbookSynchronizer).to receive(:new).
- with(expected_cookbook_hash, events).
- and_return(cookbook_synchronizer)
+ allow(Chef::CookbookSynchronizer).to receive(:new)
+ .with(expected_cookbook_hash, events)
+ .and_return(cookbook_synchronizer)
end
after do
@@ -687,15 +814,15 @@ 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).
- and_return(example1_cookbook_data)
- expect(http_api).to receive(:get).with(cookbook2_url).
- and_return(example2_cookbook_data)
+ expect(api_service).to receive(:get).with(cookbook1_url)
+ .and_return(example1_cookbook_data)
+ 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).
- and_return(example1_cookbook_object)
- expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data).
- and_return(example2_cookbook_object)
+ expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data)
+ .and_return(example1_cookbook_object)
+ expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data)
+ .and_return(example2_cookbook_object)
end
include_examples "fetching cookbooks when they exist"
@@ -720,15 +847,15 @@ 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).
- and_return(example1_cookbook_data)
- expect(http_api).to receive(:get).with(cookbook2_url).
- and_return(example2_cookbook_data)
-
- expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data).
- and_return(example1_cookbook_object)
- expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data).
- and_return(example2_cookbook_object)
+ expect(api_service).to receive(:get).with(cookbook1_url)
+ .and_return(example1_cookbook_data)
+ 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)
+ .and_return(example1_cookbook_object)
+ expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data)
+ .and_return(example2_cookbook_object)
end
include_examples "fetching cookbooks when they exist"
diff --git a/spec/unit/policy_builder_spec.rb b/spec/unit/policy_builder_spec.rb
index 674978ab63..8b07b10d08 100644
--- a/spec/unit/policy_builder_spec.rb
+++ b/spec/unit/policy_builder_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/property/state_spec.rb b/spec/unit/property/state_spec.rb
index be19dd7ec2..f120039f8b 100644
--- a/spec/unit/property/state_spec.rb
+++ b/spec/unit/property/state_spec.rb
@@ -28,7 +28,8 @@ describe "Chef::Resource#identity and #state" do
def self.english_join(values)
return "<nothing>" if values.size == 0
return values[0].inspect if values.size == 1
- "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+
+ "#{values[0..-2].map(&:inspect).join(", ")} and #{values[-1].inspect}"
end
def self.with_property(*properties, &block)
@@ -254,38 +255,38 @@ describe "Chef::Resource#identity and #state" do
end
with_property ":x, identity: true, default: 'xxx'",
- ":y, identity: true, default: 'yyy'",
- ":z, identity: true, default: 'zzz'" do
- it "identity_property raises an error if multiple identity values are defined" do
- expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
- end
- it "identity_attr raises an error if multiple identity values are defined" do
- expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
- end
- it "identity returns all identity values in a hash if multiple are defined" do
- resource.x "foo"
- resource.y "bar"
- resource.z "baz"
- expect(resource.identity).to eq(x: "foo", y: "bar", z: "baz")
- end
- it "identity returns all values whether any value is set or not" do
- expect(resource.identity).to eq(x: "xxx", y: "yyy", z: "zzz")
- end
- it "identity_properties wipes out any other identity attributes if multiple are defined" do
- resource_class.identity_properties :y
- resource.x "foo"
- resource.y "bar"
- resource.z "baz"
- expect(resource.identity).to eq "bar"
+ ":y, identity: true, default: 'yyy'",
+ ":z, identity: true, default: 'zzz'" do
+ it "identity_property raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity_attr raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity returns all identity values in a hash if multiple are defined" do
+ resource.x "foo"
+ resource.y "bar"
+ resource.z "baz"
+ expect(resource.identity).to eq(x: "foo", y: "bar", z: "baz")
+ end
+ it "identity returns all values whether any value is set or not" do
+ expect(resource.identity).to eq(x: "xxx", y: "yyy", z: "zzz")
+ end
+ it "identity_properties wipes out any other identity attributes if multiple are defined" do
+ resource_class.identity_properties :y
+ resource.x "foo"
+ resource.y "bar"
+ resource.z "baz"
+ expect(resource.identity).to eq "bar"
+ end
end
- end
with_property ":x, identity: true, name_property: true" do
it "identity when x is not defined returns the value of x" do
expect(resource.identity).to eq "blah"
end
- it "state when x is not defined returns the value of x" do
- expect(resource.state_for_resource_reporter).to eq(x: "blah")
+ it "the name property is not included in the desired state" do
+ expect(resource.state_for_resource_reporter).to eq({})
end
end
end
@@ -331,12 +332,17 @@ describe "Chef::Resource#identity and #state" do
end
with_property ":x, name_property: true" do
- # it "Unset values with name_property are included in state" do
- # expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
- # end
- it "Set values with name_property are included in state" do
+ it "Is by default the identity property if no other identity property is included" do
+ expect(resource.identity).to eq("blah")
+ end
+
+ it "Unset values with name_property are not included in state" do
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+
+ it "Set values with name_property are not included in state" do
resource.x 1
- expect(resource.state_for_resource_reporter).to eq(x: 1)
+ expect(resource.state_for_resource_reporter).to eq({})
end
end
diff --git a/spec/unit/property/validation_spec.rb b/spec/unit/property/validation_spec.rb
index 4e1b252863..c8daee1580 100644
--- a/spec/unit/property/validation_spec.rb
+++ b/spec/unit/property/validation_spec.rb
@@ -45,17 +45,29 @@ describe "Chef::Resource.property validation" do
def self.blah
"class#{Namer.next_index}"
end
+
+ action :doit do
+ # this needs to not reference any properties
+ end
+
+ action :doit2 do
+ # this needs to not reference any properties
+ end
end
end
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource) do
- resource_class.new("blah")
+ resource_class.new("blah", run_context)
end
def self.english_join(values)
return "<nothing>" if values.size == 0
return values[0].inspect if values.size == 1
- "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+
+ "#{values[0..-2].map(&:inspect).join(", ")} and #{values[-1].inspect}"
end
def self.with_property(*properties, &block)
@@ -100,13 +112,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 +125,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 +140,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 +171,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 +201,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"
@@ -251,19 +243,19 @@ describe "Chef::Resource.property validation" do
[ :b ]
validation_test ":a, is: :b",
- [ :a, :b ],
+ %i{a b},
[ :c ]
validation_test ":a, is: [ :b, :c ]",
- [ :a, :b, :c ],
+ %i{a b c},
[ :d ]
validation_test "[ :a, :b ], is: :c",
- [ :a, :b, :c ],
+ %i{a b c},
[ :d ]
validation_test "[ :a, :b ], is: [ :c, :d ]",
- [ :a, :b, :c, :d ],
+ %i{a b c d},
[ :e ]
validation_test "nil",
@@ -299,12 +291,12 @@ describe "Chef::Resource.property validation" do
[ :b ]
validation_test "is: [ :a, :b ]",
- [ :a, :b ],
- [ [ :a, :b ] ]
+ %i{a b},
+ [ %i{a b} ]
validation_test "is: [ [ :a, :b ] ]",
- [ [ :a, :b ] ],
- [ :a, :b ]
+ [ %i{a b} ],
+ %i{a b}
# Regex
validation_test "is: /abc/",
@@ -328,11 +320,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",
[ ],
@@ -366,13 +364,13 @@ describe "Chef::Resource.property validation" do
:nil_is_valid
validation_test "equal_to: [ :a, :b ]",
- [ :a, :b ],
- [ [ :a, :b ] ],
+ %i{a b},
+ [ %i{a b} ],
:nil_is_valid
validation_test "equal_to: [ [ :a, :b ] ]",
- [ [ :a, :b ] ],
- [ :a, :b ],
+ [ %i{a b} ],
+ %i{a b},
:nil_is_valid
validation_test "equal_to: nil",
@@ -589,15 +587,34 @@ 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
+ it "fails if it is not specified, on running the action, even if it is not referenced" do
+ expect { resource.run_action(:doit) }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ with_property ":x, required: %i{doit}" do
+ it "fails if it is not specified, on running the doit action, even if it is not referenced" do
+ expect { resource.run_action(:doit) }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+
+ it "does not fail if it is not specified, on running the doit2 action" do
+ expect { resource.run_action(:doit2) }.not_to raise_error
+ 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 +642,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 +656,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 +731,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 +763,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 50ff3434f6..6c64a961b4 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -51,7 +51,8 @@ describe "Chef::Resource.property" do
def self.english_join(values)
return "<nothing>" if values.size == 0
return values[0].inspect if values.size == 1
- "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+
+ "#{values[0..-2].map(&:inspect).join(", ")} and #{values[-1].inspect}"
end
def self.with_property(*properties, &block)
@@ -64,7 +65,7 @@ describe "Chef::Resource.property" do
if properties.size == 1
description = "With property #{properties.first}"
else
- description = "With properties #{english_join(properties.map { |property| "#{property.inspect}" })}"
+ description = "With properties #{english_join(properties.map { |property| (property.inspect).to_s })}"
end
context description, *tags do
before do
@@ -82,12 +83,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 +94,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 +109,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) # rubocop: disable Naming/AsciiIdentifiers
+ expect(resource.Straße "foo").to eql("foo") # rubocop: disable Naming/AsciiIdentifiers
+ expect(resource.Straße).to eql("foo") # rubocop: disable Naming/AsciiIdentifiers
+ expect(resource.Straße = "bar").to eql("bar") # rubocop: disable Naming/AsciiIdentifiers
+ expect(resource.Straße).to eql("bar") # rubocop: disable Naming/AsciiIdentifiers
+ 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
+ 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 +315,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 +536,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(FrozenError, /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(FrozenError, /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 +578,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(FrozenError, /can't modify frozen Array/)
+ end
+ it "x.first is immutable" do
+ expect { resource.x.first[:foo] = "other" }.to raise_error(FrozenError, /can't modify frozen Hash/)
+ end
+ it "x.first[:foo] is immutable" do
+ expect { resource.x.first[:foo] << "other" }.to raise_error(FrozenError, /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 +626,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 +636,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 +653,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 +768,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 +778,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
@@ -788,7 +819,7 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 2
expect(Namer.current_index).to eq 2
end
- it "setting the same lazy value on two different instances runs it on each instancee" do
+ it "setting the same lazy value on two different instances runs it on each instances" do
resource2 = resource_class.new("blah2")
l = lazy { Namer.next_index }
resource.x l
@@ -991,6 +1022,9 @@ describe "Chef::Resource.property" do
it "defaults x to resource.name" do
expect(resource.x).to eq "blah"
end
+ it "defaults to being part of the identity if there is no other identity" do
+ expect(resource.identity).to eq "blah"
+ end
it "does not pick up resource.name if set" do
expect(resource.x 10).to eq 10
expect(resource.x).to eq 10
@@ -1019,86 +1053,35 @@ 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./
- 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./
- 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./
- 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./
- 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
+ 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,
+ %r{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
- with_property ":x, default: nil, #{name}: true" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
+ 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,
+ %r{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
- with_property ":x, #{name}: true, default: 10" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
+ 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,
+ %r{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
- with_property ":x, #{name}: true, default: nil" do
- it "chooses #{name} over default" do
- expect(resource.x).to eq "blah"
- end
+ 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,
+ %r{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 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+)./
- 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+)./
- 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+)./
- 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+)./
+ expect { resource_class.property :x, name_property: false, name_attribute: 1 }.to raise_error ArgumentError,
+ /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,
+ /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,
+ /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,
+ /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
@@ -1106,13 +1089,13 @@ describe "Chef::Resource.property" do
expect do
module ::PropertySpecPropertyTypes
include Chef::Mixin::Properties
- property_type(is: [:a, :b], default: :c)
+ property_type(is: %i{a b}, default: :c)
end
- end.to raise_error(Chef::Exceptions::DeprecatedFeatureError, /Default value :c is invalid for property <property type>./)
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
expect do
module ::PropertySpecPropertyTypes
include Chef::Mixin::Properties
- property_type(is: [:a, :b], default: :b)
+ property_type(is: %i{a b}, default: :b)
end
end.not_to raise_error
end
@@ -1121,8 +1104,8 @@ describe "Chef::Resource.property" do
before :all do
module ::PropertySpecPropertyTypes
include Chef::Mixin::Properties
- ABType = property_type(is: [:a, :b])
- CDType = property_type(is: [:c, :d])
+ ABType = property_type(is: %i{a b})
+ CDType = property_type(is: %i{c d})
end
end
@@ -1177,6 +1160,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
@@ -1224,4 +1232,97 @@ describe "Chef::Resource.property" do
end
end
end
+
+ context "#copy_properties_from" do
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:node) { Chef::Node.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:thing_one_class) do
+ Class.new(Chef::Resource) do
+ resource_name :thing_one
+ provides :thing_two
+ property :foo, String
+ property :bar, String
+ end
+ end
+
+ let(:thing_two_class) do
+ Class.new(Chef::Resource) do
+ resource_name :thing_two
+ provides :thing_two
+ property :foo, String
+ property :bar, String
+ end
+ end
+
+ let(:thing_three_class) do
+ Class.new(Chef::Resource) do
+ resource_name :thing_three
+ provides :thing_three
+ property :foo, String
+ property :bar, String
+ property :baz, String
+ end
+ end
+
+ let(:thing_one_resource) do
+ thing_one_class.new("name_one", run_context)
+ end
+
+ let(:thing_two_resource) do
+ thing_two_class.new("name_two", run_context)
+ end
+
+ let(:thing_three_resource) do
+ thing_three_class.new("name_three", run_context)
+ end
+
+ it "copies foo and bar" do
+ thing_one_resource.foo "foo"
+ thing_one_resource.bar "bar"
+ thing_two_resource.copy_properties_from thing_one_resource
+ expect(thing_two_resource.name).to eql("name_two")
+ expect(thing_two_resource.foo).to eql("foo")
+ expect(thing_two_resource.bar).to eql("bar")
+ end
+
+ it "copies only foo when it is only included" do
+ thing_one_resource.foo "foo"
+ thing_one_resource.bar "bar"
+ thing_two_resource.copy_properties_from(thing_one_resource, :foo)
+ expect(thing_two_resource.name).to eql("name_two")
+ expect(thing_two_resource.foo).to eql("foo")
+ expect(thing_two_resource.bar).to eql(nil)
+ end
+
+ it "copies foo and name when bar is excluded" do
+ thing_one_resource.foo "foo"
+ thing_one_resource.bar "bar"
+ thing_two_resource.copy_properties_from(thing_one_resource, exclude: [ :bar ])
+ expect(thing_two_resource.name).to eql("name_one")
+ expect(thing_two_resource.foo).to eql("foo")
+ expect(thing_two_resource.bar).to eql(nil)
+ end
+
+ it "copies only foo when bar and name are excluded" do
+ thing_one_resource.foo "foo"
+ thing_one_resource.bar "bar"
+ thing_two_resource.copy_properties_from(thing_one_resource, exclude: %i{name bar})
+ expect(thing_two_resource.name).to eql("name_two")
+ expect(thing_two_resource.foo).to eql("foo")
+ expect(thing_two_resource.bar).to eql(nil)
+ end
+
+ it "blows up if the target resource does not implement a set property" do
+ thing_three_resource.baz "baz"
+ expect { thing_two_resource.copy_properties_from(thing_three_resource) }.to raise_error(NoMethodError)
+ end
+
+ it "does not blow up if blows up if the target resource does not implement a set properly" do
+ thing_three_resource.foo "foo"
+ thing_three_resource.bar "bar"
+ thing_two_resource.copy_properties_from(thing_three_resource)
+ end
+ end
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..1dfff6e815
--- /dev/null
+++ b/spec/unit/provider/apt_preference_spec.rb
@@ -0,0 +1,91 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Author:: Tim Smith (<tim@chef.io>)
+# Copyright:: Copyright (c) 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/apt_preference"
+
+# FIXME: provider has been moved to a custom resource, should migrate the unit tests
+#
+describe "Chef::Provider::AptPreference" do
+ 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(:new_resource) { Chef::Resource::AptPreference.new("libmysqlclient16.1*", run_context) }
+ let(:pref_dir) { Dir.mktmpdir("apt_pref_d") }
+ let(:provider) { new_resource.provider_for_action(:add) }
+
+ before do
+ node.automatic["platform_family"] = "debian"
+ Chef::Resource::AptPreference.send(:remove_const, :APT_PREFERENCE_DIR)
+ Chef::Resource::AptPreference.const_set(:APT_PREFERENCE_DIR, pref_dir)
+
+ new_resource.pin = "1.0.1"
+ new_resource.pin_priority 1001
+ 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 sanitized .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..aa2fb770c7 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:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,55 +18,61 @@
require "spec_helper"
-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
+describe "Chef::Provider::AptRepository" do
+ # 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.freeze
+ 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
-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>
+ # Output of the command:
+ # => gpg --with-fingerprint --with-colons [FILE]
+ APG_GPG_FINGER = <<~EOF.freeze
+ pub:-:1024:17:327574EE02A818DD:2009-04-22:::-:Cloudera Apt Repository:
+ fpr:::::::::F36A89E33CC1BD0F71079007327574EE02A818DD:
+ sub:-:2048:16:84080586D1CA74A1:2009-04-22::::
+ EOF
-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>
+ 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(:new_resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
+ let(:provider) { new_resource.provider_for_action(:add) }
-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>
-
-EOF
-
-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
-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
- run_context = Chef::RunContext.new(node, {}, events)
- Chef::Provider::AptRepository.new(new_resource, run_context)
+ let(:apt_key_finger_cmd) do
+ %w{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: APG_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 +87,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 --no-tty --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 --no-tty --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 --no-tty --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 +216,42 @@ 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
- target = %Q{deb "http://test/uri" unstable main\n}
+ it "creates a repository string" do
+ target = "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
- target = %Q{deb "http://test/uri" main\n}
+ it "creates a repository string with spaces" do
+ target = "deb http://test/uri%20with%20spaces unstable main\n"
+ expect(provider.build_repo("http://test/uri with spaces", "unstable", "main", false, nil)).to eql(target)
+ end
+
+ it "creates a repository string with no distribution" do
+ target = "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
- target = %Q{deb "http://test/uri" unstable main\ndeb-src "http://test/uri" unstable main\n}
+ it "creates a repository string with source" do
+ target = "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
- target = %Q{deb [trusted=yes] "http://test/uri" unstable main\n}
+ it "creates a repository string with options" do
+ target = "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
- target = %Q{deb "http://ppa.launchpad.net/chef/main/ubuntu" unstable main\n}
+ it "handles a ppa repo" do
+ target = "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..6e11235e30 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) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,24 +18,27 @@
require "spec_helper"
-describe Chef::Provider::AptUpdate do
- let(:new_resource) { Chef::Resource::AptUpdate.new("update") }
+describe "Chef::Provider::AptUpdate" do
+ 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(:new_resource) { Chef::Resource::AptUpdate.new("update", run_context) }
+ let(:provider) { new_resource.provider_for_action(:update) }
let(:config_dir) { Dir.mktmpdir("apt_update_apt_conf_d") }
let(:config_file) { File.join(config_dir, "15update-stamp") }
let(:stamp_dir) { Dir.mktmpdir("apt_update_periodic") }
before do
- stub_const("Chef::Provider::AptUpdate::APT_CONF_DIR", config_dir)
- stub_const("Chef::Provider::AptUpdate::STAMP_DIR", stamp_dir)
+ new_resource.class.send(:remove_const, :APT_CONF_DIR)
+ new_resource.class.send(:const_set, :APT_CONF_DIR, config_dir)
+ new_resource.class.send(:remove_const, :STAMP_DIR)
+ new_resource.class.send(:const_set, :STAMP_DIR, stamp_dir)
+ node.automatic["platform_family"] = "debian"
end
- let(:provider) do
- node = Chef::Node.new
- events = Chef::EventDispatch::Dispatcher.new
- run_context = Chef::RunContext.new(node, {}, events)
- Chef::Provider::AptUpdate.new(new_resource, run_context)
- end
+ let(:apt_update_cmd) { %w{apt-get -q update} }
it "responds to load_current_resource" do
expect(provider).to respond_to(:load_current_resource)
@@ -45,7 +48,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_compacted!).with(*apt_update_cmd, anything)
end
it "should create the directory" do
@@ -64,7 +67,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_compacted!).with(*apt_update_cmd, anything)
provider.run_action(:update)
expect(new_resource).to be_updated_by_last_action
end
@@ -79,14 +82,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_compacted!).with(*apt_update_cmd, 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_compacted!).with(*apt_update_cmd, anything)
provider.run_action(:periodic)
expect(new_resource).to_not be_updated_by_last_action
end
@@ -98,14 +101,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_compacted!).with(*apt_update_cmd, 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_compacted!).with(*apt_update_cmd, anything)
provider.run_action(:periodic)
expect(new_resource).to_not be_updated_by_last_action
end
diff --git a/spec/unit/provider/batch_spec.rb b/spec/unit/provider/batch_spec.rb
new file mode 100644
index 0000000000..80e914beaa
--- /dev/null
+++ b/spec/unit/provider/batch_spec.rb
@@ -0,0 +1,130 @@
+#
+# Author:: Adam Jacob (adam@chef.io)
+# Copyright:: Copyright 2009-2016, Opscode
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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::Batch do
+ let(:node) do
+ node = Chef::Node.new
+ node.default["kernel"] = {}
+ node.default["kernel"][:machine] = :x86_64.to_s
+ node
+ end
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:new_resource) do
+ new_resource = Chef::Resource::Batch.new("cmd.exe and conquer")
+ new_resource.code %q{echo "hello"}
+ new_resource
+ end
+
+ let(:provider) { Chef::Provider::Batch.new(new_resource, run_context) }
+
+ context "#grant_alternate_user_read_access" do
+ before do
+ allow(ChefUtils).to receive(:windows?).and_return(true)
+ 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)
+
+ provider.singleton_class.send(:public, :grant_alternate_user_read_access)
+ 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.grant_alternate_user_read_access("a fake path")
+ 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.grant_alternate_user_read_access("a fake path")
+ end
+ end
+ end
+
+ describe "#with_temp_script_file" do
+ before do
+ provider.singleton_class.send(:public, :with_temp_script_file)
+ provider.singleton_class.send(:public, :script_file_path)
+ end
+
+ it "should put the contents of the script in the temp file" do
+ temp_file_contents = nil
+
+ provider.with_temp_script_file do
+ temp_file_contents = File.read(provider.script_file_path)
+ end
+
+ expect(temp_file_contents.strip).to eq(%q{echo "hello"})
+ end
+ end
+
+ describe "#command" do
+ let(:basepath) { "C:\\Windows\\system32\\" }
+ let(:interpreter) { File.join(basepath, "cmd.exe") }
+
+ before do
+ allow(provider).to receive(:basepath).and_return(basepath)
+ provider.singleton_class.send(:public, :with_temp_script_file)
+ provider.singleton_class.send(:public, :script_file_path)
+ end
+
+ it 'should set the command to "interpreter" "tempfile"' do
+ command = nil
+ script_file_path = nil
+ provider.with_temp_script_file do
+ command = provider.command
+ script_file_path = provider.script_file_path
+ end
+
+ expect(command).to eq(%Q{"#{interpreter}" /c "#{script_file_path}"})
+ end
+
+ it "should set the command to 'interpreter flags tempfile'" do
+ new_resource.flags "/f"
+
+ command = nil
+ script_file_path = nil
+ provider.with_temp_script_file do
+ command = provider.command
+ script_file_path = provider.script_file_path
+ end
+
+ expect(command).to eq(%Q{"#{interpreter}" /f /c "#{script_file_path}"})
+ end
+ end
+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/content_spec.rb b/spec/unit/provider/cookbook_file/content_spec.rb
index 096ac85b64..36cffa79dc 100644
--- a/spec/unit/provider/cookbook_file/content_spec.rb
+++ b/spec/unit/provider/cookbook_file/content_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::CookbookFile::Content do
- let(:new_resource) { double("Chef::Resource::CookbookFile (new)", :cookbook_name => "apache2", :cookbook => "apache2") }
+ let(:new_resource) { double("Chef::Resource::CookbookFile (new)", cookbook_name: "apache2", cookbook: "apache2") }
let(:content) do
@run_context = double("Chef::RunContext")
@current_resource = double("Chef::Resource::CookbookFile (current)")
diff --git a/spec/unit/provider/cookbook_file_spec.rb b/spec/unit/provider/cookbook_file_spec.rb
index f49cc7d3da..26fe6899a8 100644
--- a/spec/unit/provider/cookbook_file_spec.rb
+++ b/spec/unit/provider/cookbook_file_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,9 +23,10 @@ require "ostruct"
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(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
diff --git a/spec/unit/provider/cron/unix_spec.rb b/spec/unit/provider/cron/unix_spec.rb
index 5e1fcb35ab..e5bb436bea 100644
--- a/spec/unit/provider/cron/unix_spec.rb
+++ b/spec/unit/provider/cron/unix_spec.rb
@@ -37,36 +37,41 @@ describe Chef::Provider::Cron::Unix do
end
end
- let(:status) { double("Process::Status", :exitstatus => exitstatus) }
+ let(:status) { double("Process::Status", exitstatus: exitstatus) }
let(:exitstatus) { 0 }
- let(:shell_out) { double("Mixlib::ShellOut", :status => status, :stdout => stdout, :stderr => stderr) }
+ 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
- String.new(<<-CRONTAB)
-0 2 * * * /some/other/command
+ String.new(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-* 5 * * * /bin/true
+ # Chef Name: something else
+ * 5 * * * /bin/true
-# Another comment
+ # Another comment
CRONTAB
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)
+ allow(provider).to receive(:shell_out_compacted).with("/usr/bin/crontab", "-l", user: username).and_return(shell_out)
end
it "should call crontab -l with the user" do
provider.send(:read_crontab)
- expect(provider).to have_received(:shell_out).with("/usr/bin/crontab -l", :user => username)
+ expect(provider).to have_received(:shell_out_compacted).with("/usr/bin/crontab", "-l", user: username)
end
it "should return the contents of the crontab" do
@@ -83,12 +88,12 @@ 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
context "when any other error occurs" do
- let (:exitstatus) { 2 }
+ let(:exitstatus) { 2 }
it "should raise an exception if another error occurs" do
expect do
@@ -98,7 +103,7 @@ describe Chef::Provider::Cron::Unix do
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
@@ -106,7 +111,7 @@ describe Chef::Provider::Cron::Unix do
describe "write_crontab" do
let(:stdout) { "" }
let(:stderr) { "" }
- let(:tempfile) { double("foo", :path => "/tmp/foo", :close => true) }
+ let(:tempfile) { double("foo", path: "/tmp/foo", close: true) }
before do
expect(Tempfile).to receive(:new).and_return(tempfile)
@@ -114,12 +119,12 @@ describe Chef::Provider::Cron::Unix do
expect(tempfile).to receive(:chmod).with(420)
expect(tempfile).to receive(:close!)
allow(tempfile).to receive(:<<)
- allow(provider).to receive(:shell_out).with("/usr/bin/crontab #{tempfile.path}", :user => username).and_return(shell_out)
+ allow(provider).to receive(:shell_out_compacted).with("/usr/bin/crontab", tempfile.path, user: username).and_return(shell_out)
end
it "should call crontab for the user" do
provider.send(:write_crontab, "Foo")
- expect(provider).to have_received(:shell_out).with("/usr/bin/crontab #{tempfile.path}", :user => username)
+ expect(provider).to have_received(:shell_out_compacted).with("/usr/bin/crontab", tempfile.path, user: username)
end
it "should call crontab with a file containing the crontab" do
diff --git a/spec/unit/provider/cron_spec.rb b/spec/unit/provider/cron_spec.rb
index 64916ef454..76f170312e 100644
--- a/spec/unit/provider/cron_spec.rb
+++ b/spec/unit/provider/cron_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan (btm@loftninjas.org)
-# Copyright:: Copyright 2009-2016, Bryan McLellan
+# Copyright:: Copyright 2009-2020, Bryan McLellan
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,36 +15,42 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
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
context "with a matching entry in the user's crontab" do
before :each do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-@reboot /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ @reboot /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
-CRONTAB
+ # Another comment
+ CRONTAB
end
it "should set cron_exists" do
@@ -60,20 +66,20 @@ CRONTAB
end
it "should pull env vars out" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
-
-# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-SHELL=/bin/foosh
-PATH=/bin:/foo
-HOME=/home/foo
-@reboot /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
-
-# Another comment
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
+
+ # Chef Name: cronhole some stuff
+ MAILTO=foo@example.com
+ SHELL=/bin/foosh
+ PATH=/bin:/foo
+ HOME=/home/foo
+ @reboot /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
+
+ # Another comment
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.mailto).to eq("foo@example.com")
expect(cron.shell).to eq("/bin/foosh")
@@ -84,13 +90,13 @@ CRONTAB
end
it "should parse and load generic and standard environment variables from cron entry" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-# Chef Name: cronhole some stuff
-MAILTO=warn@example.com
-TEST=lol
-FLAG=1
-@reboot /bin/true
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ # Chef Name: cronhole some stuff
+ MAILTO=warn@example.com
+ TEST=lol
+ FLAG=1
+ @reboot /bin/true
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.mailto).to eq("warn@example.com")
@@ -98,14 +104,14 @@ CRONTAB
end
it "should not break with variables that match the cron resource internals" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-# Chef Name: cronhole some stuff
-MINUTE=40
-REBOOT=midnight
-TEST=lol
-ENVIRONMENT=production
-@reboot /bin/true
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ # Chef Name: cronhole some stuff
+ MINUTE=40
+ REBOOT=midnight
+ TEST=lol
+ ENVIRONMENT=production
+ @reboot /bin/true
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.time).to eq(:reboot)
@@ -113,7 +119,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
@@ -130,9 +136,9 @@ CRONTAB
end
it "should create a crontab with the entry" do
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-# Chef Name: cronhole some stuff
-@reboot /bin/true
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ # Chef Name: cronhole some stuff
+ @reboot /bin/true
ENDCRON
@provider.run_action(:create)
end
@@ -141,18 +147,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,21 +160,21 @@ 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
context "with no matching entry in the user's crontab" do
before :each do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-* 5 * * * /bin/true
+ # Chef Name: something else
+ * 5 * * * /bin/true
-# Another comment
-CRONTAB
+ # Another comment
+ CRONTAB
end
it "should not set cron_exists or cron_empty" do
@@ -190,15 +184,15 @@ 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
it "should not fail if there's an existing cron with a numerical argument" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-# Chef Name: foo[bar] (baz)
-21 */4 * * * some_prog 1234567
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ # Chef Name: foo[bar] (baz)
+ 21 */4 * * * some_prog 1234567
+ CRONTAB
expect do
@provider.load_current_resource
end.not_to raise_error
@@ -207,16 +201,16 @@ CRONTAB
context "with a matching entry in the user's crontab" do
before :each do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-* 5 * 1 * /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ * 5 * 1 * /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
-CRONTAB
+ # Another comment
+ CRONTAB
end
it "should set cron_exists" do
@@ -237,20 +231,20 @@ CRONTAB
end
it "should pull env vars out" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
-
-# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-SHELL=/bin/foosh
-PATH=/bin:/foo
-HOME=/home/foo
-* 5 * 1 * /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
-
-# Another comment
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
+
+ # Chef Name: cronhole some stuff
+ MAILTO=foo@example.com
+ SHELL=/bin/foosh
+ PATH=/bin:/foo
+ HOME=/home/foo
+ * 5 * 1 * /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
+
+ # Another comment
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.mailto).to eq("foo@example.com")
expect(cron.shell).to eq("/bin/foosh")
@@ -266,13 +260,13 @@ CRONTAB
end
it "should parse and load generic and standard environment variables from cron entry" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-# Chef Name: cronhole some stuff
-MAILTO=warn@example.com
-TEST=lol
-FLAG=1
-* 5 * * * /bin/true
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ # Chef Name: cronhole some stuff
+ MAILTO=warn@example.com
+ TEST=lol
+ FLAG=1
+ * 5 * * * /bin/true
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.mailto).to eq("warn@example.com")
@@ -280,14 +274,14 @@ CRONTAB
end
it "should not break with variabels that match the cron resource internals" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-# Chef Name: cronhole some stuff
-MINUTE=40
-HOUR=midnight
-TEST=lol
-ENVIRONMENT=production
-* 5 * * * /bin/true
-CRONTAB
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ # Chef Name: cronhole some stuff
+ MINUTE=40
+ HOUR=midnight
+ TEST=lol
+ ENVIRONMENT=production
+ * 5 * * * /bin/true
+ CRONTAB
cron = @provider.load_current_resource
expect(cron.minute).to eq("*")
@@ -296,23 +290,23 @@ 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
context "with a matching entry in the user's crontab using month names and weekday names (#CHEF-3178)" do
before :each do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-* 5 * Jan Mon /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ * 5 * Jan Mon /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
-CRONTAB
+ # Another comment
+ CRONTAB
end
it "should set cron_exists" do
@@ -327,23 +321,24 @@ CRONTAB
expect(cron.hour).to eq("5")
expect(cron.day).to eq("*")
expect(cron.month).to eq("Jan")
- expect(cron.weekday).to eq("Mon")
+ expect(cron.weekday).to eq("1")
expect(cron.command).to eq("/bin/true param1 param2")
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
context "with a matching entry without a crontab line" do
it "should set cron_exists and leave current_resource values at defaults" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-CRONTAB
+ # Chef Name: cronhole some stuff
+ * * * * * /bin/true
+ CRONTAB
cron = @provider.load_current_resource
expect(@provider.cron_exists).to eq(true)
expect(cron.minute).to eq("*")
@@ -352,16 +347,17 @@ CRONTAB
expect(cron.month).to eq("*")
expect(cron.weekday).to eq("*")
expect(cron.time).to eq(nil)
- expect(cron.command).to eq(nil)
+ expect(cron.command).to eq("/bin/true")
end
it "should not pick up a commented out crontab line" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-#* 5 * 1 * /bin/true param1 param2
-CRONTAB
+ # Chef Name: cronhole some stuff
+ * * * * * /bin/true
+ #* 5 * 1 * /bin/true param1 param2
+ CRONTAB
cron = @provider.load_current_resource
expect(@provider.cron_exists).to eq(true)
expect(cron.minute).to eq("*")
@@ -370,20 +366,21 @@ CRONTAB
expect(cron.month).to eq("*")
expect(cron.weekday).to eq("*")
expect(cron.time).to eq(nil)
- expect(cron.command).to eq(nil)
+ expect(cron.command).to eq("/bin/true")
end
it "should not pick up a later crontab entry" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-#* 5 * 1 * /bin/true param1 param2
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ * * * * * /bin/true
+ #* 5 * 1 * /bin/true param1 param2
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
-CRONTAB
+ # Another comment
+ CRONTAB
cron = @provider.load_current_resource
expect(@provider.cron_exists).to eq(true)
expect(cron.minute).to eq("*")
@@ -392,7 +389,7 @@ CRONTAB
expect(cron.month).to eq("*")
expect(cron.weekday).to eq("*")
expect(cron.time).to eq(nil)
- expect(cron.command).to eq(nil)
+ expect(cron.command).to eq("/bin/true")
end
end
end
@@ -406,9 +403,9 @@ CRONTAB
@provider.current_resource = @current_resource
end
- [:minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home].each do |attribute|
- it "should return true if #{attribute} doesn't match" do
- @new_resource.send(attribute, "something_else")
+ %i{minute hour day month weekday command mailto path shell home}.each do |property|
+ it "should return true if #{property} doesn't match" do
+ @new_resource.send(property, "1") # we use 1 in order to pass resource validation. We're just using a value that's different.
expect(@provider.cron_different?).to eql(true)
end
end
@@ -447,9 +444,9 @@ CRONTAB
end
it "should create a crontab with the entry" do
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-# Chef Name: cronhole some stuff
-30 * * * * /bin/true
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ # Chef Name: cronhole some stuff
+ 30 * * * * /bin/true
ENDCRON
@provider.run_action(:create)
end
@@ -460,14 +457,14 @@ CRONTAB
@new_resource.shell "/bin/foosh"
@new_resource.home "/home/foo"
@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"
-TEST=LOL
-30 * * * * /bin/true
+ 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"
+ TEST=LOL
+ 30 * * * * /bin/true
ENDCRON
@provider.run_action(:create)
end
@@ -478,7 +475,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
@@ -486,26 +483,26 @@ TEST=LOL
context "when there is a crontab with no matching section" do
before :each do
@provider.cron_exists = false
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
end
it "should add the entry to the crontab" do
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
-# Chef Name: cronhole some stuff
-30 * * * * /bin/true
+ # Another comment
+ # Chef Name: cronhole some stuff
+ 30 * * * * /bin/true
ENDCRON
@provider.run_action(:create)
end
@@ -516,20 +513,20 @@ TEST=LOL
@new_resource.shell "/bin/foosh"
@new_resource.home "/home/foo"
@new_resource.environment "TEST" => "LOL"
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
-
-# Chef Name: something else
-2 * 1 * * /bin/false
-
-# Another comment
-# Chef Name: cronhole some stuff
-MAILTO="foo@example.com"
-PATH="/usr/bin:/my/custom/path"
-SHELL="/bin/foosh"
-HOME="/home/foo"
-TEST=LOL
-30 * * * * /bin/true
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
+
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
+
+ # Another comment
+ # Chef Name: cronhole some stuff
+ MAILTO="foo@example.com"
+ PATH="/usr/bin:/my/custom/path"
+ SHELL="/bin/foosh"
+ HOME="/home/foo"
+ TEST=LOL
+ 30 * * * * /bin/true
ENDCRON
@provider.run_action(:create)
end
@@ -540,7 +537,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
@@ -549,28 +546,28 @@ TEST=LOL
before :each do
@provider.cron_exists = true
allow(@provider).to receive(:cron_different?).and_return(true)
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ 30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
end
it "should update the crontab entry" do
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-30 * * * * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ 30 * * * * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:create)
end
@@ -581,20 +578,20 @@ TEST=LOL
@new_resource.shell "/bin/foosh"
@new_resource.home "/home/foo"
@new_resource.environment "TEST" => "LOL"
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-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"
-TEST=LOL
-30 * * * * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
-
-# Another comment
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 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"
+ TEST=LOL
+ 30 * * * * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
+
+ # Another comment
ENDCRON
@provider.run_action(:create)
end
@@ -605,7 +602,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
@@ -617,111 +614,214 @@ TEST=LOL
end
it "should add the crontab to the entry" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
+ # Chef Name: cronhole some stuff
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-30 * * * * /bin/true
+ # Chef Name: cronhole some stuff
+ 30 * * * * /bin/true
ENDCRON
@provider.run_action(:create)
end
it "should not blat any following entries" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-#30 * * * * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ #30 * * * * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-30 * * * * /bin/true
-#30 * * * * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ 30 * * * * /bin/true
+ #30 * * * * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:create)
end
it "should handle env vars with no crontab" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-MAILTO=bar@example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/barsh
-HOME=/home/foo
+ # Chef Name: cronhole some stuff
+ MAILTO=bar@example.com
+ PATH=/usr/bin:/my/custom/path
+ SHELL=/bin/barsh
+ HOME=/home/foo
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
@new_resource.mailto "foo@example.com"
@new_resource.path "/usr/bin:/my/custom/path"
@new_resource.shell "/bin/foosh"
@new_resource.home "/home/foo"
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 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"
-30 * * * * /bin/true
+ # Chef Name: cronhole some stuff
+ MAILTO="foo@example.com"
+ PATH="/usr/bin:/my/custom/path"
+ SHELL="/bin/foosh"
+ HOME="/home/foo"
+ 30 * * * * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:create)
end
end
context "when there is a crontab with a matching and identical section" do
- before :each do
- @provider.cron_exists = true
- allow(@provider).to receive(:cron_different?).and_return(false)
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ context "when environment variable is not used" do
+ before :each do
+ @provider.cron_exists = true
+ allow(@provider).to receive(:cron_different?).and_return(false)
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-* 5 * * * /bin/true
+ # Chef Name: cronhole some stuff
+ SHELL=/bash
+ * 5 * * * /bin/true
-# Another comment
-CRONTAB
- end
+ # Another comment
+ CRONTAB
+ end
- it "should not update the crontab" do
- expect(@provider).not_to receive(:write_crontab)
- @provider.run_action(:create)
+ it "should not update the crontab" do
+ expect(@provider).not_to receive(:write_crontab)
+ @provider.run_action(:create)
+ end
+
+ it "should not mark the resource as updated" do
+ @provider.run_action(:create)
+ expect(@new_resource).not_to be_updated_by_last_action
+ end
+
+ it "should log nothing changed" do
+ 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
- it "should not mark the resource as updated" do
- @provider.run_action(:create)
- expect(@new_resource).not_to be_updated_by_last_action
+ context "when environment variable is used" do
+ before :each do
+ @provider.cron_exists = true
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
+
+ # Chef Name: cronhole some stuff
+ SHELL=/bash
+ ENV=environment
+ 30 * * * * /bin/true
+
+ # Another comment
+ CRONTAB
+ end
+ context "contains an entry that can also be specified as a `property`" do
+ before :each do
+ @new_resource.environment = { "SHELL" => "/bash", "ENV" => "environment" }
+ end
+
+ it "should raise a warning for idempotency" do
+ expect(logger).to receive(:warn).with("cronhole some stuff: the environment property contains the 'SHELL' variable, which should be set separately as a property.")
+ @provider.run_action(:create)
+ end
+
+ it "should not update the crontab" do
+ expect(@provider).not_to receive(:write_crontab)
+ @provider.run_action(:create)
+ end
+
+ it "should not mark the resource as updated" do
+ expect(@new_resource).not_to be_updated_by_last_action
+ @provider.run_action(:create)
+ end
+ end
+
+ context "contains an entry that cannot be specified as a `property`" do
+ before :each do
+ @new_resource.environment = { "ENV" => "environment" }
+ @new_resource.shell "/bash"
+ end
+
+ it "should not raise a warning for idempotency" do
+ expect(logger).not_to receive(:warn).with("cronhole some stuff: the environment property contains the 'SHELL' variable, which should be set separately as a property.")
+ @provider.run_action(:create)
+ end
+
+ it "should not update the crontab" do
+ expect(@provider).not_to receive(:write_crontab)
+ @provider.run_action(:create)
+ end
+
+ it "should not mark the resource as updated" do
+ @provider.run_action(:create)
+ expect(@new_resource).not_to be_updated_by_last_action
+ end
+ end
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}'")
- @provider.run_action(:create)
+ context "when environment variable is used with property" do
+ before :each do
+ @provider.cron_exists = true
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
+
+ # Chef Name: cronhole some stuff
+ SHELL=/bash
+ ENV=environment
+ 30 * * * * /bin/true
+
+ # Another comment
+ CRONTAB
+ end
+
+ context "when environment variable is same as property" do
+ it "should throw an error" do
+ @new_resource.shell "/bash"
+ @new_resource.environment "SHELL" => "/bash"
+ expect do
+ @provider.run_action(:create)
+ end.to raise_error(Chef::Exceptions::Cron, /cronhole some stuff: the 'SHELL' property is set and environment property also contains the 'SHELL' variable. Remove the variable from the environment property./)
+ end
+ end
+
+ context "when environment variable is different from property" do
+ it "should not update the crontab" do
+ @new_resource.shell "/bash"
+ @new_resource.environment "ENV" => "environment"
+ expect(@provider).not_to receive(:write_crontab)
+ @provider.run_action(:create)
+ end
+
+ it "should not mark the resource as updated" do
+ @new_resource.shell "/bash"
+ @new_resource.environment "ENV" => "environment"
+ @provider.run_action(:create)
+ expect(@new_resource).not_to be_updated_by_last_action
+ end
+ end
end
end
end
@@ -739,7 +839,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
@@ -752,50 +852,50 @@ CRONTAB
context "when the user has a crontab with a matching section" do
before :each do
@provider.cron_exists = true
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ 30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
end
it "should remove the entry" do
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:delete)
end
it "should remove any env vars with the entry" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-FOO=test
-30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ MAILTO=foo@example.com
+ FOO=test
+ 30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:delete)
end
@@ -806,7 +906,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
@@ -817,61 +917,61 @@ FOO=test
end
it "should remove the section" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
+ # Chef Name: cronhole some stuff
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
ENDCRON
@provider.run_action(:delete)
end
it "should not blat following sections" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-#30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ #30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-#30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ #30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:delete)
end
it "should remove any envvars with the section" do
- allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB)
-0 2 * * * /some/other/command
+ allow(@provider).to receive(:read_crontab).and_return(<<~CRONTAB)
+ 0 2 * * * /some/other/command
-# Chef Name: cronhole some stuff
-MAILTO=foo@example.com
-#30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ # Chef Name: cronhole some stuff
+ MAILTO=foo@example.com
+ #30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
CRONTAB
- expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
-0 2 * * * /some/other/command
+ expect(@provider).to receive(:write_crontab).with(<<~ENDCRON)
+ 0 2 * * * /some/other/command
-#30 * * 3 * /bin/true
-# Chef Name: something else
-2 * 1 * * /bin/false
+ #30 * * 3 * /bin/true
+ # Chef Name: something else
+ 2 * 1 * * /bin/false
-# Another comment
+ # Another comment
ENDCRON
@provider.run_action(:delete)
end
@@ -880,130 +980,191 @@ MAILTO=foo@example.com
describe "read_crontab" do
before :each do
- @status = double("Status", :exitstatus => 0)
- @stdout = StringIO.new(<<-CRONTAB)
-0 2 * * * /some/other/command
+ @stdout = <<~CRONTAB
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-* 5 * * * /bin/true
+ # Chef Name: something else
+ * 5 * * * /bin/true
-# Another comment
+ # 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
it "should return the contents of the crontab" do
crontab = @provider.send(:read_crontab)
- expect(crontab).to eq <<-CRONTAB
-0 2 * * * /some/other/command
+ expect(crontab).to eq <<~CRONTAB
+ 0 2 * * * /some/other/command
-# Chef Name: something else
-* 5 * * * /bin/true
+ # Chef Name: something else
+ * 5 * * * /bin/true
-# Another comment
+ # Another comment
CRONTAB
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
+ 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"
+ describe "#env_var_str" do
+ context "when no env vars are set" do
+ it "returns an empty string" do
+ expect(@provider.send(:env_var_str)).to be_empty
+ end
+ end
+ let(:mailto) { "foo@example.com" }
+ context "When set directly" do
+ it "returns string with value" do
+ @new_resource.mailto mailto
+ expect(@provider.send(:env_var_str)).to include(mailto)
+ end
+ end
+ context "When set within the hash" do
+ context "env properties" do
+ it "returns string with a warning" do
+ @new_resource.environment "MAILTO" => mailto
+ expect(logger).to receive(:warn).with("cronhole some stuff: the environment property contains the 'MAILTO' variable, which should be set separately as a property.")
+ expect(@provider.send(:env_var_str)).to include(mailto)
+ end
+ end
+ context "other properties" do
+ it "returns string with no warning" do
+ @new_resource.environment "FOOMAILTO" => mailto
+ expect(logger).not_to receive(:warn).with("cronhole some stuff: the environment property contains the 'MAILTO' variable, which should be set separately as a property.")
+ expect(@provider.send(:env_var_str)).to include(mailto)
+ end
+ it "and a line break within properties" do
+ @new_resource.environment "FOOMAILTO" => mailto, "BARMAILTO" => mailto
+ expect(@provider.send(:env_var_str)).to eq("FOOMAILTO=foo@example.com\nBARMAILTO=foo@example.com")
+ end
+ end
+ context "both env and other properties" do
+ it "returns string with line break within the properties" do
+ @new_resource.mailto mailto
+ @new_resource.environment "FOOMAILTO" => mailto
+ expect(@provider.send(:env_var_str)).to eq("MAILTO=\"foo@example.com\"\nFOOMAILTO=foo@example.com")
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
- context "when weekday is symbol" do
- it "should return weekday in crontab format" do
- @new_resource.weekday :wednesday
- expect(@provider.send(:weekday_in_crontab)).to eq("3")
+ describe "#duration_str" do
+ context "time as a frequency" do
+ it "returns string" do
+ @new_resource.time :yearly
+ expect(@provider.send(:duration_str)).to eq("@yearly")
end
-
- it "should raise an error with an unknown weekday" do
- expect { @new_resource.weekday :caturday }.to raise_error(RangeError)
+ end
+ context "time as a duration" do
+ it "defaults to * (No Specific Value)" do
+ @new_resource.minute "1"
+ expect(@provider.send(:duration_str)).to eq("1 * * * *")
+ end
+ it "returns cron format string" do
+ @new_resource.minute "1"
+ @new_resource.hour "2"
+ @new_resource.day "3"
+ @new_resource.month "4"
+ @new_resource.weekday "5"
+ expect(@provider.send(:duration_str)).to eq("1 2 3 4 5")
end
end
+ end
- context "when weekday is a number in a string" do
- it "should return the string" do
- @new_resource.weekday "3"
- expect(@provider.send(:weekday_in_crontab)).to eq("3")
+ describe "#time_out_str" do
+ context "When not given" do
+ it "Returns an empty string" do
+ expect(@provider.send(:time_out_str)).to be_empty
end
-
- it "should raise an error with an out of range number" do
- expect { @new_resource.weekday "-1" }.to raise_error(RangeError)
+ end
+ context "When given" do
+ let(:time_out_str_val) { " timeout 10;" }
+ context "as String" do
+ it "returns string" do
+ @new_resource.time_out "10"
+ expect(@provider.send(:time_out_str)).to eq time_out_str_val
+ end
+ end
+ context "as Integer" do
+ it "returns string" do
+ @new_resource.time_out "10"
+ expect(@provider.send(:time_out_str)).to eq time_out_str_val
+ end
+ end
+ context "as Hash" do
+ it "returns string" do
+ @new_resource.time_out "duration" => "10"
+ expect(@provider.send(:time_out_str)).to eq time_out_str_val
+ end
+ it "also contains properties" do
+ @new_resource.time_out "duration" => "10", "foreground" => "true", "signal" => "FOO"
+ expect(@provider.send(:time_out_str)).to eq " timeout --foreground --signal FOO 10;"
+ end
end
end
+ end
- context "when weekday is string with the name of the week" do
- it "should return the string" do
- @new_resource.weekday "mon"
- expect(@provider.send(:weekday_in_crontab)).to eq("mon")
+ describe "#cmd_str" do
+ context "With command" do
+ let(:cmd) { "FOOBAR" }
+ before {
+ @new_resource.command cmd
+ }
+ it "returns a string with command" do
+ expect(@provider.send(:cmd_str)).to include(cmd)
+ end
+ it "string ends with a next line" do
+ expect(@provider.send(:cmd_str)[-1]).to eq("\n")
end
end
-
- context "when weekday is an integer" do
- it "should return the integer" do
- @new_resource.weekday 1
- expect(@provider.send(:weekday_in_crontab)).to eq("1")
+ context "Without command, passed" do
+ context "as nil" do
+ it "returns an empty string with a next line" do
+ @new_resource.command "bin/true"
+ expect(@provider.send(:cmd_str)).to eq(" bin/true\n")
+ end
end
-
- it "should raise an error with an out of range integer" do
- expect { @new_resource.weekday 45 }.to raise_error(RangeError)
+ context "as an empty string" do
+ it "returns an empty string with a next line" do
+ @new_resource.command ""
+ expect(@provider.send(:cmd_str)).to eq(" \n")
+ end
end
end
end
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 e69714280c..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 do
- @provider.run_action(:deploy)
- end.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 do
- @provider.run_action(:deploy)
- end.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 do
- @provider.run_action(:rollback)
- end.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 do
- @provider.run_action(:rollback)
- end.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 aebbaa6e81..cdc6f1baad 100644
--- a/spec/unit/provider/directory_spec.rb
+++ b/spec/unit/provider/directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -191,33 +201,22 @@ describe Chef::Provider::Directory do
end
end
- describe "on OS X" do
+ describe "on macOS" do
before do
- allow(node).to receive(:[]).with("platform").and_return("mac_os_x")
+ allow(ChefUtils).to receive(:macos?).and_return(true)
new_resource.path "/usr/bin/chef_test"
new_resource.recursive false
allow_any_instance_of(Chef::Provider::File).to receive(:do_selinux)
end
- it "os x 10.10 can write to sip locations" do
- allow(node).to receive(:[]).with("platform_version").and_return("10.10")
- allow(Dir).to receive(:mkdir).and_return([true], [])
- allow(::File).to receive(:directory?).and_return(true)
- allow(Chef::FileAccessControl).to receive(:writable?).and_return(true)
- directory.run_action(:create)
- expect(new_resource).to be_updated
- end
-
- it "os x 10.11 cannot write to sip locations" do
- allow(node).to receive(:[]).with("platform_version").and_return("10.11")
+ it "macOS cannot write to sip locations" do
allow(::File).to receive(:directory?).and_return(true)
allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
expect { directory.run_action(:create) }.to raise_error(Chef::Exceptions::InsufficientPermissions)
end
- it "os x 10.11 can write to sip exlcusions" do
+ it "macOS can write to sip exclusions" do
new_resource.path "/usr/local/chef_test"
- allow(node).to receive(:[]).with("platform_version").and_return("10.11")
allow(::File).to receive(:directory?).and_return(true)
allow(Dir).to receive(:mkdir).and_return([true], [])
allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 34eb9727f8..2540cb9df2 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,15 +20,21 @@ require "chef"
require "spec_helper"
describe Chef::Provider::DscResource do
- let (:events) { Chef::EventDispatch::Dispatcher.new }
- let (:run_context) { Chef::RunContext.new(node, {}, events) }
- let (:resource) { Chef::Resource::DscResource.new("dscresource", run_context) }
- let (:provider) do
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::DscResource.new("dscresource", run_context) }
+ let(:provider) do
Chef::Provider::DscResource.new(resource, run_context)
end
- context "when Powershell does not support Invoke-DscResource" do
- let (:node) do
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = "5.0.10586.0"
+ node
+ end
+
+ context "when PowerShell does not support Invoke-DscResource" do
+ let(:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "4.0"
node
@@ -36,15 +42,16 @@ describe Chef::Provider::DscResource do
it "raises a ProviderNotFound exception" do
expect(provider).not_to receive(:meta_configuration)
expect { provider.run_action(:run) }.to raise_error(
- Chef::Exceptions::ProviderNotFound, /5\.0\.10018\.0/)
+ Chef::Exceptions::ProviderNotFound, /5\.0\.10018\.0/
+ )
end
end
- context "when Powershell supports Invoke-DscResource" do
+ context "when PowerShell supports Invoke-DscResource" do
context "when RefreshMode is not set to Disabled" do
context "and the WMF 5 is a preview release" do
- let (:node) do
+ let(:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
node
@@ -52,11 +59,12 @@ describe Chef::Provider::DscResource do
it "raises an exception" do
expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(false)
expect { provider.run_action(:run) }.to raise_error(
- Chef::Exceptions::ProviderNotFound, /Disabled/)
+ Chef::Exceptions::ProviderNotFound, /Disabled/
+ )
end
end
context "and the WMF is 5 RTM or newer" do
- let (:node) do
+ let(:node) do
node = Chef::Node.new
node.automatic[:languages][:powershell][:version] = "5.0.10586.0"
node
@@ -72,19 +80,18 @@ describe Chef::Provider::DscResource do
end
context "when the LCM supports Invoke-DscResource" do
- let (:node) do
+ 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) }
- let (:resource_records) { [] }
+ let(:resource_result) { double("PowerShell", result: { "InDesiredState" => true }, verbose: ["description"]) }
+ let(:store) { double("ResourceStore", find: resource_records) }
+ let(:resource_records) { [] }
before do
allow(Chef::Util::DSC::ResourceStore).to receive(:instance).and_return(store)
- allow(Chef::Util::Powershell::Cmdlet).to receive(:new).and_return(invoke_dsc_resource)
+ allow(provider).to receive(:powershell_exec!).and_return(resource_result)
allow(provider).to receive(:dsc_refresh_mode_disabled?).and_return(true)
end
@@ -103,26 +110,24 @@ describe Chef::Provider::DscResource do
it "flags the resource as reboot required when required" do
expect(provider).to receive(:test_resource).and_return(false)
- expect(provider).to receive(:invoke_resource).
- and_return(double(:stdout => "", :return_value => nil))
+ expect(provider).to receive(:invoke_resource)
+ .and_return(double(result: { "RebootRequired" => true }))
expect(provider).to receive(:add_dsc_verbose_log)
- expect(provider).to receive(:return_dsc_resource_result).and_return(true)
expect(provider).to receive(:create_reboot_resource)
provider.run_action(:run)
end
it "does not flag the resource as reboot required when not required" do
expect(provider).to receive(:test_resource).and_return(false)
- expect(provider).to receive(:invoke_resource).
- and_return(double(:stdout => "", :return_value => nil))
+ expect(provider).to receive(:invoke_resource)
+ .and_return(double(stdout: "", result: {}))
expect(provider).to receive(:add_dsc_verbose_log)
- expect(provider).to receive(:return_dsc_resource_result).and_return(false)
expect(provider).to_not receive(:create_reboot_resource)
provider.run_action(:run)
end
context "resource name cannot be found" do
- let (:resource_records) { [] }
+ let(:resource_records) { [] }
it "raises ResourceNotFound" do
expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::ResourceNotFound)
@@ -131,33 +136,30 @@ describe Chef::Provider::DscResource do
context "resource name is found" do
context "no module name for resource found" do
- let (:resource_records) { [{}] }
+ let(:resource_records) { [{}] }
it "returns the default dsc resource module" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new) do |node, cmdlet, format|
- expect(cmdlet).to match(/Module PSDesiredStateConfiguration /)
- end.and_return(invoke_dsc_resource)
+ expect(provider).to receive(:powershell_exec!).with(/Module PSDesiredStateConfiguration /).and_return(resource_result)
provider.run_action(:run)
end
end
context "a module name for resource is found" do
- let (:resource_records) { [{ "Module" => { "Name" => "ModuleName" } }] }
+ let(:resource_records) { [{ "Module" => { "Name" => "ModuleName" } }] }
it "returns the default dsc resource module" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new) do |node, cmdlet, format|
- expect(cmdlet).to match(/Module ModuleName /)
- end.and_return(invoke_dsc_resource)
+ expect(provider).to receive(:powershell_exec!).with(/Module ModuleName /).and_return(resource_result)
provider.run_action(:run)
end
end
context "multiple resource are found" do
- let (:resource_records) do
+ let(:resource_records) do
[
- { "Module" => { "Name" => "ModuleName1" } },
- { "Module" => { "Name" => "ModuleName2" } },
- ] end
+ { "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)
@@ -165,4 +167,147 @@ describe Chef::Provider::DscResource do
end
end
end
+
+ describe "define_resource_requirements" do
+ 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
+ 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
+ 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
+ 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(provider).to receive(:powershell_exec!).with("Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module my_module -Verbose").and_return(nil)
+ provider.send(:invoke_resource, "my_method")
+ 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(provider).to receive(:powershell_exec!).with("Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module @{ModuleName='my_module';ModuleVersion='my_module_version'} -Verbose").and_return(nil)
+ provider.send(:invoke_resource, "my_method")
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index 5f091b8813..d59b6f2480 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
#
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,15 +23,15 @@ require "spec_helper"
describe Chef::Provider::DscScript do
context "when DSC is available" do
- let (:node) do
+ 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) }
- let (:provider) do
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::DscScript.new("script", run_context) }
+ let(:provider) do
Chef::Provider::DscScript.new(resource, run_context)
end
@@ -99,7 +99,7 @@ describe Chef::Provider::DscScript do
it "should noop if neither code or command are provided" do
allow(provider).to receive(:load_current_resource)
generator = double("Chef::Util::DSC::ConfigurationGenerator")
- expect(generator).to receive(:configuration_document_from_script_code).with("", anything(), anything(), anything())
+ expect(generator).to receive(:configuration_document_from_script_code).with("", anything, anything)
allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator)
provider.send(:generate_configuration_document, "tmp", nil)
end
@@ -145,11 +145,11 @@ describe Chef::Provider::DscScript do
end
context "when Dsc is not available" do
- let (:node) { Chef::Node.new }
- let (:events) { Chef::EventDispatch::Dispatcher.new }
- let (:run_context) { Chef::RunContext.new(node, {}, events) }
- let (:resource) { Chef::Resource::DscScript.new("script", run_context) }
- let (:provider) { Chef::Provider::DscScript.new(resource, run_context) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::DscScript.new("script", run_context) }
+ let(:provider) { Chef::Provider::DscScript.new(resource, run_context) }
describe "action_run" do
["1.0", "2.0", "3.0"].each do |version|
@@ -162,7 +162,7 @@ describe Chef::Provider::DscScript do
end
end
- it "raises an exception if Powershell is not present" do
+ it "raises an exception if PowerShell is not present" do
expect do
provider.run_action(:run)
end.to raise_error(Chef::Exceptions::ProviderNotFound)
diff --git a/spec/unit/provider/env/windows_spec.rb b/spec/unit/provider/env/windows_spec.rb
deleted file mode 100644
index 5ddc1d6f91..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) do
- new_resource = Chef::Resource::Env.new("CHEF_WINDOWS_ENV_TEST")
- new_resource.value("foo")
- new_resource
- end
- let(:provider) do
- 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
- 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::Env.new("PATH")
- new_resource.value(system_root)
- new_resource
- end
- let(:provider) do
- 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
- 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/env_spec.rb b/spec/unit/provider/env_spec.rb
deleted file mode 100644
index e99aee5ad1..0000000000
--- a/spec/unit/provider/env_spec.rb
+++ /dev/null
@@ -1,310 +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 "spec_helper"
-
-describe Chef::Provider::Env 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.value("bar")
- @provider = Chef::Provider::Env.new(@new_resource, @run_context)
- end
-
- it "assumes the key_name exists by default" do
- expect(@provider.key_exists).to be_truthy
- end
-
- describe "when loading the current status" do
- before do
- #@current_resource = @new_resource.clone
- #Chef::Resource::Env.stub(:new).and_return(@current_resource)
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:env_value).with("FOO").and_return("bar")
- allow(@provider).to receive(:env_key_exists).and_return(true)
- end
-
- it "should create a current resource with the same name as the new resource" do
- @provider.load_current_resource
- expect(@provider.new_resource.name).to eq("FOO")
- 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
- expect(@provider).to receive(:env_key_exists).with("FOO").and_return(true)
- @provider.load_current_resource
- expect(@provider.key_exists).to be_truthy
- end
-
- it "should flip the value of exists if the key does not exist" do
- expect(@provider).to receive(:env_key_exists).with("FOO").and_return(false)
- @provider.load_current_resource
- expect(@provider.key_exists).to be_falsey
- end
-
- it "should return the current resource" do
- expect(@provider.load_current_resource).to be_a_kind_of(Chef::Resource::Env)
- end
- end
-
- describe "action_create" do
- before do
- @provider.key_exists = false
- allow(@provider).to receive(:create_env).and_return(true)
- allow(@provider).to receive(:modify_env).and_return(true)
- end
-
- it "should call create_env if the key does not exist" do
- expect(@provider).to receive(:create_env).and_return(true)
- @provider.action_create
- end
-
- it "should set the new_resources updated flag when it creates the key" do
- @provider.action_create
- expect(@new_resource).to be_updated
- end
-
- it "should check to see if the values are the same if the key exists" do
- @provider.key_exists = true
- expect(@provider).to receive(:requires_modify_or_create?).and_return(false)
- @provider.action_create
- end
-
- it "should call modify_env if the key exists 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)
- @provider.action_create
- end
-
- it "should set the new_resources updated flag when it updates an existing value" do
- @provider.key_exists = true
- allow(@provider).to receive(:requires_modify_or_create?).and_return(true)
- allow(@provider).to receive(:modify_env).and_return(true)
- @provider.action_create
- expect(@new_resource).to be_updated
- end
- end
-
- describe "action_delete" do
- before(:each) do
- @provider.current_resource = @current_resource
- @provider.key_exists = false
- allow(@provider).to receive(:delete_element).and_return(false)
- allow(@provider).to receive(:delete_env).and_return(true)
- end
-
- it "should not call delete_env if the key does not exist" do
- expect(@provider).not_to receive(:delete_env)
- @provider.action_delete
- end
-
- it "should not call delete_element if the key does not exist" do
- expect(@provider).not_to receive(:delete_element)
- @provider.action_delete
- end
-
- it "should call delete_env if the key exists" do
- @provider.key_exists = true
- expect(@provider).to receive(:delete_env)
- @provider.action_delete
- end
-
- it "should set the new_resources updated flag to true if the key is deleted" do
- @provider.key_exists = true
- @provider.action_delete
- expect(@new_resource).to be_updated
- end
- end
-
- describe "action_modify" do
- before(:each) do
- @provider.current_resource = @current_resource
- @provider.key_exists = true
- allow(@provider).to receive(:modify_env).and_return(true)
- end
-
- it "should call modify_group if the key exists and values 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)
- @provider.action_modify
- expect(@new_resource).to be_updated
- end
-
- it "should not call modify_env if the key exists 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
- @provider.key_exists = false
- expect { @provider.action_modify }.to raise_error(Chef::Exceptions::Env)
- end
- end
-
- describe "delete_element" do
- before(:each) do
- @current_resource = Chef::Resource::Env.new("FOO")
-
- @new_resource.delim ";"
- @new_resource.value "C:/bar/bin"
-
- @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")
- 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)
- expect(@provider.delete_element).to eql(false)
- end
-
- it "should return true if the element is deleted" do
- @new_resource.value("C:/foo/bin")
- expect(@provider).to receive(:create_env)
- expect(@provider.delete_element).to eql(true)
- expect(@new_resource).to be_updated
- end
-
- context "when new_resource's value contains the delimiter" do
- it "should return false if all the elements are deleted" do
- # This indicates that the entire key needs to be deleted
- @new_resource.value("C:/foo/bin;C:/bar/bin")
- expect(@provider.delete_element).to eql(false)
- expect(@new_resource).not_to be_updated # This will be updated in action_delete
- end
-
- it "should return true if any, but not all, of the elements are deleted" do
- @new_resource.value("C:/foo/bin;C:/notbaz/bin")
- expect(@provider).to receive(:create_env)
- expect(@provider.delete_element).to eql(true)
- expect(@new_resource).to be_updated
- end
-
- it "should return true if none of the elements are deleted" do
- @new_resource.value("C:/notfoo/bin;C:/notbaz/bin")
- expect(@provider.delete_element).to eql(true)
- expect(@new_resource).not_to be_updated
- end
- end
- end
-
- describe "requires_modify_or_create?" do
- before(:each) do
- @new_resource.value("C:/bar")
- @current_resource = @new_resource.clone
- @provider.current_resource = @current_resource
- end
-
- it "should return false if the values are equal" do
- expect(@provider.requires_modify_or_create?).to be_falsey
- end
-
- it "should return true if the values not are equal" do
- @new_resource.value("C:/elsewhere")
- expect(@provider.requires_modify_or_create?).to be_truthy
- end
-
- it "should return false if the current value contains the element" do
- @new_resource.delim(";")
- @current_resource.value("C:/bar;C:/foo;C:/baz")
-
- expect(@provider.requires_modify_or_create?).to be_falsey
- end
-
- it "should return true if the current value does not contain the element" do
- @new_resource.delim(";")
- @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
- expect(@provider.requires_modify_or_create?).to be_truthy
- end
-
- context "when new_resource's value contains the delimiter" do
- it "should return false if all the current values are contained in specified order" do
- @new_resource.value("C:/biz;C:/baz")
- @new_resource.delim(";")
- @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
- expect(@provider.requires_modify_or_create?).to be_falsey
- end
-
- it "should return true if any of the new values are not contained" do
- @new_resource.value("C:/biz;C:/baz;C:/bin")
- @new_resource.delim(";")
- @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
- expect(@provider.requires_modify_or_create?).to be_truthy
- end
-
- it "should return true if values are contained in different order" do
- @new_resource.value("C:/biz;C:/baz")
- @new_resource.delim(";")
- @current_resource.value("C:/baz;C:/foo/bin;C:/biz")
- expect(@provider.requires_modify_or_create?).to be_truthy
- end
- end
- end
-
- describe "modify_env" do
- before(:each) do
- allow(@provider).to receive(:create_env).and_return(true)
- @new_resource.delim ";"
-
- @current_resource = Chef::Resource::Env.new("FOO")
- @current_resource.value "C:/foo/bin"
- @provider.current_resource = @current_resource
- end
-
- it "should not modify the variable passed to the resource" do
- new_value = "C:/bar/bin"
- passed_value = new_value.dup
- @new_resource.value(passed_value)
- @provider.modify_env
- expect(passed_value).to eq(new_value)
- end
-
- it "should only add values not already contained" do
- @new_resource.value("C:/foo;C:/bar;C:/baz")
- @current_resource.value("C:/bar;C:/baz;C:/foo/bar")
- @provider.modify_env
- expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
- end
-
- it "should reorder values to keep order which asked" do
- @new_resource.value("C:/foo;C:/bar;C:/baz")
- @current_resource.value("C:/foo/bar;C:/baz;C:/bar")
- @provider.modify_env
- expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
- 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..e0c2243f26 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,14 +26,15 @@ describe Chef::Provider::Execute do
let(:provider) { Chef::Provider::Execute.new(new_resource, run_context) }
let(:current_resource) { Chef::Resource::Ifconfig.new("foo_resource", run_context) }
# You will be the same object, I promise.
- @live_stream = Chef::EventDispatch::EventsOutputStream.new(run_context.events, :name => :execute)
+ @live_stream = Chef::EventDispatch::EventsOutputStream.new(Chef::EventDispatch::Dispatcher.new, name: :execute)
let(:opts) do
{
- timeout: 3600,
- returns: 0,
- log_level: :info,
- log_tag: new_resource.to_s,
+ timeout: 3600,
+ returns: 0,
+ log_level: :info,
+ default_env: false,
+ log_tag: new_resource.to_s,
}
end
@@ -41,18 +42,11 @@ describe Chef::Provider::Execute do
before do
allow(Chef::EventDispatch::EventsOutputStream).to receive(:new) { @live_stream }
- allow(ChefConfig).to receive(:windows?) { false }
- @original_log_level = Chef::Log.level
+ allow(ChefUtils).to receive(:windows?) { false }
Chef::Log.level = :info
allow(STDOUT).to receive(:tty?).and_return(false)
end
- after do
- Chef::Log.level = @original_log_level
- Chef::Config[:always_stream_execute] = false
- Chef::Config[:daemon] = false
- end
-
describe "#initialize" do
it "should return a Chef::Provider::Execute provider" do
expect(provider.class).to eql(Chef::Provider::Execute)
@@ -83,7 +77,25 @@ describe Chef::Provider::Execute do
expect(new_resource).to be_updated
end
- it "if you pass a command attribute, it runs the command" do
+ # 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 property, 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(:converge_by).with("execute #{new_resource.command}").and_call_original
@@ -92,7 +104,7 @@ describe Chef::Provider::Execute do
expect(new_resource).to be_updated
end
- it "should honor sensitive attribute" do
+ it "should honor sensitive property" do
new_resource.sensitive true
# Since the resource is sensitive, it should not have :live_stream set
opts.delete(:live_stream)
@@ -118,19 +130,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)
+ it "should raise if user specified relative path without cwd for Chef-13" do
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
+ expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::Execute)
end
end
@@ -238,6 +240,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 f840d92dbb..1d13b2fb6a 100644
--- a/spec/unit/provider/file/content_spec.rb
+++ b/spec/unit/provider/file/content_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,7 +36,7 @@ describe Chef::Provider::File::Content do
end
let(:new_resource) do
- double("Chef::Provider::File::Resource (new)", :name => "seattle.txt", :path => resource_path)
+ double("Chef::Provider::File::Resource (new)", name: "seattle.txt", path: resource_path)
end
let(:run_context) do
@@ -50,7 +50,7 @@ describe Chef::Provider::File::Content do
Chef::Provider::File::Content.new(new_resource, current_resource, run_context)
end
- describe "when the resource has a content attribute set" do
+ describe "when the resource has a content property set" do
before do
allow(new_resource).to receive(:content).and_return("Do do do do, do do do do, do do do do, do do do do")
@@ -100,7 +100,7 @@ describe Chef::Provider::File::Content do
end
- describe "when the resource does not have a content attribute set" do
+ describe "when the resource does not have a content property set" do
before do
allow(new_resource).to receive(:content).and_return(nil)
diff --git a/spec/unit/provider/file_spec.rb b/spec/unit/provider/file_spec.rb
index d8d4ed2286..69ac48a6e4 100644
--- a/spec/unit/provider/file_spec.rb
+++ b/spec/unit/provider/file_spec.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,7 @@
# limitations under the License.
#
+require "spec_helper"
require "support/shared/unit/provider/file"
describe Chef::Provider::File do
@@ -32,9 +33,10 @@ describe Chef::Provider::File do
content = double("Chef::Provider::File::Content")
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(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
diff --git a/spec/unit/provider/git_spec.rb b/spec/unit/provider/git_spec.rb
index a60c1b44c3..21af554111 100644
--- a/spec/unit/provider/git_spec.rb
+++ b/spec/unit/provider/git_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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], :log_tag => "git[web2.0 app]" }).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], :log_tag => "git[web2.0 app]" ).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
@@ -97,7 +152,7 @@ describe Chef::Provider::Git do
@resource.revision "v1.0"
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -106,7 +161,7 @@ describe Chef::Provider::Git do
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n" +
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0^{}\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("663c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -115,7 +170,7 @@ describe Chef::Provider::Git do
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/releases/v1.0\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -124,7 +179,7 @@ describe Chef::Provider::Git do
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("663c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -133,7 +188,7 @@ describe Chef::Provider::Git do
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.1\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -143,7 +198,7 @@ describe Chef::Provider::Git do
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" +
"805c22a5e41f5ae3193460cca044ed1435029f53\trefs/pulls/v1.0\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/pulls/v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/pulls/v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("805c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -152,7 +207,7 @@ describe Chef::Provider::Git do
@stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" +
"663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" +
"503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n")
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/heads/v1.0*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/heads/v1.0*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53")
end
@@ -167,21 +222,21 @@ describe Chef::Provider::Git do
it "raises an unresolvable git reference error if the revision can't be resolved to any revision and assertions are run" do
@resource.revision "FAIL, that's the revision I want"
@provider.action = :checkout
- expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", :stdout => "\n"))
+ expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", stdout: "\n"))
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::UnresolvableGitReference)
end
it "does not raise an error if the revision can't be resolved when assertions are not run" do
@resource.revision "FAIL, that's the revision I want"
- expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", :stdout => "\n"))
+ expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", stdout: "\n"))
expect(@provider.target_revision).to eq(nil)
end
it "does not raise an error when the revision is valid and assertions are run." do
@resource.revision "0.8-alpha"
@stdout = "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n"
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"0.8-alpha*\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"0.8-alpha*\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
@provider.action = :checkout
allow(::File).to receive(:directory?).with("/my/deploy").and_return(true)
@provider.define_resource_requirements
@@ -189,24 +244,24 @@ describe Chef::Provider::Git do
end
it "gives the latest HEAD revision SHA if nothing is specified" do
- @stdout = <<-SHAS
-28af684d8460ba4793eda3e7ac238c864a5d029a\tHEAD
-503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha
-28af684d8460ba4793eda3e7ac238c864a5d029a\trefs/heads/master
-c44fe79bb5e36941ce799cee6b9de3a2ef89afee\trefs/tags/0.5.2
-14534f0e0bf133dc9ff6dbe74f8a0c863ff3ac6d\trefs/tags/0.5.4
-d36fddb4291341a1ff2ecc3c560494e398881354\trefs/tags/0.5.6
-9e5ce9031cbee81015de680d010b603bce2dd15f\trefs/tags/0.6.0
-9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13\trefs/tags/0.6.2
-014a69af1cdce619de82afaf6cdb4e6ac658fede\trefs/tags/0.7.0
-fa8097ff666af3ce64761d8e1f1c2aa292a11378\trefs/tags/0.7.2
-44f9be0b33ba5c10027ddb030a5b2f0faa3eeb8d\trefs/tags/0.7.4
-d7b9957f67236fa54e660cc3ab45ffecd6e0ba38\trefs/tags/0.7.8
-b7d19519a1c15f1c1a324e2683bd728b6198ce5a\trefs/tags/0.7.8^{}
-ebc1b392fe7e8f0fbabc305c299b4d365d2b4d9b\trefs/tags/chef-server-package
-SHAS
+ @stdout = <<~SHAS
+ 28af684d8460ba4793eda3e7ac238c864a5d029a\tHEAD
+ 503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha
+ 28af684d8460ba4793eda3e7ac238c864a5d029a\trefs/heads/master
+ c44fe79bb5e36941ce799cee6b9de3a2ef89afee\trefs/tags/0.5.2
+ 14534f0e0bf133dc9ff6dbe74f8a0c863ff3ac6d\trefs/tags/0.5.4
+ d36fddb4291341a1ff2ecc3c560494e398881354\trefs/tags/0.5.6
+ 9e5ce9031cbee81015de680d010b603bce2dd15f\trefs/tags/0.6.0
+ 9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13\trefs/tags/0.6.2
+ 014a69af1cdce619de82afaf6cdb4e6ac658fede\trefs/tags/0.7.0
+ fa8097ff666af3ce64761d8e1f1c2aa292a11378\trefs/tags/0.7.2
+ 44f9be0b33ba5c10027ddb030a5b2f0faa3eeb8d\trefs/tags/0.7.4
+ d7b9957f67236fa54e660cc3ab45ffecd6e0ba38\trefs/tags/0.7.8
+ b7d19519a1c15f1c1a324e2683bd728b6198ce5a\trefs/tags/0.7.8^{}
+ ebc1b392fe7e8f0fbabc305c299b4d365d2b4d9b\trefs/tags/chef-server-package
+ SHAS
@resource.revision ""
- expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"HEAD\"", { :log_tag => "git[web2.0 app]" }).and_return(double("ShellOut result", :stdout => @stdout))
+ expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"HEAD\"", { log_tag: "git[web2.0 app]" }).and_return(double("ShellOut result", stdout: @stdout))
expect(@provider.target_revision).to eql("28af684d8460ba4793eda3e7ac238c864a5d029a")
end
end
@@ -221,15 +276,15 @@ SHAS
let(:expected_cmd) { 'git clone "git://github.com/opscode/chef.git" "/my/deploy/dir"' }
let(:default_options) do
{
- :user => deploy_user,
- :environment => { "GIT_SSH" => wrapper, "HOME" => "/home/deployNinja" },
- :log_tag => "git[web2.0 app]",
+ user: deploy_user,
+ environment: { "GIT_SSH" => wrapper, "HOME" => "/home/deployNinja" },
+ log_tag: "git[web2.0 app]",
}
end
before do
@resource.user deploy_user
@resource.ssh_wrapper wrapper
- allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/deployNinja"))
+ allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", name: @resource.user, dir: "/home/deployNinja"))
end
context "without a timeout set" do
it "clones a repo with default git options" do
@@ -238,22 +293,22 @@ SHAS
end
end
context "with a timeout set" do
- let (:seconds) { 10 }
+ let(:seconds) { 10 }
before { @resource.timeout(seconds) }
it "clones a repo with amended git options" do
- expect(@provider).to receive(:shell_out!).with(expected_cmd, default_options.merge(:timeout => seconds))
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, default_options.merge(timeout: seconds))
@provider.clone
end
end
context "with a specific home" do
- let (:override_home) do
+ let(:override_home) do
{ "HOME" => "/home/masterNinja" }
end
let(:overrided_options) do
{
- :user => deploy_user,
- :environment => { "GIT_SSH" => wrapper, "HOME" => "/home/masterNinja" },
- :log_tag => "git[web2.0 app]",
+ user: deploy_user,
+ environment: { "GIT_SSH" => wrapper, "HOME" => "/home/masterNinja" },
+ log_tag: "git[web2.0 app]",
}
end
before do
@@ -272,24 +327,24 @@ SHAS
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]",
+ 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"))
+ 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
+ let(:override_home) do
{ "HOME" => "/home/masterNinja" }
end
let(:overrided_options) do
{
- :user => 123,
- :environment => { "HOME" => "/home/masterNinja" },
- :log_tag => "git[web2.0 app]",
+ user: 123,
+ environment: { "HOME" => "/home/masterNinja" },
+ log_tag: "git[web2.0 app]",
}
end
before do
@@ -305,14 +360,14 @@ SHAS
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"))
+ 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\""
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :user => "deployNinja",
- :log_tag => "git[web2.0 app]",
- :environment => { "HOME" => "/home/deployNinja",
- "GIT_SSH" => "do_it_this_way.sh" })
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, user: "deployNinja",
+ log_tag: "git[web2.0 app]",
+ environment: { "HOME" => "/home/deployNinja",
+ "GIT_SSH" => "do_it_this_way.sh" })
@provider.clone
end
@@ -322,8 +377,8 @@ SHAS
version_response = double("shell_out")
allow(version_response).to receive(:stdout) { "git version 1.7.9" }
expect(@provider).to receive(:shell_out!).with("git --version",
- :log_tag => "git[web2.0 app]").and_return(version_response)
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]")
+ log_tag: "git[web2.0 app]").and_return(version_response)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, log_tag: "git[web2.0 app]")
@provider.clone
end
@@ -333,33 +388,34 @@ SHAS
version_response = double("shell_out")
allow(version_response).to receive(:stdout) { "git version 1.7.10" }
expect(@provider).to receive(:shell_out!).with("git --version",
- :log_tag => "git[web2.0 app]").and_return(version_response)
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]")
+ log_tag: "git[web2.0 app]").and_return(version_response)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, log_tag: "git[web2.0 app]")
@provider.clone
end
it "compiles a clone command with a remote other than ``origin''" do
@resource.remote "opscode"
expected_cmd = "git clone -o opscode \"git://github.com/opscode/chef.git\" \"/my/deploy/dir\""
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, log_tag: "git[web2.0 app]")
@provider.clone
end
- it "runs a checkout command with default options" do
- expect(@provider).to receive(:shell_out!).with("git branch -f deploy d35af14d41ae22b19da05d7d03a0bafc321b244c", :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]").ordered
- expect(@provider).to receive(:shell_out!).with("git checkout deploy", :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]").ordered
+ it "runs a checkout command when the local branch is set" do
+ @resource.checkout_branch "deploy"
+ expect(@provider).to receive(:shell_out!).with("git branch -f deploy d35af14d41ae22b19da05d7d03a0bafc321b244c", cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]").ordered
+ expect(@provider).to receive(:shell_out!).with("git checkout deploy", cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]").ordered
@provider.checkout
end
it "runs an enable_submodule command" do
@resource.enable_submodules true
expected_cmd = "git submodule sync"
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
expected_cmd = "git submodule update --init --recursive"
- expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, cwd: "/my/deploy/dir", log_tag: "git[web2.0 app]")
@provider.enable_submodules
end
@@ -370,60 +426,60 @@ SHAS
it "runs a sync command with default options" do
expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository)
- expected_cmd1 = "git fetch origin"
- expect(@provider).to receive(:shell_out!).with(expected_cmd1, :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]")
+ 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]")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd3, cwd: "/my/deploy/dir", log_tag: "git[web2.0 app]")
@provider.fetch_updates
end
it "runs a sync command with the user and group specified in the resource" do
@resource.user("whois")
- allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/whois"))
+ 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_cmd1 = "git fetch 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_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" })
+ 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" })
+ 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_command1 = "git fetch origin"
- expect(@provider).to receive(:shell_out!).with(fetch_command1, :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]")
+ 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]")
+ 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_command1 = "git fetch opscode"
- expect(@provider).to receive(:shell_out!).with(fetch_command1, :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]")
+ 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]")
+ expect(@provider).to receive(:shell_out!).with(fetch_command3, cwd: "/my/deploy/dir", log_tag: "git[web2.0 app]")
@provider.fetch_updates
end
@@ -434,37 +490,37 @@ SHAS
allow(command_response).to receive(:exitstatus) { 1 }
expected_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(expected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :returns => [0, 1, 2]).and_return(command_response)
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ returns: [0, 1, 2]).and_return(command_response)
add_remote_command = "git remote add #{@resource.remote} #{@resource.repository}"
expect(@provider).to receive(:shell_out!).with(add_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
it "runs the config with the user and group specified in the resource" do
@resource.user("whois")
@resource.group("thisis")
- allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/whois"))
+ allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", name: @resource.user, dir: "/home/whois"))
command_response = double("shell_out")
allow(command_response).to receive(:exitstatus) { 1 }
expected_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(expected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :user => "whois",
- :group => "thisis",
- :environment => { "HOME" => "/home/whois" },
- :returns => [0, 1, 2]).and_return(command_response)
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ user: "whois",
+ group: "thisis",
+ environment: { "HOME" => "/home/whois" },
+ returns: [0, 1, 2]).and_return(command_response)
add_remote_command = "git remote add #{@resource.remote} #{@resource.repository}"
expect(@provider).to receive(:shell_out!).with(add_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :user => "whois",
- :group => "thisis",
- :environment => { "HOME" => "/home/whois" })
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ user: "whois",
+ group: "thisis",
+ environment: { "HOME" => "/home/whois" })
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
@@ -474,13 +530,13 @@ SHAS
allow(command_response).to receive(:exitstatus) { 1 }
check_remote_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(check_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :returns => [0, 1, 2]).and_return(command_response)
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ returns: [0, 1, 2]).and_return(command_response)
expected_command = "git remote add #{@resource.remote} #{@resource.repository}"
expect(@provider).to receive(:shell_out!).with(expected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
end
@@ -492,13 +548,13 @@ SHAS
allow(command_response).to receive(:stdout) { "some_other_url" }
check_remote_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(check_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :returns => [0, 1, 2]).and_return(command_response)
- expected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}"
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ returns: [0, 1, 2]).and_return(command_response)
+ expected_command = "git config --replace-all remote.#{@resource.remote}.url \"#{@resource.repository}\""
expect(@provider).to receive(:shell_out!).with(expected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
@@ -508,13 +564,13 @@ SHAS
allow(command_response).to receive(:stdout) { @resource.repository }
check_remote_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(check_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :returns => [0, 1, 2]).and_return(command_response)
- unexpected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}"
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ returns: [0, 1, 2]).and_return(command_response)
+ unexpected_command = "git config --replace-all remote.#{@resource.remote}.url \"#{@resource.repository}\""
expect(@provider).not_to receive(:shell_out!).with(unexpected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
@@ -523,18 +579,57 @@ SHAS
allow(command_response).to receive(:exitstatus) { 2 }
check_remote_command = "git config --get remote.#{@resource.remote}.url"
expect(@provider).to receive(:shell_out!).with(check_remote_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]",
- :returns => [0, 1, 2]).and_return(command_response)
- expected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}"
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]",
+ returns: [0, 1, 2]).and_return(command_response)
+ expected_command = "git config --replace-all remote.#{@resource.remote}.url \"#{@resource.repository}\""
expect(@provider).to receive(:shell_out!).with(expected_command,
- :cwd => "/my/deploy/dir",
- :log_tag => "git[web2.0 app]")
+ cwd: "/my/deploy/dir",
+ log_tag: "git[web2.0 app]")
@provider.setup_remote_tracking_branches(@resource.remote, @resource.repository)
end
end
end
+ context "with why-run mode" do
+ before do
+ Chef::Config[:why_run] = true
+ @resource.user "test"
+ end
+
+ after do
+ Chef::Config[:why_run] = false
+ end
+
+ it "does not raise an error if user does not exist" do
+ allow(@provider).to receive(:get_homedir).with(@resource.user).and_return(nil)
+ expect { @provider.run_action(:sync) }.not_to raise_error
+ end
+
+ it "does not raise an error if user exists" do
+ allow(@provider).to receive(:get_homedir).with(@resource.user).and_return("/home/test")
+ expect { @provider.run_action(:sync) }.not_to raise_error
+ end
+ end
+
+ context "without why-run mode" do
+ before do
+ @resource.user "test"
+ end
+
+ it "raises an error if user does not exist" do
+ allow(@provider).to receive(:get_homedir).with(@resource.user).and_return(nil)
+ expect { @provider.run_action(:sync) }.to raise_error(Chef::Exceptions::User)
+ end
+
+ it "does not raise an error if user exists" do
+ allow(@provider).to receive(:action_sync) # stub the entire action
+ allow(::File).to receive(:directory?).with("/my/deploy").and_return(true)
+ allow(@provider).to receive(:get_homedir).with(@resource.user).and_return("/home/test")
+ expect { @provider.run_action(:sync) }.not_to raise_error
+ end
+ end
+
it "raises an error if the git clone command would fail because the enclosing directory doesn't exist" do
allow(@provider).to receive(:shell_out!)
expect { @provider.run_action(:sync) }.to raise_error(Chef::Exceptions::MissingParentDirectory)
@@ -583,7 +678,7 @@ SHAS
expect(@provider).to receive(:enable_submodules)
expect(@provider).to receive(:add_remotes)
@provider.run_action(:checkout)
- # @resource.should be_updated
+ # @resource.should be_updated
end
it "should not checkout if the destination exists or is a non empty directory" do
@@ -621,7 +716,7 @@ SHAS
expect(@provider).to receive(:enable_submodules)
expect(@provider).to receive(:add_remotes)
@provider.run_action(:sync)
- # @resource.should be_updated
+ # @resource.should be_updated
end
it "does not fetch any updates if the remote revision matches the current revision" do
@@ -641,7 +736,7 @@ SHAS
expect(@provider).to receive(:action_checkout)
expect(@provider).not_to receive(:shell_out!)
@provider.run_action(:sync)
- # @resource.should be_updated
+ # @resource.should be_updated
end
it "clones the repo instead of fetching updates if the deploy directory is empty" do
@@ -650,9 +745,9 @@ SHAS
allow(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true)
allow(@provider).to receive(:sync_command).and_return("huzzah!")
expect(@provider).to receive(:action_checkout)
- expect(@provider).not_to receive(:shell_out!).with("huzzah!", :cwd => "/my/deploy/dir")
+ expect(@provider).not_to receive(:shell_out!).with("huzzah!", cwd: "/my/deploy/dir")
@provider.run_action(:sync)
- #@resource.should be_updated
+ # @resource.should be_updated
end
it "does an export by cloning the repo then removing the .git directory" do
@@ -664,8 +759,8 @@ SHAS
describe "calling add_remotes" do
it "adds a new remote for each entry in additional remotes hash" do
- @resource.additional_remotes({ :opscode => "opscode_repo_url",
- :another_repo => "some_other_repo_url" })
+ @resource.additional_remotes({ opscode: "opscode_repo_url",
+ another_repo: "some_other_repo_url" })
allow(STDOUT).to receive(:tty?).and_return(false)
command_response = double("shell_out")
allow(command_response).to receive(:exitstatus) { 0 }
diff --git a/spec/unit/provider/group/dscl_spec.rb b/spec/unit/provider/group/dscl_spec.rb
index bc69d7580e..248b13ecdf 100644
--- a/spec/unit/provider/group/dscl_spec.rb
+++ b/spec/unit/provider/group/dscl_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Dreamcat4 (<dreamcat4@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)
- allow(@provider).to receive(:shell_out).and_return(@status)
+ @status = double(stdout: "\n", stderr: "", exitstatus: 0)
+ allow(@provider).to receive(:shell_out_compacted).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_compacted).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
@@ -301,31 +309,31 @@ describe "Test DSCL loading" do
@new_resource = Chef::Resource::Group.new("group name aj")
@new_resource.group_name("aj")
@provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context)
- @output = <<-EOF
-AppleMetaNodeLocation: /Local/Default
-Comment:
- Test Group
-GeneratedUID: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA
-NestedGroups: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAB
-Password: *
-PrimaryGroupID: 999
-RealName:
- TestGroup
-RecordName: com.apple.aj
-RecordType: dsRecTypeStandard:Groups
-GroupMembership: waka bar
-EOF
- allow(@provider).to receive(:safe_dscl).with("read /Groups/aj").and_return(@output)
+ @output = <<~EOF
+ AppleMetaNodeLocation: /Local/Default
+ Comment:
+ Test Group
+ GeneratedUID: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA
+ NestedGroups: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAB
+ Password: *
+ PrimaryGroupID: 999
+ RealName:
+ TestGroup
+ RecordName: com.apple.aj
+ RecordType: dsRecTypeStandard:Groups
+ GroupMembership: waka bar
+ EOF
+ 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..adc6efb5a6 100644
--- a/spec/unit/provider/group/gpasswd_spec.rb
+++ b/spec/unit/provider/group/gpasswd_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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_compacted!).with("gpasswd", "-M", "", "wheel")
@provider.modify_group_members
end
end
@@ -78,20 +81,20 @@ describe Chef::Provider::Group::Gpasswd, "modify_group_members" do
end
it "does not modify group membership" do
- expect(@provider).not_to receive(:shell_out!)
+ expect(@provider).not_to receive(:shell_out_compacted!)
@provider.modify_group_members
end
end
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")
- allow(@provider).to receive(:shell_out!)
+ expect(logger).to receive(:trace).with("group[wheel] setting group members to: lobster, rage, fist")
+ allow(@provider).to receive(:shell_out_compacted!)
@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_compacted!).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_compacted!).with("gpasswd", "-a", "lobster", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).with("gpasswd", "-a", "rage", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).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..50ee766cdb 100644
--- a/spec/unit/provider/group/groupadd_spec.rb
+++ b/spec/unit/provider/group/groupadd_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 |property, option|
+ it "should check for differences in #{property} between the current and new resources" do
+ expect(new_resource).to receive(property)
+ expect(current_resource).to receive(property)
+ provider.set_options
+ end
+
+ it "should set the option for #{property} if the new resources #{property} is not null" do
+ allow(new_resource).to receive(property).and_return("cactus")
+ expect(provider.set_options).to eql([ option, new_resource.send(property), 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 |property, option|
+ allow(new_resource).to receive(property).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).with("groupdel", "aj").and_return(true)
+ provider.remove_group
end
end
- [:add_member, :remove_member, :set_members].each do |m|
+ %i{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_compacted!).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..5a7d1ada77 100644
--- a/spec/unit/provider/group/groupmod_spec.rb
+++ b/spec/unit/provider/group/groupmod_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Dan Crosta (<dcrosta@late.am>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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"
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_compacted!).with("group", "mod", "-n", "wheel_bak", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).with("group", "add", "-g", "123", "-o", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).with("group", "del", "wheel_bak")
@provider.manage_group
end
end
@@ -76,8 +79,8 @@ 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(@provider).not_to receive(:shell_out!)
+ 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_compacted!)
@provider.manage_group
end
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_compacted!).with("group", "mod", "-n", "wheel_bak", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).with("user", "mod", "-G", "wheel", "lobster")
+ expect(@provider).to receive(:shell_out_compacted!).with("group", "add", "-g", "123", "-o", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).with("group", "add", "-g", "123", "wheel")
+ expect(@provider).to receive(:shell_out_compacted!).with("user", "mod", "-G", "wheel", "lobster")
+ expect(@provider).to receive(:shell_out_compacted!).with("user", "mod", "-G", "wheel", "rage")
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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..f46bb9cb48 100644
--- a/spec/unit/provider/group/pw_spec.rb
+++ b/spec/unit/provider/group/pw_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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"
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_compacted!).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_compacted!).with("pw", "groupmod", "wheel", "-g", "42", "-m", "someone").and_return(true)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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/solaris_spec.rb b/spec/unit/provider/group/solaris_spec.rb
new file mode 100644
index 0000000000..0d076b3433
--- /dev/null
+++ b/spec/unit/provider/group/solaris_spec.rb
@@ -0,0 +1,106 @@
+#
+# Author:: Joshua Justice (<jjustice6@bloomberg.net>)
+# Copyright:: Copyright (c) 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::Solaris 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("wheel")
+ @new_resource.members %w{all your base}
+ @new_resource.excluded_members [ ]
+ @provider = Chef::Provider::Group::Solaris.new(@new_resource, @run_context)
+ allow(@provider).to receive(:run_command)
+ end
+
+ describe "modify_group_members" do
+
+ describe "with an empty members array" do
+ before do
+ @new_resource.append(true)
+ @new_resource.members([])
+ end
+
+ it "should log an appropriate message" do
+ expect(@provider).not_to receive(:shell_out_compacted!)
+ @provider.modify_group_members
+ end
+ end
+
+ describe "with supplied members" do
+ platforms = {
+ "solaris2" => [ "-G" ],
+ }
+
+ before do
+ @new_resource.members(%w{all your base})
+ allow(File).to receive(:exist?).and_return(true)
+ end
+
+ it "should groupmod the whole batch when append is false" do
+ current_resource = @new_resource.dup
+ @provider.current_resource = current_resource
+ @node.automatic_attrs[:platform] = "solaris2"
+ @new_resource.append(false)
+ expect(@provider).to receive(:shell_out_compacted!).with("groupmod", "-U", "all,your,base", "wheel")
+ @provider.modify_group_members
+ end
+
+ platforms.each do |platform, flags|
+ it "should usermod +/- each user when the append option is set on #{platform}" do
+ current_resource = @new_resource.dup
+ current_resource.members(%w{are belong to us})
+ @new_resource.excluded_members(%w{are belong to us})
+ @provider.current_resource = current_resource
+ @node.automatic_attrs[:platform] = platform
+ @new_resource.append(true)
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "+wheel", "all")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "+wheel", "your")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "+wheel", "base")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "-wheel", "are")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "-wheel", "belong")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "-wheel", "to")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "-wheel", "us")
+ @provider.modify_group_members
+ end
+
+ end
+ end
+ end
+
+ describe "when loading the current resource" do
+ before(:each) do
+ 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(: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(:exist?).and_return(true)
+ expect { @provider.process_resource_requirements }.not_to raise_error
+ end
+ 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..0e04e5bd2e
--- /dev/null
+++ b/spec/unit/provider/group/suse_spec.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Tom Duffield (<tom@chef.io>)
+# Copyright:: Copyright (c) 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_compacted!).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_compacted!).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..e552516063 100644
--- a/spec/unit/provider/group/usermod_spec.rb
+++ b/spec/unit/provider/group/usermod_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,30 +34,27 @@ 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
- expect(@provider).not_to receive(:shell_out!)
+ expect(@provider).not_to receive(:shell_out_compacted!)
@provider.modify_group_members
end
end
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" ],
+ "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 +70,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 +81,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_compacted!).with("usermod", *flags, "wheel", "all")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "wheel", "your")
+ expect(@provider).to receive(:shell_out_compacted!).with("usermod", *flags, "wheel", "base")
@provider.modify_group_members
end
end
@@ -96,19 +93,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..85b88000a8 100644
--- a/spec/unit/provider/group/windows_spec.rb
+++ b/spec/unit/provider/group/windows_spec.rb
@@ -39,6 +39,12 @@ describe Chef::Provider::Group::Windows do
end
describe "when creating the group" do
+ before do
+ @current_resource = Chef::Resource::Group.new("staff")
+ @current_resource.members %w{all your base}
+ @provider.current_resource = @current_resource
+ end
+
it "should call @net_group.local_add" do
expect(@net_group).to receive(:local_set_members).with([])
expect(@net_group).to receive(:local_add)
@@ -49,6 +55,7 @@ describe Chef::Provider::Group::Windows do
describe "manage_group" do
before do
@new_resource.members([ "us" ])
+ @new_resource.comment = "this is group comment"
@current_resource = Chef::Resource::Group.new("staff")
@current_resource.members %w{all your base}
@new_resource.excluded_members %w{all}
@@ -57,24 +64,38 @@ describe Chef::Provider::Group::Windows do
allow(@net_group).to receive(:local_add_members)
allow(@net_group).to receive(:local_set_members)
allow(@provider).to receive(:lookup_account_name)
+ allow(@net_group).to receive(:local_group_set_info)
allow(@provider).to receive(:validate_member!).and_return(true)
@provider.current_resource = @current_resource
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 "when comment is present, should call @net_group.local_group_set_info" do
+ @new_resource.append(true)
+ expect(@net_group).to receive(:local_group_set_info).with(@new_resource.comment)
+ @provider.manage_group
+ end
+
+ it "when comment is not present, should not call @net_group.local_group_set_info" do
+ @new_resource.comment = nil
+ @new_resource.append(true)
+ expect(@net_group).not_to receive(:local_group_set_info).with(@new_resource.comment)
+ @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..a3701c1f45 100644
--- a/spec/unit/provider/group_spec.rb
+++ b/spec/unit/provider/group_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,10 +37,9 @@ describe Chef::Provider::User do
@provider.current_resource = @current_resource
@pw_group = double("Struct::Group",
- :name => "wheel",
- :gid => 20,
- :mem => %w{root aj}
- )
+ name: "wheel",
+ gid: 20,
+ mem: %w{root aj})
allow(Etc).to receive(:getgrnam).with("wheel").and_return(@pw_group)
end
@@ -85,9 +84,9 @@ describe Chef::Provider::User do
end
describe "when determining if the system is already in the target state" do
- [ :gid, :members ].each do |attribute|
- it "should return true if #{attribute} doesn't match" do
- allow(@current_resource).to receive(attribute).and_return("looooooooooooooooooool")
+ %i{gid members}.each do |property|
+ it "should return true if #{property} doesn't match" do
+ allow(@current_resource).to receive(property).and_return("looooooooooooooooooool")
expect(@provider.compare_group).to be_truthy
end
end
@@ -103,26 +102,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
@@ -142,7 +141,7 @@ describe Chef::Provider::User do
expect(@provider.new_resource).to be_updated
end
- it "should check to see if the group has mismatched attributes if the group exists" do
+ it "should check to see if the group has mismatched properties if the group exists" do
@provider.group_exists = true
allow(@provider).to receive(:compare_group).and_return(false)
allow(@provider).to receive(:change_desc).and_return([ ])
@@ -150,7 +149,7 @@ describe Chef::Provider::User do
expect(@provider.new_resource).not_to be_updated
end
- it "should call manage_group if the group exists and has mismatched attributes" do
+ it "should call manage_group if the group exists and has mismatched properties" do
@provider.group_exists = true
allow(@provider).to receive(:compare_group).and_return(true)
allow(@provider).to receive(:change_desc).and_return([ ])
@@ -191,7 +190,7 @@ describe Chef::Provider::User do
allow(@provider).to receive(:manage_group).and_return(true)
end
- it "should run manage_group if the group exists and has mismatched attributes" do
+ it "should run manage_group if the group exists and has mismatched properties" do
expect(@provider).to receive(:compare_group).and_return(true)
allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."])
expect(@provider).to receive(:manage_group).and_return(true)
@@ -212,7 +211,7 @@ describe Chef::Provider::User do
@provider.run_action(:manage)
end
- it "should not run manage_group if the group exists but has no differing attributes" do
+ it "should not run manage_group if the group exists but has no differing properties" do
expect(@provider).to receive(:compare_group).and_return(false)
allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."])
expect(@provider).not_to receive(:manage_group)
@@ -226,7 +225,7 @@ describe Chef::Provider::User do
allow(@provider).to receive(:manage_group).and_return(true)
end
- it "should run manage_group if the group exists and has mismatched attributes" do
+ it "should run manage_group if the group exists and has mismatched properties" do
expect(@provider).to receive(:compare_group).and_return(true)
allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."])
expect(@provider).to receive(:manage_group).and_return(true)
@@ -241,7 +240,7 @@ describe Chef::Provider::User do
expect(@new_resource).to be_updated
end
- it "should not run manage_group if the group exists but has no differing attributes" do
+ it "should not run manage_group if the group exists but has no differing properties" do
expect(@provider).to receive(:compare_group).and_return(false)
allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."])
expect(@provider).not_to receive(:manage_group)
@@ -267,7 +266,7 @@ describe Chef::Provider::User do
@new_resource.members << "user1"
allow(@new_resource).to receive(:append).and_return false
expect(@provider.compare_group).to be_truthy
- expect(@provider.change_desc).to eq([ "replace group members with new list of members" ])
+ expect(@provider.change_desc).to eq([ "replace group members with new list of members: aj, user1" ])
end
it "should report the gid will be changed when it does not match" do
diff --git a/spec/unit/provider/http_request_spec.rb b/spec/unit/provider/http_request_spec.rb
index 9a3519a95a..b30ed2dd30 100644
--- a/spec/unit/provider/http_request_spec.rb
+++ b/spec/unit/provider/http_request_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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..613291aabb 100644
--- a/spec/unit/provider/ifconfig/aix_spec.rb
+++ b/spec/unit/provider/ifconfig/aix_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,17 +22,17 @@ require "chef/exceptions"
describe Chef::Provider::Ifconfig::Aix do
before(:all) do
- @ifconfig_output = <<-IFCONFIG
-en1: flags=1e080863,480<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,CHECKSUM_OFFLOAD(ACTIVE),CHAIN>
- inet 10.153.11.59 netmask 0xffff0000 broadcast 10.153.255.255
- tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
-en0: flags=1e080863,480<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,CHECKSUM_OFFLOAD(ACTIVE),CHAIN> metric 1
- inet 172.29.174.58 netmask 0xffffc000 broadcast 172.29.191.255
- tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
-lo0: flags=e08084b,c0<UP,BROADCAST,LOOPBACK,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,LARGESEND,CHAIN>
- inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255
- inet6 ::1%1/0
-IFCONFIG
+ @ifconfig_output = <<~IFCONFIG
+ en1: flags=1e080863,480<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,CHECKSUM_OFFLOAD(ACTIVE),CHAIN>
+ inet 10.153.11.59 netmask 0xffff0000 broadcast 10.153.255.255
+ tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
+ en0: flags=1e080863,480<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,CHECKSUM_OFFLOAD(ACTIVE),CHAIN> metric 1
+ inet 172.29.174.58 netmask 0xffffc000 broadcast 172.29.191.255
+ tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
+ lo0: flags=e08084b,c0<UP,BROADCAST,LOOPBACK,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,LARGESEND,CHAIN>
+ inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255
+ inet6 ::1%1/0
+ IFCONFIG
end
before(:each) do
@@ -48,12 +48,12 @@ IFCONFIG
describe "#load_current_resource" do
before do
- @status = double(:stdout => @ifconfig_output, :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(@status)
+ @status = double(stdout: @ifconfig_output, exitstatus: 0)
+ allow(@provider).to receive(:shell_out_compacted).and_return(@status)
@new_resource.device "en0"
end
- it "should load given interface with attributes." do
+ it "should load given interface with properties." do
current_resource = @provider.load_current_resource
expect(current_resource.inet_addr).to eq("172.29.174.58")
expect(current_resource.target).to eq(@new_resource.target)
@@ -68,25 +68,25 @@ 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_compacted!).with(*command.split(" "))
@provider.run_action(:add)
expect(@new_resource).to be_updated
end
- it "should raise exception if metric attribute is set" do
+ it "should raise exception if metric property is set" do
@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
- expect { @provider.run_action(:add) }.to raise_error(Chef::Exceptions::Ifconfig, "interface metric attribute cannot be set for :add action")
+ expect { @provider.run_action(:add) }.to raise_error(Chef::Exceptions::Ifconfig, "interface metric property cannot be set for :add action")
end
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_compacted!).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_compacted!)
@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_compacted!).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_compacted!)
@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_compacted!).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..308435dea2 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,22 +114,16 @@ 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
let(:expected_string) do
- <<-EOF
-a line
-source #{tempdir_path}/*
-another line
-EOF
+ <<~EOF
+ a line
+ source #{tempdir_path}/*
+ another line
+ EOF
end
before do
@@ -148,11 +142,11 @@ EOF
context "when the /etc/network/interfaces file does not have the source line" do
let(:expected_string) do
- <<-EOF
-a line
-another line
-source #{tempdir_path}/*
-EOF
+ <<~EOF
+ a line
+ another line
+ source #{tempdir_path}/*
+ EOF
end
before 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,22 +240,16 @@ 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
let(:expected_string) do
- <<-EOF
-a line
-source #{tempdir_path}/*
-another line
+ <<~EOF
+ a line
+ source #{tempdir_path}/*
+ another line
EOF
end
@@ -279,10 +267,10 @@ another line
context "when the /etc/network/interfaces file does not have the source line" do
let(:expected_string) do
- <<-EOF
-a line
-another line
-source #{tempdir_path}/*
+ <<~EOF
+ a line
+ another line
+ source #{tempdir_path}/*
EOF
end
diff --git a/spec/unit/provider/ifconfig/redhat_spec.rb b/spec/unit/provider/ifconfig/redhat_spec.rb
index 0088cef9f9..75291d3bf9 100644
--- a/spec/unit/provider/ifconfig/redhat_spec.rb
+++ b/spec/unit/provider/ifconfig/redhat_spec.rb
@@ -25,16 +25,21 @@ 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"
+ @new_resource.vlan "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 +52,16 @@ 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*$/)
+ expect(arg).to match(/^\s*VLAN=yes\s*$/)
end
expect(@config).to receive(:run_action).with(:create)
expect(@config).to receive(:updated?).and_return(true)
@@ -64,7 +74,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..668a3ca9d9 100644
--- a/spec/unit/provider/ifconfig_spec.rb
+++ b/spec/unit/provider/ifconfig_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prajakta Purohit (prajakta@chef.io)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,6 @@
# limitations under the License.
#
-#require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
require "spec_helper"
require "chef/exceptions"
@@ -26,7 +25,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,33 +34,68 @@ 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
- before do
- @status = double(:stdout => "", :exitstatus => 1)
- allow(@provider).to receive(:shell_out).and_return(@status)
- @provider.load_current_resource
+ let(:net_tools_version) { StringIO.new <<~EOS }
+ net-tools 1.60
+ ifconfig 1.42 (2001-04-13)
+ EOS
+
+ let(:net_tools_version2) { StringIO.new <<~EOS }
+ net-tools 2.10-alpha
+ EOS
+
+ context "when ifconfig returns its version on stdout" do
+ before do
+ ifconfig = double(stdout: "", exitstatus: 1)
+ allow(@provider).to receive(:shell_out_compacted).and_return(ifconfig)
+ ifconfig_version = double(stdout: net_tools_version2, stderr: "", exitstatus: 4)
+ allow(@provider).to receive(:shell_out_compacted).with("ifconfig", "--version").and_return(ifconfig_version)
+ @provider.load_current_resource
+ end
+ it "should track state of ifconfig failure" do
+ expect(@provider.instance_variable_get("@status").exitstatus).not_to eq(0)
+ end
+ it "should thrown an exception when ifconfig fails" do
+ @provider.define_resource_requirements
+ expect { @provider.process_resource_requirements }.to raise_error Chef::Exceptions::Ifconfig
+ end
+ it "should grab the correct major.minor version of net-tools" do
+ expect(@provider.ifconfig_version).to eql("2.10")
+ end
end
- it "should track state of ifconfig failure" do
- expect(@provider.instance_variable_get("@status").exitstatus).not_to eq(0)
- end
- it "should thrown an exception when ifconfig fails" do
- @provider.define_resource_requirements
- expect { @provider.process_resource_requirements }.to raise_error Chef::Exceptions::Ifconfig
+
+ context "when ifconfig returns its version on stderr" do
+ before do
+ ifconfig = double(stdout: "", exitstatus: 1)
+ allow(@provider).to receive(:shell_out_compacted).and_return(ifconfig)
+ ifconfig_version = double(stdout: "", stderr: net_tools_version, exitstatus: 4)
+ allow(@provider).to receive(:shell_out_compacted).with("ifconfig", "--version").and_return(ifconfig_version)
+ @provider.load_current_resource
+ end
+ it "should track state of ifconfig failure" do
+ expect(@provider.instance_variable_get("@status").exitstatus).not_to eq(0)
+ end
+ it "should thrown an exception when ifconfig fails" do
+ @provider.define_resource_requirements
+ expect { @provider.process_resource_requirements }.to raise_error Chef::Exceptions::Ifconfig
+ end
+ it "should grab the correct major.minor version of net-tools" do
+ expect(@provider.ifconfig_version).to eql("1.60")
+ end
end
end
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_compacted!).with(*command.split(" "))
expect(@provider).to receive(:generate_config)
@provider.run_action(:add)
@@ -72,7 +106,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_compacted!).with(*command.split(" "))
@provider.run_action(:add)
expect(@new_resource).to be_updated
@@ -80,7 +114,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_compacted!)
@current_resource.inet_addr "10.0.0.1"
expect(@provider).to receive(:generate_config)
@@ -88,9 +122,20 @@ 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
+ it "should add a bridge interface" do
+ allow(@provider).to receive(:load_current_resource)
+ @new_resource.device "br-1234"
+ command = "ifconfig br-1234 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500"
+ expect(@provider).to receive(:shell_out_compacted!).with(*command.split(" "))
+ expect(@provider).to receive(:generate_config)
+
+ @provider.run_action(:add)
+ expect(@new_resource).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
end
describe Chef::Provider::Ifconfig, "action_enable" do
@@ -99,7 +144,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_compacted!).with(*command.split(" "))
expect(@provider).not_to receive(:generate_config)
@provider.run_action(:enable)
@@ -110,7 +155,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_compacted!).with(*command.split(" "))
@provider.run_action(:enable)
expect(@new_resource).to be_updated
@@ -133,7 +178,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_compacted!).with(*command.split(" "))
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -142,7 +187,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_compacted!)
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -156,7 +201,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_compacted!).with(*command.split(" "))
expect(@provider).not_to receive(:delete_config)
@provider.run_action(:disable)
@@ -165,7 +210,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_compacted!)
expect(@provider).not_to receive(:delete_config)
@provider.run_action(:disable)
@@ -179,7 +224,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_compacted!).with(*command.split(" "))
expect(@provider).to receive(:delete_config)
@provider.run_action(:delete)
@@ -190,7 +235,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_compacted!)
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 2893044833..6a729c2cb6 100644
--- a/spec/unit/provider/launchd_spec.rb
+++ b/spec/unit/provider/launchd_spec.rb
@@ -29,38 +29,68 @@ describe Chef::Provider::Launchd do
let(:label) { "call.mom.weekly" }
let(:new_resource) { Chef::Resource::Launchd.new(label) }
let!(:current_resource) { Chef::Resource::Launchd.new(label) }
- let(:test_plist) { 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<dict>
-\t\t<key>Hourly</key>
-\t\t<integer>10</integer>
-\t\t<key>Weekday</key>
-\t\t<integer>7</integer>
-\t</dict>
-\t<key>TimeOut</key>
-\t<integer>300</integer>
-</dict>
-</plist>
-XML
+ let(:test_plist) { 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<dict>
+ \t\t<key>Hour</key>
+ \t\t<integer>10</integer>
+ \t\t<key>Weekday</key>
+ \t\t<integer>7</integer>
+ \t</dict>
+ \t<key>TimeOut</key>
+ \t<integer>300</integer>
+ </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
{
"Label" => "call.mom.weekly",
"Program" => "/Library/scripts/call_mom.sh",
"StartCalendarInterval" => {
- "Hourly" => 10,
+ "Hour" => 10,
"Weekday" => 7,
},
"TimeOut" => 300,
- } end
+ }
+ end
before(:each) do
provider.load_current_resource
@@ -83,14 +113,14 @@ XML
describe "agent" do
it "path should be /Library/LaunchAgents/call.mom.weekly.plist" do
new_resource.type "agent"
- expect(provider.gen_path_from_type).
- to eq("/Library/LaunchAgents/call.mom.weekly.plist")
+ expect(provider.gen_path_from_type)
+ .to eq("/Library/LaunchAgents/call.mom.weekly.plist")
end
end
describe "daemon" do
it "path should be /Library/LaunchDaemons/call.mom.weekly.plist" do
- expect(provider.gen_path_from_type).
- to eq("/Library/LaunchDaemons/call.mom.weekly.plist")
+ expect(provider.gen_path_from_type)
+ .to eq("/Library/LaunchDaemons/call.mom.weekly.plist")
end
end
end
@@ -100,55 +130,66 @@ 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
- expect(provider.content?).to be_truthy
- expect(provider.content).to eql(test_plist)
+ new_resource.start_calendar_interval "Hour" => 10, "Weekday" => 7
+ expect(provider.file_content?).to be_truthy
+ expect(provider.file_content).to eql(test_plist)
end
end
- describe "hash is passed" do
- it "should produce the test_plist from the hash" do
- new_resource.hash test_hash
- expect(provider.content?).to be_truthy
- expect(provider.content).to eql(test_plist)
+ 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.file_content?).to be_truthy
+ expect(provider.file_content).to eql(test_plist_multiple_intervals)
end
- end
- end
- describe "with an :enable action" do
- describe "and the file has been updated" do
- before(:each) do
- allow(provider).to receive(
- :manage_plist).with(:create).and_return(true)
- allow(provider).to receive(
- :manage_service).with(:restart).and_return(true)
+ 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.file_content?).to be_truthy
+ %w{Minute Hour Day Weekday Month}.each do |key|
+ expect(provider.file_content).to include("<key>#{key}</key>")
+ end
end
- it "should call manage_service with a :restart action" do
- expect(provider.manage_service(:restart)).to be_truthy
+ 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 "works with action enable" do
- expect(run_resource_setup_for_action(:enable)).to be_truthy
- provider.action_enable
+ 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 "and the file has not been updated" do
- before(:each) do
- allow(provider).to receive(
- :manage_plist).with(:create).and_return(nil)
- allow(provider).to receive(
- :manage_service).with(:enable).and_return(true)
- end
-
- it "should call manage_service with a :enable action" do
- expect(provider.manage_service(:enable)).to be_truthy
- end
-
- it "works with action enable" do
- expect(run_resource_setup_for_action(:enable)).to be_truthy
- provider.action_enable
+ describe "hash is passed" do
+ it "should produce the test_plist content from the plist_hash property" do
+ new_resource.plist_hash test_hash
+ expect(provider.file_content?).to be_truthy
+ expect(provider.file_content).to eql(test_plist)
end
end
end
@@ -158,9 +199,11 @@ XML
before(:each) do
allow(File).to receive(:exists?).and_return(true)
allow(provider).to receive(
- :manage_service).with(:disable).and_return(true)
+ :manage_service
+ ).with(:disable).and_return(true)
allow(provider).to receive(
- :manage_plist).with(:delete).and_return(true)
+ :manage_plist
+ ).with(:delete).and_return(true)
end
it "should call manage_service with a :disable action" do
@@ -177,7 +220,8 @@ XML
before(:each) do
allow(File).to receive(:exists?).and_return(false)
allow(provider).to receive(
- :manage_plist).with(:delete).and_return(true)
+ :manage_plist
+ ).with(:delete).and_return(true)
end
it "works with action :delete" do
diff --git a/spec/unit/provider/link_spec.rb b/spec/unit/provider/link_spec.rb
index 9426cf41dc..eebf0a9d07 100644
--- a/spec/unit/provider/link_spec.rb
+++ b/spec/unit/provider/link_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,17 @@ require "ostruct"
require "spec_helper"
-if Chef::Platform.windows?
- require "chef/win32/file" #probably need this in spec_helper
+if ChefUtils.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
@@ -39,12 +41,12 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
end
def canonicalize(path)
- Chef::Platform.windows? ? path.tr("/", '\\') : path
+ ChefUtils.windows? ? path.tr("/", '\\') : path
end
describe "when the target is a symlink" do
before(:each) do
- lstat = double("stats", :ino => 5)
+ lstat = double("stats", ino: 5)
allow(lstat).to receive(:uid).and_return(501)
allow(lstat).to receive(:gid).and_return(501)
allow(lstat).to receive(:mode).and_return(0777)
@@ -144,7 +146,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
describe "when the target is a regular old file" do
before do
- stat = double("stats", :ino => 5)
+ 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)
@@ -176,7 +178,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
describe "and the source exists" do
before do
- stat = double("stats", :ino => 6)
+ stat = double("stats", ino: 6)
allow(stat).to receive(:uid).and_return(502)
allow(stat).to receive(:gid).and_return(502)
allow(stat).to receive(:mode).and_return(0644)
@@ -203,7 +205,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
describe "and is hardlinked to the source" do
before do
- stat = double("stats", :ino => 5)
+ 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)
@@ -252,12 +254,13 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
describe "action_delete" do
before(:each) do
- stat = double("stats", :ino => 5)
+ 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)
+ "#{CHEF_SPEC_DATA}/fofile-link"
+ ).and_return(stat)
provider.load_current_resource
end
@@ -265,12 +268,13 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
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)
+ "#{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(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
provider.run_action(:delete)
end
end
@@ -278,12 +282,13 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
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)
+ "#{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(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
provider.run_action(:delete)
end
end
@@ -291,12 +296,13 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
shared_context "delete link to files" do
before do
allow(::File).to receive(:directory?).with(
- "#{CHEF_SPEC_DATA}/fofile-link").and_return(false)
+ "#{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(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+ expect(logger).to receive(:info).with("#{provider.new_resource} deleted")
provider.run_action(:delete)
end
end
@@ -304,9 +310,11 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
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)
+ "#{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")
+ "#{CHEF_SPEC_DATA}/fofile-link"
+ ).and_return("#{CHEF_SPEC_DATA}/fofile")
end
end
@@ -319,21 +327,25 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
end
before(:each) do
- stat = double("stats", :ino => 5)
+ 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)
+ "#{CHEF_SPEC_DATA}/fofile-link"
+ ).and_return(false)
allow(File).to receive(:exists?).with(
- "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+ "#{CHEF_SPEC_DATA}/fofile-link"
+ ).and_return(true)
allow(File).to receive(:exists?).with(
- "#{CHEF_SPEC_DATA}/fofile").and_return(true)
+ "#{CHEF_SPEC_DATA}/fofile"
+ ).and_return(true)
allow(provider.file_class).to receive(:stat).with(
- "#{CHEF_SPEC_DATA}/fofile").and_return(stat)
+ "#{CHEF_SPEC_DATA}/fofile"
+ ).and_return(stat)
end
end
@@ -344,9 +356,9 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
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)
+ provider.new_resource.name
+ ).and_return(resource_link)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
context "soft links" do
@@ -376,7 +388,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
context "on Linux platform" do
before(:each) do
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
end
context "soft links" do
diff --git a/spec/unit/provider/log_spec.rb b/spec/unit/provider/log_spec.rb
index ce7b1af55a..8771304fad 100644
--- a/spec/unit/provider/log_spec.rb
+++ b/spec/unit/provider/log_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Cary Penniman (<cary@rightscale.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
require "spec_helper"
-describe Chef::Provider::Log::ChefLog do
+describe Chef::Resource::Log do
let(:log_str) { "this is my test string to log" }
@@ -28,48 +28,53 @@ describe Chef::Provider::Log::ChefLog do
let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:new_resource) { Chef::Resource::Log.new(log_str) }
+ let(:new_resource) { Chef::Resource::Log.new(log_str, run_context) }
- let(:provider) { Chef::Provider::Log::ChefLog.new(new_resource, run_context) }
+ let(:provider) { new_resource.provider_for_action(:run) }
- 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
diff --git a/spec/unit/provider/mdadm_spec.rb b/spec/unit/provider/mdadm_spec.rb
index 8ef884e131..c0f6813afd 100644
--- a/spec/unit/provider/mdadm_spec.rb
+++ b/spec/unit/provider/mdadm_spec.rb
@@ -19,33 +19,31 @@
require "spec_helper"
require "ostruct"
-describe Chef::Provider::Mdadm do
+describe Chef::Resource::Mdadm do
before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Mdadm.new("/dev/md1")
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ @new_resource = Chef::Resource::Mdadm.new("/dev/md1", run_context)
@new_resource.devices ["/dev/sdz1", "/dev/sdz2", "/dev/sdz3"]
- @provider = Chef::Provider::Mdadm.new(@new_resource, @run_context)
+ @provider = @new_resource.provider_for_action(:create)
end
describe "when determining the current metadevice status" do
it "should set the current resources mount point to the new resources mount point" do
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:status => 0))
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(status: 0))
@provider.load_current_resource
expect(@provider.current_resource.name).to eq("/dev/md1")
expect(@provider.current_resource.raid_device).to eq("/dev/md1")
end
it "determines that the metadevice exists when mdadm exit code is zero" do
- allow(@provider).to receive(:shell_out!).with("mdadm --detail --test /dev/md1", :returns => [0, 4]).and_return(OpenStruct.new(:status => 0))
+ allow(@provider).to receive(:shell_out_compacted!).with("mdadm", "--detail", "--test", "/dev/md1", returns: [0, 4]).and_return(OpenStruct.new(status: 0))
@provider.load_current_resource
expect(@provider.current_resource.exists).to be_truthy
end
it "determines that the metadevice does not exist when mdadm exit code is 4" do
- allow(@provider).to receive(:shell_out!).with("mdadm --detail --test /dev/md1", :returns => [0, 4]).and_return(OpenStruct.new(:status => 4))
+ allow(@provider).to receive(:shell_out_compacted!).with("mdadm", "--detail", "--test", "/dev/md1", returns: [0, 4]).and_return(OpenStruct.new(status: 4))
@provider.load_current_resource
expect(@provider.current_resource.exists).to be_falsey
end
diff --git a/spec/unit/provider/mount/aix_spec.rb b/spec/unit/provider/mount/aix_spec.rb
index 3371c270c5..3d77a23bd6 100644
--- a/spec/unit/provider/mount/aix_spec.rb
+++ b/spec/unit/provider/mount/aix_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,28 +22,33 @@ require "ostruct"
describe Chef::Provider::Mount::Aix do
before(:all) do
- @mounted_output = <<-MOUNT
- node mounted mounted over vfs date options
--------- --------------- --------------- ------ ------------ ---------------
- /dev/sdz1 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8
-MOUNT
-
- @unmounted_output = <<-UNMOUNTED
- node mounted mounted over vfs date options
--------- --------------- --------------- ------ ------------ ---------------
- /dev/sdz2 / jfs2 Jul 17 13:22 rw,log=/dev/hd8
-UNMOUNTED
-
- @conflict_mounted_output = <<-MOUNT
- node mounted mounted over vfs date options
--------- --------------- --------------- ------ ------------ ---------------
- /dev/sdz3 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8
-MOUNT
-
- @enabled_output = <<-ENABLED
-#MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
-/tmp/foo:/dev/sdz1:jfs2::bootfs:10485760:rw:yes:no
-ENABLED
+ @mounted_output = <<~MOUNT
+ node mounted mounted over vfs date options
+ -------- --------------- --------------- ------ ------------ ---------------
+ /dev/sdz1 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8
+ MOUNT
+
+ @unmounted_output = <<~UNMOUNTED
+ node mounted mounted over vfs date options
+ -------- --------------- --------------- ------ ------------ ---------------
+ /dev/sdz2 / jfs2 Jul 17 13:22 rw,log=/dev/hd8
+ UNMOUNTED
+
+ @conflict_mounted_output = <<~MOUNT
+ node mounted mounted over vfs date options
+ -------- --------------- --------------- ------ ------------ ---------------
+ /dev/sdz3 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8
+ MOUNT
+
+ @enabled_output = <<~ENABLED
+ #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
@@ -56,7 +61,7 @@ ENABLED
@new_resource.device_type :device
@new_resource.fstype "jfs2"
- @new_resource.supports :remount => false
+ @new_resource.supports remount: false
@provider = Chef::Provider::Mount::Aix.new(@new_resource, @run_context)
@@ -65,13 +70,13 @@ ENABLED
end
def stub_mounted(provider, mounted_output)
- response = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => mounted_output, :stderr => "")
- expect(provider).to receive(:shell_out!).with("mount").and_return(response)
+ response = double("Mixlib::ShellOut command", exitstatus: 0, stdout: mounted_output, stderr: "")
+ expect(provider).to receive(:shell_out_compacted!).with("mount").and_return(response)
end
def stub_enabled(provider, enabled_output)
- response = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => enabled_output, :stderr => "")
- expect(provider).to receive(:shell_out).with("lsfs -c #{@new_resource.mount_point}").and_return(response)
+ response = double("Mixlib::ShellOut command", exitstatus: 0, stdout: enabled_output, stderr: "")
+ expect(provider).to receive(:shell_out_compacted).with("lsfs", "-c", @new_resource.mount_point).and_return(response)
end
def stub_mounted_enabled(provider, mounted_output, enabled_output)
@@ -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?
@@ -121,7 +145,7 @@ ENABLED
it "should mount resource if it is not mounted" do
stub_mounted_enabled(@provider, @unmounted_output, "")
- expect(@provider).to receive(:shell_out!).with("mount -v #{@new_resource.fstype} #{@new_resource.device} #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-v", @new_resource.fstype, @new_resource.device, @new_resource.mount_point)
@provider.run_action(:mount)
end
@@ -139,7 +163,7 @@ ENABLED
it "should umount resource if it is already mounted" do
stub_mounted_enabled(@provider, @mounted_output, "")
- expect(@provider).to receive(:shell_out!).with("umount #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("umount", @new_resource.mount_point)
@provider.run_action(:umount)
end
@@ -155,20 +179,20 @@ ENABLED
describe "remount_fs" do
it "should remount resource if it is already mounted and it supports remounting" do
- @new_resource.supports({ :remount => true })
+ @new_resource.supports({ remount: true })
stub_mounted_enabled(@provider, @mounted_output, "")
- expect(@provider).to receive(:shell_out!).with("mount -o remount #{@new_resource.device} #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount", @new_resource.device, @new_resource.mount_point)
@provider.run_action(:remount)
end
it "should remount with new mount options if it is already mounted and it supports remounting" do
- @new_resource.supports({ :remount => true })
+ @new_resource.supports({ remount: true })
@new_resource.options("nodev,rw")
stub_mounted_enabled(@provider, @mounted_output, "")
- expect(@provider).to receive(:shell_out!).with("mount -o remount,nodev,rw #{@new_resource.device} #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount,nodev,rw", @new_resource.device, @new_resource.mount_point)
@provider.run_action(:remount)
end
@@ -178,45 +202,61 @@ ENABLED
it "should enable mount if it is mounted and not enabled" do
@new_resource.options("nodev,rw")
stub_mounted_enabled(@provider, @mounted_output, "")
+ # Add existing mount to test enable action appends additional mount with seperating blank line
filesystems = StringIO.new
+ filesystems.puts <<~ETCFILESYSTEMS
+ /tmp/abc:
+ dev = /dev/sdz2
+ vfs = jfs2
+ mount = true
+ options = rw
+ ETCFILESYSTEMS
allow(::File).to receive(:open).with("/etc/filesystems", "a").and_yield(filesystems)
@provider.run_action(:enable)
- expect(filesystems.string).to match(%r{^/tmp/foo:\n\tdev\t\t= /dev/sdz1\n\tvfs\t\t= jfs2\n\tmount\t\t= false\n\toptions\t\t= nodev,rw\n$})
+ expect(filesystems.string).to match(%r{^\n\n/tmp/foo:\n\tdev\t\t= /dev/sdz1\n\tvfs\t\t= jfs2\n\tmount\t\t= false\n\toptions\t\t= nodev,rw\n$})
end
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
it "should disable mount if it is mounted and enabled" do
stub_mounted_enabled(@provider, @mounted_output, @enabled_output)
- allow(::File).to receive(:open).with("/etc/filesystems", "r").and_return(<<-ETCFILESYSTEMS)
-/tmp/foo:
- dev = /dev/sdz1
- vfs = jfs2
- log = /dev/hd8
- mount = true
- check = true
- vol = /opt
- free = false
- quota = no
-
-/tmp/abc:
- dev = /dev/sdz2
- vfs = jfs2
- mount = true
- options = rw
-ETCFILESYSTEMS
+ allow(::File).to receive(:open).with("/etc/filesystems", "r").and_return(<<~ETCFILESYSTEMS)
+ /tmp/foo:
+ dev = /dev/sdz1
+ vfs = jfs2
+ log = /dev/hd8
+ mount = true
+ check = true
+ vol = /opt
+ free = false
+ quota = no
+
+ /tmp/abc:
+ dev = /dev/sdz2
+ vfs = jfs2
+ mount = true
+ options = rw
+ ETCFILESYSTEMS
filesystems = StringIO.new
allow(::File).to receive(:open).with("/etc/filesystems", "w").and_yield(filesystems)
diff --git a/spec/unit/provider/mount/linux_spec.rb b/spec/unit/provider/mount/linux_spec.rb
new file mode 100644
index 0000000000..3e41f895d1
--- /dev/null
+++ b/spec/unit/provider/mount/linux_spec.rb
@@ -0,0 +1,107 @@
+require "spec_helper"
+
+describe Chef::Provider::Mount::Linux do
+
+ let(:run_context) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ end
+
+ let(:new_resource) do
+ new_resource = Chef::Resource::Mount.new("/tmp/foo")
+ new_resource.device "/dev/sdz1"
+ new_resource.device_type :device
+ new_resource.fstype "ext3"
+ new_resource.supports remount: false
+ new_resource
+ end
+
+ let(:provider) do
+ Chef::Provider::Mount::Linux.new(new_resource, run_context)
+ end
+
+ before(:each) do
+ allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true
+ allow(::File).to receive(:exists?).with("/tmp/foo").and_return true
+ allow(::File).to receive(:exists?).with("//192.168.11.102/Share/backup").and_return true
+ allow(::File).to receive(:realpath).with("/dev/sdz1").and_return "/dev/sdz1"
+ allow(::File).to receive(:realpath).with("/tmp/foo").and_return "/tmp/foo"
+ end
+
+ context "to see if the volume is mounted" do
+
+ it "should set mounted true if the mount point is found in the mounts list" do
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: "/tmp/foo /dev/sdz1 type ext3 (rw)\n"))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_truthy
+ end
+
+ it "should set mounted false if another mount point beginning with the same path is found in the mounts list" do
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: "/tmp/foobar /dev/sdz1 type ext3 (rw)\n"))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_falsey
+ end
+
+ it "should set mounted true if the symlink target of the device is found in the mounts list" do
+ # expand the target path to correct specs on Windows
+ target = ::File.expand_path("/dev/mapper/target")
+
+ allow(::File).to receive(:symlink?).with((new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((new_resource.device).to_s).and_return(target)
+
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: "/tmp/foo #{target} type ext3 (rw)\n"))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_truthy
+ end
+
+ it "should set mounted true if the symlink target of the device is relative and is found in the mounts list - CHEF-4957" do
+ target = "xsdz1"
+
+ # expand the target path to correct specs on Windows
+ absolute_target = ::File.expand_path("/dev/xsdz1")
+
+ allow(::File).to receive(:symlink?).with((new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((new_resource.device).to_s).and_return(target)
+
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: "/tmp/foo #{absolute_target} type ext3 (rw)\n"))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_truthy
+ end
+
+ it "should set mounted true if the mount point is found last in the mounts list" do
+ mount = "#{new_resource.mount_point} /dev/sdy1 type ext3 (rw)\n"
+ mount << "#{new_resource.mount_point} #{new_resource.device} type ext3 (rw)\n"
+
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: mount))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_truthy
+ end
+
+ it "should set mounted false if the mount point is not last in the mounts list" do
+ mount = "#{new_resource.device} on #{new_resource.mount_point} type ext3 (rw)\n"
+ mount << "/dev/sdy1 on #{new_resource.mount_point} type ext3 (rw)\n"
+
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: mount))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_falsey
+ end
+
+ it "mounted should be false if the mount point is not found in the mounts list" do
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: "/dev/sdy1 on /tmp/foo type ext3 (rw)\n"))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_falsey
+ end
+
+ it "should set mounted true if network_device? is true and the mount point is found in the mounts list" do
+ new_resource.device "//192.168.11.102/Share/backup"
+ new_resource.fstype "cifs"
+ mount = "/tmp/foo //192.168.11.102/Share/backup[/backup] cifs rw\n"
+ mount << "#{new_resource.mount_point} #{new_resource.device} type #{new_resource.fstype}\n"
+ allow(provider).to receive(:shell_out!).and_return(double(stdout: mount))
+ provider.load_current_resource
+ expect(provider.current_resource.mounted).to be_truthy
+ end
+ end
+
+end
diff --git a/spec/unit/provider/mount/mount_spec.rb b/spec/unit/provider/mount/mount_spec.rb
index 0b956d9bb9..9a7d9198b5 100644
--- a/spec/unit/provider/mount/mount_spec.rb
+++ b/spec/unit/provider/mount/mount_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,7 +30,7 @@ describe Chef::Provider::Mount::Mount do
@new_resource.device_type :device
@new_resource.fstype "ext3"
- @new_resource.supports :remount => false
+ @new_resource.supports remount: false
@provider = Chef::Provider::Mount::Mount.new(@new_resource, @run_context)
@@ -42,7 +42,8 @@ describe Chef::Provider::Mount::Mount do
describe "when discovering the current fs state" do
before do
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => ""))
+ allow(@provider).to receive(:shell_out_compacted!).and_return(OpenStruct.new(stdout: ""))
+ allow(::File).to receive(:exist?).with("/etc/fstab").and_return(true)
allow(::File).to receive(:foreach).with("/etc/fstab")
end
@@ -53,36 +54,36 @@ 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
- @status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1)
+ 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()
+ @stdout_findfs = double("STDOUT", first: "/dev/sdz1")
+ expect(@provider).to receive(:shell_out_compacted).with("/sbin/findfs", "UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(@status)
+ @provider.load_current_resource
@provider.mountable?
end
describe "when dealing with network mounts" do
{ "nfs" => "nfsserver:/vol/path",
"cifs" => "//cifsserver/share" }.each do |type, fs_spec|
- it "should detect network fs_spec (#{type})" do
- @new_resource.device fs_spec
- expect(@provider.network_device?).to be_truthy
- end
-
- it "should ignore trailing slash and set mounted to true for network mount (#{type})" do
- @new_resource.device fs_spec
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{fs_spec}/ on /tmp/foo type #{type} (rw)\n"))
- @provider.load_current_resource
- expect(@provider.current_resource.mounted).to be_truthy
+ it "should detect network fs_spec (#{type})" do
+ @new_resource.device fs_spec
+ expect(@provider.network_device?).to be_truthy
+ end
+
+ it "should ignore trailing slash and set mounted to true for network mount (#{type})" do
+ @new_resource.device fs_spec
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "#{fs_spec}/ on /tmp/foo type #{type} (rw)\n"))
+ @provider.load_current_resource
+ expect(@provider.current_resource.mounted).to be_truthy
+ end
end
- end
end
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
@@ -94,41 +95,41 @@ describe Chef::Provider::Mount::Mount do
end
it "should raise an error if the mount device (uuid) does not exist", :not_supported_on_solaris do
- status = double(:stdout => "", :exitstatus => 1)
+ status = double(stdout: "", exitstatus: 1)
@new_resource.device_type :uuid
@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(@provider).to receive(:shell_out_compacted).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
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdz1 on /tmp/foo type ext3 (rw)\n"))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/dev/sdz1 on /tmp/foo type ext3 (rw)\n"))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_truthy
end
it "should set mounted false if another mount point beginning with the same path is found in the mounts list" do
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdz1 on /tmp/foobar type ext3 (rw)\n"))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/dev/sdz1 on /tmp/foobar type ext3 (rw)\n"))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_falsey
end
@@ -136,11 +137,11 @@ describe Chef::Provider::Mount::Mount do
# expand the target path to correct specs on Windows
target = ::File.expand_path("/dev/mapper/target")
- allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true)
- allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target)
+ allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target)
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{target} on /tmp/foo type ext3 (rw)\n"))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "#{target} on /tmp/foo type ext3 (rw)\n"))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_truthy
end
@@ -150,11 +151,11 @@ describe Chef::Provider::Mount::Mount do
# expand the target path to correct specs on Windows
absolute_target = ::File.expand_path("/dev/xsdz1")
- allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true)
- allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target)
+ allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target)
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{absolute_target} on /tmp/foo type ext3 (rw)\n"))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "#{absolute_target} on /tmp/foo type ext3 (rw)\n"))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_truthy
end
@@ -162,8 +163,8 @@ describe Chef::Provider::Mount::Mount do
mount = "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n"
mount << "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n"
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => mount))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: mount))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_truthy
end
@@ -171,14 +172,14 @@ describe Chef::Provider::Mount::Mount do
mount = "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n"
mount << "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n"
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => mount))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: mount))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_falsey
end
it "mounted should be false if the mount point is not found in the mounts list" do
- allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdy1 on /tmp/foo type ext3 (rw)\n"))
- @provider.load_current_resource()
+ allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/dev/sdy1 on /tmp/foo type ext3 (rw)\n"))
+ @provider.load_current_resource
expect(@provider.current_resource.mounted).to be_falsey
end
@@ -205,8 +206,8 @@ describe Chef::Provider::Mount::Mount do
it "should set enabled to true if the symlink target is in fstab" do
target = "/dev/mapper/target"
- allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true)
- allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target)
+ allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target)
fstab = "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n"
@@ -219,8 +220,8 @@ describe Chef::Provider::Mount::Mount do
it "should set enabled to true if the symlink target is relative and is in fstab - CHEF-4957" do
target = "xsdz1"
- allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true)
- allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target)
+ allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true)
+ allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target)
fstab = "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n"
@@ -246,15 +247,6 @@ describe Chef::Provider::Mount::Mount do
expect(@provider.current_resource.enabled).to be_falsey
end
- it "should set enabled to false if the mount point is not last in fstab" do
- line_1 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
- line_2 = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
- allow(::File).to receive(:foreach).with("/etc/fstab").and_yield(line_1).and_yield(line_2)
-
- @provider.load_current_resource
- expect(@provider.current_resource.enabled).to be_falsey
- end
-
it "should not mangle the mount options if the device in fstab is a symlink" do
# expand the target path to correct specs on Windows
target = "/dev/mapper/target"
@@ -295,32 +287,32 @@ describe Chef::Provider::Mount::Mount do
describe "mount_fs" do
it "should mount the filesystem if it is not mounted" do
- expect(@provider).to receive(:shell_out!).with("mount -t ext3 -o defaults /dev/sdz1 /tmp/foo")
- @provider.mount_fs()
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-t", "ext3", "-o", "defaults", "/dev/sdz1", "/tmp/foo")
+ @provider.mount_fs
end
it "should mount the filesystem with options if options were passed" do
options = "rw,noexec,noauto"
@new_resource.options(%w{rw noexec noauto})
- expect(@provider).to receive(:shell_out!).with("mount -t ext3 -o rw,noexec,noauto /dev/sdz1 /tmp/foo")
- @provider.mount_fs()
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-t", "ext3", "-o", "rw,noexec,noauto", "/dev/sdz1", "/tmp/foo")
+ @provider.mount_fs
end
it "should mount the filesystem specified by uuid", :not_supported_on_solaris do
- status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1)
+ status = double(stdout: "/dev/sdz1\n", exitstatus: 1)
@new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
@new_resource.device_type :uuid
- allow(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
+ allow(@provider).to receive(:shell_out_compacted).with("/sbin/findfs", "UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
@stdout_mock = double("stdout mock")
allow(@stdout_mock).to receive(:each).and_yield("#{@new_resource.device} on #{@new_resource.mount_point}")
- expect(@provider).to receive(:shell_out!).with("mount -t #{@new_resource.fstype} -o defaults -U #{@new_resource.device} #{@new_resource.mount_point}").and_return(@stdout_mock)
- @provider.mount_fs()
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-t", @new_resource.fstype, "-o", "defaults", "-U", @new_resource.device, @new_resource.mount_point).and_return(@stdout_mock)
+ @provider.mount_fs
end
it "should not mount the filesystem if it is mounted" do
allow(@current_resource).to receive(:mounted).and_return(true)
expect(@provider).not_to receive(:shell_out!)
- @provider.mount_fs()
+ @provider.mount_fs
end
end
@@ -328,41 +320,41 @@ describe Chef::Provider::Mount::Mount do
describe "umount_fs" do
it "should umount the filesystem if it is mounted" do
@current_resource.mounted(true)
- expect(@provider).to receive(:shell_out!).with("umount /tmp/foo")
- @provider.umount_fs()
+ expect(@provider).to receive(:shell_out!).with("umount", "/tmp/foo")
+ @provider.umount_fs
end
it "should not umount the filesystem if it is not mounted" do
@current_resource.mounted(false)
expect(@provider).not_to receive(:shell_out!)
- @provider.umount_fs()
+ @provider.umount_fs
end
end
describe "remount_fs" do
it "should use mount -o remount if remount is supported" do
- @new_resource.supports({ :remount => true })
+ @new_resource.supports({ remount: true })
@current_resource.mounted(true)
- expect(@provider).to receive(:shell_out!).with("mount -o remount,defaults #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount,defaults", @new_resource.mount_point)
@provider.remount_fs
end
it "should use mount -o remount with new mount options if remount is supported" do
- @new_resource.supports({ :remount => true })
+ @new_resource.supports({ remount: true })
options = "rw,noexec,noauto"
@new_resource.options(%w{rw noexec noauto})
@current_resource.mounted(true)
- expect(@provider).to receive(:shell_out!).with("mount -o remount,rw,noexec,noauto #{@new_resource.mount_point}")
+ expect(@provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount,rw,noexec,noauto", @new_resource.mount_point)
@provider.remount_fs
end
it "should umount and mount if remount is not supported" do
- @new_resource.supports({ :remount => false })
+ @new_resource.supports({ remount: false })
@current_resource.mounted(true)
expect(@provider).to receive(:umount_fs)
expect(@provider).to receive(:sleep).with(1)
expect(@provider).to receive(:mount_fs)
- @provider.remount_fs()
+ @provider.remount_fs
end
it "should not try to remount at all if mounted is false" do
@@ -370,7 +362,29 @@ describe Chef::Provider::Mount::Mount do
expect(@provider).not_to receive(:shell_out!)
expect(@provider).not_to receive(:umount_fs)
expect(@provider).not_to receive(:mount_fs)
- @provider.remount_fs()
+ @provider.remount_fs
+ 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
@@ -404,10 +418,30 @@ describe Chef::Provider::Mount::Mount do
@fstab = StringIO.new
allow(::File).to receive(:readlines).and_return([])
expect(::File).to receive(:open).once.with("/etc/fstab", "w").and_yield(@fstab)
- expect(::File).to receive(:open).once.with("/etc/fstab", "a").and_yield(@fstab)
@provider.enable_fs
end
+
+ it "should update the last matching entry if enabled is true" do
+ @new_resource.fstype("ext4")
+ @new_resource.dump(2)
+ @new_resource.pass(1)
+ allow(@current_resource).to receive(:enabled).and_return(true)
+ fstab_read = ["/dev/sdz1 /tmp/foo ext3 defaults 1 2\n",
+ "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n",
+ "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n",
+ "/dev/sdz1 /tmp/foobar ext3 defaults 1 2\n"]
+
+ fstab_write = StringIO.new
+ allow(::File).to receive(:readlines).with("/etc/fstab").and_return(fstab_read)
+ allow(::File).to receive(:open).with("/etc/fstab", "w").and_yield(fstab_write)
+
+ @provider.enable_fs
+ expect(fstab_write.string).to eq("/dev/sdz1 /tmp/foo ext3 defaults 1 2\n" +
+ "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n" +
+ "/dev/sdz1 /tmp/foo #{@new_resource.fstype} defaults #{@new_resource.dump} #{@new_resource.pass}\n" +
+ "/dev/sdz1 /tmp/foobar ext3 defaults 1 2\n")
+ end
end
describe "when disabling the fs" do
@@ -470,5 +504,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_compacted).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 264c8b9b36..4ca22b4b78 100644
--- a/spec/unit/provider/mount/solaris_spec.rb
+++ b/spec/unit/provider/mount/solaris_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,7 +48,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
new_resource.fsck_device fsck_device
new_resource.fstype fstype
new_resource.options options
- new_resource.supports :remount => false
+ new_resource.supports remount: false
new_resource
end
@@ -57,22 +57,22 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
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
- #
- fd - /dev/fd fd - no -
- /proc - /proc proc - no -
- # swap
- /dev/dsk/c0t0d0s1 - - swap - no -
- # root
- /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no -
- # tmpfs
- swap - /tmp tmpfs - yes -
- # nfs
- cartman:/share2 - /cartman nfs - yes rw,soft
- # ufs
- /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ <<~EOF
+ #device device mount FS fsck mount mount
+ #to mount to fsck point type pass at boot options
+ #
+ fd - /dev/fd fd - no -
+ /proc - /proc proc - no -
+ # swap
+ /dev/dsk/c0t0d0s1 - - swap - no -
+ # root
+ /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no -
+ # tmpfs
+ swap - /tmp tmpfs - yes -
+ # nfs
+ cartman:/share2 - /cartman nfs - yes rw,soft
+ # ufs
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -84,15 +84,15 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
end
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
+ /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 )
- allow(provider).to receive(:shell_out!).with("mount -v").and_return(OpenStruct.new(:stdout => mount_output))
+ allow(provider).to receive(:shell_out_compacted!).with("mount", "-v").and_return(OpenStruct.new(stdout: mount_output))
allow(File).to receive(:symlink?).with(device).and_return(false)
allow(File).to receive(:exist?).and_call_original # Tempfile.open on ruby 1.8.7 calls File.exist?
allow(File).to receive(:exist?).with(device).and_return(true)
@@ -103,7 +103,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
describe "#define_resource_requirements" do
before do
# we're not testing the actual actions so stub them all out
- [:mount_fs, :umount_fs, :remount_fs, :enable_fs, :disable_fs].each { |m| allow(provider).to receive(m) }
+ %i{mount_fs umount_fs remount_fs enable_fs disable_fs}.each { |m| allow(provider).to receive(m) }
end
it "run_action(:mount) should raise an error if the device does not exist" do
@@ -215,22 +215,22 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when loading a normal UFS filesystem with noauto, don't mount at boot" do
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
- #
- fd - /dev/fd fd - no -
- /proc - /proc proc - no -
- # swap
- /dev/dsk/c0t0d0s1 - - swap - no -
- # root
- /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no -
- # tmpfs
- swap - /tmp tmpfs - yes -
- # nfs
- cartman:/share2 - /cartman nfs - yes rw,soft
- # ufs
- /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 no -
+ <<~EOF
+ #device device mount FS fsck mount mount
+ #to mount to fsck point type pass at boot options
+ #
+ fd - /dev/fd fd - no -
+ /proc - /proc proc - no -
+ # swap
+ /dev/dsk/c0t0d0s1 - - swap - no -
+ # root
+ /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no -
+ # tmpfs
+ swap - /tmp tmpfs - yes -
+ # nfs
+ cartman:/share2 - /cartman nfs - yes rw,soft
+ # ufs
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 no -
EOF
end
@@ -245,13 +245,13 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the device is an smbfs mount" do
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
+ //solarsystem/tmp on /mnt type smbfs read/write/setuid/devices/dev=5080000 on Tue Mar 29 11:40:18 2011
EOF
end
let(:vfstab_file_contents) do
- <<-EOF.gsub /^\s*/, ""
- //WORKGROUP;username:password@host/share - /mountpoint smbfs - no fileperms=0777,dirperms=0777
+ <<~EOF
+ //WORKGROUP;username:password@host/share - /mountpoint smbfs - no fileperms=0777,dirperms=0777
EOF
end
@@ -264,14 +264,14 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the device is an NFS mount" do
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
+ 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) do
- <<-EOF.gsub /^\s*/, ""
- cartman:/share2 - /cartman nfs - yes rw,soft
+ <<~EOF
+ cartman:/share2 - /cartman nfs - yes rw,soft
EOF
end
@@ -335,14 +335,14 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
let(:target) { "/dev/mapper/target" }
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
+ #{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) do
- <<-EOF.gsub /^\s*/, ""
- #{target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ <<~EOF
+ #{target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -350,7 +350,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
expect(File).to receive(:readlink).with(device).at_least(:once).and_return(target)
- provider.load_current_resource()
+ provider.load_current_resource
end
it "should set mounted true if the symlink target of the device is found in the mounts list" do
@@ -372,14 +372,14 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
let(:absolute_target) { File.expand_path(target, File.dirname(device)) }
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
+ #{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) do
- <<-EOF.gsub /^\s*/, ""
- #{absolute_target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ <<~EOF
+ #{absolute_target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -387,7 +387,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
expect(File).to receive(:readlink).with(device).at_least(:once).and_return(target)
- provider.load_current_resource()
+ provider.load_current_resource
end
it "should set mounted true if the symlink target of the device is found in the mounts list" do
@@ -405,59 +405,59 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the matching mount point is last in the mounts list" do
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
+ /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()
+ provider.load_current_resource
expect(provider.current_resource.mounted).to be_truthy
end
end
context "when the matching mount point is not last in the mounts list" do
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
+ /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()
+ provider.load_current_resource
expect(provider.current_resource.mounted).to be_falsey
end
end
context "when the matching mount point is not in the mounts list (mountpoint wrong)" do
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
+ /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()
+ provider.load_current_resource
expect(provider.current_resource.mounted).to be_falsey
end
end
context "when the matching mount point is not in the mounts list (raw device wrong)" do
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
+ /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()
+ provider.load_current_resource
expect(provider.current_resource.mounted).to be_falsey
end
end
context "when the mount point is last in fstab" do
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
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -469,9 +469,9 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the mount point is not last in fstab and is a substring of another mount" do
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
+ /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
@@ -483,9 +483,9 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the mount point is not last in fstab" do
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
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s72 /mnt/foo ufs 2 yes -
EOF
end
@@ -497,8 +497,8 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the mount point is not in fstab, but the mountpoint is a substring of one that is" do
let(:vfstab_file_contents) do
- <<-EOF.gsub /^\s*/, ""
- /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foob ufs 2 yes -
+ <<~EOF
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foob ufs 2 yes -
EOF
end
@@ -510,8 +510,8 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the mount point is not in fstab, but the device is a substring of one that is" do
let(:vfstab_file_contents) do
- <<-EOF.gsub /^\s*/, ""
- /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ <<~EOF
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -523,8 +523,8 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
context "when the mountpoint line is commented out" do
let(:vfstab_file_contents) do
- <<-EOF.gsub /^\s*/, ""
- #/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ <<~EOF
+ #/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
EOF
end
@@ -538,36 +538,36 @@ 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}")
- provider.mount_fs()
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-F", fstype, device, mountpoint)
+ provider.mount_fs
end
it "should mount the filesystem with options if options were passed" do
options = "logging,noatime,largefiles,nosuid,rw,quota"
- new_resource.options(options.split(/,/))
- expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o #{options} #{device} #{mountpoint}")
- provider.mount_fs()
+ new_resource.options(options.split(","))
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-F", fstype, "-o", options, device, mountpoint)
+ provider.mount_fs
end
it "should delete the 'noauto' magic option" do
options = "rw,noauto"
new_resource.options(%w{rw noauto})
- expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o rw #{device} #{mountpoint}")
- provider.mount_fs()
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-F", fstype, "-o", "rw", device, mountpoint)
+ provider.mount_fs
end
end
describe "umount_fs" do
it "should umount the filesystem if it is mounted" do
- expect(provider).to receive(:shell_out!).with("umount #{mountpoint}")
- provider.umount_fs()
+ expect(provider).to receive(:shell_out_compacted!).with("umount", mountpoint)
+ provider.umount_fs
end
end
describe "remount_fs without options and do not mount at boot" do
it "should use mount -o remount" do
new_resource.options(%w{noauto})
- expect(provider).to receive(:shell_out!).with("mount -o remount #{new_resource.mount_point}")
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount", new_resource.mount_point)
provider.remount_fs
end
end
@@ -575,7 +575,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
describe "remount_fs with options and do not mount at boot" do
it "should use mount -o remount,rw" do
new_resource.options(%w{rw noauto})
- expect(provider).to receive(:shell_out!).with("mount -o remount,rw #{new_resource.mount_point}")
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount,rw", new_resource.mount_point)
provider.remount_fs
end
end
@@ -583,7 +583,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
describe "remount_fs with options and mount at boot" do
it "should use mount -o remount,rw" do
new_resource.options(%w{rw})
- expect(provider).to receive(:shell_out!).with("mount -o remount,rw #{new_resource.mount_point}")
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount,rw", new_resource.mount_point)
provider.remount_fs
end
end
@@ -591,7 +591,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
describe "remount_fs without options and mount at boot" do
it "should use mount -o remount" do
new_resource.options([])
- expect(provider).to receive(:shell_out!).with("mount -o remount #{new_resource.mount_point}")
+ expect(provider).to receive(:shell_out_compacted!).with("mount", "-o", "remount", new_resource.mount_point)
provider.remount_fs
end
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/windows_spec.rb b/spec/unit/provider/mount/windows_spec.rb
index fdb44836b5..3c2999e07e 100644
--- a/spec/unit/provider/mount/windows_spec.rb
+++ b/spec/unit/provider/mount/windows_spec.rb
@@ -23,14 +23,15 @@ class Chef
class Windows
class NetUse
end
+
class Volume
end
end
end
end
-GUID = "\\\\?\\Volume{578e72b5-6e70-11df-b5c5-000c29d4a7d9}\\"
-REMOTE = "\\\\server-name\\path"
+GUID = "\\\\?\\Volume{578e72b5-6e70-11df-b5c5-000c29d4a7d9}\\".freeze
+REMOTE = "\\\\server-name\\path".freeze
describe Chef::Provider::Mount::Windows do
before(:each) do
@@ -99,10 +100,10 @@ describe Chef::Provider::Mount::Windows do
end
it "should mount the filesystem if it is not mounted" do
- expect(@vol).to receive(:add).with(:remote => @new_resource.device,
- :username => @new_resource.username,
- :domainname => @new_resource.domain,
- :password => @new_resource.password)
+ expect(@vol).to receive(:add).with(remote: @new_resource.device,
+ username: @new_resource.username,
+ domainname: @new_resource.domain,
+ password: @new_resource.password)
@provider.mount_fs
end
diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb
index 19967e8496..7800d0c666 100644
--- a/spec/unit/provider/mount_spec.rb
+++ b/spec/unit/provider/mount_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)
@@ -144,6 +151,24 @@ describe Chef::Provider::Mount do
provider.run_action(:enable)
expect(new_resource).not_to be_updated_by_last_action
end
+
+ it "should enable the mount if device changed" do
+ allow(current_resource).to receive(:enabled).and_return(true)
+ expect(provider).to receive(:mount_options_unchanged?).and_return(true)
+ expect(provider).to receive(:device_unchanged?).and_return(false)
+ expect(provider).to receive(:enable_fs).and_return(true)
+ provider.run_action(:enable)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should not enable the mount if device not changed" do
+ allow(current_resource).to receive(:enabled).and_return(true)
+ expect(provider).to receive(:mount_options_unchanged?).and_return(true)
+ expect(provider).to receive(:device_unchanged?).and_return(true)
+ expect(provider).not_to receive(:enable_fs)
+ provider.run_action(:enable)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
end
describe "when the target state is to disable the mount" do
@@ -181,4 +206,17 @@ describe Chef::Provider::Mount do
it "should delegates the disable implementation to subclasses" do
expect { provider.disable_fs }.to raise_error(Chef::Exceptions::UnsupportedAction)
end
+
+ # Not supported on solaris because it can't cope with a LABEL device type.
+ describe "#device_unchanged?", :not_supported_on_solaris do
+ it "should be true when device_type not changed" do
+ expect(provider.device_unchanged?).to be_truthy
+ end
+
+ it "should be false when device_type changed" do
+ new_resource.device_type :label
+ current_resource.device_type :device
+ expect(provider.device_unchanged?).to be_falsey
+ end
+ end
end
diff --git a/spec/unit/provider/ohai_spec.rb b/spec/unit/provider/ohai_spec.rb
deleted file mode 100644
index fad08ba589..0000000000
--- a/spec/unit/provider/ohai_spec.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# Author:: Michael Leinartas (<mleinartas@gmail.com>)
-# Copyright:: Copyright 2010-2016, Michael Leinartas
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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/run_context"
-
-describe Chef::Provider::Ohai do
- before(:each) do
- # Copied from client_spec
- @fqdn = "hostname.domainname"
- @hostname = "hostname"
- @platform = "example-platform"
- @platform_version = "example-platform"
- Chef::Config[:node_name] = @fqdn
- mock_ohai = {
- :fqdn => @fqdn,
- :hostname => @hostname,
- :platform => @platform,
- :platform_version => @platform_version,
- :data => {
- :origdata => "somevalue",
- },
- :data2 => {
- :origdata => "somevalue",
- :newdata => "somevalue",
- },
- }
- allow(mock_ohai).to receive(:all_plugins).and_return(true)
- allow(mock_ohai).to receive(:data).and_return(mock_ohai[:data],
- mock_ohai[:data2])
- allow(Ohai::System).to receive(:new).and_return(mock_ohai)
- allow(Chef::Platform).to receive(:find_platform_and_version).and_return({ "platform" => @platform,
- "platform_version" => @platform_version })
- # Fake node with a dummy save
- @node = Chef::Node.new
- @node.name(@fqdn)
- allow(@node).to receive(:save).and_return(@node)
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Ohai.new("ohai_reload")
- ohai = Ohai::System.new
- ohai.all_plugins
- @node.consume_external_attrs(ohai.data, {})
-
- @provider = Chef::Provider::Ohai.new(@new_resource, @run_context)
- end
-
- describe "when reloading ohai" do
- before do
- @node.automatic_attrs[:origdata] = "somevalue"
- end
-
- it "applies updated ohai data to the node" do
- expect(@node[:origdata]).to eq("somevalue")
- expect(@node[:newdata]).to be_nil
- @provider.run_action(:reload)
- expect(@node[:origdata]).to eq("somevalue")
- expect(@node[:newdata]).to eq("somevalue")
- end
-
- it "should reload a specific plugin and cause node to pick up new values" do
- @new_resource.plugin "someplugin"
- @provider.run_action(:reload)
- expect(@node[:origdata]).to eq("somevalue")
- expect(@node[:newdata]).to eq("somevalue")
- end
- end
-end
diff --git a/spec/unit/provider/osx_profile_spec.rb b/spec/unit/provider/osx_profile_spec.rb
deleted file mode 100644
index a1dcf3ecd6..0000000000
--- a/spec/unit/provider/osx_profile_spec.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-#
-# Author:: Nate Walck (<nate.walck@gmail.com>)
-# Copyright:: Copyright 2015-2016, Chef, 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::OsxProfile do
- let(:shell_out_success) do
- double("shell_out", :exitstatus => 0, :error? => false)
- end
- describe "action_create" do
- let(:node) { Chef::Node.new }
- let(:events) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:new_resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
- let(:provider) { Chef::Provider::OsxProfile.new(new_resource, run_context) }
- let(:all_profiles) do
- { "_computerlevel" => [{ "ProfileDisplayName" => "Finder Settings",
- "ProfileIdentifier" => "com.apple.finder",
- "ProfileInstallDate" => "2015-11-08 23:15:21 +0000",
- "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.finder" => { "Forced" => [{ "mcx_preference_settings" => { "ShowExternalHardDrivesOnDesktop" => false } }] } } },
- "PayloadDisplayName" => "Custom: (com.apple.finder)",
- "PayloadIdentifier" => "com.apple.finder",
- "PayloadType" => "com.apple.ManagedClient.preferences",
- "PayloadUUID" => "a017048f-684b-4e81-baa3-43afe316d739",
- "PayloadVersion" => 1 }],
- "ProfileOrganization" => "Chef",
- "ProfileRemovalDisallowed" => "false",
- "ProfileType" => "Configuration",
- "ProfileUUID" => "e2e09bef-e673-44a6-bcbe-ecb5f1c1b740",
- "ProfileVerificationState" => "unsigned",
- "ProfileVersion" => 1 },
- { "ProfileDisplayName" => "ScreenSaver Settings",
- "ProfileIdentifier" => "com.testprofile.screensaver",
- "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
- "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
- "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
- "PayloadIdentifier" => "com.apple.screensaver",
- "PayloadType" => "com.apple.ManagedClient.preferences",
- "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c110",
- "PayloadVersion" => 1 }],
- "ProfileOrganization" => "Chef",
- "ProfileRemovalDisallowed" => "false",
- "ProfileType" => "Configuration",
- "ProfileUUID" => "6e95927c-f200-54b4-85c7-52ab99b61c47",
- "ProfileVerificationState" => "unsigned",
- "ProfileVersion" => 1 }],
- }
- end
- # If anything is changed within this profile, be sure to update the
- # ProfileUUID in all_profiles to match the new config specific UUID
- let(:test_profile) do
- {
- "PayloadIdentifier" => "com.testprofile.screensaver",
- "PayloadRemovalDisallowed" => false,
- "PayloadScope" => "System",
- "PayloadType" => "Configuration",
- "PayloadUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
- "PayloadOrganization" => "Chef",
- "PayloadVersion" => 1,
- "PayloadDisplayName" => "Screensaver Settings",
- "PayloadContent" => [
- {
- "PayloadType" => "com.apple.ManagedClient.preferences",
- "PayloadVersion" => 1,
- "PayloadIdentifier" => "com.testprofile.screensaver",
- "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c108",
- "PayloadEnabled" => true,
- "PayloadDisplayName" => "com.apple.screensaver",
- "PayloadContent" => {
- "com.apple.screensaver" => {
- "Forced" => [
- {
- "mcx_preference_settings" => {
- "idleTime" => 0,
- },
- },
- ],
- },
- },
- },
- ],
- }
- end
- let(:no_profiles) do
- {}
- end
-
- before(:each) do
- allow(provider).to receive(:cookbook_file_available?).and_return(true)
- allow(provider).to receive(:cache_cookbook_profile).and_return("/tmp/test.mobileconfig.remote")
- allow(provider).to receive(:get_new_profile_hash).and_return(test_profile)
- allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
- allow(provider).to receive(:read_plist).and_return(all_profiles)
- allow(::File).to receive(:unlink).and_return(true)
- end
-
- it "should build the get all profiles shellout command correctly" do
- profile_name = "com.testprofile.screensaver.mobileconfig"
- tempfile = "/tmp/allprofiles.plist"
- new_resource.profile_name profile_name
- allow(provider).to receive(:generate_tempfile).and_return(tempfile)
- allow(provider).to receive(:get_installed_profiles).and_call_original
- allow(provider).to receive(:read_plist).and_return(all_profiles)
- expect(provider).to receive(:shell_out!).with("profiles -P -o '/tmp/allprofiles.plist'")
- provider.load_current_resource
- end
-
- it "should use profile name as profile when no profile is set" do
- profile_name = "com.testprofile.screensaver.mobileconfig"
- new_resource.profile_name profile_name
- provider.load_current_resource
- expect(new_resource.profile_name).to eql(profile_name)
- end
-
- it "should use identifier from specified profile" do
- new_resource.profile test_profile
- provider.load_current_resource
- expect(
- provider.instance_variable_get(:@new_profile_identifier)
- ).to eql(test_profile["PayloadIdentifier"])
- end
-
- it "should install when not installed" do
- new_resource.profile test_profile
- allow(provider).to receive(:get_installed_profiles).and_return(no_profiles)
- provider.load_current_resource
- expect { provider.run_action(:install) }.to_not raise_error
- end
-
- it "does not install if the profile is already installed" do
- new_resource.profile test_profile
- allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
- provider.load_current_resource
- expect(provider).to_not receive(:install_profile)
- expect { provider.action_install }.to_not raise_error
- end
-
- it "should install when installed but uuid differs" do
- new_resource.profile test_profile
- all_profiles["_computerlevel"][1]["ProfileUUID"] = "1781fbec-3325-565f-9022-9bb39245d4dd"
- provider.load_current_resource
- expect { provider.run_action(:install) }.to_not raise_error
- end
-
- it "should build the shellout install command correctly" do
- profile_path = "/tmp/test.mobileconfig"
- new_resource.profile test_profile
- # Change the profile so it triggers an install
- all_profiles["_computerlevel"][1]["ProfileUUID"] = "1781fbec-3325-565f-9022-9bb39245d4dd"
- provider.load_current_resource
- allow(provider).to receive(:write_profile_to_disk).and_return(profile_path)
- expect(provider).to receive(:shell_out).with("profiles -I -F '#{profile_path}'").and_return(shell_out_success)
- provider.action_install()
- end
-
- it "should fail if there is no identifier inside the profile" do
- test_profile.delete("PayloadIdentifier")
- new_resource.profile test_profile
- error_message = "The specified profile does not seem to be valid"
- expect { provider.run_action(:install) }.to raise_error(RuntimeError, error_message)
- end
-
- end
-
- describe "action_remove" do
- let(:node) { Chef::Node.new }
- let(:events) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:new_resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
- let(:provider) { Chef::Provider::OsxProfile.new(new_resource, run_context) }
- let(:current_resource) { Chef::Resource::OsxProfile.new("Profile Test") }
- let(:all_profiles) do
- { "_computerlevel" => [{ "ProfileDisplayName" => "ScreenSaver Settings",
- "ProfileIdentifier" => "com.apple.screensaver",
- "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
- "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
- "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
- "PayloadIdentifier" => "com.apple.screensaver",
- "PayloadType" => "com.apple.ManagedClient.preferences",
- "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c108",
- "PayloadVersion" => 1 }],
- "ProfileOrganization" => "Chef",
- "ProfileRemovalDisallowed" => "false",
- "ProfileType" => "Configuration",
- "ProfileUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
- "ProfileVerificationState" => "unsigned",
- "ProfileVersion" => 1 },
- { "ProfileDisplayName" => "ScreenSaver Settings",
- "ProfileIdentifier" => "com.testprofile.screensaver",
- "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
- "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
- "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
- "PayloadIdentifier" => "com.apple.screensaver",
- "PayloadType" => "com.apple.ManagedClient.preferences",
- "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c110",
- "PayloadVersion" => 1 }],
- "ProfileOrganization" => "Chef",
- "ProfileRemovalDisallowed" => "false",
- "ProfileType" => "Configuration",
- "ProfileUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
- "ProfileVerificationState" => "unsigned",
- "ProfileVersion" => 1 }],
- }
- end
- before(:each) do
- provider.current_resource = current_resource
- allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
- end
-
- it "should use resource name for identifier when not specified" do
- new_resource.profile_name "com.testprofile.screensaver"
- new_resource.action(:remove)
- provider.load_current_resource
- expect(provider.instance_variable_get(:@new_profile_identifier)
- ).to eql(new_resource.profile_name)
- end
-
- it "should use specified identifier" do
- new_resource.identifier "com.testprofile.screensaver"
- new_resource.action(:remove)
- provider.load_current_resource
- expect(provider.instance_variable_get(:@new_profile_identifier)
- ).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)
- provider.load_current_resource
- expect(provider).to receive(:shell_out).with("profiles -R -p '#{new_resource.identifier}'").and_return(shell_out_success)
- provider.action_remove()
- end
- end
-end
diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb
deleted file mode 100644
index 2b574bb7df..0000000000
--- a/spec/unit/provider/package/aix_spec.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-#
-# Author:: Deepali Jagtap (deepali.jagtap@clogeny.com)
-# Author:: Prabhu Das (prabhu.das@clogeny.com)
-# 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 "spec_helper"
-
-describe Chef::Provider::Package::Aix 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("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)
- end
-
- describe "assessing the current package status" do
- before do
- @bffinfo = "/usr/lib/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:
- /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:"
-
- @empty_status = double("Status", :stdout => "", :exitstatus => 0)
- end
-
- 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)
- @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)
- @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)
- @provider.load_current_resource
- @provider.define_resource_requirements
- expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Package)
- end
-
- 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)
- @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(Chef::Log).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")
- expect(@new_resource.version).to eq("3.3.12.0")
- end
-
- it "should return the current version installed if found by lslpp" 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)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq("3.3.12.0")
- end
-
- 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)
- allow(@provider).to receive(:shell_out).and_return(status)
- expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
- end
-
- it "should raise an exception if installp/lslpp fails to run" do
- status = double(:stdout => "", :exitstatus => -1, :format_for_exception => "")
- allow(@provider).to receive(:shell_out).and_return(status)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
-
- 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)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to be_nil
- end
-
- it "should raise an exception if the source doesn't provide the requested package" 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.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
- end
-
- 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 )
- @provider.candidate_version
- end
-
- it "should lookup the candidate_version if the variable is not already set" do
- status = double(:stdout => "", :exitstatus => 0)
- expect(@provider).to receive(:shell_out).and_return(status)
- @provider.candidate_version
- end
-
- it "should throw and exception if the exitstatus is not 0" do
- @status = double(:stdout => "", :exitstatus => 1, :format_for_exception => "")
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
- end
-
- end
-
- 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)
- @provider.install_package("samba.base", "3.3.12.0")
- end
-
- it "should run 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)
- 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)
- @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)
- @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)
- @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)
- @provider.remove_package("samba.base", "3.3.12.0")
- end
-
- end
-end
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index a549ecc72e..2ff1f0fddc 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,429 +20,612 @@ 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
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ before(:each) do
+ @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_compacted!).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)
+ sudo:
+ Installed: 1.7.2p1-1ubuntu5.3
+ Candidate: 1.7.2p1-1ubuntu5.3
+ Version table:
+ *** 1.7.2p1-1ubuntu5.3 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages
+ 500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages
+ 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_compacted!).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_compacted!).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_compacted!).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_compacted!).with(
+ "apt-cache", "policy", "libmysqlclient15-dev",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ ).and_return(virtual_package)
+ showpkg_out = <<~SHOWPKG_STDOUT
+ Package: libmysqlclient15-dev
+ Versions:
+
+ Reverse Depends:
+ libmysqlclient-dev,libmysqlclient15-dev
+ libmysqlclient-dev,libmysqlclient15-dev
+ libmysqlclient-dev,libmysqlclient15-dev
+ libmysqlclient-dev,libmysqlclient15-dev
+ libmysqlclient-dev,libmysqlclient15-dev
+ libmysqlclient-dev,libmysqlclient15-dev
+ Dependencies:
+ Provides:
+ 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_compacted!).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
+ Version table:
+ *** 5.1.41-3ubuntu12.10 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages
+ 100 /var/lib/dpkg/status
+ 5.1.41-3ubuntu12.7 0
+ 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_compacted!).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_compacted!).with(
+ "apt-cache", "policy", "mp3-decoder",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ ).and_return(virtual_package)
+ showpkg_out = <<~SHOWPKG_STDOUT
+ Package: mp3-decoder
+ Versions:
+
+ Reverse Depends:
+ nautilus,mp3-decoder
+ vux,mp3-decoder
+ plait,mp3-decoder
+ ecasound,mp3-decoder
+ nautilus,mp3-decoder
+ Dependencies:
+ Provides:
+ Reverse Provides:
+ vlc-nox 1.0.6-1ubuntu1.8
+ vlc 1.0.6-1ubuntu1.8
+ vlc-nox 1.0.6-1ubuntu1
+ 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_compacted!).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_compacted!).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_compacted!).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
+
+ def ubuntu1804downgrade_stubs
+ so = instance_double(Mixlib::ShellOut, stdout: "apt 1.6~beta1 (amd64)\notherstuff\n")
+ so2 = instance_double(Mixlib::ShellOut, error?: false)
+ allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.6~beta1", "gt", "1.1.0").and_return(so2)
+ end
+
+ def ubuntu1404downgrade_stubs
+ so = instance_double(Mixlib::ShellOut, stdout: "apt 1.0.1ubuntu2 for amd64 compiled on Dec 8 2016 16:23:38\notherstuff\n")
+ so2 = instance_double(Mixlib::ShellOut, error?: true)
+ allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "gt", "1.1.0").and_return(so2)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "eq", "1.1.0").and_return(so2)
+ end
+
+ describe "install_package" 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
-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}",
- :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
+ ubuntu1804downgrade_stubs
+ end
- 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
- Version table:
- *** 1.7.2p1-1ubuntu5.3 0
- 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages
- 500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages
- 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
+ it "should run apt-get install with the package name and version" do
+ expect(@provider).to receive(:shell_out_compacted!). with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-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 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
+ it "should run apt-get install with the package name and version and options if specified" do
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-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
- # 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",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out = <<-SHOWPKG_STDOUT
-Package: libmysqlclient15-dev
-Versions:
-
-Reverse Depends:
- libmysqlclient-dev,libmysqlclient15-dev
- libmysqlclient-dev,libmysqlclient15-dev
- libmysqlclient-dev,libmysqlclient15-dev
- libmysqlclient-dev,libmysqlclient15-dev
- libmysqlclient-dev,libmysqlclient15-dev
- libmysqlclient-dev,libmysqlclient15-dev
-Dependencies:
-Provides:
-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",
- :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
- Version table:
- *** 5.1.41-3ubuntu12.10 0
- 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages
- 100 /var/lib/dpkg/status
- 5.1.41-3ubuntu12.7 0
- 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",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- ).and_return(real_package)
- @provider.load_current_resource
- 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
- 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",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out = <<-SHOWPKG_STDOUT
-Package: mp3-decoder
-Versions:
-
-Reverse Depends:
- nautilus,mp3-decoder
- vux,mp3-decoder
- plait,mp3-decoder
- ecasound,mp3-decoder
- nautilus,mp3-decoder
-Dependencies:
-Provides:
-Reverse Provides:
-vlc-nox 1.0.6-1ubuntu1.8
-vlc 1.0.6-1ubuntu1.8
-vlc-nox 1.0.6-1ubuntu1
-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",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- ).and_return(showpkg)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-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
+ )
- 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",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
- end
+ @provider.install_package(["irssi"], ["0.8.12-7"])
+ 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
+ it "should run apt-get install with the package name and quotes options if specified" do
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "--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 "install_package with old apt-get" do
+ # tests apt-get on 1404 that does not support --allow-downgrades
+ before(:each) do
+ ubuntu1404downgrade_stubs
+ end
+
+ it "should run apt-get install with the package name and version" do
+ expect(@provider).to receive(:shell_out_compacted!). 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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).with(
+ "apt-get", "-q", "-y", "purge", "irssi",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ )
+ @provider.purge_package(["irssi"], ["0.8.12-7"])
end
- context "after loading the current resource" do
+ it "should run apt-get purge with the package name and options if specified" do
+ expect(@provider).to receive(:shell_out_compacted!).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 given a response file" do
+ it_behaves_like "given a response file" do
before do
- @current_resource = resource_klass.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,
- },
- })
+ @provider = Chef::Provider::Package::Apt.new(new_resource, run_context)
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" },
- :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" },
- :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" },
- :timeout => @timeout
- )
-
- @provider.install_package(["irssi"], ["0.8.12-7"])
- end
+ let(:new_resource) do
+ new_resource = Chef::Resource::AptPackage.new("irssi", run_context)
+ new_resource.response_file("irssi.response")
+ new_resource.cookbook_name = "irssi"
+ new_resource
end
+ let(:path) { "preseed/irssi" }
+ let(:tmp_path) { "/tmp/preseed/irssi" }
+ let(:package_name) { "irssi" }
+ let(:package_version) { "1.0.5-1" }
+ let(:response) { "irssi.response" }
+ let(:tmp_preseed_path) { "/tmp/preseed/irssi/irssi-1.0.5-1.seed" }
+ let(:preseed_path) { "/preseed--irssi--irssi-1.0.5-1.seed" }
+ end
+ end
- describe resource_klass, "upgrade_package" do
+ 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 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
+ it "should get the full path to the preseed response file" do
+ file = "/tmp/irssi-0.8.12-7.seed"
- 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" },
- :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
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "debconf-set-selections", "/tmp/irssi-0.8.12-7.seed",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ )
- 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
+ @provider.preseed_package(file)
+ 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
-
- 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
+ it "should run debconf-set-selections on the preseed file if it has changed" do
+ expect(@provider).to receive(:shell_out_compacted!).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
- 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
+ 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_compacted!)
+ @provider.run_action(:reconfig)
+ 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 install libmysqlclient15-dev",
- :env => { "DEBIAN_FRONTEND" => "noninteractive" },
- :timeout => @timeout
- )
- @provider.install_package(["libmysqlclient15-dev"], ["not_a_real_version"])
- end
- end
+ describe "when reconfiguring a package" do
+ it "should run dpkg-reconfigure package" do
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "dpkg-reconfigure", "irssi",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ )
+ @provider.reconfig_package("irssi")
+ 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 locking a package" do
+ it "should run apt-mark hold package" do
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).with(
+ "apt-mark", "showhold", timeout: 900
+ ).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 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 unlocking a package" do
+ it "should run apt-mark unhold package" do
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).with(
+ "apt-mark", "showhold", timeout: 900
+ ).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 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 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 "when installing a virtual package" do
+ it "should install the package without specifying a version" do
+ ubuntu1804downgrade_stubs
+ @provider.package_data["libmysqlclient15-dev"][:virtual] = true
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-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_compacted!).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_compacted!).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
+ ubuntu1804downgrade_stubs
+ # https://github.com/chef/chef/issues/2914
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-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(%w{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_compacted).with(
+ "dpkg", "--compare-versions", "1.4.0", "gt", "0.8.12-7", timeout: 900
+ ).and_return(double(error?: false))
+ @provider.run_action(:upgrade)
+ end
+
+ it "should install the package if the installed version is older" do
+ ubuntu1804downgrade_stubs
+ expect(@provider).to receive(:version_compare).with("1.6~beta1", "1.1.0").and_return(1)
+ 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).with("0.4.0", any_args).and_return(-1)
+ expect(@provider).to receive(:shell_out_compacted!).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
+ ubuntu1804downgrade_stubs
+ expect(@provider).to receive(:version_compare).with("1.6~beta1", "1.1.0").and_return(1)
+ 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).with("0.4.0", any_args)
+ expect(@provider).to receive(:shell_out_compacted!).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/bff_spec.rb b/spec/unit/provider/package/bff_spec.rb
new file mode 100644
index 0000000000..680e5cf22a
--- /dev/null
+++ b/spec/unit/provider/package/bff_spec.rb
@@ -0,0 +1,187 @@
+#
+# Author:: Deepali Jagtap (deepali.jagtap@clogeny.com)
+# Author:: Prabhu Das (prabhu.das@clogeny.com)
+# Copyright:: Copyright (c) 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::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::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
+ before do
+ @bffinfo = "/usr/lib/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:
+ /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:"
+
+ @empty_status = double("Status", stdout: "", exitstatus: 0)
+ end
+
+ 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_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).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_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).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_compacted).and_return(@empty_status)
+ 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)
+ end
+
+ 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_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).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_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
+ expect(logger).to receive(:warn).once.with(/bff package by product name/)
+ @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 return the current version installed if found by lslpp" do
+ status = double("Status", stdout: @bffinfo, exitstatus: 0)
+ @stdout = StringIO.new(@bffinfo)
+ @stdin, @stderr = StringIO.new, StringIO.new
+ expect(@provider).to receive(:shell_out_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).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
+
+ 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::Bff.new(@new_resource, @run_context)
+ allow(@provider).to receive(:shell_out_compacted).and_return(status)
+ expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "should raise an exception if installp/lslpp fails to run" do
+ status = double(stdout: "", exitstatus: -1, format_for_exception: "")
+ allow(@provider).to receive(:shell_out_compacted).and_return(status)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ 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_compacted).with("installp", "-L", "-d", "/tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).with("lslpp", "-lcq", "samba.base", timeout: 900).and_return(@empty_status)
+ @provider.load_current_resource
+ expect(@provider.current_resource.version).to be_nil
+ end
+
+ it "should raise an exception if the source doesn't provide the requested package" 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_compacted).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
+
+ 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_compacted)
+ @provider.candidate_version
+ end
+
+ it "should lookup the candidate_version if the variable is not already set" do
+ status = double(stdout: "", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
+ @provider.candidate_version
+ end
+
+ it "should throw and exception if the exitstatus is not 0" do
+ @status = double(stdout: "", exitstatus: 1, format_for_exception: "")
+ allow(@provider).to receive(:shell_out_compacted).and_return(@status)
+ expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ end
+
+ describe "install and upgrade" do
+ it "should run installp -aYF -d with the package source to install" do
+ expect(@provider).to receive(:shell_out_compacted!).with("installp", "-aYF", "-d", "/tmp/samba.base", "samba.base", timeout: 900)
+ @provider.install_package("samba.base", "3.3.12.0")
+ end
+
+ 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::Bff.new(@new_resource, @run_context)
+ expect(@new_resource.source).to eq("/tmp/samba.base")
+ expect(@provider).to receive(:shell_out_compacted!).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
+ @new_resource.options("-e/tmp/installp.log")
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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
+ @new_resource.options("-e/tmp/installp.log")
+ expect(@provider).to receive(:shell_out_compacted!).with("installp", "-u", "-e/tmp/installp.log", "samba.base", timeout: 900)
+ @provider.remove_package("samba.base", "3.3.12.0")
+ end
+
+ end
+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..45d1e8ffcb
--- /dev/null
+++ b/spec/unit/provider/package/cab_spec.rb
@@ -0,0 +1,272 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright (c) 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"]).to_s, "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 downloaded 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"]).to_s, "\\", "Test6.1-KB2664825-v3-x64.cab")
+ else
+ expect(path).to be == File.join((ENV["TEMP"]).to_s, "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"]).to_s, "test6.1-kb2664825-v3-x64.cab")
+ path = provider.cab_file_source
+ if windows?
+ expect(path).to be == File.join((ENV["TEMP"].downcase).to_s, "\\", "test6.1-kb2664825-v3-x64.cab")
+ else
+ expect(path).to be == File.join((ENV["TEMP"]).to_s, "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).to_s, "\\", "test6.1-kb2664825-v3-x64.cab"))
+ else
+ expect(provider).to receive(:download_source_file).and_return(File.join((ENV["TEMP"]).to_s, "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 704ef1aef2..ba5739fe55 100644
--- a/spec/unit/provider/package/chocolatey_spec.rb
+++ b/spec/unit/provider/package/chocolatey_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
require "spec_helper"
-describe Chef::Provider::Package::Chocolatey do
+describe Chef::Provider::Package::Chocolatey, :windows_only do
let(:timeout) { 900 }
let(:new_resource) { Chef::Resource::ChocolateyPackage.new("git") }
@@ -35,31 +35,37 @@ 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
+ Chocolatey v0.9.9.11
+ chocolatey|0.9.9.11
+ ConEmu|15.10.25.0
EOF
end
before do
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)
+ local_list_obj = double(stdout: local_list_stdout)
+ allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-l", "-r", { returns: [0, 2], 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
-Git|2.6.2
-munin-node|1.6.1.20130823
+ remote_list_stdout = <<~EOF
+ Chocolatey v0.9.9.11
+ chocolatey|0.9.9.11
+ ConEmu|15.10.25.1
+ Git|2.6.1
+ 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 -r #{package_names.join ' '}#{args}", { timeout: timeout }).and_return(remote_list_obj)
+ package_names.each do |pkg|
+ if args
+ allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-r", pkg, *args, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
+ else
+ allow(provider).to receive(:shell_out_compacted!).with(choco_exe, "list", "-r", pkg, { returns: [0, 2], timeout: timeout }).and_return(remote_list_obj)
+ end
+ end
end
describe "#initialize" do
@@ -140,6 +146,15 @@ munin-node|1.6.1.20130823
)
end
+ it "installing a package that does not exist throws an error" do
+ new_resource.package_name("package-does-not-exist")
+ new_resource.returns([0])
+ allow(provider).to receive(:shell_out_compacted!)
+ .with(choco_exe, "list", "-r", new_resource.package_name.first, { returns: new_resource.returns, timeout: timeout })
+ .and_raise(Mixlib::ShellOut::ShellCommandFailed, "Expected process to exit with [0], but received '2'")
+ expect { provider.send(:available_packages) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed, "Expected process to exit with [0], but received '2'")
+ end
+
it "should set 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])
@@ -180,7 +195,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_compacted!).with(choco_exe, "install", "-y", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -191,7 +206,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_compacted!).with(choco_exe, "install", "-y", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -220,7 +235,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_compacted!).with(choco_exe, "install", "-y", "--version", "15.10.25.1", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -233,7 +248,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_compacted!).with(choco_exe, "install", "-y", "--version", "15.10.25.1", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -243,7 +258,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_compacted!).with(choco_exe, "install", "-y", "--version", "15.10.25.1", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -253,27 +268,27 @@ 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_compacted!).with(choco_exe, "install", "-y", "--version", "15.10.25.1", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
+ expect(provider).to receive(:shell_out_compacted!).with(choco_exe, "install", "-y", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
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
- allow_remote_list(["git", "munin-node"])
- new_resource.package_name(["git", "munin-node"])
+ allow_remote_list(%w{git munin-node})
+ new_resource.package_name(%w{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_compacted!).with(choco_exe, "install", "-y", "git", "munin-node", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
context "when passing a source argument" do
it "should pass options into the install command" do
- allow_remote_list(["git"], " -source localpackages")
+ 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_compacted!).with(choco_exe, "install", "-y", "-source", "localpackages", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -283,7 +298,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_compacted!).with(choco_exe, "install", "-y", "-force", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -296,28 +311,63 @@ munin-node|1.6.1.20130823
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"])
+ allow_remote_list(%w{git package-does-not-exist})
+ new_resource.package_name(%w{git package-does-not-exist})
provider.load_current_resource
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
context "alternate source" do
it "installing a package that does not exist throws an error" do
- allow_remote_list(["package-does-not-exist"], " -source alternate_source")
+ allow_remote_list(["package-does-not-exist"], ["-source", "alternate_source"])
new_resource.package_name("package-does-not-exist")
new_resource.source("alternate_source")
provider.load_current_resource
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
end
+
+ context "private source" do
+ it "installing a package with valid credentials" do
+ allow_remote_list(["git"], ["-source", "auth_source", "--user", "ubuntu", "--password", "ubuntu@123"])
+ new_resource.source("auth_source")
+ new_resource.user("ubuntu")
+ new_resource.password("ubuntu@123")
+ provider.load_current_resource
+ expect(provider).to receive(:shell_out_compacted!).with(choco_exe, "install", "-y", "-source", "auth_source", "--user", "ubuntu", "--password", "ubuntu@123", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "installing a package with invalid credentials throws an error" do
+ allow_remote_list(["package-invalid-auth"], ["-source", "auth_source", "--user", "ubuntu", "--password", "ubuntu@123"])
+ new_resource.package_name("package-invalid-auth")
+ new_resource.source("auth_source")
+ new_resource.user("ubuntu")
+ new_resource.password("ubuntu@123")
+ provider.load_current_resource
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "only credentials and list options pass into the list command" do
+ allow_remote_list(["git"], ["-source", "auth_source", "--user", "ubuntu", "--password", "ubuntu@123", "--local-only"])
+ new_resource.source("auth_source")
+ new_resource.list_options("--local-only")
+ new_resource.user("ubuntu")
+ new_resource.password("ubuntu@123")
+ provider.load_current_resource
+ expect(provider.send(:available_packages)).to eql(
+ { "chocolatey" => "0.9.9.11", "conemu" => "15.10.25.1", "git" => "2.6.2", "munin-node" => "1.6.1.20130823" }
+ )
+ end
+ end
end
describe "#action_upgrade" do
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_compacted!).with(choco_exe, "upgrade", "-y", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -326,7 +376,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_compacted!).with(choco_exe, "upgrade", "-y", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -335,7 +385,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_compacted!).with(choco_exe, "upgrade", "-y", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -344,7 +394,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_compacted!).with(choco_exe, "upgrade", "-y", "chocolatey", { returns: [0, 2], timeout: timeout })
provider.run_action(:upgrade)
expect(new_resource).not_to be_updated_by_last_action
end
@@ -353,7 +403,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_compacted!).with(choco_exe, "upgrade", "-y", "--version", "2.6.2", "git", { returns: [0, 2], timeout: timeout })
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -361,7 +411,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_compacted!).with(choco_exe, "upgrade", "-y", "conemu", "git", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -374,15 +424,15 @@ munin-node|1.6.1.20130823
end
it "upgrading 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"])
+ allow_remote_list(%w{git package-does-not-exist})
+ new_resource.package_name(%w{git package-does-not-exist})
provider.load_current_resource
expect { provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package)
end
context "alternate source" do
it "installing a package that does not exist throws an error" do
- allow_remote_list(["package-does-not-exist"], " -source alternate_source")
+ allow_remote_list(["package-does-not-exist"], ["-source", "alternate_source"])
new_resource.package_name("package-does-not-exist")
new_resource.source("alternate_source")
provider.load_current_resource
@@ -401,8 +451,8 @@ munin-node|1.6.1.20130823
end
it "does nothing when all the packages are already removed" do
- allow_remote_list(["git", "package-does-not-exist"])
- new_resource.package_name(["git", "package-does-not-exist"])
+ allow_remote_list(%w{git package-does-not-exist})
+ new_resource.package_name(%w{git package-does-not-exist})
provider.load_current_resource
expect(provider).not_to receive(:remove_package)
provider.run_action(:remove)
@@ -413,7 +463,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_compacted!).with(choco_exe, "uninstall", "-y", "ConEmu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:remove)
expect(new_resource).to be_updated_by_last_action
end
@@ -422,7 +472,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_compacted!).with(choco_exe, "uninstall", "-y", "conemu", { returns: [0, 2], timeout: timeout }).and_return(double)
provider.run_action(:remove)
expect(new_resource).to be_updated_by_last_action
end
@@ -432,24 +482,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_compacted!).with(choco_exe, "uninstall", "-y", "conemu", { returns: [0, 2], 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
@@ -464,11 +501,10 @@ describe "behavior when Chocolatey is not installed" do
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)
+ allow(provider).to receive(:choco_install_path).and_return("")
# 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 => ""))
+ allow(provider).to receive(:shell_out_compacted!).and_return(double(stdout: ""))
end
let(:error_regex) do
diff --git a/spec/unit/provider/package/deb_spec.rb b/spec/unit/provider/package/deb_spec.rb
new file mode 100644
index 0000000000..da06b2d1b6
--- /dev/null
+++ b/spec/unit/provider/package/deb_spec.rb
@@ -0,0 +1,135 @@
+#
+# Author:: Kapil Chouhan (<kapil.chouhan@msystechnologies.com>)
+# Copyright:: Copyright (c) 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::Deb do
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = :just_testing
+ node.automatic_attrs[:platform_version] = :just_testing
+ 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::AptPackage.new("emacs", run_context) }
+ let(:current_resource) { Chef::Resource::AptPackage.new("emacs", run_context) }
+ let(:candidate_version) { "1.0" }
+ let(:provider) do
+ provider = Chef::Provider::Package::Apt.new(new_resource, run_context) { include Chef::Provider::Package::Deb }
+ 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 reconfiguring the package" do
+ before(:each) do
+ allow(provider).to receive(:reconfig_package).and_return(true)
+ end
+
+ context "when reconfigure the package" do
+ it "reconfigure the package and update the resource" do
+ allow(provider).to receive(:get_current_versions).and_return("1.0")
+ allow(new_resource).to receive(:response_file).and_return(true)
+ 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(logger).to receive(:info).with("apt_package[emacs] reconfigured")
+ expect(provider).to receive(:reconfig_package)
+ provider.run_action(:reconfig)
+ expect(new_resource).to be_updated
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when not reconfigure the package" do
+ it "does not reconfigure the package if the package is not installed" do
+ allow(provider).to receive(:get_current_versions).and_return(nil)
+ allow(provider.load_current_resource).to receive(:version).and_return(nil)
+ expect(logger).to receive(:trace).with("apt_package[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
+ end
+
+ it "does not reconfigure the package if no response_file is given" do
+ allow(provider).to receive(:get_current_versions).and_return("1.0")
+ allow(new_resource).to receive(:response_file).and_return(nil)
+ expect(logger).to receive(:trace).with("apt_package[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
+ end
+
+ it "does not reconfigure the package if the response_file has not changed" do
+ allow(provider).to receive(:get_current_versions).and_return("1.0")
+ 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(logger).to receive(:trace).with("apt_package[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
+ end
+
+ describe "Subclass with use_multipackage_api" do
+ class MyDebianPackageResource < Chef::Resource::Package
+ end
+
+ class MyDebianPackageProvider < Chef::Provider::Package
+ include Chef::Provider::Package::Deb
+ use_multipackage_api
+ end
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { MyDebianPackageResource.new("installs the packages") }
+ let(:current_resource) { MyDebianPackageResource.new("installs the packages") }
+ let(:provider) do
+ provider = MyDebianPackageProvider.new(new_resource, run_context)
+ provider.current_resource = current_resource
+ provider
+ end
+
+ it "has use_multipackage_api? methods on the class and instance" do
+ expect(MyDebianPackageProvider.use_multipackage_api?).to be true
+ expect(provider.use_multipackage_api?).to be true
+ end
+
+ it "when user passes string to package_name, passes arrays to reconfig_package" do
+ new_resource.package_name "vim"
+ current_resource.package_name "vim"
+ current_resource.version [ "1.0" ]
+ allow(new_resource).to receive(:response_file).and_return(true)
+ allow(new_resource).to receive(:resource_name).and_return(:apt_package)
+ 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(provider).to receive(:reconfig_package).with([ "vim" ]).and_return(true)
+ provider.run_action(:reconfig)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+end
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..1f94147273
--- /dev/null
+++ b/spec/unit/provider/package/dnf/python_helper_spec.rb
@@ -0,0 +1,29 @@
+#
+# Copyright:: Copyright (c) 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", :rhel do
+ allow(helper).to receive(:dnf_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/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb
index 613f7a326e..318b91c77e 100644
--- a/spec/unit/provider/package/dpkg_spec.rb
+++ b/spec/unit/provider/package/dpkg_spec.rb
@@ -31,29 +31,28 @@ describe Chef::Provider::Package::Dpkg do
let(:provider) { Chef::Provider::Package::Dpkg.new(new_resource, run_context) }
let(:dpkg_deb_version) { "1.11.4" }
- let(:dpkg_deb_status) { status = double(:stdout => "#{package}\t#{dpkg_deb_version}", :exitstatus => 0) }
+ let(:dpkg_deb_status) { status = double(stdout: "#{package}\t#{dpkg_deb_version}", exitstatus: 0) }
let(:dpkg_s_version) { "1.11.4-1ubuntu1" }
let(:dpkg_s_status) do
- stdout = <<-DPKG_S
-Package: #{package}
-Status: install ok installed
-Priority: important
-Section: web
-Installed-Size: 1944
-Maintainer: Ubuntu Core developers <ubuntu-devel-discuss@lists.ubuntu.com>
-Architecture: amd64
-Version: #{dpkg_s_version}
-Config-Version: #{dpkg_s_version}
-Depends: libc6 (>= 2.8~20080505), libssl0.9.8 (>= 0.9.8f-5)
-Conflicts: wget-ssl
+ stdout = <<~DPKG_S
+ Package: #{package}
+ Status: install ok installed
+ Priority: important
+ Section: web
+ Installed-Size: 1944
+ Maintainer: Ubuntu Core developers <ubuntu-devel-discuss@lists.ubuntu.com>
+ Architecture: amd64
+ Version: #{dpkg_s_version}
+ Config-Version: #{dpkg_s_version}
+ Depends: libc6 (>= 2.8~20080505), libssl0.9.8 (>= 0.9.8f-5)
+ Conflicts: wget-ssl
DPKG_S
- status = double(:stdout => stdout, :exitstatus => 1)
+ status = double(stdout: stdout, exitstatus: 1)
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(::File).to receive(:exist?).with(source).and_return(true)
+ allow(provider).to receive(:shell_out_compacted!).with("dpkg-deb", "-W", source, timeout: 900).and_return(dpkg_deb_status)
+ allow(provider).to receive(:shell_out_compacted!).with("dpkg", "-s", package, timeout: 900, returns: [0, 1]).and_return(double(stdout: "", exitstatus: 1))
end
describe "#define_resource_requirements" do
@@ -104,6 +103,9 @@ Conflicts: wget-ssl
end
describe "when loading the current resource state" do
+ before(:each) do
+ allow(::File).to receive(:exist?).with(source).and_return(true)
+ end
it "should create a current resource with the name of the new_resource" do
provider.load_current_resource
@@ -112,8 +114,8 @@ 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)
+ status = double(stdout: "wget\t#{version}", exitstatus: 0)
+ expect(provider).to receive(:shell_out_compacted!).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,34 +167,34 @@ 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_compacted!).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
it "on new debian/ubuntu we get an exit(1) and no stdout from dpkg -s for uninstalled" do
dpkg_s_status = double(
- exitstatus: 1, stdout: "", stderr: <<-EOF
-dpkg-query: package '#{package}' is not installed and no information is available
-Use dpkg --info (= dpkg-deb --info) to examine archive files,
-and dpkg --contents (= dpkg-deb --contents) to list their contents.
+ exitstatus: 1, stdout: "", stderr: <<~EOF
+ dpkg-query: package '#{package}' is not installed and no information is available
+ 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_compacted!).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
it "on old debian/ubuntu we get an exit(0) and we get info on stdout from dpkg -s for uninstalled" do
dpkg_s_status = double(
- exitstatus: 0, stderr: "", stdout: <<-EOF
-Package: #{package}
-Status: unknown ok not-installed
-Priority: extra
-Section: ruby
+ exitstatus: 0, stderr: "", stdout: <<~EOF
+ Package: #{package}
+ Status: unknown ok not-installed
+ 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_compacted!).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,21 +203,25 @@ 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_compacted!).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)
+ status = double(stdout: "", exitstatus: -1)
+ expect(provider).to receive(:shell_out_compacted!).with("dpkg-deb", "-W", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb", timeout: 900).and_raise(Mixlib::ShellOut::ShellCommandFailed)
expect { provider.load_current_resource }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
end
describe Chef::Provider::Package::Dpkg, "install and upgrade" do
+ before(:each) do
+ allow(::File).to receive(:exist?).with(source).and_return(true)
+ end
+
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 +230,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 +238,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 +246,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 +260,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,18 +276,39 @@ 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")
provider.purge_package(["wget"], ["1.11.4-1ubuntu1"])
end
end
+
+ describe "when given a response file" do
+ it_behaves_like "given a response file" do
+ before do
+ @provider = Chef::Provider::Package::Dpkg.new(new_resource, run_context)
+ end
+ let(:new_resource) do
+ new_resource = Chef::Resource::DpkgPackage.new("wget", run_context)
+ new_resource.response_file("wget.response")
+ new_resource.cookbook_name = "wget"
+ new_resource
+ end
+ let(:path) { "preseed/wget" }
+ let(:tmp_path) { "/tmp/preseed/wget" }
+ let(:package_name) { "wget" }
+ let(:package_version) { "1.11.4" }
+ let(:response) { "wget.response" }
+ let(:tmp_preseed_path) { "/tmp/preseed/wget/wget-1.11.4.seed" }
+ let(:preseed_path) { "/preseed--wget--wget-1.11.4.seed" }
+ end
+ end
end
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
deleted file mode 100644
index 8890f62f73..0000000000
--- a/spec/unit/provider/package/freebsd/pkg_spec.rb
+++ /dev/null
@@ -1,274 +0,0 @@
-#
-# Authors:: Bryan McLellan (btm@loftninjas.org)
-# Matthew Landauer (matthew@openaustralia.org)
-# Copyright:: Copyright 2009-2016, Bryan McLellan, Matthew Landauer
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES 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 "ostruct"
-
-describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" 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("zsh")
- @current_resource = Chef::Resource::Package.new("zsh")
-
- @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)
- end
-
- describe "when determining the current package state" do
- before do
- allow(@provider).to receive(:ports_candidate_version).and_return("4.3.6")
- end
-
- it "should create a current resource with the name of the new_resource" do
- current_resource = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context).current_resource
- expect(current_resource.name).to eq("zsh")
- end
-
- it "should return a version if the package is installed" do
- expect(@provider).to receive(:current_installed_version).and_return("4.3.6_7")
- @provider.load_current_resource
- expect(@current_resource.version).to eq("4.3.6_7")
- end
-
- it "should return nil if the package is not installed" do
- expect(@provider).to receive(:current_installed_version).and_return(nil)
- @provider.load_current_resource
- expect(@current_resource.version).to be_nil
- end
-
- it "should return a candidate version if it exists" do
- expect(@provider).to receive(:current_installed_version).and_return(nil)
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("4.3.6")
- end
- end
-
- describe "when querying for package state and attributes" do
- before do
- #@new_resource = Chef::Resource::Package.new("zsh")
-
- #@provider = Chef::Provider::Package::Freebsd::Pkg.new(@node, @new_resource)
-
- #@status = double("Status", :exitstatus => 0)
- #@stdin = double("STDIN", :null_object => true)
- #@stdout = double("STDOUT", :null_object => true)
- #@stderr = double("STDERR", :null_object => true)
- #@pid = double("PID", :null_object => true)
- end
-
- 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)
- 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)
- 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)
- allow(@provider).to receive(:port_name).and_return("zsh")
- expect(@provider.port_path).to eq("/usr/ports/shells/zsh")
- end
-
- # Not happy with the form of these tests as they are far too closely tied to the implementation and so very fragile.
- 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.ports_candidate_version).to eq("4.3.6")
- end
-
- it "should figure out the package name when we have ports" 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)
- #@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7")
- expect(@provider.package_name).to eq("zsh")
- end
- end
-
- describe Chef::Provider::Package::Freebsd::Pkg, "install_package" do
- before(:each) do
- @cmd_result = OpenStruct.new(:status => true)
-
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:package_name).and_return("zsh")
- allow(@provider).to receive(:latest_link_name).and_return("zsh")
- allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh")
- 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)
- @provider.install_package("zsh", "4.3.6_7")
- end
- end
-
- describe Chef::Provider::Package::Freebsd::Pkg, "port path" do
- before do
- #@node = Chef::Node.new
- @new_resource = Chef::Resource::Package.new("zsh")
- @new_resource.cookbook_name = "adventureclub"
- @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
- end
-
- 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.port_path).to eq("/usr/ports/shells/zsh")
- end
-
- it "should use the package_name as the port path when it starts with /" do
- new_resource = Chef::Resource::Package.new("/usr/ports/www/wordpress")
- provider = Chef::Provider::Package::Freebsd::Pkg.new(new_resource, @run_context)
- expect(provider).not_to receive(:popen4)
- expect(provider.port_path).to eq("/usr/ports/www/wordpress")
- end
-
- it "should use the package_name as a relative path from /usr/ports when it contains / but doesn't start with it" do
- # @new_resource = double( "Chef::Resource::Package",
- # :package_name => "www/wordpress",
- # :cookbook_name => "xenoparadox")
- new_resource = Chef::Resource::Package.new("www/wordpress")
- provider = Chef::Provider::Package::Freebsd::Pkg.new(new_resource, @run_context)
- expect(provider).not_to receive(:popen4)
- expect(provider.port_path).to eq("/usr/ports/www/wordpress")
- end
- end
-
- describe Chef::Provider::Package::Freebsd::Pkg, "ruby-iconv (package with a dash in the name)" do
- before(:each) do
- @new_resource = Chef::Resource::Package.new("ruby-iconv")
- @current_resource = Chef::Resource::Package.new("ruby-iconv")
- @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:port_path).and_return("/usr/ports/converters/ruby-iconv")
- allow(@provider).to receive(:package_name).and_return("ruby18-iconv")
- allow(@provider).to receive(:latest_link_name).and_return("ruby18-iconv")
-
- @install_result = OpenStruct.new(:status => true)
- 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)
- @provider.install_package("ruby-iconv", "1.0")
- end
- end
-
- describe Chef::Provider::Package::Freebsd::Pkg, "remove_package" do
- before(:each) do
- @pkg_delete = OpenStruct.new(:status => true)
- @new_resource.version "4.3.6_7"
- @current_resource.version "4.3.6_7"
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:package_name).and_return("zsh")
- 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)
- @provider.remove_package("zsh", "4.3.6_7")
- end
- end
-
- # CHEF-4371
- # There are some port names that contain special characters such as +'s. This breaks the regular expression used to determine what
- # version of a package is currently installed and to get the port_path.
- # Example package name: bonnie++
-
- describe Chef::Provider::Package::Freebsd::Pkg, "bonnie++ (package with a plus in the name :: CHEF-4371)" do
- before(:each) do
- @new_resource = Chef::Resource::Package.new("bonnie++")
- @current_resource = Chef::Resource::Package.new("bonnie++")
- @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- end
-
- 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)
- 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)
- allow(@provider).to receive(:package_name).and_return("bonnie++")
- expect(@provider.current_installed_version).to eq("1.96")
- end
- end
-
- # A couple of examples to show up the difficulty of determining the command to install the binary package given the port:
- # PORT DIRECTORY INSTALLED PACKAGE NAME COMMAND TO INSTALL PACKAGE
- # /usr/ports/lang/perl5.8 perl-5.8.8_1 pkg_add -r perl
- # /usr/ports/databases/mysql50-server mysql-server-5.0.45_1 pkg_add -r mysql50-server
- #
- # So, in one case it appears the command to install the package can be derived from the name of the port directory and in the
- # other case it appears the command can be derived from the package name. Very confusing!
- # Well, luckily, after much poking around, I discovered that the two can be disambiguated through the use of the LATEST_LINK
- # variable which is set by the ports Makefile
- #
- # PORT DIRECTORY LATEST_LINK INSTALLED PACKAGE NAME COMMAND TO INSTALL PACKAGE
- # /usr/ports/lang/perl5.8 perl perl-5.8.8_1 pkg_add -r perl
- # /usr/ports/databases/mysql50-server mysql50-server mysql-server-5.0.45_1 pkg_add -r mysql50-server
- #
- # The variable LATEST_LINK is named that way because the directory that "pkg_add -r" downloads from is called "Latest" and
- # contains the "latest" versions of package as symbolic links to the files in the "All" directory.
-
- describe Chef::Provider::Package::Freebsd::Pkg, "install_package latest link fixes" do
- it "should install the perl binary package with the correct name" do
- @new_resource = Chef::Resource::Package.new("perl5.8")
- @current_resource = Chef::Resource::Package.new("perl5.8")
- @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:package_name).and_return("perl")
- 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)
- @provider.install_package("perl5.8", "5.8.8_1")
- end
-
- it "should install the mysql50-server binary package with the correct name" do
-
- @new_resource = Chef::Resource::Package.new("mysql50-server")
- @current_resource = Chef::Resource::Package.new("mysql50-server")
- @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- allow(@provider).to receive(:package_name).and_return("mysql-server")
- 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)
- @provider.install_package("mysql50-server", "5.0.45_1")
- end
- end
-end
diff --git a/spec/unit/provider/package/freebsd/pkgng_spec.rb b/spec/unit/provider/package/freebsd/pkgng_spec.rb
index e823875630..ce5d58b7dc 100644
--- a/spec/unit/provider/package/freebsd/pkgng_spec.rb
+++ b/spec/unit/provider/package/freebsd/pkgng_spec.rb
@@ -62,27 +62,26 @@ describe Chef::Provider::Package::Freebsd::Port do
describe "determining current installed version" do
before(:each) do
- allow(@provider).to receive(:supports_pkgng?)
- @pkg_info = OpenStruct.new(:stdout => "zsh-3.1.7\nVersion : 3.1.7\n")
+ @pkg_info = OpenStruct.new(stdout: "zsh-3.1.7\nVersion : 3.1.7\n")
end
it "should query pkg database" do
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 1], timeout: 900).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out_compacted!).with("pkg", "info", "zsh", env: nil, returns: [0, 1, 70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
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)
+ pkg_query = OpenStruct.new(stdout: "5.0.5\n", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted!).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)
+ pkg_query = OpenStruct.new(stdout: "5.0.3\n", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted!).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
@@ -94,54 +93,54 @@ describe Chef::Provider::Package::Freebsd::Port do
describe "installing a binary package" do
before(:each) do
- @install_result = OpenStruct.new(:status => true)
+ @install_result = OpenStruct.new(status: true)
end
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).
- and_return(@install_result)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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
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).
- and_return(@install_result)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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
describe "removing a binary package" do
before(:each) do
- @install_result = OpenStruct.new(:status => true)
+ @install_result = OpenStruct.new(status: true)
end
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)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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 4ae8d960a2..069c4e8dec 100644
--- a/spec/unit/provider/package/freebsd/port_spec.rb
+++ b/spec/unit/provider/package/freebsd/port_spec.rb
@@ -62,44 +62,32 @@ describe Chef::Provider::Package::Freebsd::Port do
describe "determining current installed version" do
before(:each) do
- @pkg_info = OpenStruct.new(:stdout => "zsh-3.1.7\n")
+ @pkg_info = OpenStruct.new(stdout: "zsh-3.1.7\n")
end
- 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)
+ it "should check 'pkg info' to determine the current version" do
+ expect(@provider).to receive(:shell_out_compacted!).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
- 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)
- 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)
+ expect(@provider).to receive(:shell_out_compacted!).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
describe "determining candidate version" do
before(:each) do
- @port_version = OpenStruct.new(:stdout => "5.0.5\n", :exitstatus => 0)
+ @port_version = OpenStruct.new(stdout: "5.0.5\n", exitstatus: 0)
end
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).
- and_return(@port_version)
+ expect(@provider).to receive(:shell_out_compacted!).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
@@ -121,42 +109,42 @@ describe Chef::Provider::Package::Freebsd::Port do
end
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)
+ whereis = OpenStruct.new(stdout: "zsh: /usr/ports/shells/zsh\n")
+ expect(@provider).to receive(:shell_out_compacted!).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)
+ whereis = OpenStruct.new(stdout: "zsh:\n")
+ expect(@provider).to receive(:shell_out_compacted!).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
describe "building a binary package" do
before(:each) do
- @install_result = OpenStruct.new(:status => true)
+ @install_result = OpenStruct.new(status: true)
end
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).
- and_return(@install_result)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .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
end
describe "removing a binary package" do
before(:each) do
- @install_result = OpenStruct.new(:status => true)
+ @install_result = OpenStruct.new(status: true)
end
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).
- and_return(@install_result)
+ expect(@provider).to receive(:shell_out_compacted!)
+ .with("make", "deinstall", timeout: 300, cwd: "/usr/ports/shells/zsh", env: nil)
+ .and_return(@install_result)
@provider.remove_package("zsh", "5.0.5")
end
end
diff --git a/spec/unit/provider/package/homebrew_spec.rb b/spec/unit/provider/package/homebrew_spec.rb
index 17ed5ccc41..9975e72b02 100644
--- a/spec/unit/provider/package/homebrew_spec.rb
+++ b/spec/unit/provider/package/homebrew_spec.rb
@@ -1,6 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright 2014-2016, Chef Software, Inc. <legal@chef.io>
+# Copyright:: Copyright (c) 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.
@@ -19,273 +20,379 @@ 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(:new_resource) { Chef::Resource::HomebrewPackage.new("emacs") }
- let(:current_resource) { Chef::Resource::HomebrewPackage.new("emacs") }
-
+ let(:new_resource) { Chef::Resource::HomebrewPackage.new(%w{emacs vim}) }
+ let(:current_resource) { Chef::Resource::HomebrewPackage.new("emacs, vim") }
let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
Chef::Provider::Package::Homebrew.new(new_resource, run_context)
end
let(:homebrew_uid) { 1001 }
- let(:uninstalled_brew_info) do
- {
- "name" => "emacs",
- "homepage" => "http://www.gnu.org/software/emacs",
- "versions" => {
- "stable" => "24.3",
- "bottle" => false,
- "devel" => nil,
- "head" => nil,
- },
- "revision" => 0,
- "installed" => [],
- "linked_keg" => nil,
- "keg_only" => nil,
- "dependencies" => [],
- "conflicts_with" => [],
- "caveats" => nil,
- "options" => [],
- }
- end
-
- let(:installed_brew_info) do
- {
- "name" => "emacs",
- "homepage" => "http://www.gnu.org/software/emacs/",
- "versions" => {
- "stable" => "24.3",
- "bottle" => false,
- "devel" => nil,
- "head" => "HEAD",
- },
- "revision" => 0,
- "installed" => [{ "version" => "24.3" }],
- "linked_keg" => "24.3",
- "keg_only" => nil,
- "dependencies" => [],
- "conflicts_with" => [],
- "caveats" => "",
- "options" => [],
- }
- end
-
- let(:keg_only_brew_info) do
- {
- "name" => "emacs-kegger",
- "homepage" => "http://www.gnu.org/software/emacs/",
- "versions" => {
- "stable" => "24.3-keggy",
- "bottle" => false,
- "devel" => nil,
- "head" => "HEAD",
- },
- "revision" => 0,
- "installed" => [{ "version" => "24.3-keggy" }],
- "linked_keg" => nil,
- "keg_only" => true,
- "dependencies" => [],
- "conflicts_with" => [],
- "caveats" => "",
- "options" => [],
- }
- end
-
- let(:keg_only_uninstalled_brew_info) do
- {
- "name" => "emacs-kegger",
- "homepage" => "http://www.gnu.org/software/emacs/",
- "versions" => {
- "stable" => "24.3-keggy",
- "bottle" => false,
- "devel" => nil,
- "head" => "HEAD",
- },
- "revision" => 0,
- "installed" => [],
- "linked_keg" => nil,
- "keg_only" => true,
- "dependencies" => [],
- "conflicts_with" => [],
- "caveats" => "",
- "options" => [],
- }
+ let(:brew_cmd_output_data) { '[{"name":"emacs","full_name":"emacs","oldname":null,"aliases":[],"versioned_formulae":[],"desc":"GNU Emacs text editor","homepage":"https://www.gnu.org/software/emacs/","versions":{"stable":"26.3","devel":null,"head":"HEAD","bottle":true},"urls":{"stable":{"url":"https://ftp.gnu.org/gnu/emacs/emacs-26.3.tar.xz","tag":null,"revision":null}},"revision":0,"version_scheme":0,"bottle":{"stable":{"rebuild":0,"cellar":"/usr/local/Cellar","prefix":"/usr/local","root_url":"https://homebrew.bintray.com/bottles","files":{"catalina":{"url":"https://homebrew.bintray.com/bottles/emacs-26.3.catalina.bottle.tar.gz","sha256":"9ab33f4386ca5f7326a8c28da1324556ec990f682a7ca88641203da0b42dbdae"},"mojave":{"url":"https://homebrew.bintray.com/bottles/emacs-26.3.mojave.bottle.tar.gz","sha256":"8162a26246de7db44c53ea0d0ef0a806140318d19c69e8e5e33aa88ce7e823a8"},"high_sierra":{"url":"https://homebrew.bintray.com/bottles/emacs-26.3.high_sierra.bottle.tar.gz","sha256":"6a2629b6deddf99f81abb1990ecd6c87f0242a0eecbb6b6c2e4c3540e421d4c4"},"sierra":{"url":"https://homebrew.bintray.com/bottles/emacs-26.3.sierra.bottle.tar.gz","sha256":"2a47477e71766d7dd6b16c29ad5ba71817ed80d06212e3261ef3c776e7e9f5a2"}}}},"keg_only":false,"bottle_disabled":false,"options":[],"build_dependencies":["pkg-config"],"dependencies":["gnutls"],"recommended_dependencies":[],"optional_dependencies":[],"uses_from_macos":["libxml2","ncurses"],"requirements":[],"conflicts_with":[],"caveats":null,"installed":[],"linked_keg":null,"pinned":false,"outdated":false},{"name":"vim","full_name":"vim","oldname":null,"aliases":[],"versioned_formulae":[],"desc":"Vi \'workalike\' with many additional features","homepage":"https://www.vim.org/","versions":{"stable":"8.2.0550","devel":null,"head":"HEAD","bottle":true},"urls":{"stable":{"url":"https://github.com/vim/vim/archive/v8.2.0550.tar.gz","tag":null,"revision":null}},"revision":0,"version_scheme":0,"bottle":{"stable":{"rebuild":0,"cellar":"/usr/local/Cellar","prefix":"/usr/local","root_url":"https://homebrew.bintray.com/bottles","files":{"catalina":{"url":"https://homebrew.bintray.com/bottles/vim-8.2.0550.catalina.bottle.tar.gz","sha256":"8f9252500775aa85d8f826af30ca9e1118a56145fc2f961c37abed48bf78cf6b"},"mojave":{"url":"https://homebrew.bintray.com/bottles/vim-8.2.0550.mojave.bottle.tar.gz","sha256":"7566c83b770f3e8c4d4b462a39e5eb26609b37a8f8db6690a2560a3e22ded6b6"},"high_sierra":{"url":"https://homebrew.bintray.com/bottles/vim-8.2.0550.high_sierra.bottle.tar.gz","sha256":"a76e517fc69bf67b6903cb82295bc085c5eb4b46b4659f034c694dd97d2ee2d9"}}}},"keg_only":false,"bottle_disabled":false,"options":[],"build_dependencies":[],"dependencies":["gettext","lua","perl","python","ruby"],"recommended_dependencies":[],"optional_dependencies":[],"uses_from_macos":["ncurses"],"requirements":[],"conflicts_with":["ex-vi","macvim"],"caveats":null,"installed":[{"version":"8.2.0550","used_options":[],"built_as_bottle":true,"poured_from_bottle":true,"runtime_dependencies":[{"full_name":"gettext","version":"0.20.1"},{"full_name":"lua","version":"5.3.5"},{"full_name":"perl","version":"5.30.2"},{"full_name":"gdbm","version":"1.18.1"},{"full_name":"openssl@1.1","version":"1.1.1f"},{"full_name":"readline","version":"8.0.4"},{"full_name":"sqlite","version":"3.31.1"},{"full_name":"xz","version":"5.2.5"},{"full_name":"python","version":"3.7.7"},{"full_name":"libyaml","version":"0.2.2"},{"full_name":"ruby","version":"2.7.1"}],"installed_as_dependency":false,"installed_on_request":true}],"linked_keg":"8.2.0550","pinned":false,"outdated":false}]' }
+
+ let(:brew_info_data) do
+ { "openssl@1.1" =>
+ { "name" => "openssl@1.1",
+ "full_name" => "openssl@1.1",
+ "oldname" => nil,
+ "aliases" => ["openssl"],
+ "versioned_formulae" => [],
+ "desc" => "Cryptography and SSL/TLS Toolkit",
+ "homepage" => "https://openssl.org/",
+ "versions" => { "stable" => "1.1.1f", "devel" => nil, "head" => nil, "bottle" => true },
+ "urls" => { "stable" => { "url" => "https://www.openssl.org/source/openssl-1.1.1f.tar.gz", "tag" => nil, "revision" => nil } },
+ "revision" => 0,
+ "version_scheme" => 1,
+ "bottle" =>
+ { "stable" =>
+ { "rebuild" => 0,
+ "cellar" => "/usr/local/Cellar",
+ "prefix" => "/usr/local",
+ "root_url" => "https://homebrew.bintray.com/bottles",
+ "files" =>
+ { "catalina" => { "url" => "https://homebrew.bintray.com/bottles/openssl@1.1-1.1.1f.catalina.bottle.tar.gz", "sha256" => "724cd97c269952cdc28e24798e350fcf520a32c5985aeb26053ce006a09d8179" },
+ "mojave" => { "url" => "https://homebrew.bintray.com/bottles/openssl@1.1-1.1.1f.mojave.bottle.tar.gz", "sha256" => "25ab844d2f14fc85c7f52958b4b89bdd2965bbd9c557445829eff6473f238744" },
+ "high_sierra" => { "url" => "https://homebrew.bintray.com/bottles/openssl@1.1-1.1.1f.high_sierra.bottle.tar.gz", "sha256" => "27f26e2442222ac0565193fe0b86d8719559d776bcdd070d6113c16bb13accf6" } } } },
+ "keg_only" => true,
+ "bottle_disabled" => false,
+ "options" => [],
+ "build_dependencies" => [],
+ "dependencies" => [],
+ "recommended_dependencies" => [],
+ "optional_dependencies" => [],
+ "uses_from_macos" => [],
+ "requirements" => [],
+ "conflicts_with" => [],
+ "caveats" =>
+ "A CA file has been bootstrapped using certificates from the system\nkeychain. To add additional certificates, place .pem files in\n $(brew --prefix)/etc/openssl@1.1/certs\n\nand run\n $(brew --prefix)/opt/openssl@1.1/bin/c_rehash\n",
+ "installed" => [{ "version" => "1.1.1a", "used_options" => [], "built_as_bottle" => true, "poured_from_bottle" => true, "runtime_dependencies" => [], "installed_as_dependency" => true, "installed_on_request" => false }],
+ "linked_keg" => nil,
+ "pinned" => false,
+ "outdated" => false },
+ "kubernetes-cli" =>
+ { "name" => "kubernetes-cli",
+ "full_name" => "kubernetes-cli",
+ "oldname" => nil,
+ "aliases" => ["kubectl"],
+ "versioned_formulae" => [],
+ "desc" => "Kubernetes command-line interface",
+ "homepage" => "https://kubernetes.io/",
+ "versions" => { "stable" => "1.18.1", "devel" => nil, "head" => "HEAD", "bottle" => true },
+ "urls" => { "stable" => { "url" => "https://github.com/kubernetes/kubernetes.git", "tag" => "v1.18.1", "revision" => "7879fc12a63337efff607952a323df90cdc7a335" } },
+ "revision" => 0,
+ "version_scheme" => 0,
+ "bottle" =>
+ { "stable" =>
+ { "rebuild" => 0,
+ "cellar" => ":any_skip_relocation",
+ "prefix" => "/usr/local",
+ "root_url" => "https://homebrew.bintray.com/bottles",
+ "files" =>
+ { "catalina" => { "url" => "https://homebrew.bintray.com/bottles/kubernetes-cli-1.18.1.catalina.bottle.tar.gz", "sha256" => "0b3d688ee458b70b914a37a4ba867e202c6e71190d0c40a27f84628aec744749" },
+ "mojave" => { "url" => "https://homebrew.bintray.com/bottles/kubernetes-cli-1.18.1.mojave.bottle.tar.gz", "sha256" => "21fddfc86ec6d3e4f7ea787310b0fafd845d368de37524569bbe45938b18ba09" },
+ "high_sierra" => { "url" => "https://homebrew.bintray.com/bottles/kubernetes-cli-1.18.1.high_sierra.bottle.tar.gz", "sha256" => "1e20dcd177fd16b862b2432950984807b048cca5879c27bec59e85590f40eece" } } } },
+ "keg_only" => false,
+ "bottle_disabled" => false,
+ "options" => [],
+ "build_dependencies" => ["go"],
+ "dependencies" => [],
+ "recommended_dependencies" => [],
+ "optional_dependencies" => [],
+ "uses_from_macos" => [],
+ "requirements" => [],
+ "conflicts_with" => [],
+ "caveats" => nil,
+ "installed" => [],
+ "linked_keg" => nil,
+ "pinned" => false,
+ "outdated" => false },
+ "vim" =>
+ { "name" => "vim",
+ "full_name" => "vim",
+ "oldname" => nil,
+ "aliases" => [],
+ "versioned_formulae" => [],
+ "desc" => "Vi 'workalike' with many additional features",
+ "homepage" => "https://www.vim.org/",
+ "versions" => { "stable" => "8.2.0550", "devel" => nil, "head" => "HEAD", "bottle" => true },
+ "urls" => { "stable" => { "url" => "https://github.com/vim/vim/archive/v8.2.0550.tar.gz", "tag" => nil, "revision" => nil } },
+ "revision" => 0,
+ "version_scheme" => 0,
+ "bottle" =>
+ { "stable" =>
+ { "rebuild" => 0,
+ "cellar" => "/usr/local/Cellar",
+ "prefix" => "/usr/local",
+ "root_url" => "https://homebrew.bintray.com/bottles",
+ "files" =>
+ { "catalina" => { "url" => "https://homebrew.bintray.com/bottles/vim-8.2.0550.catalina.bottle.tar.gz", "sha256" => "8f9252500775aa85d8f826af30ca9e1118a56145fc2f961c37abed48bf78cf6b" },
+ "mojave" => { "url" => "https://homebrew.bintray.com/bottles/vim-8.2.0550.mojave.bottle.tar.gz", "sha256" => "7566c83b770f3e8c4d4b462a39e5eb26609b37a8f8db6690a2560a3e22ded6b6" },
+ "high_sierra" => { "url" => "https://homebrew.bintray.com/bottles/vim-8.2.0550.high_sierra.bottle.tar.gz", "sha256" => "a76e517fc69bf67b6903cb82295bc085c5eb4b46b4659f034c694dd97d2ee2d9" } } } },
+ "keg_only" => false,
+ "bottle_disabled" => false,
+ "options" => [],
+ "build_dependencies" => [],
+ "dependencies" => %w{gettext lua perl python ruby},
+ "recommended_dependencies" => [],
+ "optional_dependencies" => [],
+ "uses_from_macos" => ["ncurses"],
+ "requirements" => [],
+ "conflicts_with" => %w{ex-vi macvim},
+ "caveats" => nil,
+ "installed" =>
+ [{ "version" => "8.2.0550",
+ "used_options" => [],
+ "built_as_bottle" => true,
+ "poured_from_bottle" => true,
+ "runtime_dependencies" =>
+ [{ "full_name" => "gettext", "version" => "0.20.1" },
+ { "full_name" => "lua", "version" => "5.3.5" },
+ { "full_name" => "perl", "version" => "5.30.2" },
+ { "full_name" => "gdbm", "version" => "1.18.1" },
+ { "full_name" => "openssl@1.1", "version" => "1.1.1f" },
+ { "full_name" => "readline", "version" => "8.0.4" },
+ { "full_name" => "sqlite", "version" => "3.31.1" },
+ { "full_name" => "xz", "version" => "5.2.5" },
+ { "full_name" => "python", "version" => "3.7.7" },
+ { "full_name" => "libyaml", "version" => "0.2.2" },
+ { "full_name" => "ruby", "version" => "2.7.1" }],
+ "installed_as_dependency" => false,
+ "installed_on_request" => true }],
+ "linked_keg" => "8.2.0550",
+ "pinned" => false,
+ "outdated" => false },
+ "curl" =>
+ { "name" => "curl",
+ "full_name" => "curl",
+ "oldname" => nil,
+ "aliases" => [],
+ "versioned_formulae" => [],
+ "desc" => "Get a file from an HTTP, HTTPS or FTP server",
+ "homepage" => "https://curl.haxx.se/",
+ "versions" => { "stable" => "7.69.1", "devel" => nil, "head" => "HEAD", "bottle" => true },
+ "urls" => { "stable" => { "url" => "https://curl.haxx.se/download/curl-7.69.1.tar.bz2", "tag" => nil, "revision" => nil } },
+ "revision" => 0,
+ "version_scheme" => 0,
+ "bottle" =>
+ { "stable" =>
+ { "rebuild" => 0,
+ "cellar" => ":any",
+ "prefix" => "/usr/local",
+ "root_url" => "https://homebrew.bintray.com/bottles",
+ "files" =>
+ { "catalina" => { "url" => "https://homebrew.bintray.com/bottles/curl-7.69.1.catalina.bottle.tar.gz", "sha256" => "400500fede02f9335bd38c16786b2bbf5e601e358dfac8c21e363d2a8fdd8fac" },
+ "mojave" => { "url" => "https://homebrew.bintray.com/bottles/curl-7.69.1.mojave.bottle.tar.gz", "sha256" => "f082c275f9af1e8e93be12b63a1aff659ba6efa48c8528a97e26c9858a6f95b6" },
+ "high_sierra" => { "url" => "https://homebrew.bintray.com/bottles/curl-7.69.1.high_sierra.bottle.tar.gz", "sha256" => "ad023093c252799a4c60646a149bfe14ffa6984817cf463a6f0e98f6551057fe" } } } },
+ "keg_only" => true,
+ "bottle_disabled" => false,
+ "options" => [],
+ "build_dependencies" => ["pkg-config"],
+ "dependencies" => [],
+ "recommended_dependencies" => [],
+ "optional_dependencies" => [],
+ "uses_from_macos" => ["openssl@1.1", "zlib"],
+ "requirements" => [],
+ "conflicts_with" => [],
+ "caveats" => nil,
+ "installed" => [],
+ "linked_keg" => nil,
+ "pinned" => false,
+ "outdated" => false } }
end
- before(:each) do
-
- end
-
- describe "load_current_resource" do
+ describe "#load_current_resource" do
before(:each) do
- allow(provider).to receive(:current_installed_version).and_return(nil)
- allow(provider).to receive(:candidate_version).and_return("24.3")
+ allow(provider).to receive(:installed_version).and_return(nil)
+ allow(provider).to receive(:available_version).and_return("1.0")
end
it "creates a current resource with the name of the new resource" do
provider.load_current_resource
expect(provider.current_resource).to be_a(Chef::Resource::Package)
- expect(provider.current_resource.name).to eql("emacs")
+ expect(provider.current_resource.name).to eql("emacs, vim")
end
it "creates a current resource with the version if the package is installed" do
- expect(provider).to receive(:current_installed_version).and_return("24.3")
+ expect(provider).to receive(:get_current_versions).and_return(["1.0", "2.0"])
provider.load_current_resource
- expect(provider.current_resource.version).to eql("24.3")
+ expect(provider.current_resource.version).to eql(["1.0", "2.0"])
end
it "creates a current resource with a nil version if the package is not installed" do
provider.load_current_resource
- expect(provider.current_resource.version).to be_nil
+ expect(provider.current_resource.version).to eq([nil, nil])
end
it "sets a candidate version if one exists" do
provider.load_current_resource
- expect(provider.candidate_version).to eql("24.3")
+ expect(provider.candidate_version).to eql(["1.0", "1.0"])
+ end
+ end
+
+ describe "#brew_info" do
+ it "returns a hash of data per package" do
+ allow(provider).to receive(:brew_cmd_output).and_return(brew_cmd_output_data)
+ expect(provider.brew_info).to have_key("vim")
+ end
+
+ it "returns empty hash for packages if they lack data" do
+ new_resource.package_name %w{bogus}
+ allow(provider).to receive(:brew_cmd_output).and_return("")
+ expect(provider.brew_info).to eq("bogus" => {})
end
end
- describe "current_installed_version" do
+ describe "#installed_version" do
it "returns the latest version from brew info if the package is keg only" do
- allow(provider).to receive(:brew_info).and_return(keg_only_brew_info)
- expect(provider.current_installed_version).to eql("24.3-keggy")
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.installed_version("openssl@1.1")).to eql("1.1.1a")
end
it "returns the linked keg version if the package is not keg only" do
- allow(provider).to receive(:brew_info).and_return(installed_brew_info)
- expect(provider.current_installed_version).to eql("24.3")
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.installed_version("vim")).to eql("8.2.0550")
end
it "returns nil if the package is not installed" do
- allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
- expect(provider.current_installed_version).to be_nil
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.installed_version("kubernetes-cli")).to be_nil
end
it "returns nil if the package is keg only and not installed" do
- allow(provider).to receive(:brew_info).and_return(keg_only_uninstalled_brew_info)
- expect(provider.current_installed_version).to be_nil
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.installed_version("curl")).to be_nil
+ end
+
+ it "returns the version if a package alias is given" do
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.installed_version("openssl")).to eql("1.1.1a")
+ end
+ end
+
+ describe "#available_version" do
+ it "returns version of package when exact name given" do
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.available_version("openssl@1.1")).to eql("1.1.1f")
+ end
+
+ it "returns version of package when alias is given" do
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.available_version("openssl")).to eql("1.1.1f")
+ end
+
+ it "returns nil if the package is not installed" do
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ expect(provider.available_version("bogus")).to be_nil
end
end
- describe "brew" do
+ describe "#brew_cmd_output" do
before do
expect(provider).to receive(:find_homebrew_uid).and_return(homebrew_uid)
- expect(Etc).to receive(:getpwuid).with(homebrew_uid).and_return(OpenStruct.new(:name => "name", :dir => "/"))
+ expect(Etc).to receive(:getpwuid).with(homebrew_uid).and_return(OpenStruct.new(name: "name", dir: "/"))
end
- it "passes a single to the brew command and return stdout" do
- allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "zombo"))
- expect(provider.brew).to eql("zombo")
+ it "passes a single pkg to the brew command and return stdout" do
+ allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "zombo"))
+ expect(provider.brew_cmd_output).to eql("zombo")
end
it "takes multiple arguments as an array" do
- allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "homestarrunner"))
- expect(provider.brew("info", "opts", "bananas")).to eql("homestarrunner")
+ allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "homestarrunner"))
+ expect(provider.brew_cmd_output("info", "opts", "bananas")).to eql("homestarrunner")
end
context "when new_resource is Package" do
let(:new_resource) { Chef::Resource::Package.new("emacs") }
it "does not try to read homebrew_user from Package, which does not have it" do
- allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "zombo"))
- expect(provider.brew).to eql("zombo")
+ allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "zombo"))
+ expect(provider.brew_cmd_output).to eql("zombo")
end
end
end
- context "when testing actions" do
+ describe "resource actions" do
before(:each) do
provider.current_resource = current_resource
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
end
- describe "install_package" do
- before(:each) do
- allow(provider).to receive(:candidate_version).and_return("24.3")
+ describe "install" do
+ it "calls brew_cmd_output to install only the necessary packages" do
+ new_resource.package_name %w{curl openssl}
+ expect(provider).to receive(:brew_cmd_output).with("install", nil, ["curl"])
+ provider.run_action(:install)
end
- it "installs the named package with brew install" 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")
- provider.install_package("emacs", "24.3")
- end
-
- it "does not do anything if the package is installed" 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).not_to receive(:get_response_from_command)
- provider.install_package("emacs", "24.3")
+ it "does not do anything if all the packages are already installed" do
+ new_resource.package_name %w{vim openssl}
+ expect(provider).not_to receive(:brew_cmd_output)
+ provider.run_action(:install)
end
it "uses options to the brew command if specified" do
- allow(provider.new_resource).to receive(:options).and_return("--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")
- provider.install_package("emacs", "24.3")
+ new_resource.package_name "curl"
+ new_resource.options "--cocoa"
+ expect(provider).to receive(:brew_cmd_output).with("install", ["--cocoa"], ["curl"])
+ provider.run_action(:install)
end
end
- describe "upgrade_package" 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")
- provider.upgrade_package("emacs", "24.3")
- end
-
- it "does not do anything if the package version is already installed" 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).not_to receive(:get_response_from_command)
- provider.install_package("emacs", "24.3")
+ describe "upgrade" do
+ it "calls #brew_cmd_output to upgrade the packages" do
+ new_resource.package_name %w{openssl}
+ allow(provider.current_resource).to receive(:version).and_return(["1.0.1a"])
+ expect(provider).to receive(:brew_cmd_output).with("upgrade", nil, ["openssl"])
+ provider.run_action(:upgrade)
end
- 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")
- provider.upgrade_package("emacs", "24.3")
+ it "calls #brew_cmd_output to both upgrade and install the packages as necessary" do
+ new_resource.package_name %w{openssl kubernetes-cli}
+ allow(provider.current_resource).to receive(:version).and_return(["1.0.1a", nil])
+ expect(provider).to receive(:brew_cmd_output).with("upgrade", nil, ["openssl"])
+ expect(provider).to receive(:brew_cmd_output).with("install", nil, ["kubernetes-cli"])
+ provider.run_action(:upgrade)
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")
- provider.upgrade_package("emacs", "24.3")
+ new_resource.package_name %w{openssl}
+ allow(provider.current_resource).to receive(:version).and_return(["1.0.1a"])
+ allow(provider).to receive(:brew_info).and_return(brew_info_data)
+ new_resource.options "--cocoa"
+ expect(provider).to receive(:brew_cmd_output).with("upgrade", [ "--cocoa" ], ["openssl"])
+ provider.run_action(:upgrade)
end
end
- describe "remove_package" 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")
- provider.remove_package("emacs", "24.3")
+ describe "remove" do
+ it "calls #brew_cmd_output to uninstall the packages" do
+ new_resource.package_name %w{curl openssl}
+ expect(provider).to receive(:brew_cmd_output).with("uninstall", nil, %w{curl openssl})
+ provider.run_action(:remove)
end
it "does not do anything if the package is not installed" do
- allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
- expect(provider).not_to receive(:get_response_from_command)
- provider.remove_package("emacs", "24.3")
+ new_resource.package_name %w{kubernetes-cli}
+ expect(provider).not_to receive(:brew_cmd_output)
+ provider.run_action(:remove)
end
end
- describe "purge_package" 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")
- provider.purge_package("emacs", "24.3")
+ describe "purge" do
+ it "call #brew_cmd_output to uninstall --force the packages" do
+ new_resource.package_name %w{curl openssl}
+ expect(provider).to receive(:brew_cmd_output).with("uninstall", "--force", nil, %w{curl openssl})
+ provider.run_action(:purge)
end
it "does not do anything if the package is not installed" do
- allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
- expect(provider).not_to receive(:get_response_from_command)
- provider.purge_package("emacs", "24.3")
+ new_resource.package_name %w{kubernetes-cli}
+ expect(provider).not_to receive(:brew_cmd_output)
+ provider.run_action(:purge)
end
end
end
diff --git a/spec/unit/provider/package/ips_spec.rb b/spec/unit/provider/package/ips_spec.rb
index f47385da09..9ab077089f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,128 +26,134 @@ 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
def local_output
stdin = StringIO.new
stdout = ""
- stderr = <<-PKG_STATUS
-pkg: info: no packages matching the following patterns you specified are
-installed on the system. Try specifying -r to query remotely:
+ stderr = <<~PKG_STATUS
+ pkg: info: no packages matching the following patterns you specified are
+ 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)
+ crypto/gnupg
+ PKG_STATUS
+ OpenStruct.new(stdout: stdout, stdin: stdin, stderr: stderr, status: @status, exitstatus: 1)
end
def remote_output
- stdout = <<-PKG_STATUS
- Name: security/sudo
- Summary: sudo - authority delegation tool
- State: Not Installed
- Publisher: omnios
- Version: 1.8.4.1 (1.8.4p1)
- Build Release: 5.11
- Branch: 0.151002
-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
+ stdout = <<~PKG_STATUS
+ Name: security/sudo
+ Summary: sudo - authority delegation tool
+ State: Not Installed
+ Publisher: omnios
+ Version: 1.8.4.1 (1.8.4p1)
+ Build Release: 5.11
+ Branch: 0.151002
+ 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
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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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
it "should set the installed version if package has one" do
local = local_output
- local.stdout = <<-INSTALLED
- Name: crypto/gnupg
- Summary: GNU Privacy Guard
- Description: A complete and free implementation of the OpenPGP Standard as
- defined by RFC4880.
- Category: Applications/System Utilities
- State: Installed
- Publisher: solaris
- Version: 2.0.17
- Build Release: 5.11
- Branch: 0.175.0.0.0.2.537
-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)
+ local.stdout = <<~INSTALLED
+ Name: crypto/gnupg
+ Summary: GNU Privacy Guard
+ Description: A complete and free implementation of the OpenPGP Standard as
+ defined by RFC4880.
+ Category: Applications/System Utilities
+ State: Installed
+ Publisher: solaris
+ Version: 2.0.17
+ Build Release: 5.11
+ Branch: 0.175.0.0.0.2.537
+ 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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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_compacted!).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_compacted!).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
- Name: security/sudo
- Summary: sudo - authority delegation tool
- State: Not Installed
- Publisher: omnios
- Version: 1.8.4.1 (1.8.4p1)
- Build Release: 5.11
- Branch: 0.151002
-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)
+ remote.stdout = <<~PKG_STATUS
+ Name: security/sudo
+ Summary: sudo - authority delegation tool
+ State: Not Installed
+ Publisher: omnios
+ Version: 1.8.4.1 (1.8.4p1)
+ Build Release: 5.11
+ Branch: 0.151002
+ 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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out_compacted!).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")
@@ -155,51 +161,51 @@ PKG_STATUS
it "should not upgrade the package if it is already installed" do
local = local_output
- local.stdout = <<-INSTALLED
- Name: crypto/gnupg
- Summary: GNU Privacy Guard
- Description: A complete and free implementation of the OpenPGP Standard as
- defined by RFC4880.
- Category: Applications/System Utilities
- State: Installed
- Publisher: solaris
- Version: 2.0.17
- Build Release: 5.11
- Branch: 0.175.0.0.0.2.537
-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
+ local.stdout = <<~INSTALLED
+ Name: crypto/gnupg
+ Summary: GNU Privacy Guard
+ Description: A complete and free implementation of the OpenPGP Standard as
+ defined by RFC4880.
+ Category: Applications/System Utilities
+ State: Installed
+ Publisher: solaris
+ Version: 2.0.17
+ Build Release: 5.11
+ Branch: 0.175.0.0.0.2.537
+ 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
remote = remote_output
- remote.stdout = <<-REMOTE
- Name: crypto/gnupg
- Summary: GNU Privacy Guard
- Description: A complete and free implementation of the OpenPGP Standard as
- defined by RFC4880.
- Category: Applications/System Utilities
- State: Not Installed
- Publisher: solaris
- Version: 2.0.18
- Build Release: 5.11
- Branch: 0.175.0.0.0.2.537
-Packaging Date: October 19, 2011 09:14:50 AM
- Size: 8.07 MB
- 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)
+ remote.stdout = <<~REMOTE
+ Name: crypto/gnupg
+ Summary: GNU Privacy Guard
+ Description: A complete and free implementation of the OpenPGP Standard as
+ defined by RFC4880.
+ Category: Applications/System Utilities
+ State: Not Installed
+ Publisher: solaris
+ Version: 2.0.18
+ Build Release: 5.11
+ Branch: 0.175.0.0.0.2.537
+ Packaging Date: October 19, 2011 09:14:50 AM
+ Size: 8.07 MB
+ 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_compacted).with("pkg", "info", @new_resource.package_name, timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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..75d16de432 100644
--- a/spec/unit/provider/package/macports_spec.rb
+++ b/spec/unit/provider/package/macports_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: David Balatero (<dbalatero@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -70,33 +70,33 @@ describe Chef::Provider::Package::Macports do
describe "current_installed_version" do
it "should return the current version if the package is installed" do
- stdout = <<EOF
-The following ports are currently installed:
- openssl @0.9.8k_0 (active)
-EOF
+ stdout = <<~EOF
+ The following ports are currently installed:
+ openssl @0.9.8k_0 (active)
+ EOF
- status = double(:stdout => stdout, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: stdout, exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
expect(@provider.current_installed_version).to eq("0.9.8k_0")
end
it "should return nil if a package is not currently installed" do
- status = double(:stdout => " \n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: " \n", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
expect(@provider.current_installed_version).to be_nil
end
end
describe "macports_candidate_version" do
it "should return the latest available version of a given package" do
- status = double(:stdout => "version: 4.2.7\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: "version: 4.2.7\n", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
expect(@provider.macports_candidate_version).to eq("4.2.7")
end
it "should return nil if there is no version for a given package" do
- status = double(:stdout => "Error: port fadsfadsfads not found\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: "Error: port fadsfadsfads not found\n", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
expect(@provider.macports_candidate_version).to be_nil
end
end
@@ -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_compacted!).with("port", "install", "zsh", "@4.2.7", timeout: 900)
@provider.install_package("zsh", "4.2.7")
end
@@ -113,7 +113,7 @@ EOF
it "should not do anything if a package already exists with the same version" do
expect(@current_resource).to receive(:version).and_return("4.2.7")
@provider.current_resource = @current_resource
- expect(@provider).not_to receive(:shell_out!)
+ expect(@provider).not_to receive(:shell_out_compacted!)
@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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).with("port", "upgrade", "zsh", "@4.2.7", timeout: 900)
@provider.upgrade_package("zsh", "4.2.7")
end
@@ -177,7 +177,7 @@ EOF
it "should not run the port upgrade command if the version is already installed" do
expect(@current_resource).to receive(:version).at_least(:once).and_return("4.2.7")
@provider.current_resource = @current_resource
- expect(@provider).not_to receive(:shell_out!)
+ expect(@provider).not_to receive(:shell_out_compacted!)
@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_compacted!).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..897ae254f0
--- /dev/null
+++ b/spec/unit/provider/package/msu_spec.rb
@@ -0,0 +1,283 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 downloaded 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("#{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..154e5ba333 100644
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Provider::Package::Openbsd do
node
end
- let (:provider) do
+ let(:provider) do
events = Chef::EventDispatch::Dispatcher.new
run_context = Chef::RunContext.new(node, {}, events)
Chef::Provider::Package::Openbsd.new(new_resource, run_context)
@@ -45,19 +45,20 @@ 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_compacted!).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(
- instance_double("shellout", :stdout => "#{name}-#{version}\n"))
- expect(provider).to receive(:shell_out!).with(
- "pkg_add -r #{name}-#{version}",
- { :env => { "PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/" }, timeout: 900 }
- ) { OpenStruct.new :status => true }
+ expect(provider).to receive(:shell_out_compacted!).with("pkg_info", "-I", name, anything).and_return(
+ instance_double("shellout", stdout: "#{name}-#{version}\n")
+ )
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "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)
end
end
@@ -69,8 +70,9 @@ 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(
- instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n"))
+ expect(provider).to receive(:shell_out_compacted!).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
end
@@ -83,13 +85,14 @@ 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(
- instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor}\n"))
- expect(provider).to receive(:shell_out!).with(
- "pkg_add -r #{name}-#{version}-#{flavor}",
+ expect(provider).to receive(:shell_out_compacted!).with("pkg_info", "-e", "#{package_name}->0", anything).and_return(instance_double("shellout", stdout: ""))
+ expect(provider).to receive(:shell_out_compacted!).with("pkg_info", "-I", name, anything).and_return(
+ instance_double("shellout", stdout: "#{name}-#{version}-#{flavor}\n")
+ )
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "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 }
+ ) { OpenStruct.new status: true }
provider.run_action(:install)
end
end
@@ -98,14 +101,15 @@ 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(
- instance_double("shellout", :stdout => "#{name}-#{version}-#{flavor_a}\n"))
+ expect(provider).to receive(:shell_out_compacted!).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}",
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "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 }
+ ) { OpenStruct.new status: true }
provider.run_action(:install)
end
end
@@ -116,15 +120,15 @@ describe Chef::Provider::Package::Openbsd do
describe "delete a package" do
before do
@name = "ihavetoes"
- @new_resource = Chef::Resource::Package.new(@name)
+ @new_resource = Chef::Resource::Package.new(@name)
@current_resource = Chef::Resource::Package.new(@name)
@provider = Chef::Provider::Package::Openbsd.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
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
- ) { OpenStruct.new :status => true }
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "pkg_delete", @name, env: nil, timeout: 900
+ ) { OpenStruct.new status: true }
@provider.remove_package(@name, nil)
end
end
diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb
index ce9107f31b..e44df52e42 100644
--- a/spec/unit/provider/package/pacman_spec.rb
+++ b/spec/unit/provider/package/pacman_spec.rb
@@ -15,179 +15,94 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
require "spec_helper"
-describe Chef::Provider::Package::Pacman do
+RSpec.shared_examples "current_resource" do |pkg, version, candidate|
+ let(:current_resource) { @provider.load_current_resource }
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("nano")
- @current_resource = Chef::Resource::Package.new("nano")
-
- @status = double(:stdout => "", :exitstatus => 0)
- @provider = Chef::Provider::Package::Pacman.new(@new_resource, @run_context)
- allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
-
- allow(@provider).to receive(:shell_out).and_return(@status)
- @stdin = StringIO.new
- @stdout = StringIO.new(<<-ERR)
-error: package "nano" not found
-ERR
- @stderr = StringIO.new
- @pid = 2342
+ @provider = create_provider_for(pkg)
end
- describe "when determining the current package state" do
- it "should create a current resource with the name of the new_resource" do
- expect(Chef::Resource::Package).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(@current_resource).to receive(:package_name).with(@new_resource.package_name)
- @provider.load_current_resource
- 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)
- @provider.load_current_resource
- end
-
- it "should read stdout on pacman" do
- allow(@provider).to receive(:shell_out).and_return(@status)
- @provider.load_current_resource
- end
-
- it "should set the installed version to nil on the current resource if pacman installed version not exists" do
- allow(@provider).to receive(:shell_out).and_return(@status)
- @provider.load_current_resource
- end
-
- it "should set the installed version if pacman has one" do
- stdout = <<-PACMAN
-Name : nano
-Version : 2.2.2-1
-URL : http://www.nano-editor.org
-Licenses : GPL
-Groups : base
-Provides : None
-Depends On : glibc ncurses
-Optional Deps : None
-Required By : None
-Conflicts With : None
-Replaces : None
-Installed Size : 1496.00 K
-Packager : Andreas Radke <andyrtr@archlinux.org>
-Architecture : i686
-Build Date : Mon 18 Jan 2010 06:16:16 PM CET
-Install Date : Mon 01 Feb 2010 10:06:30 PM CET
-Install Reason : Explicitly installed
-Install Script : Yes
-Description : Pico editor clone with enhancements
-PACMAN
-
- status = double(:stdout => stdout, :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(status)
- @provider.load_current_resource
- expect(@current_resource.version).to eq("2.2.2-1")
- end
-
- it "should set the candidate version if pacman has one" do
- status = double(:stdout => "core nano 2.2.3-1", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(status)
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("2.2.3-1")
- end
-
- it "should use pacman.conf to determine valid repo names for package versions" do
- @pacman_conf = <<-PACMAN_CONF
-[options]
-HoldPkg = pacman glibc
-Architecture = auto
-
-[customrepo]
-Server = https://my.custom.repo
+ it "sets current_resource name" do
+ expect(current_resource.package_name).to eql(pkg)
+ end
-[core]
-Include = /etc/pacman.d/mirrorlist
+ it "sets current_resource version" do
+ expect(current_resource.version).to eql(version)
+ end
-[extra]
-Include = /etc/pacman.d/mirrorlist
+ it "sets candidate version" do
+ current_resource
+ expect(@provider.candidate_version).to eql(candidate)
+ end
+end
-[community]
-Include = /etc/pacman.d/mirrorlist
-PACMAN_CONF
+describe Chef::Provider::Package::Pacman do
+ def create_provider_for(name)
+ new_resource = Chef::Resource::Package.new(name)
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ provider = Chef::Provider::Package::Pacman.new(new_resource, run_context)
+
+ pacman_out = <<~PACMAN_OUT
+ extra nano 3.450-1
+ extra emacs 0.12.0-1 [installed]
+ core sed 3.234-2 [installed: 3.234-1]
+ PACMAN_OUT
+
+ allow(provider).to receive(:shell_out_compacted).and_return(double(stdout: pacman_out, exitstatus: 0))
+ provider
+ end
- 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(:read).with("/etc/pacman.conf").and_return(@pacman_conf)
- allow(@provider).to receive(:shell_out).and_return(status)
+ before(:each) do
+ pacman_conf = <<~PACMAN_CONF
+ [options]
+ HoldPkg = pacman glibc
+ Architecture = auto
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("1.2.3-4")
- end
+ [customrepo]
+ Server = https://my.custom.repo
- it "should raise an exception if pacman fails" do
- expect(@status).to receive(:exitstatus).and_return(2)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
+ [core]
+ Include = /etc/pacman.d/mirrorlist
- it "should not raise an exception if pacman succeeds" do
- expect(@status).to receive(:exitstatus).and_return(0)
- expect { @provider.load_current_resource }.not_to raise_error
- end
+ [extra]
+ Include = /etc/pacman.d/mirrorlist
- it "should raise an exception if pacman does not return a candidate version" do
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
- end
+ [community]
+ Include = /etc/pacman.d/mirrorlist
+ PACMAN_CONF
- it "should return the current resouce" do
- expect(@provider.load_current_resource).to eql(@current_resource)
- end
+ 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)
end
- 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 })
- @provider.install_package("nano", "1.0")
- end
+ describe "loading the current resource" do
- 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")
-
- @provider.install_package("nano", "1.0")
+ describe "for an existing and installed but upgradable package" do
+ include_examples "current_resource", ["sed"], ["3.234-1"], ["3.234-2"]
end
- end
- describe Chef::Provider::Package::Pacman, "upgrade_package" do
- it "should run install_package with the name and version" do
- expect(@provider).to receive(:install_package).with("nano", "1.0")
- @provider.upgrade_package("nano", "1.0")
+ describe "for an existing and installed package" do
+ include_examples "current_resource", ["emacs"], ["0.12.0-1"], ["0.12.0-1"]
end
- end
- 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 })
- @provider.remove_package("nano", "1.0")
+ describe "for an existing non installed package" do
+ include_examples "current_resource", ["nano"], [nil], ["3.450-1"]
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")
-
- @provider.remove_package("nano", "1.0")
+ describe "for a non existing and an upgradable package" do
+ include_examples "current_resource", %w{nano sed}, [nil, "3.234-1"], ["3.450-1", "3.234-2"]
end
- end
- describe Chef::Provider::Package::Pacman, "purge_package" do
- it "should run remove_package with the name and version" do
- expect(@provider).to receive(:remove_package).with("nano", "1.0")
- @provider.purge_package("nano", "1.0")
+ describe "for a non existing package" do
+ let(:current_resource) { @provider.load_current_resource }
+ before(:each) do
+ @provider = create_provider_for("vim")
+ end
+
+ it "raises an error" do
+ expect { current_resource }.to raise_error(Chef::Exceptions::Package)
+ end
end
end
diff --git a/spec/unit/provider/package/paludis_spec.rb b/spec/unit/provider/package/paludis_spec.rb
index b984aeb83f..746f78a34f 100644
--- a/spec/unit/provider/package/paludis_spec.rb
+++ b/spec/unit/provider/package/paludis_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Vasiliy Tolstov <v.tolstov@selfip.ru>
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,84 +33,84 @@ describe Chef::Provider::Package::Paludis do
@stdin = StringIO.new
@stderr = StringIO.new
- @stdout = <<-PKG_STATUS
-group/ntp 0 accounts
-group/ntp 0 installed-accounts
-net/ntp 4.2.6_p5-r2 arbor
-user/ntp 0 accounts
-user/ntp 0 installed-accounts
-net/ntp 4.2.6_p5-r1 installed
-PKG_STATUS
+ @stdout = <<~PKG_STATUS
+ group/ntp 0 accounts
+ group/ntp 0 installed-accounts
+ net/ntp 4.2.6_p5-r2 arbor
+ user/ntp 0 accounts
+ user/ntp 0 installed-accounts
+ net/ntp 4.2.6_p5-r1 installed
+ PKG_STATUS
@pid = 12345
- @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
+ @shell_out = 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!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
expect(Chef::Resource::Package).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!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
expect(@current_resource).to receive(:package_name).with(@new_resource.package_name)
@provider.load_current_resource
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_compacted!).with("cave", "-L", "warning", "print-ids", "-M", "none", "-m", @new_resource.package_name, "-f", "%c/%p %v %r\n", timeout: 900).and_return(@shell_out)
@provider.load_current_resource
end
it "should return new version if package is installed" do
- @stdout.replace(<<-INSTALLED)
-group/ntp 0 accounts
-group/ntp 0 installed-accounts
-net/ntp 4.2.6_p5-r2 arbor
-user/ntp 0 accounts
-user/ntp 0 installed-accounts
-net/ntp 4.2.6_p5-r1 installed
-INSTALLED
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ @stdout.replace(<<~INSTALLED)
+ group/ntp 0 accounts
+ group/ntp 0 installed-accounts
+ net/ntp 4.2.6_p5-r2 arbor
+ user/ntp 0 accounts
+ user/ntp 0 installed-accounts
+ net/ntp 4.2.6_p5-r1 installed
+ INSTALLED
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
@provider.load_current_resource
expect(@current_resource.version).to eq("4.2.6_p5-r1")
expect(@provider.candidate_version).to eql("4.2.6_p5-r2")
end
it "should return the current resource" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
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("cave -L warning resolve -x \"=net/ntp-4.2.6_p5-r2\"", { :timeout => @new_resource.timeout })
+ expect(@provider).to receive(:shell_out_compacted!).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_compacted!).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
it "should not contain invalid characters for the version string" do
- @stdout.replace(<<-PKG_STATUS)
-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 })
+ @stdout.replace(<<~PKG_STATUS)
+ sys-process/lsof 4.87 arbor
+ sys-process/lsof 4.87 x86_64
+ PKG_STATUS
+ expect(@provider).to receive(:shell_out_compacted!).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
it "should not include the human-readable version in the candidate_version" do
- @stdout.replace(<<-PKG_STATUS)
-sys-process/lsof 4.87 arbor
-sys-process/lsof 4.87 x86_64
-PKG_STATUS
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ @stdout.replace(<<~PKG_STATUS)
+ sys-process/lsof 4.87 arbor
+ sys-process/lsof 4.87 x86_64
+ PKG_STATUS
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
@provider.load_current_resource
expect(@current_resource.version).to be_nil
expect(@provider.candidate_version).to eql("4.87")
@@ -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_compacted!).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_compacted!).with("cave", "-L", "warning", "uninstall", "-x", "=net/ntp-4.2.6_p5-r2", timeout: 900)
@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 ebb5b3139f..8fdd7f877a 100644
--- a/spec/unit/provider/package/portage_spec.rb
+++ b/spec/unit/provider/package/portage_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Caleb Tennis (<caleb.tennis@gmail.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,19 +22,19 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new("dev-util/git")
- @new_resource_without_category = Chef::Resource::Package.new("git")
- @current_resource = Chef::Resource::Package.new("dev-util/git")
+ @new_resource = Chef::Resource::PortagePackage.new("dev-util/git")
+ @new_resource_without_category = Chef::Resource::PortagePackage.new("git")
+ @current_resource = Chef::Resource::PortagePackage.new("dev-util/git")
@provider = Chef::Provider::Package::Portage.new(@new_resource, @run_context)
- allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ allow(Chef::Resource::PortagePackage).to receive(:new).and_return(@current_resource)
end
describe "when determining the current state of the package" do
it "should create a current resource with the name of new_resource" do
allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0"])
- expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ expect(Chef::Resource::PortagePackage).to receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
@@ -108,207 +108,70 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
describe Chef::Provider::Package::Portage, "candidate_version" do
it "should return the candidate_version variable if already set" do
@provider.candidate_version = "1.0.0"
- expect(@provider).not_to receive(:shell_out)
+ expect(@provider).not_to receive(:shell_out_compacted)
@provider.candidate_version
end
it "should throw an exception if the exitstatus is not 0" do
- status = double(:stdout => "", :exitstatus => 1)
- allow(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: "", stderr: "", exitstatus: 1)
+ allow(@provider).to receive(:shell_out_compacted).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)
- expect(@provider).to receive(:shell_out).and_return(status)
- expect(@provider.candidate_version).to eq("1.6.0.6")
+ it "should find the candidate_version if a category is specified and there are no duplicates" do
+ status = double(stdout: "dev-vcs/git-2.16.2", exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).and_return(status)
+ 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)
+ it "should find the candidate_version if a category is not specified and there are no duplicates" do
+ 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).to receive(:shell_out_compacted).and_return(status)
+ 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
-
-* 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)
+ stderr_output = <<~EOF
+ You specified an unqualified atom that matched multiple packages:
+ * app-misc/sphinx
+ * dev-python/sphinx
+
+ Please use a more specific atom.
+ EOF
+ 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).to receive(:shell_out_compacted).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_compacted!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "=dev-util/git-1.0.0", timeout: 3600)
@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_compacted!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "~dev-util/git-1.0.0", timeout: 3600)
@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_compacted!).with("emerge", "-g", "--color", "n", "--nospinner", "--quiet", "--oneshot", "=dev-util/git-1.0.0", timeout: 3600)
+ @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_compacted!).with("emerge", "--unmerge", "--color", "n", "--nospinner", "--quiet", "dev-util/git", timeout: 3600)
@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_compacted!).with("emerge", "--unmerge", "--color", "n", "--nospinner", "--quiet", "=dev-util/git-1.0.0", timeout: 3600)
@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..2f4da8c179
--- /dev/null
+++ b/spec/unit/provider/package/powershell_spec.rb
@@ -0,0 +1,503 @@
+#
+# Author:: Dheeraj Dubey(<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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, :windows_only, :windows_gte_10 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
+ node.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ 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(:tls_set_command) { "if ([Net.ServicePointManager]::SecurityProtocol -lt [Net.SecurityProtocolType]::Tls12) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 };" }
+ let(:generated_command) { "#{tls_set_command} ( Get-Package posh-git -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version" }
+ let(:generated_get_cmdlet) { "#{tls_set_command} ( Get-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version" }
+ let(:generated_get_cmdlet_with_version) { "#{tls_set_command} ( Get-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_find_cmdlet) { "#{tls_set_command} ( Find-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version" }
+ let(:generated_find_cmdlet_with_version) { "#{tls_set_command} ( Find-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_find_cmdlet_with_source) { "#{tls_set_command} ( Find-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -Source MyGallery ).Version" }
+ let(:generated_find_cmdlet_with_source_and_version) { "#{tls_set_command} ( Find-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 1.0.0.0 -Source MyGallery ).Version" }
+ let(:generated_install_cmdlet) { "#{tls_set_command} ( Install-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version" }
+ let(:generated_install_cmdlet_with_version) { "#{tls_set_command} ( Install-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 1.0.0.0 ).Version" }
+ let(:generated_install_cmdlet_with_source) { "#{tls_set_command} ( Install-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -Source MyGallery ).Version" }
+ let(:generated_install_cmdlet_with_source_and_version) { "#{tls_set_command} ( Install-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 1.0.0.0 -Source MyGallery ).Version" }
+ let(:generated_uninstall_cmdlet) { "#{tls_set_command} ( Uninstall-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version" }
+ let(:generated_uninstall_cmdlet_with_version) { "#{tls_set_command} ( Uninstall-Package xNetworking -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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 separated" do
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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(%w{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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -Source MyGallery ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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 package without the publisher check" do
+ provider.load_current_resource
+ new_resource.package_name(["xCertificate"])
+ new_resource.version(nil)
+ new_resource.skip_publisher_check(true)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -SkipPublisherCheck ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 -SkipPublisherCheck ).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("#{tls_set_command} ( Find-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_7zip_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package '7-Zip 16.02 (x64)' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.11.0.0 ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("#{tls_set_command} ( Install-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("#{tls_set_command} ( Install-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("#{tls_set_command} ( Install-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -Source MyGallery ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -Source MyGallery ).Version", { timeout: new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Install-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 -Source MyGallery ).Version", { timeout: new_resource.timeout })
+ expect(provider).to receive(:powershell_out).with("#{tls_set_command} ( Install-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 -Source MyGallery).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Uninstall-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_not_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Find-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xnetworking_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xNetworking' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -RequiredVersion 2.1.0.0 ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Uninstall-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue -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("#{tls_set_command} ( Find-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).Version", { timeout: new_resource.timeout }).and_return(package_xcertificate_available)
+ allow(provider).to receive(:powershell_out).with("#{tls_set_command} ( Get-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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("#{tls_set_command} ( Uninstall-Package 'xCertificate' -Force -ForceBootstrap -WarningAction SilentlyContinue ).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..edf39b0f3f 100644
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,7 @@ describe Chef::Provider::Package::Rpm do
let(:package_name) { "ImageMagick-c++" }
let(:new_resource) do
- Chef::Resource::Package.new(package_name).tap do |resource|
+ Chef::Resource::RpmPackage.new(package_name).tap do |resource|
resource.source(package_source)
end
end
@@ -41,17 +41,17 @@ 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)
- allow(provider).to receive(:shell_out).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
+ allow(provider).to receive(:shell_out_compacted!).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
+ allow(provider).to receive(:shell_out_compacted).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
end
describe "when the package source is not valid" do
- context "when source is not defiend" do
- let(:new_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+ context "when source is not defined" do
+ let(:new_resource) { Chef::Resource::RpmPackage.new("ImageMagick-c++") }
it "should raise an exception when attempting any action" do
expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
@@ -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")
@@ -85,19 +85,19 @@ describe Chef::Provider::Package::Rpm do
describe "when the package source is valid" do
before do
- expect(provider).to receive(:shell_out!).
- with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{package_source}", timeout: 900).
- and_return(rpm_qp_status)
+ expect(provider).to receive(:shell_out_compacted!)
+ .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).
- and_return(rpm_q_status)
+ expect(provider).to receive(:shell_out_compacted)
+ .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_compacted!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_install
end
@@ -162,23 +162,28 @@ 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_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_install
end
end
context "when an older version is desired" do
let(:new_resource) do
- Chef::Resource::RpmPackage.new(package_name).tap do |r|
- r.source(package_source)
- r.allow_downgrade(true)
- end
+ r = Chef::Resource::RpmPackage.new(package_name)
+ r.source(package_source)
+ r
end
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_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_install
+ end
+
+ it "if downgrades are not allowed it should not downgrade" do
+ new_resource.allow_downgrade(false)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_install
end
@@ -192,7 +197,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_compacted!).with("rpm", "-i", "/tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_upgrade
end
@@ -203,7 +208,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_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
provider.action_upgrade
end
end
@@ -219,7 +224,13 @@ 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_compacted!).with("rpm", "-U", "--oldpackage", "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_upgrade
+ end
+
+ it "should run rpm -u --oldpackage with the package source to downgrade" do
+ new_resource.allow_downgrade(false)
+ expect(provider).not_to receive(:shell_out_compacted!).with("rpm", "-U", any_args)
provider.action_upgrade
end
@@ -231,7 +242,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_compacted!).with("rpm", "-e", "ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
provider.action_remove
end
end
@@ -276,7 +287,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 +333,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 +368,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_compacted!).with("rpm", "-i", package_source, timeout: 900)
provider.action_install
end
@@ -365,7 +376,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_compacted!).with("rpm", "--dbpath", "/var/lib/rpm", "-i", package_source, timeout: 900)
provider.action_install
end
end
@@ -376,7 +387,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_compacted!).with("rpm", "-i", package_source, timeout: 900)
provider.action_upgrade
end
@@ -387,7 +398,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_compacted!).with("rpm", "-e", "ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
provider.action_remove
end
end
@@ -404,24 +415,24 @@ 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)
- Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ allow(::File).to receive(:exist?).with(package_source).and_return(true)
+ Chef::Resource::RpmPackage.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
end
- let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+ let(:current_resource) { Chef::Resource::RpmPackage.new("ImageMagick-c++") }
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_compacted!).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
- it "should uprgrade from a path when the package is a path and the source is nil" do
+ it "should upgrade 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")
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_compacted!).with("rpm", "-U", "--oldpackage", "/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 4934505583..58a7eccb04 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -27,14 +27,31 @@ module GemspecBackcompatCreator
end
end
+# this is a global variable we construct of the highest rspec-core version which is installed, using APIs which
+# will break out of the bundle -- and done this way so that we can mock all these internal Gem APIs later...
+class RspecVersionString
+ def self.rspec_version_string
+ @rspec_version_string ||= begin
+ stubs = Gem::Specification.send(:installed_stubs, Gem::Specification.dirs, "rspec-core-*.gemspec")
+ stubs.select! { |stub| stub.name == "rspec-core" && Gem::Dependency.new("rspec-core", ">= 0").requirement.satisfied_by?(stub.version) }
+ stubs.max_by(&:version).version.to_s
+ end
+ end
+end
+RspecVersionString.rspec_version_string
+
require "spec_helper"
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)
+
+ WebMock.disable_net_connect!
end
it "determines the gem paths from the in memory rubygems" do
@@ -43,16 +60,17 @@ 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)
+ else # >= Rubygems 1.8 behavior
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)
end
expect(@gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil))).to eq(gems)
end
it "determines the installed versions of gems from the source index (part2: the unmockening)" do
- expected = ["rspec-core", Gem::Version.new(RSpec::Core::Version::STRING)]
+ expected = ["rspec-core", Gem::Version.new( RspecVersionString.rspec_version_string )]
actual = @gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil)).map { |spec| [spec.name, spec.version] }
expect(actual).to include(expected)
end
@@ -88,40 +106,62 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
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
+ expect(dep_installer).not_to receive(:find_gems_with_sources)
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
+ end
+
+ it "finds a matching gem candidate version on rubygems 2.0.0+" do
+ stub_request(:head, "https://rubygems.org/api/v1/dependencies")
+
+ stub_request(:get, "https://rubygems.org/api/v1/dependencies?gems=sexp_processor")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor")))
+
+ stub_request(:get, "https://rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
+
+ dep = Gem::Dependency.new("sexp_processor", ">= 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")
- 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
+ stub_request(:head, "https://rubygems.org/api/v1/dependencies")
+
+ stub_request(:get, "https://rubygems.org/api/v1/dependencies?gems=nonexistent_gem")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "nonexistent_gem")))
+
+ dep = Gem::Dependency.new("nonexistent_gem", ">= 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 (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)
+ stub_request(:head, "https://rubygems2.org/api/v1/dependencies")
+
+ stub_request(:get, "https://rubygems2.org/api/v1/dependencies?gems=sexp_processor")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor")))
+
+ stub_request(:get, "https://rubygems2.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
+
+ dep = Gem::Dependency.new("sexp_processor", ">= 0")
+ expect(@gem_env.candidate_version_from_remote(dep, "https://rubygems2.org")).to be_kind_of(Gem::Version)
end
end
context "old rubygems caching behavior" do
before do
Chef::Config[:rubygems_cache_enabled] = true
+
+ stub_request(:get, "https://rubygems.org/latest_specs.4.8.gz")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "latest_specs.4.8.gz")))
end
it "finds a matching gem candidate version on rubygems 2.0.0+" do
- dep = Gem::Dependency.new("rspec", ">= 0")
+ stub_request(:get, "https://rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
+
+ dep = Gem::Dependency.new("sexp_processor", ">= 0")
expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
end
@@ -131,8 +171,11 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
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)
+ stub_request(:get, "https://rubygems.org/quick/Marshal.4.8/sexp_processor-4.15.1.gemspec.rz")
+ .to_return(status: 200, body: File.binread(File.join(CHEF_SPEC_DATA, "rubygems.org", "sexp_processor-4.15.1.gemspec.rz")))
+
+ dep = Gem::Dependency.new("sexp_processor", ">= 0")
+ expect(@gem_env.candidate_version_from_remote(dep, "http://rubygems2.org")).to be_kind_of(Gem::Version)
end
end
@@ -144,30 +187,30 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
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
@@ -184,22 +227,22 @@ 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)
- expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
+ shell_out_result = OpenStruct.new(stdout: gem_env_output)
+ expect(@gem_env).to receive(:shell_out_compacted!).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)
- expect(@gem_env).to receive(:shell_out!).with("/usr/weird/bin/gem env gempath").and_return(shell_out_result)
+ shell_out_result = OpenStruct.new(stdout: gem_env_output)
+ expect(@gem_env).to receive(:shell_out_compacted!).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"])
expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache["/usr/weird/bin/gem"]).to eq(expected)
end
it "uses the cached result for gem paths when available" do
- expect(@gem_env).not_to receive(:shell_out!)
+ expect(@gem_env).not_to receive(:shell_out_compacted!)
expected = ["/path/to/gems", "/another/path/to/gems"]
Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache["/usr/weird/bin/gem"] = expected
expect(@gem_env.gem_paths).to eq(["/path/to/gems", "/another/path/to/gems"])
@@ -207,24 +250,20 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
it "builds the gems source index from the gem paths" do
allow(@gem_env).to receive(:gem_paths).and_return(["/path/to/gems", "/another/path/to/gems"])
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
- @gem_env.gem_specification
- expect(Gem::Specification.dirs).to eq([ "/path/to/gems/specifications", "/another/path/to/gems/specifications" ])
- else
- expect(Gem::SourceIndex).to receive(:from_gems_in).with("/path/to/gems/specifications", "/another/path/to/gems/specifications")
- @gem_env.gem_source_index
- end
+ @gem_env.gem_specification
+ expect(Gem::Specification.dirs).to eq([ "/path/to/gems/specifications", "/another/path/to/gems/specifications" ])
end
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)
+ else # >= rubygems 1.8 behavior
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
- allow(@gem_env).to receive(:gem_source_index).and_return(Gem.source_index)
- expect(@gem_env.gem_source_index).to receive(:search).with(rspec_dep).and_return(gems)
end
expect(@gem_env.installed_versions(Gem::Dependency.new("rspec", nil))).to eq(gems)
end
@@ -238,40 +277,40 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
end
skip("cant find your gem executable") if path_to_gem.empty?
gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem)
- expected = ["rspec-core", Gem::Version.new(RSpec::Core::Version::STRING)]
+ expected = ["rspec-core", Gem::Version.new( RspecVersionString.rspec_version_string )]
actual = gem_env.installed_versions(Gem::Dependency.new("rspec-core", nil)).map { |s| [s.name, s.version] }
expect(actual).to include(expected)
end
it "detects when the target gem environment is the jruby platform" do
- gem_env_out = <<-JRUBY_GEM_ENV
-RubyGems Environment:
- - RUBYGEMS VERSION: 1.3.6
- - RUBY VERSION: 1.8.7 (2010-05-12 patchlevel 249) [java]
- - INSTALLATION DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0
- - RUBY EXECUTABLE: /Users/you/.rvm/rubies/jruby-1.5.0/bin/jruby
- - EXECUTABLE DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0/bin
- - RUBYGEMS PLATFORMS:
- - ruby
- - universal-java-1.6
- - GEM PATHS:
- - /Users/you/.rvm/gems/jruby-1.5.0
- - /Users/you/.rvm/gems/jruby-1.5.0@global
- - GEM CONFIGURATION:
- - :update_sources => true
- - :verbose => true
- - :benchmark => false
- - :backtrace => false
- - :bulk_threshold => 1000
- - "install" => "--env-shebang"
- - "update" => "--env-shebang"
- - "gem" => "--no-rdoc --no-ri"
- - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
- - REMOTE SOURCES:
- - https://rubygems.org/
- - http://gems.github.com/
+ gem_env_out = <<~JRUBY_GEM_ENV
+ RubyGems Environment:
+ - RUBYGEMS VERSION: 1.3.6
+ - RUBY VERSION: 1.8.7 (2010-05-12 patchlevel 249) [java]
+ - INSTALLATION DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0
+ - RUBY EXECUTABLE: /Users/you/.rvm/rubies/jruby-1.5.0/bin/jruby
+ - EXECUTABLE DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0/bin
+ - RUBYGEMS PLATFORMS:
+ - ruby
+ - universal-java-1.6
+ - GEM PATHS:
+ - /Users/you/.rvm/gems/jruby-1.5.0
+ - /Users/you/.rvm/gems/jruby-1.5.0@global
+ - GEM CONFIGURATION:
+ - :update_sources => true
+ - :verbose => true
+ - :benchmark => false
+ - :backtrace => false
+ - :bulk_threshold => 1000
+ - "install" => "--env-shebang"
+ - "update" => "--env-shebang"
+ - "gem" => "--no-rdoc --no-ri"
+ - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
+ - REMOTE SOURCES:
+ - 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_compacted!).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
@@ -279,41 +318,41 @@ RubyGems Environment:
end
it "uses the cached result for gem platforms if available" do
- expect(@gem_env).not_to receive(:shell_out!)
+ expect(@gem_env).not_to receive(:shell_out_compacted!)
expected = ["ruby", Gem::Platform.new("universal-java-1.6")]
Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache["/usr/weird/bin/gem"] = expected
expect(@gem_env.gem_platforms).to eq(expected)
end
it "uses the current gem platforms when the target env is not jruby" do
- gem_env_out = <<-RBX_GEM_ENV
-RubyGems Environment:
- - RUBYGEMS VERSION: 1.3.6
- - RUBY VERSION: 1.8.7 (2010-05-14 patchlevel 174) [x86_64-apple-darwin10.3.0]
- - INSTALLATION DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
- - RUBYGEMS PREFIX: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514
- - RUBY EXECUTABLE: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514/bin/rbx
- - EXECUTABLE DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514/bin
- - RUBYGEMS PLATFORMS:
- - ruby
- - x86_64-darwin-10
- - x86_64-rubinius-1.0
- - GEM PATHS:
- - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
- - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514@global
- - GEM CONFIGURATION:
- - :update_sources => true
- - :verbose => true
- - :benchmark => false
- - :backtrace => false
- - :bulk_threshold => 1000
- - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
- - "gem" => "--no-rdoc --no-ri"
- - REMOTE SOURCES:
- - https://rubygems.org/
- - http://gems.github.com/
+ gem_env_out = <<~RBX_GEM_ENV
+ RubyGems Environment:
+ - RUBYGEMS VERSION: 1.3.6
+ - RUBY VERSION: 1.8.7 (2010-05-14 patchlevel 174) [x86_64-apple-darwin10.3.0]
+ - INSTALLATION DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
+ - RUBYGEMS PREFIX: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514
+ - RUBY EXECUTABLE: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514/bin/rbx
+ - EXECUTABLE DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514/bin
+ - RUBYGEMS PLATFORMS:
+ - ruby
+ - x86_64-darwin-10
+ - x86_64-rubinius-1.0
+ - GEM PATHS:
+ - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514
+ - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514@global
+ - GEM CONFIGURATION:
+ - :update_sources => true
+ - :verbose => true
+ - :benchmark => false
+ - :backtrace => false
+ - :bulk_threshold => 1000
+ - :sources => ["https://rubygems.org/", "http://gems.github.com/"]
+ - "gem" => "--no-rdoc --no-ri"
+ - REMOTE SOURCES:
+ - 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_compacted!).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
@@ -338,9 +377,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) { nil }
let(:new_resource) do
new_resource = Chef::Resource::GemPackage.new(gem_name)
@@ -348,10 +388,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)
@@ -368,8 +409,15 @@ describe Chef::Provider::Package::Rubygems do
before(:each) do
# We choose detect omnibus via RbConfig::CONFIG['bindir'] in Chef::Provider::Package::Rubygems.new
allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(bindir)
- # Rubygems uses this interally
+ # Rubygems uses these two interally
allow(RbConfig::CONFIG).to receive(:[]).with("arch").and_call_original
+ allow(RbConfig::CONFIG).to receive(:[]).with("ruby_install_name").and_call_original
+ allow(File).to receive(:executable?).and_return false
+ allow(File).to receive(:executable?).with("#{bindir}/gem").and_return true
+ # XXX: we can't stub the provider object directly here because referencing it will create it and that
+ # will break later tests that want to test the initialize method, so we stub any instance
+ # (yet more evidence that initialize methods should be thin and do very little work)
+ allow_any_instance_of(Chef::Provider::Package::Rubygems).to receive(:needs_nodocument?).and_return true
end
describe "when new_resource version is nil" do
@@ -379,10 +427,15 @@ 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
- let(:current_version) { RSpec::Core::Version::STRING }
+ let(:current_version) { RspecVersionString.rspec_version_string }
let(:target_version) { current_version }
it "triggers a gem configuration load so a later one will not stomp its config values" do
@@ -403,7 +456,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)
@@ -437,9 +490,10 @@ 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(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
+ 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
@@ -449,13 +503,15 @@ 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(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
+ 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
@@ -526,29 +582,68 @@ describe Chef::Provider::Package::Rubygems do
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(: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)
+ .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)
+ .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")
@@ -566,18 +661,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 << "https://rubygems.org" if provider.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: [ "https://rubygems.org" ])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -586,7 +680,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])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -615,9 +709,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: [ "https://rubygems.org" ])
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -626,8 +720,39 @@ 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}"
- expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org #{options}"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "unmockening needs_nodocument?" do
+ expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org #{options}"
+ expect(provider).to receive(:needs_nodocument?).and_call_original
+ stub_const("Gem::VERSION", "3.0.0")
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "when the rubygems_version is old it uses the old flags" do
+ expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://rubygems.org #{options}"
+ expect(provider).to receive(:needs_nodocument?).and_call_original
+ stub_const("Gem::VERSION", "2.8.0")
+ expect(provider).to receive(:shell_out_compacted!).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" do
+ Chef::Config[:rubygems_url] = "https://mirror1"
+ expect(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, "https://mirror1").and_return(version)
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -637,22 +762,93 @@ describe Chef::Provider::Package::Rubygems do
let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
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"
- expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ it "installs the gem" do
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source}"
+ expect(provider).to receive(:shell_out_compacted!).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 true" do
+ let(:include_default_source) { true }
+
+ it "ignores the Chef::Config setting" do
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source} --source=https://rubygems.org"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ 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-document -v \"#{target_version}\" --clear-sources --source=#{source}"
+ expect(provider).to receive(:shell_out_compacted!).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-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2"
+ expect(provider).to receive(:shell_out_compacted!).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 true" do
+ let(:include_default_source) { true }
+
+ it "installs the gem with rubygems as a source" do
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2 --source=https://rubygems.org"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ 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-document -v \"#{target_version}\" --clear-sources --source=https://mirror1 --source=https://mirror2"
+ expect(provider).to receive(:shell_out_compacted!).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
+ context "when clear_sources is set true and an explicit source is specified" do
let(:gem_binary) { "/foo/bar" }
let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
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}"
- expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source}"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when clear_sources is set false and an explicit source is specified" do
+ let(:gem_binary) { "/foo/bar" }
+ let(:source) { "http://mirror.ops.rhcloud.com/mirror/ruby" }
+
+ it "installs the gem" do
+ new_resource.clear_sources(false)
+ expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=#{source}"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -663,18 +859,18 @@ 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}"
- expect(provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ expected = "gem install rspec-core -q --no-document -v \"#{candidate_version}\" --source=https://rubygems.org #{options}"
+ expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
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: [ "https://rubygems.org" ] }.merge(options))
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -684,14 +880,14 @@ 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: [ "https://rubygems.org" ] )
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
end
describe "at version specified with comparison operator" do
- context "if current version satisifies requested version" do
+ context "if current version satisfies requested version" do
let(:target_version) { ">=2.3.0" }
let(:current_version) { "2.3.3" }
@@ -727,7 +923,23 @@ 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_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "unmockening needs_nodocument?" do
+ expect(provider).to receive(:needs_nodocument?).and_call_original
+ expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "3.0.0\n"))
+ expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "when the rubygems_version is old it uses the old flags" do
+ expect(provider).to receive(:needs_nodocument?).and_call_original
+ expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "2.8.0\n"))
+ expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://rubygems.org", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -735,10 +947,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_compacted!).with("#{gem_binary} install #{source} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -747,11 +958,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_compacted!).with("#{gem_binary} install #{gem_name} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900)
provider.run_action(:install)
expect(new_resource).to be_updated_by_last_action
end
@@ -782,7 +992,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:
@@ -798,7 +1008,7 @@ describe Chef::Provider::Package::Rubygems do
let(:options) { "-i /alt/install/location" }
it "uninstalls via the gem command" do
- expect(provider).to receive(:shell_out!).with("gem uninstall rspec -q -x -I -a #{options}", env: nil, timeout: 900)
+ expect(provider).to receive(:shell_out_compacted!).with("gem uninstall rspec -q -x -I -a #{options}", env: nil, timeout: 900)
provider.action_remove
end
end
@@ -817,10 +1027,160 @@ describe Chef::Provider::Package::Rubygems do
let(:gem_binary) { "/usr/weird/bin/gem" }
it "uninstalls via the gem command" do
- expect(provider).to receive(:shell_out!).with("#{gem_binary} uninstall rspec -q -x -I -a", env: nil, timeout: 900)
+ expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} uninstall rspec -q -x -I -a", env: nil, timeout: 900)
provider.action_remove
end
end
end
end
end
+
+describe Chef::Provider::Package::Rubygems, "clear_sources?" do
+ let(:new_resource) do
+ Chef::Resource::GemPackage.new("foo")
+ end
+
+ let(:provider) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ Chef::Provider::Package::Rubygems.new(new_resource, run_context)
+ end
+
+ it "is false when clear_sources is unset" do
+ expect(provider.clear_sources?).to be false
+ end
+
+ it "is false when clear_sources is set false" do
+ new_resource.clear_sources(false)
+ expect(provider.clear_sources?).to be false
+ end
+
+ it "is true when clear_sources is set true" do
+ new_resource.clear_sources(true)
+ expect(provider.clear_sources?).to be true
+ end
+
+ context "when a source is set" do
+ before do
+ new_resource.source("http://mirror.ops.rhcloud.com/mirror/ruby")
+ end
+
+ it "is true when clear_sources is unset" do
+ expect(provider.clear_sources?).to be true
+ end
+
+ it "is false when clear_sources is set false" do
+ new_resource.clear_sources(false)
+ expect(provider.clear_sources?).to be false
+ end
+
+ it "is true when clear_sources is set true" do
+ new_resource.clear_sources(true)
+ expect(provider.clear_sources?).to be true
+ end
+ end
+
+ context "when Chef::Config[:rubygems_url] is set" do
+ before do
+ Chef::Config.rubygems_url = "https://example.com/"
+ end
+
+ it "is true when clear_sources is unset" do
+ expect(provider.clear_sources?).to be true
+ end
+
+ it "is false when clear_sources is set false" do
+ new_resource.clear_sources(false)
+ expect(provider.clear_sources?).to be false
+ end
+
+ it "is true when clear_sources is set true" do
+ new_resource.clear_sources(true)
+ expect(provider.clear_sources?).to be true
+ end
+ end
+end
+
+describe Chef::Provider::Package::Rubygems, "include_default_source?" do
+ let(:new_resource) do
+ Chef::Resource::GemPackage.new("foo")
+ end
+
+ let(:provider) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ Chef::Provider::Package::Rubygems.new(new_resource, run_context)
+ end
+
+ it "is true when include_default_source is unset" do
+ expect(provider.include_default_source?).to be true
+ end
+
+ it "is false when include_default_source is set false" do
+ new_resource.include_default_source(false)
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is true when include_default_source is set true" do
+ new_resource.include_default_source(true)
+ expect(provider.include_default_source?).to be true
+ end
+
+ context "when a source is set" do
+ before do
+ new_resource.source("http://mirror.ops.rhcloud.com/mirror/ruby")
+ end
+
+ it "is false when include_default_source is unset" do
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is false when include_default_source is set false" do
+ new_resource.include_default_source(false)
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is true when include_default_source is set true" do
+ new_resource.include_default_source(true)
+ expect(provider.include_default_source?).to be true
+ end
+ end
+
+ context "when Chef::Config[:rubygems_url] is set" do
+ before do
+ Chef::Config.rubygems_url = "https://example.com/"
+ end
+
+ it "is true when include_default_source is unset" do
+ expect(provider.include_default_source?).to be true
+ end
+
+ it "is false when include_default_source is set false" do
+ new_resource.include_default_source(false)
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is true when include_default_source is set true" do
+ new_resource.include_default_source(true)
+ expect(provider.include_default_source?).to be true
+ end
+ end
+
+ context "when clear_sources is set" do
+ before do
+ new_resource.clear_sources(true)
+ end
+
+ it "is false when include_default_source is unset" do
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is false when include_default_source is set false" do
+ new_resource.include_default_source(false)
+ expect(provider.include_default_source?).to be false
+ end
+
+ it "is true when include_default_source is set true" do
+ new_resource.include_default_source(true)
+ expect(provider.include_default_source?).to be true
+ end
+ end
+end
diff --git a/spec/unit/provider/package/smartos_spec.rb b/spec/unit/provider/package/smartos_spec.rb
index 51bffa17b6..5d815a579e 100644
--- a/spec/unit/provider/package/smartos_spec.rb
+++ b/spec/unit/provider/package/smartos_spec.rb
@@ -17,7 +17,7 @@
# limitations under the License.
#
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
+require "spec_helper"
require "ostruct"
describe Chef::Provider::Package::SmartOS, "load_current_resource" do
@@ -25,42 +25,42 @@ describe Chef::Provider::Package::SmartOS, "load_current_resource" do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new("varnish")
+ @new_resource = Chef::Resource::Package.new("varnish")
@current_resource = Chef::Resource::Package.new("varnish")
- @status = double("Status", :exitstatus => 0)
+ @status = double("Status", exitstatus: 0)
@provider = Chef::Provider::Package::SmartOS.new(@new_resource, @run_context)
allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
@stdin = StringIO.new
@stdout = "varnish-2.1.5nb2\n"
@stderr = StringIO.new
@pid = 10
- @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
+ @shell_out = OpenStruct.new(stdout: @stdout, stdin: @stdin, stderr: @stderr, status: @status, exitstatus: 0)
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!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should set the current resource package name" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
expect(@current_resource).to receive(:package_name).with(@new_resource.package_name)
@provider.load_current_resource
end
it "should set the installed version if it is installed" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(@shell_out)
@provider.load_current_resource
expect(@current_resource.version).to eq("2.1.5nb2")
end
it "should set the installed version to nil if it's not installed" do
- out = OpenStruct.new(:stdout => nil)
- expect(@provider).to receive(:shell_out!).and_return(out)
+ out = OpenStruct.new(stdout: nil)
+ expect(@provider).to receive(:shell_out_compacted!).and_return(out)
@provider.load_current_resource
expect(@current_resource.version).to eq(nil)
end
@@ -70,27 +70,27 @@ describe Chef::Provider::Package::SmartOS, "load_current_resource" do
describe "candidate_version" do
it "should return the candidate_version variable if already setup" do
@provider.candidate_version = "2.1.1"
- expect(@provider).not_to receive(:shell_out!)
+ expect(@provider).not_to receive(:shell_out_compacted!)
@provider.candidate_version
end
it "should lookup the candidate_version if the variable is not already set (pkgin separated by spaces)" do
- search = double()
- expect(search).to receive(:each_line).
- and_yield("something-varnish-1.1.1 something varnish like\n").
- and_yield("varnish-2.3.4 actual varnish\n")
- @shell_out = double("shell_out!", :stdout => search)
- expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin", "se", "varnish", :env => nil, :returns => [0, 1], :timeout => 900).and_return(@shell_out)
+ search = double
+ expect(search).to receive(:each_line)
+ .and_yield("something-varnish-1.1.1 something varnish like\n")
+ .and_yield("varnish-2.3.4 actual varnish\n")
+ @shell_out = double("shell_out!", stdout: search)
+ expect(@provider).to receive(:shell_out_compacted!).with("/opt/local/bin/pkgin", "se", "varnish", env: nil, returns: [0, 1], timeout: 900).and_return(@shell_out)
expect(@provider.candidate_version).to eq("2.3.4")
end
it "should lookup the candidate_version if the variable is not already set (pkgin separated by semicolons)" do
- search = double()
- expect(search).to receive(:each_line).
- and_yield("something-varnish-1.1.1;;something varnish like\n").
- and_yield("varnish-2.3.4;;actual varnish\n")
- @shell_out = double("shell_out!", :stdout => search)
- expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin", "se", "varnish", :env => nil, :returns => [0, 1], :timeout => 900).and_return(@shell_out)
+ search = double
+ expect(search).to receive(:each_line)
+ .and_yield("something-varnish-1.1.1;;something varnish like\n")
+ .and_yield("varnish-2.3.4;;actual varnish\n")
+ @shell_out = double("shell_out!", stdout: search)
+ expect(@provider).to receive(:shell_out_compacted!).with("/opt/local/bin/pkgin", "se", "varnish", env: nil, returns: [0, 1], timeout: 900).and_return(@shell_out)
expect(@provider.candidate_version).to eq("2.3.4")
end
end
@@ -98,9 +98,9 @@ describe Chef::Provider::Package::SmartOS, "load_current_resource" do
describe "when manipulating a resource" do
it "run pkgin and install the package" do
- out = OpenStruct.new(:stdout => nil)
- expect(@provider).to receive(:shell_out!).with("/opt/local/sbin/pkg_info", "-E", "varnish*", { :env => nil, :returns => [0, 1], :timeout => 900 }).and_return(@shell_out)
- expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin", "-y", "install", "varnish-2.1.5nb2", { :env => nil, :timeout => 900 }).and_return(out)
+ out = OpenStruct.new(stdout: nil)
+ expect(@provider).to receive(:shell_out_compacted!).with("/opt/local/sbin/pkg_info", "-E", "varnish*", { env: nil, returns: [0, 1], timeout: 900 }).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out_compacted!).with("/opt/local/bin/pkgin", "-y", "install", "varnish-2.1.5nb2", { env: nil, timeout: 900 }).and_return(out)
@provider.load_current_resource
@provider.install_package("varnish", "2.1.5nb2")
end
diff --git a/spec/unit/provider/package/snap_spec.rb b/spec/unit/provider/package/snap_spec.rb
new file mode 100644
index 0000000000..7968f0c699
--- /dev/null
+++ b/spec/unit/provider/package/snap_spec.rb
@@ -0,0 +1,208 @@
+# Author:: S.Cavallo (smcavallo@hotmail.com)
+# Copyright:: Copyright (c) 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"
+require "chef/provider/package/snap"
+require "json"
+
+describe Chef::Provider::Package::Snap do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:package) { "hello" }
+ let(:source) { "/tmp/hello_20.snap" }
+ let(:new_resource) do
+ new_resource = Chef::Resource::SnapPackage.new(package)
+ new_resource.source source
+ new_resource
+ end
+ let(:provider) { Chef::Provider::Package::Snap.new(new_resource, run_context) }
+ let(:snap_status) do
+ stdout = <<~SNAP_S
+ path: "/tmp/hello_20.snap"
+ name: hello
+ summary: GNU Hello, the "hello world" snap
+ version: 2.10 -
+ SNAP_S
+ status = double(stdout: stdout, stderr: "", exitstatus: 0)
+ allow(status).to receive(:error!).with(no_args).and_return(false)
+ status
+ end
+
+ before(:each) do
+ allow(provider).to receive(:shell_out_compacted!).with("snap", "info", source, timeout: 900).and_return(snap_status)
+ end
+
+ # Example output from https://github.com/snapcore/snapd/wiki/REST-API
+ find_result_success = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "find_result_success.json")))
+ find_result_fail = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "find_result_failure.json")))
+ get_by_name_result_success = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "get_by_name_result_success.json")))
+ get_by_name_result_fail = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "get_by_name_result_failure.json")))
+ async_result_success = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "async_result_success.json")))
+ result_fail = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "result_failure.json")))
+ change_id_result = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "change_id_result.json")))
+ get_conf_success = JSON.parse(File.read(File.join(CHEF_SPEC_DATA, "snap_package", "get_conf_success.json")))
+
+ describe "#define_resource_requirements" do
+
+ before do
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/snaps/#{package}").and_return(get_by_name_result_success)
+ end
+
+ it "should raise an exception if a source is supplied but not found when :install" do
+ allow(::File).to receive(:exist?).with(source).and_return(false)
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "should raise an exception if a source is supplied but not found when :upgrade" do
+ allow(::File).to receive(:exist?).with(source).and_return(false)
+ expect { provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package)
+ end
+ end
+
+ describe "when using a local file source" do
+ let(:source) { "/tmp/hello_20.snap" }
+
+ before do
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/snaps/#{package}").and_return(get_by_name_result_success)
+ end
+
+ it "should create a current resource with the name of the new_resource" do
+ provider.load_current_resource
+ expect(provider.current_resource.package_name).to eq("hello")
+ end
+
+ describe "gets the candidate version from the source package" do
+
+ def check_version(version)
+ provider.load_current_resource
+ expect(provider.current_resource.package_name).to eq("hello")
+ expect(provider.get_current_versions).to eq(["1.15.71"])
+ expect(provider.candidate_version).to eq([version])
+ end
+
+ it "checks the installed and local candidate versions" do
+ check_version("2.10")
+ end
+
+ it "generates multipart form data" do
+ expected = <<~SNAP_S
+ Host:
+ Content-Type: multipart/form-data; boundary=foo
+ Content-Length: 20480
+
+ --foo
+ Content-Disposition: form-data; name="action"
+
+ install
+ --foo
+ Content-Disposition: form-data; name="devmode"
+
+ true
+ --foo
+ Content-Disposition: form-data; name="snap"; filename="hello-world_27.snap"
+
+ <20480 bytes of snap file data>
+ --foo
+ SNAP_S
+
+ options = {}
+ options["devmode"] = true
+ path = "hello-world_27.snap"
+ content_length = "20480"
+
+ result = provider.send(:generate_multipart_form_data, "foo", "install", options, path, content_length)
+
+ expect(result).to eq(expected)
+
+ end
+
+ end
+ end
+
+ describe "when using the snap store" do
+ let(:source) { nil }
+ describe "gets the candidate version from the snap store" do
+ before do
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/find?name=#{package}").and_return(find_result_success)
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/snaps/#{package}").and_return(get_by_name_result_success)
+ end
+
+ def check_version(version)
+ provider.load_current_resource
+ expect(provider.current_resource.package_name).to eq("hello")
+ expect(provider.get_current_versions).to eq(["1.15.71"])
+ expect(provider.candidate_version).to eq([version])
+ end
+
+ it "checks the installed and store candidate versions" do
+ check_version("2.10")
+ end
+
+ end
+
+ describe "fails to get the candidate version from the snap store" do
+ before do
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/find?name=#{package}").and_return(find_result_fail)
+ allow_any_instance_of(Chef::Provider::Package::Snap).to receive(:call_snap_api).with("GET", "/v2/snaps/#{package}").and_return(get_by_name_result_fail)
+ end
+
+ it "throws an error if candidate version not found" do
+ provider.load_current_resource
+ expect { provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "does not throw an error if installed version not found" do
+ provider.load_current_resource
+ expect(provider.get_current_versions).to eq([])
+ end
+ end
+ end
+
+ describe "when calling async operations" do
+
+ it "should should throw if the async response is an error" do
+ expect { provider.send(:get_id_from_async_response, result_fail) }.to raise_error(RuntimeError)
+ end
+
+ it "should get the id from an async response" do
+ result = provider.send(:get_id_from_async_response, async_result_success)
+ expect(result).to eq("401")
+ end
+
+ it "should wait for change completion" do
+ result = provider.send(:get_id_from_async_response, async_result_success)
+ expect(result).to eq("401")
+ end
+ end
+
+ describe Chef::Provider::Package::Snap do
+
+ it "should post the correct json" do
+ snap_names = ["hello"]
+ action = "install"
+ channel = "stable"
+ options = {}
+ revision = nil
+ actual = provider.send(:generate_snap_json, snap_names, action, channel, options, revision)
+
+ expect(actual).to eq("action" => "install", "snaps" => ["hello"], "channel" => "stable")
+ end
+
+ end
+end
diff --git a/spec/unit/provider/package/solaris_spec.rb b/spec/unit/provider/package/solaris_spec.rb
index 9cc8deeb2a..c07367c221 100644
--- a/spec/unit/provider/package/solaris_spec.rb
+++ b/spec/unit/provider/package/solaris_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,52 +27,52 @@ 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
before do
- @pkginfo = <<-PKGINFO
-PKGINST: SUNWbash
-NAME: GNU Bourne-Again shell (bash)
-CATEGORY: system
-ARCH: sparc
-VERSION: 11.10.0,REV=2005.01.08.05.16
-BASEDIR: /
-VENDOR: Sun Microsystems, Inc.
-DESC: GNU Bourne-Again shell (bash) version 3.0
-PSTAMP: sfw10-patch20070430084444
-INSTDATE: Nov 04 2009 01:02
-HOTLINE: Please contact your local service provider
-PKGINFO
-
- @status = double("Status", :stdout => "", :exitstatus => 0)
+ @pkginfo = <<~PKGINFO
+ PKGINST: SUNWbash
+ NAME: GNU Bourne-Again shell (bash)
+ CATEGORY: system
+ ARCH: sparc
+ VERSION: 11.10.0,REV=2005.01.08.05.16
+ BASEDIR: /
+ VENDOR: Sun Microsystems, Inc.
+ DESC: GNU Bourne-Again shell (bash) version 3.0
+ PSTAMP: sfw10-patch20070430084444
+ INSTDATE: Nov 04 2009 01:02
+ HOTLINE: Please contact your local service provider
+ PKGINFO
+
+ @status = double("Status", stdout: "", exitstatus: 0)
end
it "should create a current resource with the name of new_resource" do
- allow(@provider).to receive(:shell_out).and_return(@status)
+ allow(@provider).to receive(:shell_out_compacted).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.name).to eq("SUNWbash")
end
- it "should set the current reource package name to the new resource package name" do
- allow(@provider).to receive(:shell_out).and_return(@status)
+ it "should set the current resource package name to the new resource package name" do
+ allow(@provider).to receive(:shell_out_compacted).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("SUNWbash")
end
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(@provider).to receive(:shell_out_compacted).and_return(@status)
+ 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)
end
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)
+ status = double(stdout: @pkginfo, exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted).with("pkginfo", "-l", "SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("SUNWbash")
@@ -80,9 +80,9 @@ PKGINFO
end
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)
+ status = double(stdout: @pkginfo, exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out_compacted).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
@@ -90,19 +90,19 @@ PKGINFO
it "should raise an exception if the source is not set but we are installing" do
@new_resource = Chef::Resource::Package.new("SUNWbash")
@provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context)
- allow(@provider).to receive(:shell_out).and_return(@status)
+ allow(@provider).to receive(:shell_out_compacted).and_return(@status)
expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
it "should raise an exception if pkginfo fails to run" do
- status = double(:stdout => "", :exitstatus => -1)
- allow(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: "", exitstatus: -1)
+ allow(@provider).to receive(:shell_out_compacted).and_return(status)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
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_compacted).with("pkginfo", "-l", "-d", "/tmp/bash.pkg", "SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out_compacted).with("pkginfo", "-l", "SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.version).to be_nil
end
@@ -111,20 +111,20 @@ PKGINFO
describe "candidate_version" do
it "should return the candidate_version variable if already setup" do
@provider.candidate_version = "11.10.0,REV=2005.01.08.05.16"
- expect(@provider).not_to receive(:shell_out)
+ expect(@provider).not_to receive(:shell_out_compacted)
@provider.candidate_version
end
it "should lookup the candidate_version if the variable is not already set" do
- status = double(:stdout => "", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(status)
- expect(@provider).to receive(:shell_out)
+ status = double(stdout: "", exitstatus: 0)
+ allow(@provider).to receive(:shell_out_compacted).and_return(status)
+ expect(@provider).to receive(:shell_out_compacted)
@provider.candidate_version
end
it "should throw and exception if the exitstatus is not 0" do
- status = double(:stdout => "", :exitstatus => 1)
- allow(@provider).to receive(:shell_out).and_return(status)
+ status = double(stdout: "", exitstatus: 1)
+ allow(@provider).to receive(:shell_out_compacted).and_return(status)
expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package)
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_compacted!).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_compacted!).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_compacted!).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_compacted!).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_compacted!).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 f18cbf3dca..d5387eaf9e 100644
--- a/spec/unit/provider/package/windows/exe_spec.rb
+++ b/spec/unit/provider/package/windows/exe_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock <matt@mattwrock.com>
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
require "chef/provider/package/windows/exe"
-unless Chef::Platform.windows?
+unless ChefUtils.windows?
class Chef
module ReservedNames::Win32
class File
@@ -99,7 +99,7 @@ describe Chef::Provider::Package::Windows::Exe do
end
end
- it "returns the version attribute if given" do
+ it "returns the version property if given" do
new_resource.version("v55555")
expect(provider.package_version).to eql("v55555")
end
@@ -117,7 +117,7 @@ describe Chef::Provider::Package::Windows::Exe do
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))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"uninst_dir/uninst_file\" /S /NCRC & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.remove_package
end
end
@@ -126,7 +126,7 @@ describe Chef::Provider::Package::Windows::Exe 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])
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"uninst_dir/uninst_file\" /S /NCRC & exit %%%%ERRORLEVEL%%%%}, default_env: false, timeout: 300, returns: [0, 3010])
provider.remove_package
end
end
@@ -148,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 \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{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 \"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))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"uninst_dir1/uninst_file1\" /S /NCRC & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"uninst_dir2/uninst_file2\" /S /NCRC & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.remove_package
end
end
@@ -167,7 +167,7 @@ describe Chef::Provider::Package::Windows::Exe do
let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :nsis, uninstall_entry) }
it "calls installer with the correct flags" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"#{Regexp.quote(new_resource.source)}\" /S /NCRC & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.install_package
end
end
@@ -176,7 +176,7 @@ describe Chef::Provider::Package::Windows::Exe do
let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :installshield, uninstall_entry) }
it "calls installer with the correct flags" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s \/sms & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"#{Regexp.quote(new_resource.source)}\" /s /sms & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.install_package
end
end
@@ -185,7 +185,7 @@ describe Chef::Provider::Package::Windows::Exe do
let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :inno, uninstall_entry) }
it "calls installer with the correct flags" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/VERYSILENT \/SUPPRESSMSGBOXES \/NORESTART & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"#{Regexp.quote(new_resource.source)}\" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.install_package
end
end
@@ -194,7 +194,7 @@ describe Chef::Provider::Package::Windows::Exe do
let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :wise, uninstall_entry) }
it "calls installer with the correct flags" do
- expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{start \"\" /wait \"#{Regexp.quote(new_resource.source)}\" /s & exit %%%%ERRORLEVEL%%%%}, kind_of(Hash))
provider.install_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..f756793857 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ require "chef/provider/package/windows/msi"
describe Chef::Provider::Package::Windows::MSI 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(:run_context) { double("Chef::RunContext", node: node, events: events) }
let(:package_name) { "calculator" }
let(:resource_source) { "calculator.msi" }
let(:resource_version) { nil }
@@ -104,14 +104,14 @@ describe Chef::Provider::Package::Windows::MSI do
describe "install_package" do
it "calls msiexec /qn /i" do
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{msiexec /qn /i \"#{Regexp.quote(new_resource.source)}\"}, kind_of(Hash))
provider.install_package
end
end
describe "remove_package" do
it "calls msiexec /qn /x" do
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{msiexec /qn /x \"#{Regexp.quote(new_resource.source)}\"}, kind_of(Hash))
provider.remove_package
end
@@ -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(%r{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(%r{msiexec /x {guid} /q}, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(%r{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(%r{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(%r{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..9ce1151285
--- /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..6531d9c5ec 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,13 +22,13 @@ require "chef/provider/package/windows/msi"
describe Chef::Provider::Package::Windows, :windows_only do
before(:each) do
- allow(Chef::Util::PathHelper).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
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 }
@@ -42,7 +42,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
let(:cache_path) { 'c:\\cache\\' }
before(:each) do
- allow(::File).to receive(:exist?).with(provider.new_resource.source).and_return(true)
+ allow(::File).to receive(:exist?).with(new_resource.source).and_return(true)
end
describe "load_current_resource" do
@@ -50,7 +50,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
before(:each) do
allow(Chef::Util::PathHelper).to receive(:validate_path)
allow(provider).to receive(:package_provider).and_return(double("package_provider",
- :installed_version => "1.0", :package_version => "2.0"))
+ installed_version: "1.0", package_version: "2.0"))
end
it "creates a current resource with the name of the new resource" do
@@ -66,20 +66,22 @@ describe Chef::Provider::Package::Windows, :windows_only do
it "sets the version to be installed" do
provider.load_current_resource
- expect(provider.new_resource.version).to eql("2.0")
+ expect(new_resource.version).to eql("2.0")
end
end
- context "when the source is a uri" do
- let(:resource_source) { "https://foo.bar/calculator.msi" }
-
- context "when the source has not been downloaded" do
+ context "when the source is not present it loads from cache" do
+ context "when the package is not installed" do
before(:each) do
- allow(provider).to receive(:downloadable_file_missing?).and_return(true)
+ allow(provider).to receive(:uri_scheme?).and_return(false)
+ allow(provider.package_provider).to receive(:get_product_property).and_return(nil)
+ allow(provider.package_provider).to receive(:get_installed_version).and_return(nil)
+ allow(provider.package_provider).to receive(:package_version).and_return(nil)
end
- it "sets the current version to unknown" do
+
+ it "sets the current version nil" do
provider.load_current_resource
- expect(provider.current_resource.version).to eql("unknown")
+ expect(provider.current_resource.version).to eql(nil)
end
end
@@ -224,7 +226,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
@@ -233,9 +235,9 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
end
- it "returns @installer_type if it is set" do
- provider.new_resource.installer_type(:downeaster)
- expect(provider.installer_type).to eql(:downeaster)
+ it "returns the resource's installer_type if it is set" do
+ new_resource.installer_type(:nsis)
+ expect(provider.installer_type).to eql(:nsis)
end
it "sets installer_type to inno if the source contains inno" do
@@ -275,7 +277,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
it "raises an error" do
allow(::Kernel).to receive(:open).and_yield(StringIO.new(""))
- provider.new_resource.installer_type(nil)
+ new_resource.installer_type(nil)
expect { provider.installer_type }.to raise_error(Chef::Exceptions::CannotDetermineWindowsInstallerType)
end
end
@@ -312,6 +314,10 @@ describe Chef::Provider::Package::Windows, :windows_only do
let(:resource_source) { "https://foo.bar/calculator.exe" }
it "downloads the http resource" do
+ allow(provider).to receive(:uri_scheme?).and_return(true)
+ allow(provider).to receive(:installer_type).and_return(nil)
+ allow(File).to receive(:exist?).with("https\\foo.bar\\calculator.exe").and_return(false)
+ allow(provider).to receive(:compile_and_converge_action)
expect(provider).to receive(:download_source_file)
provider.run_action(:install)
end
@@ -359,6 +365,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
before do
new_resource.version("5.5.5")
allow(provider).to receive(:current_version_array).and_return([ ["5.5.0", "4.3.0", "1.1.1"] ])
+ allow(provider).to receive(:version_compare).and_return(false)
end
it "installs given version" do
@@ -393,5 +400,42 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
end
end
+
+ context "a missing local file is given" do
+ let(:resource_source) { "C:/a_missing_file.exe" }
+ let(:installer_type) { nil }
+ before do
+ allow(::File).to receive(:exist?).with(new_resource.source).and_return(false)
+ provider.load_current_resource
+ end
+
+ it "raises a Package error" do
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "why_run mode doesn't raise an error" do
+ Chef::Config[:why_run] = true
+ expect { provider.run_action(:install) }.not_to raise_error
+ Chef::Config[:why_run] = false
+ end
+ end
+
+ it "does not raise an error with a valid checksum" do
+ expect(Chef::Digester).to receive(:checksum_for_file).with(new_resource.source).and_return("abcdef1234567890")
+ expect(provider).to receive(:install_package)
+
+ new_resource.checksum("abcdef1234567890")
+
+ provider.run_action(:install)
+ end
+
+ it "raises an error with an invalid checksum" do
+ expect(Chef::Digester).to receive(:checksum_for_file).with(new_resource.source).and_return("abcdef1234567890")
+ expect(provider).not_to receive(:install_package)
+
+ new_resource.checksum("ffffffffffffffff")
+
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ 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..39c067d135
--- /dev/null
+++ b/spec/unit/provider/package/yum/python_helper_spec.rb
@@ -0,0 +1,29 @@
+#
+# Copyright:: Copyright (c) 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
index e9d615d734..312cae7ac7 100644
--- a/spec/unit/provider/package/yum/yum_cache_spec.rb
+++ b/spec/unit/provider/package/yum/yum_cache_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +18,92 @@
require "spec_helper"
describe Chef::Provider::Package::Yum::YumCache do
+ let(:yum_cache) { Chef::Provider::Package::Yum::YumCache.instance }
- it "can find yum-dump.py" do
- expect(File.exist?(Chef::Provider::Package::Yum::YumCache.instance.yum_dump_path)).to be true
+ 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
+
+ %i{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 e9aec933e2..0000000000
--- a/spec/unit/provider/package/yum_spec.rb
+++ /dev/null
@@ -1,2268 +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) do
- 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
- )
- end
-
- 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 do
- Chef::Provider::Package::Yum::RPMVersion.new()
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5", "extra")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5", "extra")
- end.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)
- expect(@rpm.provides[0].name).to eql("testing")
- expect(@rpm.provides[0].version.evr).to eql("1:1.6.5-9.36.el5")
- expect(@rpm.provides[0].flag).to eql(:==)
- 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)
- expect(@rpm.provides[0].name).to eql("testing")
- expect(@rpm.provides[0].version.evr).to eql("1:1.6.5-9.36.el5")
- expect(@rpm.provides[0].flag).to eql(:==)
- end
- end
-
- it "should raise an error unless passed 4 or 6 args" do
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new()
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [], "extra")
- end.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 do
- Chef::Provider::Package::Yum::RPMDependency.new()
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==, "extra")
- end.to raise_error(ArgumentError)
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
- end.not_to raise_error
- expect do
- Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==, "extra")
- end.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 do
- @rpmprovide.satisfy?("hi")
- end.to raise_error(ArgumentError)
- expect do
- @rpmprovide.satisfy?(@rpmrequire)
- end.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 do
- @rpmdb.whatprovides("hi")
- end.to raise_error(ArgumentError)
- expect do
- @rpmdb.whatprovides(@rpmrequire)
- end.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) do
- StringIO.new("#!/usr/bin/python\n\naldsjfa\ldsajflkdsjf\lajsdfj")
- end
-
- let(:bin_exe) do
- StringIO.new(SecureRandom.random_bytes)
- end
-
- 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 8838c26b71..a1f6629f55 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,22 +30,22 @@ describe Chef::Provider::Package::Zypper do
Chef::Provider::Package::Zypper.new(new_resource, run_context)
end
- let(:status) { double(:stdout => "\n", :exitstatus => 0) }
+ let(:status) { double(stdout: "\n", exitstatus: 0) }
before(:each) do
allow(Chef::Resource::Package).to receive(:new).and_return(current_resource)
- allow(provider).to receive(:shell_out!).and_return(status)
+ allow(provider).to receive(:shell_out_compacted!).and_return(status)
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_compacted).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_compacted!).with(*command, **options)
end
describe "when loading the current package state" do
@@ -61,47 +61,39 @@ 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
it "should set the installed version to nil on the current resource if zypper info installed version is (none)" do
- allow(provider).to receive(:shell_out).and_return(status)
+ allow(provider).to receive(:shell_out_compacted).and_return(status)
expect(current_resource).to receive(:version).with([nil]).and_return(true)
provider.load_current_resource
end
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)
+ status = double(stdout: "Version: 1.0\nInstalled: Yes\n", exitstatus: 0)
- allow(provider).to receive(:shell_out!).and_return(status)
+ allow(provider).to receive(:shell_out_compacted!).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.0)" do
- status = double(:stdout => "Version : 1.0 \nInstalled : Yes \n", :exitstatus => 0)
+ status = double(stdout: "Version : 1.0 \nInstalled : Yes \n", exitstatus: 0)
- allow(provider).to receive(:shell_out!).and_return(status)
+ allow(provider).to receive(:shell_out_compacted!).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)
+ 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)
- provider.load_current_resource
- 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)
+ allow(provider).to receive(:shell_out_compacted!).and_return(status)
+ expect(current_resource).to receive(:version).with(["1.0"]).and_return(true)
provider.load_current_resource
- expect(provider.candidate_version).to eql(["1.0"])
end
it "should return the current resouce" do
@@ -111,25 +103,63 @@ 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", "--oldpackage", "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 "should run zypper install with gpg checks" do
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "--oldpackage", "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 disable gpg checks" do
+ new_resource.gpg_check false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "--no-gpg-checks", "install", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
)
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ 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", "--oldpackage", "emacs=1.0"
+ )
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "setting the property should disallow downgrade" do
+ new_resource.allow_downgrade false
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "install", "--auto-agree-with-licenses", "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", "install", "--user-provided", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
+ )
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--user-provided", "--non-interactive", "install", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
+ )
+ provider.install_package(["emacs"], ["1.0"])
+ end
+
+ it "should add multiple user provided global options" do
+ new_resource.global_options "--user-provided1 --user-provided2"
+ shell_out_expectation!(
+ "zypper", "--user-provided1", "--user-provided2", "--non-interactive", "install", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
)
provider.install_package(["emacs"], ["1.0"])
end
@@ -137,31 +167,36 @@ 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", "--oldpackage", "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", "--oldpackage", "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", "--oldpackage", "emacs=1.0"
)
+ provider.upgrade_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", "--oldpackage", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
- it "should run zypper upgrade without gpg checks" do
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks install " + "--auto-agree-with-licenses emacs=1.0"
+ "zypper", "--user-provided", "--non-interactive", "install", "--auto-agree-with-licenses", "--oldpackage", "emacs=1.0"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
@@ -171,9 +206,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
@@ -181,25 +215,36 @@ 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"
+ )
+ provider.remove_package(["emacs"], ["1.0"])
+ end
+ 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"
+ "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 add user provided options to the command" do
+ new_resource.options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "remove", "--user-provided", "emacs=1.0"
)
+ provider.remove_package(["emacs"], ["1.0"])
+ end
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0"
+ "zypper", "--user-provided", "--non-interactive", "remove", "emacs=1.0"
)
provider.remove_package(["emacs"], ["1.0"])
end
@@ -209,28 +254,163 @@ 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"
+ )
+ provider.purge_package(["emacs"], ["1.0"])
+ end
+ 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"
+ "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 add user provided options to the command" do
+ new_resource.options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--non-interactive", "remove", "--user-provided", "--clean-deps", "emacs=1.0"
)
+ provider.purge_package(["emacs"], ["1.0"])
+ end
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
shell_out_expectation!(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ "zypper", "--user-provided", "--non-interactive", "remove", "--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
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name, timeout: 900
+ ).and_return(status)
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "locks", timeout: 900
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | somethingelse | package | (any)"
+ ))
+ expect(provider).to receive(:lock_package).with(["cups"], [nil])
+
+ provider.load_current_resource
+ provider.action_lock
+ end
+
+ it "should not lock if the package is already locked" do
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name, timeout: 900
+ ).and_return(status)
+ expect(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "locks", timeout: 900
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | cups | package | (any)"
+ ))
+ expect(provider).to_not receive(:lock_package)
+
+ provider.load_current_resource
+ provider.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
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--user-provided", "--non-interactive", "addlock", "emacs"
+ )
+ provider.lock_package(["emacs"], [nil])
+ end
+ end
+
+ describe "action_unlock" do
+ it "should unlock if the package is not already unlocked" do
+ allow(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name, timeout: 900
+ ).and_return(status)
+ allow(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "locks", timeout: 900
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | cups | package | (any)"
+ ))
+ expect(provider).to receive(:unlock_package).with(["cups"], [nil])
+
+ provider.load_current_resource
+ provider.action_unlock
+ end
+ it "should not unlock if the package is already unlocked" do
+ allow(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "--non-interactive", "info", new_resource.package_name, timeout: 900
+ ).and_return(status)
+ allow(provider).to receive(:shell_out_compacted!).with(
+ "zypper", "locks", timeout: 900
+ ).and_return(instance_double(
+ Mixlib::ShellOut, stdout: "1 | somethingelse | package | (any)"
+ ))
+ expect(provider).to_not receive(:unlock_package)
+
+ provider.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
+ it "should add user provided global options" do
+ new_resource.global_options "--user-provided"
+ shell_out_expectation!(
+ "zypper", "--user-provided", "--non-interactive", "removelock", "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")
@@ -239,7 +419,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", "--oldpackage", "-y", "emacs"
)
provider.install_package(["emacs"], ["1.0"])
end
@@ -248,7 +428,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", "--oldpackage", "-y", "emacs"
)
provider.upgrade_package(["emacs"], ["1.0"])
end
@@ -257,7 +437,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
@@ -266,17 +446,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", "--oldpackage", "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 40b7516b5c..a6e1da188c 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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
@@ -54,27 +61,9 @@ describe Chef::Provider::Package do
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
- 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,
- provider.candidate_version
- ).and_return("/var/cache/preseed-test")
-
- expect(provider).to receive(:preseed_package).with(
- "/var/cache/preseed-test"
- ).and_return(true)
- provider.run_action(:install)
- end
-
- it "should not call preseed_package if a response_file is not given" do
- expect(provider).not_to receive(:preseed_package)
- provider.run_action(:install)
- end
-
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 +73,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 +84,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 +132,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 +153,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(allow_downgrade) emacs to 1.0")
provider.run_action(:upgrade)
expect(new_resource).to be_updated_by_last_action
end
@@ -274,51 +263,74 @@ describe Chef::Provider::Package do
end
- describe "when reconfiguring the package" do
+ describe "When locking the package" do
before(:each) do
- allow(provider).to receive(:reconfig_package).and_return(true)
- end
-
- it "should info log, reconfigure the package and update the resource" do
- allow(current_resource).to receive(:version).and_return("1.0")
- allow(new_resource).to receive(:response_file).and_return(true)
- 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(provider).to receive(:reconfig_package)
- provider.run_action(:reconfig)
- expect(new_resource).to be_updated
- expect(new_resource).to be_updated_by_last_action
+ allow(provider).to receive(:lock_package).with(
+ new_resource.package_name,
+ nil
+ ).and_return(true)
end
- 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(provider).not_to receive(:reconfig_package)
- provider.run_action(:reconfig)
- expect(new_resource).not_to be_updated_by_last_action
+ 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 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(provider).not_to receive(:reconfig_package)
- provider.run_action(:reconfig)
+ 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 debug log and not reconfigure the package if the response_file has not changed" do
- allow(current_resource).to receive(:version).and_return("1.0")
- 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(provider).not_to receive(:reconfig_package)
- provider.run_action(:reconfig)
+ 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
@@ -344,93 +356,16 @@ describe Chef::Provider::Package do
end
it "should raise UnsupportedAction for reconfig" do
- expect { provider.reconfig_package("emacs", "1.4.2") }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ expect { provider.reconfig_package("emacs") }.to raise_error(Chef::Exceptions::UnsupportedAction)
end
- end
-
- describe "when given a response file" do
- let(:cookbook_repo) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) }
- let(:cookbook_loader) do
- Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo)
- Chef::CookbookLoader.new(cookbook_repo)
- end
- let(:cookbook_collection) do
- cookbook_loader.load_cookbooks
- Chef::CookbookCollection.new(cookbook_loader)
- end
- let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
- let(:new_resource) do
- new_resource = Chef::Resource::Package.new("emacs")
- new_resource.response_file("java.response")
- new_resource.cookbook_name = "java"
- new_resource
- end
-
- describe "creating the cookbook file resource to fetch the response file" do
- before do
- expect(Chef::FileCache).to receive(:create_cache_path).with("preseed/java").and_return("/tmp/preseed/java")
- end
-
- it "sets the preseed resource's runcontext to its own run context" do
- allow(Chef::FileCache).to receive(:create_cache_path).and_return("/tmp/preseed/java")
- expect(provider.preseed_resource("java", "6").run_context).not_to be_nil
- expect(provider.preseed_resource("java", "6").run_context).to equal(provider.run_context)
- end
-
- it "should set the cookbook name of the remote file to the new resources cookbook name" do
- expect(provider.preseed_resource("java", "6").cookbook_name).to eq("java")
- end
-
- it "should set remote files source to the new resources response file" do
- expect(provider.preseed_resource("java", "6").source).to eq("java.response")
- end
-
- it "should never back up the cached response file" do
- expect(provider.preseed_resource("java", "6").backup).to be_falsey
- end
-
- it "sets the install path of the resource to $file_cache/$cookbook/$pkg_name-$pkg_version.seed" do
- expect(provider.preseed_resource("java", "6").path).to eq("/tmp/preseed/java/java-6.seed")
- end
- end
-
- 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) 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)
- end
-
- after do
- FileUtils.rm(response_file_destination) if ::File.exist?(response_file_destination)
- end
-
- it "creates the preseed file in the cache" do
- expect(response_file_resource).to receive(:run_action).with(:create)
- provider.get_preseed_file("java", "6")
- end
-
- it "returns the path to the response file if the response file was updated" do
- expect(provider.get_preseed_file("java", "6")).to eq(response_file_destination)
- end
-
- it "should return false if the response file has not been updated" do
- response_file_resource.updated_by_last_action(false)
- expect(response_file_resource).not_to be_updated_by_last_action
- # don't let the response_file_resource set updated to true
- expect(response_file_resource).to receive(:run_action).with(:create)
- expect(provider.get_preseed_file("java", "6")).to be(false)
- 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
end
@@ -458,20 +393,6 @@ describe "Subclass with use_multipackage_api" do
expect(provider.use_multipackage_api?).to be true
end
- context "#a_to_s utility for subclasses" do
- 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
new_resource.package_name "vim"
new_resource.version nil
@@ -551,22 +472,6 @@ describe "Subclass with use_multipackage_api" do
expect(new_resource).to be_updated_by_last_action
expect(new_resource.version).to eql(nil)
end
-
- it "when user passes string to package_name, passes arrays to reconfig_package" do
- new_resource.package_name "vim"
- current_resource.package_name "vim"
- current_resource.version [ "1.0" ]
- allow(new_resource).to receive(:response_file).and_return(true)
- 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(provider).to receive(:reconfig_package).with(
- [ "vim" ],
- [ "1.0" ]
- ).and_return(true)
- provider.run_action(:reconfig)
- expect(new_resource).to be_updated_by_last_action
- end
end
describe "Chef::Provider::Package - Multi" do
@@ -612,8 +517,8 @@ describe "Chef::Provider::Package - Multi" do
end
it "installs the specified version when some are out of date" do
- current_resource.version(["1.0", "6.2"])
- new_resource.version(["1.0", "6.1"])
+ current_resource.version(["1.0", "6.1"])
+ new_resource.version(["1.0", "6.2"])
provider.run_action(:install)
expect(new_resource).to be_updated
end
@@ -625,13 +530,28 @@ describe "Chef::Provider::Package - Multi" do
expect(new_resource).not_to be_updated_by_last_action
end
+ it "does install an older version by default" do
+ current_resource.version(["1.1", "6.2"])
+ new_resource.version(["1.0", "6.1"])
+ provider.run_action(:install)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "does not install an older version if the resource subclass has allow_downgrade set to false" do
+ allow(new_resource).to receive(:allow_downgrade).and_return(false)
+ current_resource.version(["1.1", "6.2"])
+ new_resource.version(["1.0", "6.1"])
+ provider.run_action(:install)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
it "does not install any version if all are installed, and no version was specified" do
current_resource.version(["1.0", "6.2"])
provider.run_action(:install)
expect(new_resource).not_to be_updated_by_last_action
end
- it "raises an exception if both are not installed and no caondidates are available" do
+ it "raises an exception if both are not installed and no candidates are available" do
current_resource.version([nil, nil])
provider.candidate_version = [nil, nil]
expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
@@ -711,7 +631,7 @@ describe "Chef::Provider::Package - Multi" do
expect(new_resource).not_to be_updated_by_last_action
end
- it "should raise an exception if both are not installed and no caondidates are available" do
+ it "should raise an exception if both are not installed and no candidates are available" do
current_resource.version([nil, nil])
provider.candidate_version = [nil, nil]
expect { provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package)
@@ -847,37 +767,25 @@ describe "Chef::Provider::Package - Multi" do
end
end
- describe "shell_out helpers" do
- [ :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|
- it "#{method} defaults to 900 seconds" do
- expect(provider).to receive(stubbed_method).with(*command, timeout: 900)
- provider.send(method, *command)
- end
- it "#{method} overrides the default timeout with its options" do
- expect(provider).to receive(stubbed_method).with(*command, timeout: 1)
- provider.send(method, *command, timeout: 1)
- end
- it "#{method} overrides both timeouts with the new_resource.timeout" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with(*command, timeout: 99)
- provider.send(method, *command, timeout: 1)
- end
- it "#{method} defaults to 900 seconds and preserves options" do
- expect(provider).to receive(stubbed_method).with(*command, env: nil, timeout: 900)
- provider.send(method, *command, env: nil)
- end
- it "#{method} overrides the default timeout with its options and preserves options" do
- expect(provider).to receive(stubbed_method).with(*command, timeout: 1, env: nil)
- provider.send(method, *command, timeout: 1, env: nil)
- end
- it "#{method} overrides both timeouts with the new_resource.timeout and preseves options" do
- new_resource.timeout(99)
- expect(provider).to receive(stubbed_method).with(*command, timeout: 99, env: nil)
- provider.send(method, *command, timeout: 1, env: nil)
- end
- end
+ describe "version_compare" do
+ it "tests equality" do
+ expect(provider.version_compare("1.3", "1.3")).to eql(0)
+ end
+
+ it "tests less than" do
+ expect(provider.version_compare("1.2", "1.3")).to eql(-1)
+ end
+
+ it "tests greater than" do
+ expect(provider.version_compare("1.5", "1.3")).to eql(1)
+ end
+
+ it "x.10 is greater than x.2 (so does not do floating point comparisons)" do
+ expect(provider.version_compare("1.10", "1.2")).to eql(1)
+ end
+
+ it "sanitizes inputs" do
+ expect(provider.version_compare("1.3_3", "1.3")).to eql(0)
end
end
end
diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 4fd3f3534d..0bece73bd0 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,89 +18,44 @@
require "spec_helper"
describe Chef::Provider::PowershellScript, "action_run" do
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
- let(:powershell_version) { nil }
- let(:node) do
- node = Chef::Node.new
- node.default["kernel"] = Hash.new
- node.default["kernel"][:machine] = :x86_64.to_s
- if ! powershell_version.nil?
- node.default[:languages] = { :powershell => { :version => powershell_version } }
- end
- node
+ let(:run_context) { Chef::RunContext.new(Chef::Node.new, {}, events) }
+
+ let(:new_resource) do
+ Chef::Resource::PowershellScript.new("run some powershell code", run_context)
end
let(:provider) do
- empty_events = Chef::EventDispatch::Dispatcher.new
- run_context = Chef::RunContext.new(node, {}, empty_events)
- new_resource = Chef::Resource::PowershellScript.new("run some powershell code", run_context)
Chef::Provider::PowershellScript.new(new_resource, run_context)
end
- context "when setting interpreter flags" do
- context "on nano" do
- before(:each) 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(:[]).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
- flags = provider.command.split(" ").keep_if { |flag| flag =~ /^-/ }
- expect(flags.pop).to eq("-Command")
- end
+ describe "#command" do
+ before(:each) do
+ allow(provider).to receive(:basepath).and_return("C:\\Windows\\system32")
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
- context "not on nano" do
- before(:each) 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(:[]).with("os_info").and_return(os_info_double)
- allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32")
- end
+ it "includes the user's flags after the default flags when building the command" do
+ new_resource.flags = "-InputFormat Fabulous"
+ provider.send(:script_file_path=, "C:\\Temp\\Script.ps1")
- it "sets the -File flag as the last flag" do
- flags = provider.command.split(" ").keep_if { |flag| flag =~ /^-/ }
- expect(flags.pop).to eq("-File")
- end
+ expected = <<~CMD.strip
+ "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass -InputFormat None -InputFormat Fabulous -File "C:\\Temp\\Script.ps1"
+ CMD
- let(:execution_policy_flag) do
- execution_policy_index = 0
- provider_flags = provider.flags.split(" ")
- execution_policy_specified = false
-
- provider_flags.find do |value|
- execution_policy_index += 1
- execution_policy_specified = value.casecmp("-ExecutionPolicy".downcase).zero?
- end
+ expect(provider.command).to eq(expected)
+ end
- execution_policy = execution_policy_specified ? provider_flags[execution_policy_index] : nil
- end
+ it "uses pwsh when given the pwsh interpreter" do
+ new_resource.interpreter = "pwsh"
+ provider.send(:script_file_path=, "C:\\Temp\\Script.ps1")
- context "when running with an unspecified PowerShell version" do
- let(:powershell_version) { nil }
- it "sets the -ExecutionPolicy flag to 'Unrestricted' by default" do
- expect(execution_policy_flag.downcase).to eq("unrestricted".downcase)
- end
- end
+ expected = <<~CMD.strip
+ "pwsh" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass -InputFormat None -File "C:\\Temp\\Script.ps1"
+ CMD
- { "2.0" => "Unrestricted",
- "2.5" => "Unrestricted",
- "3.0" => "Bypass",
- "3.6" => "Bypass",
- "4.0" => "Bypass",
- "5.0" => "Bypass" }.each do |version_policy|
- let(:powershell_version) { version_policy[0].to_f }
- context "when running PowerShell version #{version_policy[0]}" do
- let(:powershell_version) { version_policy[0].to_f }
- it "sets the -ExecutionPolicy flag to '#{version_policy[1]}'" do
- expect(execution_policy_flag.downcase).to eq(version_policy[1].downcase)
- end
- end
- end
+ expect(provider.command).to eq(expected)
end
end
end
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
index 9e19d02d5e..fe643a0ec3 100644
--- a/spec/unit/provider/registry_key_spec.rb
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (lamont@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -275,20 +275,20 @@ describe Chef::Provider::RegistryKey do
context "when the key data is safe" do
let(:keyname) { 'HKLM\Software\Opscode\Testing\Safe' }
- let(:testval1) { { :name => "one", :type => :string, :data => "1" } }
- let(:testval1_wrong_type) { { :name => "one", :type => :multi_string, :data => "1" } }
- let(:testval1_wrong_data) { { :name => "one", :type => :string, :data => "2" } }
- let(:testval2) { { :name => "two", :type => :string, :data => "2" } }
+ let(:testval1) { { name: "one", type: :string, data: "1" } }
+ let(:testval1_wrong_type) { { name: "one", type: :multi_string, data: "1" } }
+ let(:testval1_wrong_data) { { name: "one", type: :string, data: "2" } }
+ let(:testval2) { { name: "two", type: :string, data: "2" } }
it_should_behave_like "a registry key"
end
context "when the key data is unsafe" do
let(:keyname) { 'HKLM\Software\Opscode\Testing\Unsafe' }
- let(:testval1) { { :name => "one", :type => :binary, :data => 255.chr * 1 } }
- let(:testval1_wrong_type) { { :name => "one", :type => :string, :data => 255.chr * 1 } }
- let(:testval1_wrong_data) { { :name => "one", :type => :binary, :data => 254.chr * 1 } }
- let(:testval2) { { :name => "two", :type => :binary, :data => 0.chr * 1 } }
+ let(:testval1) { { name: "one", type: :binary, data: 255.chr * 1 } }
+ let(:testval1_wrong_type) { { name: "one", type: :string, data: 255.chr * 1 } }
+ let(:testval1_wrong_data) { { name: "one", type: :binary, data: 254.chr * 1 } }
+ let(:testval2) { { name: "two", type: :binary, data: 0.chr * 1 } }
it_should_behave_like "a registry key"
end
@@ -296,8 +296,8 @@ describe Chef::Provider::RegistryKey do
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" } }
+ 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
@@ -309,5 +309,179 @@ describe Chef::Provider::RegistryKey do
@provider.action_create
end
end
+
+ context "when sensitive is true" do
+ before(:each) do
+ @new_resource.sensitive(true)
+ end
+
+ context "and key exists" do
+ let(:keyname) { 'hklm\\software\\opscode\\testing\\sensitive\exists' }
+ before(:each) do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
+ expect(@double_registry).to receive(:get_values).with(keyname).and_return(
+ [
+ { name: "one", type: :string, data: "initial value" },
+ { name: "two", type: :dword, data: 9001 },
+ ]
+ )
+ end
+
+ context "and type is a string" do
+ let(:testval1) { { name: "one", type: :string, data: "first_value" } }
+
+ it "sets the unscrubbed value" do
+ expect(@double_registry).to receive(:set_value).with(keyname, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+
+ context "and type is a dword" do
+ let(:testval1) { { name: "two", type: :dword, data: 12345 } }
+
+ it "sets the unscrubbed value" do
+ expect(@double_registry).to receive(:set_value).with(keyname, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ end
+
+ context "and key does not exist" do
+ let(:keyname) { 'hklm\\software\\opscode\\testing\\sensitive\missing' }
+ let(:testval1) { { name: "one", type: :string, data: "first_value" } }
+
+ before(:each) do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false)
+ expect(@double_registry).to receive(:create_key).with(keyname, false)
+ end
+
+ it "sets the unscrubbed value" do
+ expect(@double_registry).to receive(:set_value).with(keyname, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ end
+ end
+
+ describe "action_create_if_missing" do
+ context "when sensitive is true" do
+ let(:keyname) { 'hklm\\software\\opscode\\testing\\create_if_missing\\sensitive' }
+ let(:testval1) { { name: "one", type: :string, data: "first_value" } }
+
+ before(:each) do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
+ expect(@double_registry).to receive(:get_values).with(keyname).and_return([])
+ @new_resource.sensitive(true)
+ end
+
+ it "sets the unscrubbed value" do
+ expect(@double_registry).to receive(:set_value).with(keyname, testval1)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ 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 cb1b6e3cd8..a845664a0d 100644
--- a/spec/unit/provider/remote_directory_spec.rb
+++ b/spec/unit/provider/remote_directory_spec.rb
@@ -90,8 +90,8 @@ describe Chef::Provider::RemoteDirectory do
it "configures access control on files in the directory" do
@resource.cookbook "berlin_style_tasty_cupcakes"
cookbook_file = @provider.send(:cookbook_file_resource,
- "/target/destination/path.txt",
- "relative/source/path.txt")
+ "/target/destination/path.txt",
+ "relative/source/path.txt")
expect(cookbook_file.cookbook_name).to eq("berlin_style_tasty_cupcakes")
expect(cookbook_file.source).to eq("remotedir_root/relative/source/path.txt")
expect(cookbook_file.mode).to eq("0640")
@@ -104,14 +104,14 @@ describe Chef::Provider::RemoteDirectory do
@resource.cookbook "gondola_rides"
@resource.sensitive true
cookbook_file = @provider.send(:cookbook_file_resource,
- "/target/destination/path.txt",
- "relative/source/path.txt")
+ "/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")
+ "/target/destination/path.txt",
+ "relative/source/path.txt")
expect(cookbook_file.sensitive).to eq(false)
end
end
@@ -193,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
@@ -201,17 +201,17 @@ describe Chef::Provider::RemoteDirectory do
@fclass = Chef::CFCCheck.new
Dir.mktmpdir do |tmp_dir|
- begin
- @fclass.file_class.symlink(tmp_dir.dup, symlinked_dir_path)
- expect(::File.exist?(symlinked_dir_path)).to be_truthy
- @provider.run_action
+ @fclass.file_class.symlink(tmp_dir.dup, symlinked_dir_path)
+ expect(::File.exist?(symlinked_dir_path)).to be_truthy
+
+ @provider.run_action
+
+ expect(::File.exist?(symlinked_dir_path)).to be_falsey
+ expect(::File.exist?(tmp_dir)).to be_truthy
+ rescue Chef::Exceptions::Win32APIError
+ skip "This must be run as an Administrator to create symlinks"
- expect(::File.exist?(symlinked_dir_path)).to be_falsey
- expect(::File.exist?(tmp_dir)).to be_truthy
- rescue Chef::Exceptions::Win32APIError
- skip "This must be run as an Administrator to create symlinks"
- end
end
end
end
diff --git a/spec/unit/provider/remote_file/cache_control_data_spec.rb b/spec/unit/provider/remote_file/cache_control_data_spec.rb
index a80aa38e77..19848e4456 100644
--- a/spec/unit/provider/remote_file/cache_control_data_spec.rb
+++ b/spec/unit/provider/remote_file/cache_control_data_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,8 +45,8 @@ describe Chef::Provider::RemoteFile::CacheControlData do
context "when loading data for an unknown URI" do
before do
- expect(Chef::FileCache).to receive(:has_key?).with(cache_path).and_return(false)
- expect(Chef::FileCache).to receive(:has_key?).with(old_cache_path).and_return(false)
+ expect(Chef::FileCache).to receive(:key?).with(cache_path).and_return(false)
+ expect(Chef::FileCache).to receive(:key?).with(old_cache_path).and_return(false)
end
context "and there is no current copy of the file" do
@@ -93,7 +93,7 @@ describe Chef::Provider::RemoteFile::CacheControlData do
context "when the cache control data uses sha256 for its name" do
before do
- expect(Chef::FileCache).to receive(:has_key?).with(cache_path).and_return(true)
+ expect(Chef::FileCache).to receive(:key?).with(cache_path).and_return(true)
expect(Chef::FileCache).to receive(:load).with(cache_path).and_return(cache_json_data)
end
@@ -118,7 +118,7 @@ describe Chef::Provider::RemoteFile::CacheControlData do
context "and the cached checksum matches the on-disk copy" do
context "when the filename uses sha256" do
before do
- expect(Chef::FileCache).not_to receive(:has_key?).with(old_cache_path)
+ expect(Chef::FileCache).not_to receive(:key?).with(old_cache_path)
end
it "populates the cache control data" do
expect(cache_control_data.etag).to eq(etag)
@@ -148,8 +148,8 @@ describe Chef::Provider::RemoteFile::CacheControlData do
context "when the filename uses md5" do
before do
- expect(Chef::FileCache).to receive(:has_key?).with(cache_path).and_return(false)
- expect(Chef::FileCache).to receive(:has_key?).with(old_cache_path).and_return(true)
+ expect(Chef::FileCache).to receive(:key?).with(cache_path).and_return(false)
+ expect(Chef::FileCache).to receive(:key?).with(old_cache_path).and_return(true)
expect(Chef::FileCache).to receive(:load).with(old_cache_path).and_return(cache_json_data)
end
@@ -182,7 +182,7 @@ describe Chef::Provider::RemoteFile::CacheControlData do
cache_control_data.checksum = fetched_file_checksum
end
- it "serializes its attributes to JSON" do
+ it "serializes its properties to JSON" do
# we have to test this separately because ruby 1.8 hash order is unstable
# so we can't count on the order of the keys in the json format.
diff --git a/spec/unit/provider/remote_file/content_spec.rb b/spec/unit/provider/remote_file/content_spec.rb
index c6a560b123..36aaab8399 100644
--- a/spec/unit/provider/remote_file/content_spec.rb
+++ b/spec/unit/provider/remote_file/content_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -84,7 +84,7 @@ describe Chef::Provider::RemoteFile::Content do
describe "when the fetcher returns nil for the tempfile" do
before do
- http_fetcher = double("Chef::Provider::RemoteFile::HTTP", :fetch => nil)
+ http_fetcher = double("Chef::Provider::RemoteFile::HTTP", fetch: nil)
expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri, new_resource, current_resource).and_return(http_fetcher)
end
@@ -97,7 +97,7 @@ describe Chef::Provider::RemoteFile::Content do
let(:mtime) { Time.now }
let(:tempfile) { double("Tempfile") }
- let(:http_fetcher) { double("Chef::Provider::RemoteFile::HTTP", :fetch => tempfile) }
+ let(:http_fetcher) { double("Chef::Provider::RemoteFile::HTTP", fetch: tempfile) }
before do
expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri, new_resource, current_resource).and_return(http_fetcher)
@@ -159,9 +159,9 @@ 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
+ if [ Net::HTTPClientException, Net::HTTPFatalError ].include? exception_class
exception_class.new("message", { "something" => 1 })
else
exception_class.new
@@ -177,9 +177,10 @@ describe Chef::Provider::RemoteFile::Content do
Errno::ENOENT,
Errno::EACCES,
Timeout::Error,
- Net::HTTPServerException,
+ Net::HTTPClientException,
Net::HTTPFatalError,
Net::FTPError,
+ Errno::ETIMEDOUT,
].each do |exception|
describe "with an exception of #{exception}" do
before do
@@ -198,7 +199,7 @@ describe Chef::Provider::RemoteFile::Content do
before do
@tempfile = double("Tempfile")
mtime = Time.now
- http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", :fetch => @tempfile)
+ http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", fetch: @tempfile)
expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri1, new_resource, current_resource).and_return(http_fetcher_works)
end
@@ -235,7 +236,7 @@ describe Chef::Provider::RemoteFile::Content do
expect(URI).not_to receive(:parse).with(new_resource.source[1])
@tempfile = double("Tempfile")
mtime = Time.now
- http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", :fetch => @tempfile)
+ http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", fetch: @tempfile)
expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri0, new_resource, current_resource).and_return(http_fetcher_works)
end
diff --git a/spec/unit/provider/remote_file/fetcher_spec.rb b/spec/unit/provider/remote_file/fetcher_spec.rb
index 0fa213cdb2..2f63ad5f53 100644
--- a/spec/unit/provider/remote_file/fetcher_spec.rb
+++ b/spec/unit/provider/remote_file/fetcher_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) 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::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(ChefUtils).to receive(:windows?).and_return(true)
end
context "when host is a name" do
@@ -45,7 +46,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
describe "when passed an http url" do
- let(:uri) { double("uri", :scheme => "http" ) }
+ let(:uri) { double("uri", scheme: "http" ) }
before do
expect(Chef::Provider::RemoteFile::HTTP).to receive(:new).and_return(fetcher_instance)
end
@@ -55,7 +56,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
describe "when passed an https url" do
- let(:uri) { double("uri", :scheme => "https" ) }
+ let(:uri) { double("uri", scheme: "https" ) }
before do
expect(Chef::Provider::RemoteFile::HTTP).to receive(:new).and_return(fetcher_instance)
end
@@ -65,7 +66,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
describe "when passed an ftp url" do
- let(:uri) { double("uri", :scheme => "ftp" ) }
+ let(:uri) { double("uri", scheme: "ftp" ) }
before do
expect(Chef::Provider::RemoteFile::FTP).to receive(:new).and_return(fetcher_instance)
end
@@ -75,7 +76,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
describe "when passed a file url" do
- let(:uri) { double("uri", :scheme => "file" ) }
+ let(:uri) { double("uri", scheme: "file" ) }
before do
expect(Chef::Provider::RemoteFile::LocalFile).to receive(:new).and_return(fetcher_instance)
end
@@ -85,7 +86,7 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
describe "when passed a url we do not recognize" do
- let(:uri) { double("uri", :scheme => "xyzzy" ) }
+ let(:uri) { double("uri", scheme: "xyzzy" ) }
it "throws an ArgumentError exception" do
expect { described_class.for_resource(uri, new_resource, current_resource) }.to raise_error(ArgumentError)
end
diff --git a/spec/unit/provider/remote_file/ftp_spec.rb b/spec/unit/provider/remote_file/ftp_spec.rb
index b2fbb7300c..72bb910e66 100644
--- a/spec/unit/provider/remote_file/ftp_spec.rb
+++ b/spec/unit/provider/remote_file/ftp_spec.rb
@@ -108,7 +108,7 @@ describe Chef::Provider::RemoteFile::FTP do
before do
current_resource.checksum(current_resource_checksum)
- #Chef::Provider::RemoteFile::CacheControlData.should_receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data)
+ # Chef::Provider::RemoteFile::CacheControlData.should_receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data)
end
it "should connect to the host from the uri on the default port 21" do
diff --git a/spec/unit/provider/remote_file/http_spec.rb b/spec/unit/provider/remote_file/http_spec.rb
index f58a3d3c14..032cf474ef 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
@@ -290,7 +292,7 @@ describe Chef::Provider::RemoteFile::HTTP do
context "and the target file is a tarball [CHEF-3140]" do
let(:uri) { URI.parse("http://opscode.com/tarball.tgz") }
- let(:expected_http_opts) { { :disable_gzip => true } }
+ let(:expected_http_opts) { { disable_gzip: true } }
# CHEF-3140
# Some servers return tarballs as content type tar and encoding gzip, which
@@ -303,7 +305,7 @@ describe Chef::Provider::RemoteFile::HTTP do
it "should disable gzip compression in the client" do
# Before block in the parent context has set an expectation on
- # Chef::HTTP::Simple.new() being called with expected arguments. Here we fufil
+ # Chef::HTTP::Simple.new() being called with expected arguments. Here we fulfill
# that expectation, so that we can explicitly set it for this test.
# This is intended to provide insurance that refactoring of the parent
# context does not negate the value of this particular example.
diff --git a/spec/unit/provider/remote_file/local_file_spec.rb b/spec/unit/provider/remote_file/local_file_spec.rb
index 6f345cadd1..cfca09ad18 100644
--- a/spec/unit/provider/remote_file/local_file_spec.rb
+++ b/spec/unit/provider/remote_file/local_file_spec.rb
@@ -31,7 +31,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
context "when parsing source path on windows" do
before do
- allow(Chef::Platform).to receive(:windows?).and_return(true)
+ allow(ChefUtils).to receive(:windows?).and_return(true)
end
describe "when given local unix path" do
@@ -84,8 +84,8 @@ describe Chef::Provider::RemoteFile::LocalFile do
describe "when fetching the object" do
- let(:tempfile) { double("Tempfile", :path => "/tmp/foo/bar/nyan.png", :close => nil) }
- let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", :tempfile => tempfile) }
+ let(:tempfile) { double("Tempfile", path: "/tmp/foo/bar/nyan.png", close: nil) }
+ let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", tempfile: tempfile) }
before do
current_resource.source("file:///nyan_cat.png")
diff --git a/spec/unit/provider/remote_file/network_file_spec.rb b/spec/unit/provider/remote_file/network_file_spec.rb
index de065c83e2..8327a04332 100644
--- a/spec/unit/provider/remote_file/network_file_spec.rb
+++ b/spec/unit/provider/remote_file/network_file_spec.rb
@@ -1,6 +1,7 @@
+
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)") }
@@ -28,12 +28,17 @@ describe Chef::Provider::RemoteFile::NetworkFile do
describe "when fetching the object" do
- let(:tempfile) { double("Tempfile", :path => "/tmp/foo/bar/Foo.tar.gz", :close => nil) }
- let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", :tempfile => tempfile) }
+ 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(ChefUtils).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 7be507dc89..244440d5c3 100644
--- a/spec/unit/provider/remote_file/sftp_spec.rb
+++ b/spec/unit/provider/remote_file/sftp_spec.rb
@@ -19,7 +19,7 @@
require "spec_helper"
describe Chef::Provider::RemoteFile::SFTP do
- #built out dependencies
+ # built out dependencies
let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
@@ -117,20 +117,20 @@ 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")
+ expect(Net::SFTP).to receive(:start).with("opscode.com:8021", "conan", password: "cthu1hu")
fetcher.fetch
end
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 6ceb1d450d..403dbc43e3 100644
--- a/spec/unit/provider/remote_file_spec.rb
+++ b/spec/unit/provider/remote_file_spec.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,9 +34,9 @@ describe Chef::Provider::RemoteFile do
content = double("Chef::Provider::File::Content::RemoteFile")
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(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
diff --git a/spec/unit/provider/route_spec.rb b/spec/unit/provider/route_spec.rb
index 2e3f6e4e9e..d39ca8b155 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)
@@ -67,7 +72,7 @@ describe Chef::Provider::Route do
expect(provider.is_running).to be_falsey
end
- it "should detect existing routes and set is_running attribute correctly" do
+ it "should detect existing routes and set is_running property correctly" do
resource = Chef::Resource::Route.new("192.168.100.0/24")
allow(resource).to receive(:gateway).and_return("192.168.132.9")
allow(resource).to receive(:device).and_return("eth0")
@@ -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)
@@ -109,25 +114,25 @@ describe Chef::Provider::Route do
end
it "should not delete config file for :add action (CHEF-3332)" do
- @node.automatic_attrs[:platform] = "centos"
+ @node.automatic_attrs[:platform_family] = "rhel"
route_file = StringIO.new
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)
expect(route_file.string.split("\n").size).to eq(1)
- expect(route_file.string).to match(/^192\.168\.1\.0\/24 via 192\.168\.0\.1$/)
+ expect(route_file.string).to match(%r{^192\.168\.1\.0/24 via 192\.168\.0\.1$})
end
end
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(%r{/\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(%r{/\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 gateway 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(%r{/\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(%r{/\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(%r{/\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(%r{/\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
@@ -210,33 +220,49 @@ describe Chef::Provider::Route do
end
describe Chef::Provider::Route, "generate_config method" do
- %w{ centos redhat fedora }.each do |platform|
- it "should write a route file on #{platform} platform" do
- @node.automatic_attrs[:platform] = platform
+ %w{ rhel fedora amazon }.each do |platform_family|
+ it "should write a route file on #{platform_family} platform family" do
+ @node.automatic_attrs[:platform_family] = platform_family
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
+
+ it "should write a default route file on #{platform_family} platform family" do
+ @node.automatic_attrs[:platform_family] = platform_family
+
+ 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
- @node.automatic_attrs[:platform] = "centos"
+ @node.automatic_attrs[:platform_family] = "rhel"
route_file = StringIO.new
expect(File).to receive(:new).and_return(route_file)
@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).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.split("\n").size).to eq(4)
+ expect(route_file.string).to match(%r{^192\.168\.1\.0/24 via 192\.168\.0\.1$})
+ expect(route_file.string).to match(%r{^192\.168\.2\.0/24 via 192\.168\.0\.1$})
+ expect(route_file.string).to match(%r{^192\.168\.3\.0/24 via 192\.168\.0\.1$})
+ expect(route_file.string).to match(%r{^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 7e34a8f083..18a8a3305b 100644
--- a/spec/unit/provider/script_spec.rb
+++ b/spec/unit/provider/script_spec.rb
@@ -34,81 +34,34 @@ describe Chef::Provider::Script, "action_run" do
let(:provider) { Chef::Provider::Script.new(new_resource, run_context) }
- let(:tempfile) { Tempfile.open("rspec-provider-script") }
-
- before(:each) do
- allow(provider).to receive(:shell_out!).and_return(true)
- allow(provider).to receive(:script_file).and_return(tempfile)
- end
-
- context "#script_file" do
- it "creates a temporary file to store the script" do
- allow(provider).to receive(:script_file).and_call_original
- expect(provider.script_file).to be_an_instance_of(Tempfile)
- end
- end
-
- context "#unlink_script_file" do
- it "unlinks the tempfile" do
- tempfile_path = tempfile.path
- provider.unlink_script_file
- expect(File.exist?(tempfile_path)).to be false
+ describe "#command" do
+ it "is only the intepreter in quotes by default" do
+ expect(provider.command.strip).to eq(%q{"perl"})
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
+ it "is the interpreter in quotes with the flags when flags are used" do
+ new_resource.flags "-f"
+ expect(provider.command).to eq(%q{"perl" -f})
end
end
- context "with the script file set to the correct owner and group" do
+ describe "#action_run" do
before do
- allow(provider).to receive(:set_owner_and_group)
+ allow(provider).to receive(:stream_to_stdout?).and_return(false)
end
- describe "when writing the script to the file" do
- it "should put the contents of the script in the temp file" do
- allow(provider).to receive(:unlink_script_file) # stub to avoid remove
- provider.action_run
- expect(IO.read(tempfile.path)).to eq("$| = 1; print 'i like beans'\n")
- provider.unlink_script_file
- end
-
- it "closes before executing the script and unlinks it when finished" do
- tempfile_path = tempfile.path
- provider.action_run
- expect(tempfile).to be_closed
- expect(File.exist?(tempfile_path)).to be false
- end
- end
-
- describe "when running the script" do
- 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)
- end
-
- it 'should set the command to "interpreter" "tempfile"' do
- expect(provider.command).to eq(%Q{"perl" "#{tempfile.path}"})
- end
-
- it "should call shell_out! with the command" do
- expect(provider).to receive(:shell_out!).with(provider.command, default_opts).and_return(true)
- provider.action_run
- end
-
- it "should set the command to 'interpreter flags tempfile'" do
- new_resource.flags "-f"
- expect(provider.command).to eq(%Q{"perl" -f "#{tempfile.path}"})
- end
+ it "should call shell_out! with the command and correct options" do
+ opts = {
+ timeout: 3600,
+ returns: 0,
+ default_env: false,
+ log_level: :info,
+ log_tag: "script[run some perl code]",
+ input: "$| = 1; print 'i like beans'",
+ }
+
+ expect(provider).to receive(:shell_out!).with(provider.command, opts).and_return(true)
+ provider.action_run
end
end
-
end
diff --git a/spec/unit/provider/service/aix_service_spec.rb b/spec/unit/provider/service/aix_service_spec.rb
index 802ccee2c7..176d537abb 100644
--- a/spec/unit/provider/service/aix_service_spec.rb
+++ b/spec/unit/provider/service/aix_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh <kaustubh@clogeny.com>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,7 +33,7 @@ describe Chef::Provider::Service::Aix do
describe "load current resource" do
it "should create a current resource with the name of the new resource and determine the status" do
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
@@ -47,7 +47,7 @@ describe Chef::Provider::Service::Aix do
describe "determine current status" do
context "when the service is active" do
before do
- @status = double("Status", :exitstatus => 0, :stdout => "chef chef 12345 active\n")
+ @status = double("Status", exitstatus: 0, stdout: "chef chef 12345 active\n")
end
it "current resource is running" do
@@ -61,7 +61,7 @@ describe Chef::Provider::Service::Aix do
context "when the service is inoperative" do
before do
- @status = double("Status", :exitstatus => 0, :stdout => "chef chef inoperative\n")
+ @status = double("Status", exitstatus: 0, stdout: "chef chef inoperative\n")
end
it "current resource is not running" do
@@ -75,7 +75,7 @@ describe Chef::Provider::Service::Aix do
context "when there is no such service" do
before do
- @status = double("Status", :exitstatus => 1, :stdout => "0513-085 The chef Subsystem is not on file.\n")
+ @status = double("Status", exitstatus: 1, stdout: "0513-085 The chef Subsystem is not on file.\n")
end
it "current resource is not running" do
expect(@provider).to receive(:shell_out!).with("lssrc -s chef").and_return(@status)
@@ -90,7 +90,7 @@ describe Chef::Provider::Service::Aix do
describe "is resource group" do
context "when there are multiple subsystems associated with group" do
before do
- @status = double("Status", :exitstatus => 0, :stdout => "chef1 chef 12345 active\nchef2 chef 12334 active\nchef3 chef inoperative")
+ @status = double("Status", exitstatus: 0, stdout: "chef1 chef 12345 active\nchef2 chef 12334 active\nchef3 chef inoperative")
end
it "service is a group" do
@@ -102,7 +102,7 @@ describe Chef::Provider::Service::Aix do
context "when there is a single subsystem in the group" do
before do
- @status = double("Status", :exitstatus => 0, :stdout => "chef1 chef inoperative\n")
+ @status = double("Status", exitstatus: 0, stdout: "chef1 chef inoperative\n")
end
it "service is a group" do
@@ -114,8 +114,8 @@ describe Chef::Provider::Service::Aix do
context "when the service is a subsystem" do
before do
- @group_status = double("Status", :exitstatus => 1, :stdout => "0513-086 The chef Group is not on file.\n")
- @service_status = double("Status", :exitstatus => 0, :stdout => "chef chef inoperative\n")
+ @group_status = double("Status", exitstatus: 1, stdout: "0513-086 The chef Group is not on file.\n")
+ @service_status = double("Status", exitstatus: 0, stdout: "chef chef inoperative\n")
end
it "service is a subsystem" do
diff --git a/spec/unit/provider/service/aixinit_service_spec.rb b/spec/unit/provider/service/aixinit_service_spec.rb
index 09c177903b..d47bebd0d3 100644
--- a/spec/unit/provider/service/aixinit_service_spec.rb
+++ b/spec/unit/provider/service/aixinit_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: kaustubh (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Provider::Service::AixInit do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "fuuuu" }
+ @node.automatic_attrs[:command] = { ps: "fuuuu" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -35,7 +35,7 @@ describe Chef::Provider::Service::AixInit do
end
describe "load_current_resource" do
- it "sets current resource attributes" do
+ it "sets current resource properties" do
expect(@provider).to receive(:set_current_resource_attributes)
@provider.load_current_resource
@@ -209,7 +209,7 @@ describe Chef::Provider::Service::AixInit do
before do
@files = ["/etc/rc.d/rc2.d/S20apache", "/etc/rc.d/rc2.d/K80apache"]
# FIXME: this is clearly buggy the duplicated keys do not work
- #@priority = {2 => [:start, 20], 2 => [:stop, 80]}
+ # @priority = {2 => [:start, 20], 2 => [:stop, 80]}
@priority = { 2 => [:stop, 80] }
allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]chef"]).and_return(@files)
@@ -256,7 +256,7 @@ describe Chef::Provider::Service::AixInit do
before do
files = ["/etc/rc.d/rc2.d/Sapache", "/etc/rc.d/rc2.d/Kapache"]
# FIXME: this is clearly buggy the duplicated keys do not work
- #@priority = {2 => [:start, ''], 2 => [:stop, '']}
+ # @priority = {2 => [:start, ''], 2 => [:stop, '']}
@priority = { 2 => [:stop, ""] }
allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return(files)
diff --git a/spec/unit/provider/service/arch_service_spec.rb b/spec/unit/provider/service/arch_service_spec.rb
index 506a1616c5..026db3dc75 100644
--- a/spec/unit/provider/service/arch_service_spec.rb
+++ b/spec/unit/provider/service/arch_service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Jan Zimmek (<jan.zimmek@web.de>)
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,24 +26,25 @@ require "ostruct"
describe Chef::Provider::Service::Arch, "load_current_resource" do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "ps -ef" }
+ @node.automatic_attrs[:command] = { ps: "ps -ef" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@new_resource = Chef::Resource::Service.new("chef")
@new_resource.pattern("chef")
- @new_resource.supports({ :status => false })
+ @new_resource.supports({ status: false })
@provider = Chef::Provider::Service::Arch.new(@new_resource, @run_context)
- allow(::File).to receive(:exists?).with("/etc/rc.conf").and_return(true)
+ allow(::File).to receive(:exist?).with("/etc/rc.d/chef").and_return(false)
+ allow(::File).to receive(:exist?).with("/etc/rc.conf").and_return(true)
allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network apache sshd)")
end
describe "when first created" do
it "should set the current resources service name to the new resources service name" do
- allow(@provider).to receive(:shell_out).and_return(OpenStruct.new(:exitstatus => 0, :stdout => ""))
+ allow(@provider).to receive(:shell_out).and_return(OpenStruct.new(exitstatus: 0, stdout: ""))
@provider.load_current_resource
expect(@provider.current_resource.service_name).to eq("chef")
end
@@ -51,22 +52,22 @@ describe Chef::Provider::Service::Arch, "load_current_resource" do
describe "when the service supports status" do
before do
- @new_resource.supports({ :status => true })
+ @new_resource.supports({ status: true })
end
it "should run '/etc/rc.d/service_name status'" do
- expect(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0))
+ expect(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(exitstatus: 0))
@provider.load_current_resource
end
it "should set running to true if the status command returns 0" do
- allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0))
+ allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(exitstatus: 0))
@provider.load_current_resource
expect(@provider.current_resource.running).to be_truthy
end
it "should set running to false if the status command returns anything except 0" do
- allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 1))
+ allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(exitstatus: 1))
@provider.load_current_resource
expect(@provider.current_resource.running).to be_falsey
end
@@ -85,28 +86,28 @@ describe Chef::Provider::Service::Arch, "load_current_resource" do
end
it "should run the services status command if one has been specified" do
- expect(@provider).to receive(:shell_out).with("/etc/rc.d/chefhasmonkeypants status").and_return(OpenStruct.new(:exitstatus => 0))
+ expect(@provider).to receive(:shell_out).with("/etc/rc.d/chefhasmonkeypants status").and_return(OpenStruct.new(exitstatus: 0))
@provider.load_current_resource
end
end
- it "should raise error if the node has a nil ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => nil }
+ it "should raise error if the node has a nil ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: nil }
@provider.define_resource_requirements
@provider.action = :start
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
- it "should raise error if the node has an empty ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => "" }
+ it "should raise error if the node has an empty ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: "" }
@provider.define_resource_requirements
@provider.action = :start
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
it "should fail if file /etc/rc.conf does not exist" do
- allow(::File).to receive(:exists?).with("/etc/rc.conf").and_return(false)
+ allow(::File).to receive(:exist?).with("/etc/rc.conf").and_return(false)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service)
end
@@ -117,22 +118,22 @@ describe Chef::Provider::Service::Arch, "load_current_resource" do
describe "when discovering service status with ps" do
before do
- @stdout = StringIO.new(<<-DEFAULT_PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
-aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
-aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
-DEFAULT_PS
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~DEFAULT_PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
+ aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
+ aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
+ DEFAULT_PS
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
- @node.automatic_attrs[:command] = { :ps => "ps -ef" }
+ @node.automatic_attrs[:command] = { ps: "ps -ef" }
end
it "determines the service is running when it appears in ps" do
- @stdout = StringIO.new(<<-RUNNING_PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 chef
-aj 7842 5057 0 21:26 pts/2 00:00:06 poos
-RUNNING_PS
+ @stdout = StringIO.new(<<~RUNNING_PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 chef
+ aj 7842 5057 0 21:26 pts/2 00:00:06 poos
+ RUNNING_PS
allow(@status).to receive(:stdout).and_return(@stdout)
@provider.load_current_resource
expect(@provider.current_resource.running).to be_truthy
@@ -181,7 +182,7 @@ RUNNING_PS
it "should add chef to DAEMONS array" do
allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network)")
expect(@provider).to receive(:update_daemons).with(%w{network chef})
- @provider.enable_service()
+ @provider.enable_service
end
end
@@ -202,7 +203,7 @@ RUNNING_PS
it "should remove chef from DAEMONS array" do
allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network chef)")
expect(@provider).to receive(:update_daemons).with(["network", "!chef"])
- @provider.disable_service()
+ @provider.disable_service
end
end
@@ -221,14 +222,14 @@ 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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally")
- @provider.start_service()
+ @new_resource.start_command("/etc/rc.d/chef startyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/chef startyousillysally", default_env: false)
+ @provider.start_service
end
it "should call '/etc/rc.d/service_name start' if no start command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} start")
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} start", default_env: false)
+ @provider.start_service
end
end
@@ -247,14 +248,14 @@ 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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef itoldyoutostop")
- @provider.stop_service()
+ @new_resource.stop_command("/etc/rc.d/chef itoldyoutostop")
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/chef itoldyoutostop", default_env: false)
+ @provider.stop_service
end
it "should call '/etc/rc.d/service_name stop' if no stop command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} stop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} stop", default_env: false)
+ @provider.stop_service
end
end
@@ -274,22 +275,22 @@ 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 })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restart")
- @provider.restart_service()
+ @new_resource.supports({ restart: true })
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} restart", default_env: false)
+ @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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restartinafire")
- @provider.restart_service()
+ @new_resource.restart_command("/etc/rc.d/chef restartinafire")
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} restartinafire", default_env: false)
+ @provider.restart_service
end
it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do
expect(@provider).to receive(:stop_service)
expect(@provider).to receive(:sleep).with(1)
expect(@provider).to receive(:start_service)
- @provider.restart_service()
+ @provider.restart_service
end
end
@@ -309,15 +310,15 @@ 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 })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} reload")
- @provider.reload_service()
+ @new_resource.supports({ reload: true })
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} reload", default_env: false)
+ @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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} lollerpants")
- @provider.reload_service()
+ @new_resource.reload_command("/etc/rc.d/chef lollerpants")
+ expect(@provider).to receive(:shell_out!).with("/etc/rc.d/#{@new_resource.service_name} lollerpants", default_env: false)
+ @provider.reload_service
end
end
end
diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb
index 799ed991a3..d0cd048c4f 100644
--- a/spec/unit/provider/service/debian_service_spec.rb
+++ b/spec/unit/provider/service/debian_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, HJK Solutions, LLC
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Provider::Service::Debian do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "fuuuu" }
+ @node.automatic_attrs[:command] = { ps: "fuuuu" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -32,11 +32,22 @@ 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
it "ensures /usr/sbin/update-rc.d is available" do
- expect(File).to receive(:exists?).with("/usr/sbin/update-rc.d") .and_return(false)
+ expect(File).to receive(:exist?).with("/usr/sbin/update-rc.d").and_return(false)
@provider.define_resource_requirements
expect do
@@ -47,23 +58,14 @@ describe Chef::Provider::Service::Debian do
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,35 @@ 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 +119,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 do
- @provider.process_resource_requirements
- end.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
@@ -308,21 +183,21 @@ insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
describe "enable_service" do
let(:service_name) { @new_resource.service_name }
context "when the service doesn't set a priority" do
- it "calls update-rc.d 'service_name' defaults" do
+ it "assumes default priority 20 and calls update-rc.d remove => defaults 20 80" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} defaults 20 80",
])
@provider.enable_service
end
end
- context "when the service sets a simple priority" do
+ context "when the service sets a simple priority 75" do
before do
@new_resource.priority(75)
end
- it "calls update-rc.d 'service_name' defaults" do
+ it "calls update-rc.d remove => defaults 75 25" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
"/usr/sbin/update-rc.d #{service_name} defaults 75 25",
@@ -331,15 +206,17 @@ insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
end
end
- context "when the service sets complex priorities" do
+ context "when the service sets complex priorities using Hash" do
before do
@new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
end
- it "calls update-rc.d 'service_name' with those priorities" do
+ it "calls update-rc.d remove => defaults => enable|disable <runlevel>" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d #{service_name} start 20 2 . stop 55 3 . ",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} enable 2",
+ "/usr/sbin/update-rc.d #{service_name} disable 3",
])
@provider.enable_service
end
@@ -348,25 +225,44 @@ insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
describe "disable_service" do
let(:service_name) { @new_resource.service_name }
+
context "when the service doesn't set a priority" do
- it "calls update-rc.d -f 'service_name' remove + stop with default priority" do
+ it "calls update-rc.d remove => defaults => disable" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d -f #{service_name} stop 80 2 3 4 5 .",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} disable",
])
@provider.disable_service
end
end
- context "when the service sets a simple priority" do
+ context "when the service sets a simple priority 75" do
before do
@new_resource.priority(75)
end
- it "calls update-rc.d -f 'service_name' remove + stop with the specified priority" do
+ it "ignores priority and calls update-rc.d remove => defaults => disable" do
expect_commands(@provider, [
"/usr/sbin/update-rc.d -f #{service_name} remove",
- "/usr/sbin/update-rc.d -f #{service_name} stop #{100 - @new_resource.priority} 2 3 4 5 .",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} disable",
+ ])
+ @provider.disable_service
+ end
+ end
+
+ context "when the service sets complex priorities using Hash" do
+ before do
+ @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55])
+ end
+
+ it "ignores priority and calls update-rc.d remove => defaults => enable|disable <runlevel>" do
+ expect_commands(@provider, [
+ "/usr/sbin/update-rc.d -f #{service_name} remove",
+ "/usr/sbin/update-rc.d #{service_name} defaults",
+ "/usr/sbin/update-rc.d #{service_name} enable 2",
+ "/usr/sbin/update-rc.d #{service_name} disable 3",
])
@provider.disable_service
end
diff --git a/spec/unit/provider/service/freebsd_service_spec.rb b/spec/unit/provider/service/freebsd_service_spec.rb
index 10eb3c1a14..4392786edc 100644
--- a/spec/unit/provider/service/freebsd_service_spec.rb
+++ b/spec/unit/provider/service/freebsd_service_spec.rb
@@ -27,14 +27,14 @@ end
describe Chef::Provider::Service::Freebsd do
let(:node) do
node = Chef::Node.new
- node.automatic_attrs[:command] = { :ps => "ps -ax" }
+ node.automatic_attrs[:command] = { ps: "ps -ax" }
node
end
let(:new_resource) do
new_resource = Chef::Resource::Service.new("apache22")
new_resource.pattern("httpd")
- new_resource.supports({ :status => false })
+ new_resource.supports({ status: false })
new_resource
end
@@ -99,7 +99,7 @@ describe Chef::Provider::Service::Freebsd do
end
context "when a status command has been specified" do
- let(:status) { double(:stdout => "", :exitstatus => 0) }
+ let(:status) { double(stdout: "", exitstatus: 0) }
before do
new_resource.status_command("/bin/chefhasmonkeypants status")
@@ -112,10 +112,10 @@ describe Chef::Provider::Service::Freebsd do
end
context "when the service supports status" do
- let(:status) { double(:stdout => "", :exitstatus => 0) }
+ let(:status) { double(stdout: "", exitstatus: 0) }
before do
- new_resource.supports({ :status => true })
+ new_resource.supports({ status: true })
end
it "should run '/etc/init.d/service_name status'" do
@@ -136,18 +136,18 @@ describe Chef::Provider::Service::Freebsd do
end
end
- context "when we have a 'ps' attribute" do
+ context "when we have a 'ps' property" do
let(:stdout) do
- StringIO.new(<<-PS_SAMPLE)
-413 ?? Ss 0:02.51 /usr/sbin/syslogd -s
-539 ?? Is 0:00.14 /usr/sbin/sshd
-545 ?? Ss 0:17.53 sendmail: accepting connections (sendmail)
-PS_SAMPLE
+ StringIO.new(<<~PS_SAMPLE)
+ 413 ?? Ss 0:02.51 /usr/sbin/syslogd -s
+ 539 ?? Is 0:00.14 /usr/sbin/sshd
+ 545 ?? Ss 0:17.53 sendmail: accepting connections (sendmail)
+ PS_SAMPLE
end
- let(:status) { double(:stdout => stdout, :exitstatus => 0) }
+ let(:status) { double(stdout: stdout, exitstatus: 0) }
before do
- node.automatic_attrs[:command] = { :ps => "ps -ax" }
+ node.automatic_attrs[:command] = { ps: "ps -ax" }
end
it "should shell_out! the node's ps command" do
@@ -163,9 +163,9 @@ PS_SAMPLE
context "when the regex matches the output" do
let(:stdout) do
- StringIO.new(<<-PS_SAMPLE)
-555 ?? Ss 0:05.16 /usr/sbin/cron -s
- 9881 ?? Ss 0:06.67 /usr/local/sbin/httpd -DNOHTTPACCEPT
+ StringIO.new(<<~PS_SAMPLE)
+ 555 ?? Ss 0:05.16 /usr/sbin/cron -s
+ 9881 ?? Ss 0:06.67 /usr/local/sbin/httpd -DNOHTTPACCEPT
PS_SAMPLE
end
@@ -191,7 +191,7 @@ PS_SAMPLE
context "when ps is empty string" do
before do
- node.automatic_attrs[:command] = { :ps => "" }
+ node.automatic_attrs[:command] = { ps: "" }
end
it "should set running to nil" do
@@ -261,7 +261,8 @@ PS_SAMPLE
[
%Q{thing_#{new_resource.service_name}_enable="YES"},
%Q{#{new_resource.service_name}_enable="NO"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -273,7 +274,8 @@ PS_SAMPLE
[
%Q{#{new_resource.service_name}_thing_enable="YES"},
%Q{#{new_resource.service_name}_enable="NO"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -285,7 +287,8 @@ PS_SAMPLE
[
%Q{thing_#{new_resource.service_name}_enable="NO"},
%Q{#{new_resource.service_name}_enable="YES"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -297,7 +300,8 @@ PS_SAMPLE
[
%Q{#{new_resource.service_name}_thing_enable="NO"},
%Q{#{new_resource.service_name}_enable="YES"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -341,10 +345,10 @@ PS_SAMPLE
context "when the rc script has a 'name' variable" do
let(:rcscript) do
- StringIO.new(<<-EOF)
-name="#{new_resource.service_name}"
-rcvar=`set_rcvar`
-EOF
+ StringIO.new(<<~EOF)
+ name="#{new_resource.service_name}"
+ rcvar=`set_rcvar`
+ EOF
end
it "should not raise an exception if the rcscript have a name variable" do
@@ -363,24 +367,24 @@ EOF
describe "when the rcscript does not have a name variable" do
let(:rcscript) do
- StringIO.new <<-EOF
-rcvar=`set_rcvar`
-EOF
+ StringIO.new <<~EOF
+ rcvar=`set_rcvar`
+ EOF
end
before do
- status = double(:stdout => rcvar_stdout, :exitstatus => 0)
+ status = double(stdout: rcvar_stdout, exitstatus: 0)
allow(provider).to receive(:shell_out!).with("/usr/local/etc/rc.d/#{new_resource.service_name} rcvar").and_return(status)
end
describe "when rcvar returns foobar_enable" do
let(:rcvar_stdout) do
- rcvar_stdout = <<-EOF
-# apache22
-#
-# #{new_resource.service_name}_enable="YES"
-# (default: "")
-EOF
+ rcvar_stdout = <<~EOF
+ # apache22
+ #
+ # #{new_resource.service_name}_enable="YES"
+ # (default: "")
+ EOF
end
it "should get the service name from rcvar if the rcscript does not have a name variable" do
@@ -394,10 +398,10 @@ EOF
describe "when rcvar does not return foobar_enable" do
let(:rcvar_stdout) do
- rcvar_stdout = <<-EOF
-# service_with_noname
-#
-EOF
+ rcvar_stdout = <<~EOF
+ # service_with_noname
+ #
+ EOF
end
it "should return nil" do
@@ -444,46 +448,46 @@ EOF
describe Chef::Provider::Service::Freebsd, "start_service" do
it "should call the start command if one is specified" do
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()
+ expect(provider).to receive(:shell_out!).with("/etc/rc.d/chef startyousillysally", default_env: false)
+ provider.start_service
end
it "should call '/usr/local/etc/rc.d/service_name faststart' if no start command is specified" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststart")
- provider.start_service()
+ expect(provider).to receive(:shell_out!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststart", default_env: false)
+ provider.start_service
end
end
describe Chef::Provider::Service::Freebsd, "stop_service" do
it "should call the stop command if one is specified" do
new_resource.stop_command("/etc/init.d/chef itoldyoutostop")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop")
- provider.stop_service()
+ expect(provider).to receive(:shell_out!).with("/etc/init.d/chef itoldyoutostop", default_env: false)
+ provider.stop_service
end
it "should call '/usr/local/etc/rc.d/service_name faststop' if no stop command is specified" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststop")
- provider.stop_service()
+ expect(provider).to receive(:shell_out!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststop", default_env: false)
+ provider.stop_service
end
end
describe Chef::Provider::Service::Freebsd, "restart_service" do
it "should call 'restart' on the service_name if the resource supports it" do
- new_resource.supports({ :restart => true })
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} fastrestart")
- provider.restart_service()
+ new_resource.supports({ restart: true })
+ expect(provider).to receive(:shell_out!).with("/usr/local/etc/rc.d/#{new_resource.service_name} fastrestart", default_env: false)
+ provider.restart_service
end
it "should call the restart_command if one has been specified" do
new_resource.restart_command("/etc/init.d/chef restartinafire")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire")
- provider.restart_service()
+ expect(provider).to receive(:shell_out!).with("/etc/init.d/chef restartinafire", default_env: false)
+ provider.restart_service
end
it "otherwise it should call stop and start" do
expect(provider).to receive(:stop_service)
expect(provider).to receive(:start_service)
- provider.restart_service()
+ provider.restart_service
end
end
end
@@ -550,21 +554,21 @@ EOF
allow(current_resource).to receive(:enabled).and_return(false)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "#{new_resource.service_name}_enable=\"NO\"", "bar" ])
expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""])
- provider.enable_service()
+ provider.enable_service
end
it "should not partial match an already enabled service" do
allow(current_resource).to receive(:enabled).and_return(false)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "thing_#{new_resource.service_name}_enable=\"NO\"", "bar" ])
expect(provider).to receive(:write_rc_conf).with(["foo", "thing_#{new_resource.service_name}_enable=\"NO\"", "bar", "#{new_resource.service_name}_enable=\"YES\""])
- provider.enable_service()
+ provider.enable_service
end
it "should enable the service if it is not enabled and not already specified in the rc.conf file" do
allow(current_resource).to receive(:enabled).and_return(false)
expect(provider).to receive(:read_rc_conf).and_return(%w{foo bar})
expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""])
- provider.enable_service()
+ provider.enable_service
end
it "should not enable the service if it is already enabled" do
@@ -577,7 +581,7 @@ EOF
allow(current_resource).to receive(:enabled).and_return(false)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "bar", "\# #{new_resource.service_name}_enable=\"YES\"", "\# #{new_resource.service_name}_enable=\"NO\""])
expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""])
- provider.enable_service()
+ provider.enable_service
end
end
@@ -591,27 +595,27 @@ EOF
allow(current_resource).to receive(:enabled).and_return(true)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "#{new_resource.service_name}_enable=\"YES\"", "bar" ])
expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"NO\""])
- provider.disable_service()
+ provider.disable_service
end
it "should not disable an enabled service that partially matches" do
allow(current_resource).to receive(:enabled).and_return(true)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "thing_#{new_resource.service_name}_enable=\"YES\"", "bar" ])
expect(provider).to receive(:write_rc_conf).with(["foo", "thing_#{new_resource.service_name}_enable=\"YES\"", "bar", "#{new_resource.service_name}_enable=\"NO\""])
- provider.disable_service()
+ provider.disable_service
end
it "should not disable the service if it is already disabled" do
allow(current_resource).to receive(:enabled).and_return(false)
expect(provider).not_to receive(:write_rc_conf)
- provider.disable_service()
+ provider.disable_service
end
it "should remove commented out versions of it being disabled or enabled" do
allow(current_resource).to receive(:enabled).and_return(true)
expect(provider).to receive(:read_rc_conf).and_return([ "foo", "bar", "\# #{new_resource.service_name}_enable=\"YES\"", "\# #{new_resource.service_name}_enable=\"NO\""])
expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"NO\""])
- provider.disable_service()
+ provider.disable_service
end
end
end
diff --git a/spec/unit/provider/service/gentoo_service_spec.rb b/spec/unit/provider/service/gentoo_service_spec.rb
index a00ca7aadd..f195fbe40b 100644
--- a/spec/unit/provider/service/gentoo_service_spec.rb
+++ b/spec/unit/provider/service/gentoo_service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Lee Jensen (<ljensen@engineyard.com>)
# Author:: AJ Christensen (<aj@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,18 +30,18 @@ describe Chef::Provider::Service::Gentoo do
@provider = Chef::Provider::Service::Gentoo.new(@new_resource, @run_context)
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out).and_return(@status)
- allow(File).to receive(:exists?).with("/etc/init.d/chef").and_return(true)
- allow(File).to receive(:exists?).with("/sbin/rc-update").and_return(true)
- allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(false)
+ allow(File).to receive(:exist?).with("/etc/init.d/chef").and_return(true)
+ allow(File).to receive(:exist?).with("/sbin/rc-update").and_return(true)
+ allow(File).to receive(:exist?).with("/etc/runlevels/default/chef").and_return(false)
allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(false)
end
- # new test: found_enabled state
+ # new test: found_enabled state
#
describe "load_current_resource" do
it "should raise Chef::Exceptions::Service if /sbin/rc-update does not exist" do
- expect(File).to receive(:exists?).with("/sbin/rc-update").and_return(false)
+ expect(File).to receive(:exist?).with("/sbin/rc-update").and_return(false)
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
@@ -65,7 +65,7 @@ describe Chef::Provider::Service::Gentoo do
describe "and the file exists and is readable" do
before do
- allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(true)
+ allow(File).to receive(:exist?).with("/etc/runlevels/default/chef").and_return(true)
allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(true)
end
it "should set enabled to true" do
@@ -76,7 +76,7 @@ describe Chef::Provider::Service::Gentoo do
describe "and the file exists but is not readable" do
before do
- allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(true)
+ allow(File).to receive(:exist?).with("/etc/runlevels/default/chef").and_return(true)
allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(false)
end
@@ -88,7 +88,7 @@ describe Chef::Provider::Service::Gentoo do
describe "and the file does not exist" do
before do
- allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(false)
+ allow(File).to receive(:exist?).with("/etc/runlevels/default/chef").and_return(false)
allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return("foobarbaz")
end
@@ -129,14 +129,14 @@ describe Chef::Provider::Service::Gentoo do
describe Chef::Provider::Service::Gentoo, "enable_service" do
it "should call rc-update add *service* default" do
expect(@provider).to receive(:shell_out!).with("/sbin/rc-update add chef default")
- @provider.enable_service()
+ @provider.enable_service
end
end
describe Chef::Provider::Service::Gentoo, "disable_service" do
it "should call rc-update del *service* default" do
expect(@provider).to receive(:shell_out!).with("/sbin/rc-update del chef default")
- @provider.disable_service()
+ @provider.disable_service
end
end
end
diff --git a/spec/unit/provider/service/init_service_spec.rb b/spec/unit/provider/service/init_service_spec.rb
index 4b31e9c399..6f60cc717d 100644
--- a/spec/unit/provider/service/init_service_spec.rb
+++ b/spec/unit/provider/service/init_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Provider::Service::Init, "load_current_resource" do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "ps -ef" }
+ @node.automatic_attrs[:command] = { ps: "ps -ef" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -32,12 +32,12 @@ describe Chef::Provider::Service::Init, "load_current_resource" do
@provider = Chef::Provider::Service::Init.new(@new_resource, @run_context)
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @stdout = StringIO.new(<<-PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
-aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
-aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
-PS
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
+ aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
+ aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
+ PS
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
end
@@ -53,7 +53,7 @@ PS
describe "when the service supports status" do
before do
- @new_resource.supports({ :status => true })
+ @new_resource.supports({ status: true })
end
it "should run '/etc/init.d/service_name status'" do
@@ -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,12 +95,12 @@ 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
it "should use the init_command if one has been specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/opt/chef-server/service/erchef start")
+ expect(@provider).to receive(:shell_out!).with("/opt/chef-server/service/erchef start", default_env: false)
@provider.start_service
end
@@ -108,16 +108,16 @@ PS
describe "when the node has not specified a ps command" do
- it "should raise an error if the node has a nil ps attribute" do
- @node.automatic_attrs[:command] = { :ps => nil }
+ it "should raise an error if the node has a nil ps property" do
+ @node.automatic_attrs[:command] = { ps: nil }
@provider.load_current_resource
@provider.action = :start
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
- it "should raise an error if the node has an empty ps attribute" do
- @node.automatic_attrs[:command] = { :ps => "" }
+ it "should raise an error if the node has an empty ps property" do
+ @node.automatic_attrs[:command] = { ps: "" }
@provider.load_current_resource
@provider.action = :start
@provider.define_resource_requirements
@@ -126,17 +126,17 @@ PS
end
- describe "when we have a 'ps' attribute" do
+ describe "when we have a 'ps' property" do
it "should shell_out! the node's ps command" do
expect(@provider).to receive(:shell_out!).and_return(@status)
@provider.load_current_resource
end
it "should set running to true if the regex matches the output" do
- @stdout = StringIO.new(<<-RUNNING_PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 chef
-aj 7842 5057 0 21:26 pts/2 00:00:06 poos
-RUNNING_PS
+ @stdout = StringIO.new(<<~RUNNING_PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 chef
+ aj 7842 5057 0 21:26 pts/2 00:00:06 poos
+ RUNNING_PS
allow(@status).to receive(:stdout).and_return(@stdout)
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
@@ -164,68 +164,68 @@ RUNNING_PS
describe "when starting the service" do
it "should call the start command if one is specified" do
@new_resource.start_command("/etc/init.d/chef startyousillysally")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef startyousillysally")
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/chef startyousillysally", default_env: false)
+ @provider.start_service
end
it "should call '/etc/init.d/service_name start' if no start command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} start")
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} start", default_env: false)
+ @provider.start_service
end
end
describe Chef::Provider::Service::Init, "stop_service" do
it "should call the stop command if one is specified" do
@new_resource.stop_command("/etc/init.d/chef itoldyoutostop")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/chef itoldyoutostop", default_env: false)
+ @provider.stop_service
end
it "should call '/etc/init.d/service_name stop' if no stop command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} stop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} stop", default_env: false)
+ @provider.stop_service
end
end
describe "when restarting a service" do
it "should call 'restart' on the service_name if the resource supports it" do
- @new_resource.supports({ :restart => true })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} restart")
- @provider.restart_service()
+ @new_resource.supports({ restart: true })
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} restart", default_env: false)
+ @provider.restart_service
end
it "should call the restart_command if one has been specified" do
@new_resource.restart_command("/etc/init.d/chef restartinafire")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} restartinafire")
- @provider.restart_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/#{@new_resource.service_name} restartinafire", default_env: false)
+ @provider.restart_service
end
it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do
expect(@provider).to receive(:stop_service)
expect(@provider).to receive(:sleep).with(1)
expect(@provider).to receive(:start_service)
- @provider.restart_service()
+ @provider.restart_service
end
end
describe "when reloading a service" do
it "should call 'reload' on the service if it supports it" do
- @new_resource.supports({ :reload => true })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef reload")
- @provider.reload_service()
+ @new_resource.supports({ reload: true })
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/chef reload", default_env: false)
+ @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
@new_resource.reload_command("/etc/init.d/chef lollerpants")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef lollerpants")
- @provider.reload_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/chef lollerpants", default_env: false)
+ @provider.reload_service
end
end
describe "when a custom command has been specified" do
before do
@new_resource.start_command("/etc/init.d/chef startyousillysally")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef startyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/chef startyousillysally", default_env: false)
end
it "should still pass all why run assertions" do
diff --git a/spec/unit/provider/service/insserv_service_spec.rb b/spec/unit/provider/service/insserv_service_spec.rb
index 3b2b19c432..a46e8745fc 100644
--- a/spec/unit/provider/service/insserv_service_spec.rb
+++ b/spec/unit/provider/service/insserv_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,20 +23,20 @@ describe Chef::Provider::Service::Insserv do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @node.automatic_attrs[:command] = { :ps => "ps -ax" }
+ @node.automatic_attrs[:command] = { ps: "ps -ax" }
@new_resource = Chef::Resource::Service.new("initgrediant")
@current_resource = Chef::Resource::Service.new("initgrediant")
@provider = Chef::Provider::Service::Insserv.new(@new_resource, @run_context)
- @status = double("Process::Status mock", :exitstatus => 0, :stdout => "")
+ @status = double("Process::Status mock", exitstatus: 0, stdout: "")
allow(@provider).to receive(:shell_out!).and_return(@status)
end
describe "load_current_resource" do
describe "when startup links exist" do
before do
- allow(Dir).to receive(:glob).with("/etc/rc**/S*initgrediant").and_return(["/etc/rc5.d/S18initgrediant", "/etc/rc2.d/S18initgrediant", "/etc/rc4.d/S18initgrediant", "/etc/rc3.d/S18initgrediant"])
+ allow(Dir).to receive(:glob).with("/etc/rc*/**/S*initgrediant").and_return(["/etc/rc.d/rc5.d/S18initgrediant", "/etc/rc.d/rc2.d/S18initgrediant", "/etc/rc.d/rc4.d/S18initgrediant", "/etc/rc.d/rc3.d/S18initgrediant"])
end
it "sets the current enabled status to true" do
@@ -47,7 +47,7 @@ describe Chef::Provider::Service::Insserv do
describe "when startup links do not exist" do
before do
- allow(Dir).to receive(:glob).with("/etc/rc**/S*initgrediant").and_return([])
+ allow(Dir).to receive(:glob).with("/etc/rc*/**/S*initgrediant").and_return([])
end
it "sets the current enabled status to false" do
diff --git a/spec/unit/provider/service/invokercd_service_spec.rb b/spec/unit/provider/service/invokercd_service_spec.rb
index 57b13d0c51..cff02f045b 100644
--- a/spec/unit/provider/service/invokercd_service_spec.rb
+++ b/spec/unit/provider/service/invokercd_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Provider::Service::Invokercd, "load_current_resource" do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "ps -ef" }
+ @node.automatic_attrs[:command] = { ps: "ps -ef" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -32,12 +32,12 @@ describe Chef::Provider::Service::Invokercd, "load_current_resource" do
@provider = Chef::Provider::Service::Invokercd.new(@new_resource, @run_context)
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @stdout = StringIO.new(<<-PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
-aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
-aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
-PS
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
+ aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
+ aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb
+ PS
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
end
@@ -53,7 +53,7 @@ PS
describe "when the service supports status" do
before do
- @new_resource.supports({ :status => true })
+ @new_resource.supports({ status: true })
end
it "should run '/usr/sbin/invoke-rc.d service_name status'" do
@@ -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
@@ -94,15 +94,15 @@ PS
end
describe "when the node has not specified a ps command" do
- it "should raise error if the node has a nil ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => nil }
+ it "should raise error if the node has a nil ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: nil }
@provider.action = :start
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
- it "should raise error if the node has an empty ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => "" }
+ it "should raise error if the node has an empty ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: "" }
@provider.action = :start
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
@@ -110,26 +110,26 @@ PS
end
- describe "when we have a 'ps' attribute" do
+ describe "when we have a 'ps' property" do
it "should shell_out! the node's ps command" do
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
expect(@provider).to receive(:shell_out!).with(@node[:command][:ps]).and_return(@status)
@provider.load_current_resource
end
it "should set running to true if the regex matches the output" do
- @stdout = StringIO.new(<<-RUNNING_PS)
-aj 7842 5057 0 21:26 pts/2 00:00:06 chef
-aj 7842 5057 0 21:26 pts/2 00:00:06 poos
-RUNNING_PS
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~RUNNING_PS)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 chef
+ aj 7842 5057 0 21:26 pts/2 00:00:06 poos
+ RUNNING_PS
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
expect(@provider).to receive(:shell_out!).and_return(@status)
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
end
it "should set running to false if the regex doesn't match" do
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
expect(@provider).to receive(:shell_out!).and_return(@status)
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
@@ -151,61 +151,61 @@ RUNNING_PS
describe "when starting the service" do
it "should call the start command if one is specified" do
@new_resource.start_command("/usr/sbin/invoke-rc.d chef startyousillysally")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef startyousillysally")
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef startyousillysally", default_env: false)
+ @provider.start_service
end
it "should call '/usr/sbin/invoke-rc.d service_name start' if no start command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} start")
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} start", default_env: false)
+ @provider.start_service
end
end
describe Chef::Provider::Service::Invokercd, "stop_service" do
it "should call the stop command if one is specified" do
@new_resource.stop_command("/usr/sbin/invoke-rc.d chef itoldyoutostop")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef itoldyoutostop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef itoldyoutostop", default_env: false)
+ @provider.stop_service
end
it "should call '/usr/sbin/invoke-rc.d service_name stop' if no stop command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} stop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} stop", default_env: false)
+ @provider.stop_service
end
end
describe "when restarting a service" do
it "should call 'restart' on the service_name if the resource supports it" do
- @new_resource.supports({ :restart => true })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restart")
- @provider.restart_service()
+ @new_resource.supports({ restart: true })
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restart", default_env: false)
+ @provider.restart_service
end
it "should call the restart_command if one has been specified" do
@new_resource.restart_command("/usr/sbin/invoke-rc.d chef restartinafire")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restartinafire")
- @provider.restart_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restartinafire", default_env: false)
+ @provider.restart_service
end
it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do
expect(@provider).to receive(:stop_service)
expect(@provider).to receive(:sleep).with(1)
expect(@provider).to receive(:start_service)
- @provider.restart_service()
+ @provider.restart_service
end
end
describe "when reloading a service" do
it "should call 'reload' on the service if it supports it" do
- @new_resource.supports({ :reload => true })
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef reload")
- @provider.reload_service()
+ @new_resource.supports({ reload: true })
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef reload", default_env: false)
+ @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
@new_resource.reload_command("/usr/sbin/invoke-rc.d chef lollerpants")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef lollerpants")
- @provider.reload_service()
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/invoke-rc.d chef lollerpants", default_env: false)
+ @provider.reload_service
end
end
end
diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb
index 12f97431ba..6cc0f88725 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -1,6 +1,7 @@
#
# Author:: Igor Afonov <afonov@gmail.com>
# Copyright:: Copyright 2011-2016, Igor Afonov
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,288 +45,288 @@ 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 }
- let(:plutil_stdout) { 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>
- <key>Label</key>
- <string>io.redis.redis-server</string>
-</dict>
-</plist>
-XML
+ let(:plutil_stdout) { 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>
+ <key>Label</key>
+ <string>io.redis.redis-server</string>
+ </dict>
+ </plist>
+ XML
%w{Daemon Agent}.each do |service_type|
["redis-server", "io.redis.redis-server"].each do |service_name|
- ["10.9", "10.10", "10.11"].each do |platform_version|
- let(:plist) { "/Library/LaunchDaemons/io.redis.redis-server.plist" }
- let(:session) { StringIO.new }
- if service_type == "Agent"
- let(:plist) { "/Library/LaunchAgents/io.redis.redis-server.plist" }
- let(:session) { "-S Aqua " }
- let(:su_cmd) { "su -l igor -c" }
- if platform_version == "10.9"
- let(:su_cmd) { "su igor -c" }
- end
- end
- let(:service_label) { "io.redis.redis-server" }
- before do
- allow(Dir).to receive(:glob).and_return([plist], [])
- allow(Etc).to receive(:getlogin).and_return("igor")
- 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).
- with(/(#{su_cmd} '#{cmd}'|#{cmd})/).
- and_return(double("Status",
- :stdout => launchctl_stdout, :exitstatus => 0))
- allow(File).to receive(:exists?).and_return([true], [])
- allow(provider).to receive(:shell_out_with_systems_locale!).
- with(/plutil -convert xml1 -o/).
- and_return(double("Status", :stdout => plutil_stdout))
- end
+ let(:plist) { "/Library/LaunchDaemons/io.redis.redis-server.plist" }
+ let(:session) { StringIO.new }
+ if service_type == "Agent"
+ let(:plist) { "/Library/LaunchAgents/io.redis.redis-server.plist" }
+ let(:session) { "-S Aqua " }
+ let(:su_cmd) { "su -l igor -c" }
+ 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], [])
+ @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("10.11.1")
+ cmd = "launchctl list #{service_label}"
+ allow(provider).to receive(:shell_out)
+ .with(/(#{su_cmd} '#{cmd}'|#{cmd})/, default_env: false)
+ .and_return(double("Status",
+ stdout: launchctl_stdout, exitstatus: 0))
+ allow(File).to receive(:exist?).and_return([true], [])
+ allow(provider).to receive(:shell_out!)
+ .with(/plutil -convert xml1 -o/, default_env: false)
+ .and_return(double("Status", stdout: plutil_stdout))
+ end
- context "#{service_name} that is a #{service_type} running Osx #{platform_version}" do
- let(:new_resource) { Chef::Resource::MacosxService.new(service_name) }
- let!(:current_resource) { Chef::Resource::MacosxService.new(service_name) }
+ context "#{service_name} that is a #{service_type}" do
+ let(:new_resource) { Chef::Resource::MacosxService.new(service_name) }
+ let!(:current_resource) { Chef::Resource::MacosxService.new(service_name) }
- describe "#load_current_resource" do
+ describe "#load_current_resource" do
- # CHEF-5223 "you can't glob for a file that hasn't been converged
- # onto the node yet."
- context "when the plist doesn't exist" do
+ # CHEF-5223 "you can't glob for a file that hasn't been converged
+ # onto the node yet."
+ context "when the plist doesn't exist" do
- def run_resource_setup_for_action(action)
- new_resource.action(action)
- provider.action = action
- provider.load_current_resource
- provider.define_resource_requirements
- provider.process_resource_requirements
- end
+ def run_resource_setup_for_action(action)
+ new_resource.action(action)
+ provider.action = action
+ provider.load_current_resource
+ provider.define_resource_requirements
+ provider.process_resource_requirements
+ end
- before do
- allow(Dir).to receive(:glob).and_return([])
- allow(File).to receive(:exists?).and_return([true], [])
- allow(provider).to receive(:shell_out!).
- with(/plutil -convert xml1 -o/).
- and_raise(Mixlib::ShellOut::ShellCommandFailed)
- end
+ before do
+ allow(Dir).to receive(:glob).and_return([])
+ allow(File).to receive(:exist?).and_return([true], [])
+ allow(provider).to receive(:shell_out!)
+ .with(/plutil -convert xml1 -o/)
+ .and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ end
- it "works for action :nothing" do
- expect { run_resource_setup_for_action(:nothing) }.not_to raise_error
- end
+ it "works for action :nothing" do
+ expect { run_resource_setup_for_action(:nothing) }.not_to raise_error
+ end
- it "works for action :start" do
- expect { run_resource_setup_for_action(:start) }.not_to raise_error
- end
+ it "works for action :start" do
+ expect { run_resource_setup_for_action(:start) }.not_to raise_error
+ end
- it "errors if action is :enable" do
- expect { run_resource_setup_for_action(:enable) }.to raise_error(Chef::Exceptions::Service)
- end
+ it "errors if action is :enable" do
+ expect { run_resource_setup_for_action(:enable) }.to raise_error(Chef::Exceptions::Service)
+ end
- it "errors if action is :disable" do
- expect { run_resource_setup_for_action(:disable) }.to raise_error(Chef::Exceptions::Service)
- end
+ it "errors if action is :disable" do
+ expect { run_resource_setup_for_action(:disable) }.to raise_error(Chef::Exceptions::Service)
end
+ end
- context "when launchctl returns pid in service list" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
-{
- "LimitLoadToSessionType" = "System";
- "Label" = "io.redis.redis-server";
- "TimeOut" = 30;
- "OnDemand" = false;
- "LastExitStatus" = 0;
- "PID" = 62803;
- "Program" = "do_some.sh";
- "ProgramArguments" = (
- "path/to/do_something.sh";
- "-f";
- );
-};
-SVC_LIST
+ context "when launchctl returns pid in service list" do
+ let(:launchctl_stdout) { StringIO.new <<~SVC_LIST }
+ {
+ "LimitLoadToSessionType" = "System";
+ "Label" = "io.redis.redis-server";
+ "TimeOut" = 30;
+ "OnDemand" = false;
+ "LastExitStatus" = 0;
+ "PID" = 62803;
+ "Program" = "do_some.sh";
+ "ProgramArguments" = (
+ "path/to/do_something.sh";
+ "-f";
+ );
+ };
+ SVC_LIST
- before do
- provider.load_current_resource
- end
+ before do
+ provider.load_current_resource
+ end
- it "sets resource running state to true" do
- expect(provider.current_resource.running).to be_truthy
- end
+ it "sets resource running state to true" do
+ expect(provider.current_resource.running).to be_truthy
+ end
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
- end
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
end
+ end
- describe "running unsupported actions" do
- before do
- allow(Dir).to receive(:glob).and_return(["#{plist}"], [])
- allow(File).to receive(:exists?).and_return([true], [])
- end
- it "should throw an exception when reload action is attempted" do
- expect { provider.run_action(:reload) }.to raise_error(Chef::Exceptions::UnsupportedAction)
- end
+ describe "running unsupported actions" do
+ before do
+ allow(Dir).to receive(:glob).and_return([(plist).to_s], [])
+ allow(File).to receive(:exist?).and_return([true], [])
end
- context "when launchctl returns empty service pid" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
-{
- "LimitLoadToSessionType" = "System";
- "Label" = "io.redis.redis-server";
- "TimeOut" = 30;
- "OnDemand" = false;
- "LastExitStatus" = 0;
- "Program" = "do_some.sh";
- "ProgramArguments" = (
- "path/to/do_something.sh";
- "-f";
- );
-};
-SVC_LIST
+ it "should throw an exception when reload action is attempted" do
+ expect { provider.run_action(:reload) }.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
+ end
+ context "when launchctl returns empty service pid" do
+ let(:launchctl_stdout) { StringIO.new <<~SVC_LIST }
+ {
+ "LimitLoadToSessionType" = "System";
+ "Label" = "io.redis.redis-server";
+ "TimeOut" = 30;
+ "OnDemand" = false;
+ "LastExitStatus" = 0;
+ "Program" = "do_some.sh";
+ "ProgramArguments" = (
+ "path/to/do_something.sh";
+ "-f";
+ );
+ };
+ SVC_LIST
- before do
- provider.load_current_resource
- end
+ before do
+ provider.load_current_resource
+ end
- it "sets resource running state to false" do
- expect(provider.current_resource.running).to be_falsey
- end
+ it "sets resource running state to false" do
+ expect(provider.current_resource.running).to be_falsey
+ end
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
- end
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
end
+ end
+
+ context "when launchctl doesn't return service entry at all" do
+ let(:launchctl_stdout) { StringIO.new <<~SVC_LIST }
+ Could not find service "io.redis.redis-server" in domain for system
+ SVC_LIST
- context "when launchctl doesn't return service entry at all" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
-Could not find service "io.redis.redis-server" in domain for system
-SVC_LIST
+ it "sets service running state to false" do
+ provider.load_current_resource
+ expect(provider.current_resource.running).to be_falsey
+ end
- it "sets service running state to false" do
+ context "and plist for service is not available" do
+ before do
+ allow(Dir).to receive(:glob).and_return([])
provider.load_current_resource
- expect(provider.current_resource.running).to be_falsey
end
- context "and plist for service is not available" do
- before do
- allow(Dir).to receive(:glob).and_return([])
- provider.load_current_resource
- end
-
- it "sets resouce enabled state to false" do
- expect(provider.current_resource.enabled).to be_falsey
- end
+ it "sets resouce enabled state to false" do
+ expect(provider.current_resource.enabled).to be_falsey
end
+ end
- context "and plist for service is available" do
- before do
- allow(Dir).to receive(:glob).and_return(["#{plist}"], [])
- provider.load_current_resource
- end
+ context "and plist for service is available" do
+ before do
+ allow(Dir).to receive(:glob).and_return([(plist).to_s], [])
+ provider.load_current_resource
+ end
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
- end
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
end
+ end
- describe "and several plists match service name" do
- it "throws exception" do
- allow(Dir).to receive(:glob).and_return(["#{plist}",
- "/Users/wtf/something.plist"])
- provider.load_current_resource
- provider.define_resource_requirements
- expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
- end
+ describe "and several plists match service name" do
+ it "throws exception" do
+ allow(Dir).to receive(:glob).and_return([(plist).to_s,
+ "/Users/wtf/something.plist"])
+ provider.load_current_resource
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
end
end
- describe "#start_service" do
- before do
- allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(false)
- end
+ end
+ describe "#start_service" do
+ before do
+ allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
+ provider.load_current_resource
+ allow(current_resource).to receive(:running).and_return(false)
+ end
- it "calls the start command if one is specified and service is not running" do
- allow(new_resource).to receive(:start_command).and_return("cowsay dirty")
+ it "calls the start command if one is specified and service is not running" do
+ allow(new_resource).to receive(:start_command).and_return("cowsay dirty")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("cowsay dirty")
- provider.start_service
- end
+ expect(provider).to receive(:shell_out!).with("cowsay dirty", default_env: false)
+ provider.start_service
+ end
- 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")
+ it "shows warning message if service is already running" do
+ allow(current_resource).to receive(:running).and_return(true)
+ expect(logger).to receive(:trace).with("macosx_service[#{service_name}] already running, not starting")
- provider.start_service
- end
+ provider.start_service
+ end
- it "starts service via launchctl if service found" do
- cmd = "launchctl load -w " + session + plist
- expect(provider).to receive(:shell_out_with_systems_locale).
- with(/(#{su_cmd} .#{cmd}.|#{cmd})/).
- and_return(0)
+ it "starts service via launchctl if service found" do
+ cmd = "launchctl load -w " + session + plist
+ expect(provider).to receive(:shell_out)
+ .with(/(#{su_cmd} .#{cmd}.|#{cmd})/, default_env: false)
+ .and_return(0)
- provider.start_service
- end
+ provider.start_service
end
+ end
- describe "#stop_service" do
- before do
- allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
+ describe "#stop_service" do
+ before do
+ allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(true)
- end
+ provider.load_current_resource
+ allow(current_resource).to receive(:running).and_return(true)
+ end
- it "calls the stop command if one is specified and service is running" do
- allow(new_resource).to receive(:stop_command).and_return("kill -9 123")
+ it "calls the stop command if one is specified and service is running" do
+ allow(new_resource).to receive(:stop_command).and_return("kill -9 123")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("kill -9 123")
- provider.stop_service
- end
+ expect(provider).to receive(:shell_out!).with("kill -9 123", default_env: false)
+ provider.stop_service
+ end
- 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")
+ it "shows warning message if service is not running" do
+ allow(current_resource).to receive(:running).and_return(false)
+ expect(logger).to receive(:trace).with("macosx_service[#{service_name}] not running, not stopping")
- provider.stop_service
- end
+ provider.stop_service
+ end
- it "stops the service via launchctl if service found" do
- cmd = "launchctl unload -w " + plist
- expect(provider).to receive(:shell_out_with_systems_locale).
- with(/(#{su_cmd} .#{cmd}.|#{cmd})/).
- and_return(0)
+ it "stops the service via launchctl if service found" do
+ cmd = "launchctl unload -w " + plist
+ expect(provider).to receive(:shell_out)
+ .with(/(#{su_cmd} .#{cmd}.|#{cmd})/, default_env: false)
+ .and_return(0)
- provider.stop_service
- end
+ provider.stop_service
end
+ end
- describe "#restart_service" do
- before do
- allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ describe "#restart_service" do
+ before do
+ allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(true)
- allow(provider).to receive(:sleep)
- end
+ provider.load_current_resource
+ allow(current_resource).to receive(:running).and_return(true)
+ allow(provider).to receive(:sleep)
+ end
- it "issues a command if given" do
- allow(new_resource).to receive(:restart_command).and_return("reload that thing")
+ it "issues a command if given" do
+ allow(new_resource).to receive(:restart_command).and_return("reload that thing")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("reload that thing")
- provider.restart_service
- end
+ expect(provider).to receive(:shell_out!).with("reload that thing", default_env: false)
+ provider.restart_service
+ end
- it "stops and then starts service" do
- expect(provider).to receive(:unload_service)
- expect(provider).to receive(:load_service);
+ it "stops and then starts service" do
+ expect(provider).to receive(:unload_service)
+ expect(provider).to receive(:load_service)
- provider.restart_service
- end
+ provider.restart_service
end
end
end
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
index 872a3bc400..dfa491fc38 100644
--- a/spec/unit/provider/service/openbsd_service_spec.rb
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -31,11 +31,11 @@ end
describe Chef::Provider::Service::Openbsd do
let(:node) do
node = Chef::Node.new
- node.automatic_attrs[:command] = { :ps => "ps -ax" }
+ node.automatic_attrs[:command] = { ps: "ps -ax" }
node
end
- let(:supports) { { :status => false } }
+ let(:supports) { { status: false } }
let(:new_resource) do
new_resource = Chef::Resource::Service.new("sndiod")
@@ -93,7 +93,7 @@ describe Chef::Provider::Service::Openbsd do
end
context "when a status command has been specified" do
- let(:status) { double(:stdout => "", :exitstatus => 0) }
+ let(:status) { double(stdout: "", exitstatus: 0) }
before do
new_resource.status_command("/bin/chefhasmonkeypants status")
@@ -106,9 +106,9 @@ describe Chef::Provider::Service::Openbsd do
end
context "when the service supports status" do
- let(:status) { double(:stdout => "", :exitstatus => 0) }
+ let(:status) { double(stdout: "", exitstatus: 0) }
- let(:supports) { { :status => true } }
+ let(:supports) { { status: true } }
it "should run '/etc/rc.d/service_name status'" do
expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
@@ -178,7 +178,8 @@ describe Chef::Provider::Service::Openbsd do
[
%Q{thing_#{provider.builtin_service_enable_variable_name}="YES"},
%Q{#{provider.builtin_service_enable_variable_name}="NO"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -189,8 +190,9 @@ describe Chef::Provider::Service::Openbsd do
let(:lines) do
[
%Q{#{provider.builtin_service_enable_variable_name}_thing="YES"},
- %Q{#{provider.builtin_service_enable_variable_name}},
- ] end
+ (provider.builtin_service_enable_variable_name).to_s,
+ ]
+ end
it "sets enabled based on the exact match (false)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be false
@@ -202,7 +204,8 @@ describe Chef::Provider::Service::Openbsd do
[
%Q{thing_#{provider.builtin_service_enable_variable_name}="NO"},
%Q{#{provider.builtin_service_enable_variable_name}="YES"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -214,7 +217,8 @@ describe Chef::Provider::Service::Openbsd do
[
%Q{#{provider.builtin_service_enable_variable_name}_thing="NO"},
%Q{#{provider.builtin_service_enable_variable_name}="YES"},
- ] end
+ ]
+ end
it "sets enabled based on the exact match (true)" do
provider.determine_enabled_status!
expect(current_resource.enabled).to be true
@@ -285,26 +289,26 @@ describe Chef::Provider::Service::Openbsd do
describe Chef::Provider::Service::Openbsd, "start_service" do
it "should call the start command if one is specified" do
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()
+ expect(provider).to receive(:shell_out!).with("/etc/rc.d/chef startyousillysally", default_env: false)
+ provider.start_service
end
it "should call '/usr/local/etc/rc.d/service_name start' if no start command is specified" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} start")
- provider.start_service()
+ expect(provider).to receive(:shell_out!).with("/etc/rc.d/#{new_resource.service_name} start", default_env: false)
+ provider.start_service
end
end
describe Chef::Provider::Service::Openbsd, "stop_service" do
it "should call the stop command if one is specified" do
new_resource.stop_command("/etc/init.d/chef itoldyoutostop")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop")
- provider.stop_service()
+ expect(provider).to receive(:shell_out!).with("/etc/init.d/chef itoldyoutostop", default_env: false)
+ provider.stop_service
end
it "should call '/usr/local/etc/rc.d/service_name stop' if no stop command is specified" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} stop")
- provider.stop_service()
+ expect(provider).to receive(:shell_out!).with("/etc/rc.d/#{new_resource.service_name} stop", default_env: false)
+ provider.stop_service
end
end
@@ -312,21 +316,21 @@ describe Chef::Provider::Service::Openbsd do
context "when the new_resource supports restart" do
let(:supports) { { restart: true } }
it "should call 'restart' on the service_name if the resource supports it" do
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart")
- provider.restart_service()
+ expect(provider).to receive(:shell_out!).with("/etc/rc.d/#{new_resource.service_name} restart", default_env: false)
+ provider.restart_service
end
end
it "should call the restart_command if one has been specified" do
new_resource.restart_command("/etc/init.d/chef restartinafire")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire")
- provider.restart_service()
+ expect(provider).to receive(:shell_out!).with("/etc/init.d/chef restartinafire", default_env: false)
+ provider.restart_service
end
it "otherwise it should call stop and start" do
expect(provider).to receive(:stop_service)
expect(provider).to receive(:start_service)
- provider.restart_service()
+ provider.restart_service
end
end
end
diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb
index 40f9bc3a91..d5d2c7ddc0 100644
--- a/spec/unit/provider/service/redhat_spec.rb
+++ b/spec/unit/provider/service/redhat_spec.rb
@@ -16,24 +16,24 @@
# limitations under the License.
#
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
+require "spec_helper"
require "ostruct"
shared_examples_for "define_resource_requirements_common" do
it "should raise an error if /sbin/chkconfig does not exist" do
- allow(File).to receive(:exists?).with("/sbin/chkconfig").and_return(false)
+ allow(File).to receive(:exist?).with("/sbin/chkconfig").and_return(false)
allow(@provider).to receive(:shell_out).with("/sbin/service chef status").and_raise(Errno::ENOENT)
- allow(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_raise(Errno::ENOENT)
+ allow(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_raise(Errno::ENOENT)
@provider.load_current_resource
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
it "should not raise an error if the service exists but is not added to any runlevels" do
- status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "")
+ status = double("Status", exitstatus: 0, stdout: "" , stderr: "")
expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "", :stderr => "service chef supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add chef')")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "", stderr: "service chef supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add chef')")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
@provider.load_current_resource
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.not_to raise_error
@@ -44,7 +44,7 @@ describe "Chef::Provider::Service::Redhat" do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "foo" }
+ @node.automatic_attrs[:command] = { ps: "foo" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -55,7 +55,7 @@ describe "Chef::Provider::Service::Redhat" do
@provider = Chef::Provider::Service::Redhat.new(@new_resource, @run_context)
@provider.action = :start
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- allow(File).to receive(:exists?).with("/sbin/chkconfig").and_return(true)
+ allow(File).to receive(:exist?).with("/sbin/chkconfig").and_return(true)
end
describe "while not in why run mode" do
@@ -65,13 +65,13 @@ describe "Chef::Provider::Service::Redhat" do
describe "load current resource" do
before do
- status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "")
+ status = double("Status", exitstatus: 0, stdout: "" , stderr: "")
allow(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
end
it "sets supports[:status] to true by default" do
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
@provider.load_current_resource
expect(@provider.supports[:status]).to be true
@@ -80,9 +80,9 @@ describe "Chef::Provider::Service::Redhat" do
it "lets the user override supports[:status] in the new_resource" do
@new_resource.supports( { status: false } )
@new_resource.pattern "myservice"
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
- foo_out = double("ps_command", :exitstatus => 0, :stdout => "a line that matches myservice", :stderr => "")
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
+ foo_out = double("ps_command", exitstatus: 0, stdout: "a line that matches myservice", stderr: "")
expect(@provider).to receive(:shell_out!).with("foo").and_return(foo_out)
expect(@provider.service_missing).to be false
expect(@provider).not_to receive(:shell_out).with("/sbin/service chef status")
@@ -91,16 +91,16 @@ describe "Chef::Provider::Service::Redhat" do
end
it "sets the current enabled status to true if the service is enabled for any run level" do
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
@provider.load_current_resource
expect(@current_resource.enabled).to be true
end
it "sets the current enabled status to false if the regex does not match" do
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:off 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:off 2:off 3:off 4:off 5:off 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
expect(@provider.load_current_resource).to eql(@current_resource)
expect(@current_resource.enabled).to be false
@@ -108,8 +108,8 @@ describe "Chef::Provider::Service::Redhat" do
it "sets the current enabled status to true if the service is enabled at specified run levels" do
@new_resource.run_levels([1, 2])
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:on 3:off 4:off 5:off 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:on 2:on 3:off 4:off 5:off 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
@provider.load_current_resource
expect(@current_resource.enabled).to be true
@@ -118,8 +118,8 @@ describe "Chef::Provider::Service::Redhat" do
it "sets the current enabled status to false if the service is enabled at a run level it should not" do
@new_resource.run_levels([1, 2])
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:on 3:on 4:off 5:off 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:on 2:on 3:on 4:off 5:off 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
@provider.load_current_resource
expect(@current_resource.enabled).to be false
@@ -128,8 +128,8 @@ describe "Chef::Provider::Service::Redhat" do
it "sets the current enabled status to false if the service is not enabled at specified run levels" do
@new_resource.run_levels([ 2 ])
- chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:off 3:off 4:off 5:off 6:off", :stderr => "")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", exitstatus: 0, stdout: "chef 0:off 1:on 2:off 3:off 4:off 5:off 6:off", stderr: "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
expect(@provider.service_missing).to be false
@provider.load_current_resource
expect(@current_resource.enabled).to be false
@@ -142,10 +142,10 @@ describe "Chef::Provider::Service::Redhat" do
context "when the service does not exist" do
before do
- status = double("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service")
+ status = double("Status", exitstatus: 1, stdout: "", stderr: "chef: unrecognized service")
expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
- chkconfig = double("Chkconfig", :existatus => 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", existatus: 1, stdout: "", stderr: "error reading information on service chef: No such file or directory")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
@provider.load_current_resource
@provider.define_resource_requirements
end
@@ -194,10 +194,10 @@ describe "Chef::Provider::Service::Redhat" do
it_should_behave_like "define_resource_requirements_common"
it "should not raise an error if the service does not exist" do
- status = double("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service")
+ status = double("Status", exitstatus: 1, stdout: "", stderr: "chef: unrecognized service")
expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
- chkconfig = double("Chkconfig", :existatus => 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory")
- expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0, 1]).and_return(chkconfig)
+ chkconfig = double("Chkconfig", existatus: 1, stdout: "", stderr: "error reading information on service chef: No such file or directory")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", returns: [0, 1]).and_return(chkconfig)
@provider.load_current_resource
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.not_to raise_error
diff --git a/spec/unit/provider/service/simple_service_spec.rb b/spec/unit/provider/service/simple_service_spec.rb
index 499e0cc2d3..2546f9cef7 100644
--- a/spec/unit/provider/service/simple_service_spec.rb
+++ b/spec/unit/provider/service/simple_service_spec.rb
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Provider::Service::Simple, "load_current_resource" do
before(:each) do
@node = Chef::Node.new
- @node.automatic_attrs[:command] = { :ps => "ps -ef" }
+ @node.automatic_attrs[:command] = { ps: "ps -ef" }
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -31,12 +31,12 @@ describe Chef::Provider::Service::Simple, "load_current_resource" do
@provider = Chef::Provider::Service::Simple.new(@new_resource, @run_context)
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ)
-aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
-aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
-aj 8119 6041 0 21:34 pts/3 00:00:03 vi simple_service_spec.rb
-NOMOCKINGSTRINGSPLZ
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~NOMOCKINGSTRINGSPLZ)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb
+ aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash
+ aj 8119 6041 0 21:34 pts/3 00:00:03 vi simple_service_spec.rb
+ NOMOCKINGSTRINGSPLZ
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
end
@@ -50,19 +50,19 @@ NOMOCKINGSTRINGSPLZ
@provider.load_current_resource
end
- it "should raise error if the node has a nil ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => nil }
+ it "should raise error if the node has a nil ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: nil }
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
- it "should raise error if the node has an empty ps attribute and no other means to get status" do
- @node.automatic_attrs[:command] = { :ps => "" }
+ it "should raise error if the node has an empty ps property and no other means to get status" do
+ @node.automatic_attrs[:command] = { ps: "" }
@provider.define_resource_requirements
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
end
- describe "when we have a 'ps' attribute" do
+ describe "when we have a 'ps' property" do
it "should shell_out! the node's ps command" do
expect(@provider).to receive(:shell_out!).with(@node[:command][:ps]).and_return(@status)
@provider.load_current_resource
@@ -75,11 +75,11 @@ NOMOCKINGSTRINGSPLZ
end
it "should set running to true if the regex matches the output" do
- @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ)
-aj 7842 5057 0 21:26 pts/2 00:00:06 chef
-aj 7842 5057 0 21:26 pts/2 00:00:06 poos
-NOMOCKINGSTRINGSPLZ
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
+ @stdout = StringIO.new(<<~NOMOCKINGSTRINGSPLZ)
+ aj 7842 5057 0 21:26 pts/2 00:00:06 chef
+ aj 7842 5057 0 21:26 pts/2 00:00:06 poos
+ NOMOCKINGSTRINGSPLZ
+ @status = double("Status", exitstatus: 0, stdout: @stdout)
allow(@provider).to receive(:shell_out!).and_return(@status)
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
@@ -106,9 +106,9 @@ 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}")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("#{@new_resource.start_command}")
- @provider.start_service()
+ @new_resource.start_command((@new_resource.start_command).to_s)
+ expect(@provider).to receive(:shell_out!).with((@new_resource.start_command).to_s, default_env: false)
+ @provider.start_service
end
it "should raise an exception if no start command is specified" do
@@ -121,8 +121,8 @@ NOMOCKINGSTRINGSPLZ
describe "when stopping a service" do
it "should call the stop command if one is specified" do
@new_resource.stop_command("/etc/init.d/themadness stop")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/themadness stop")
- @provider.stop_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/themadness stop", default_env: false)
+ @provider.stop_service
end
it "should raise an exception if no stop command is specified" do
@@ -135,8 +135,8 @@ NOMOCKINGSTRINGSPLZ
describe Chef::Provider::Service::Simple, "restart_service" do
it "should call the restart command if one has been specified" do
@new_resource.restart_command("/etc/init.d/foo restart")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/foo restart")
- @provider.restart_service()
+ expect(@provider).to receive(:shell_out!).with("/etc/init.d/foo restart", default_env: false)
+ @provider.restart_service
end
it "should raise an exception if the resource doesn't support restart, no restart command is provided, and no stop command is provided" do
@@ -149,7 +149,7 @@ NOMOCKINGSTRINGSPLZ
expect(@provider).to receive(:stop_service)
expect(@provider).to receive(:sleep).with(1)
expect(@provider).to receive(:start_service)
- @provider.restart_service()
+ @provider.restart_service
end
end
@@ -162,8 +162,8 @@ NOMOCKINGSTRINGSPLZ
it "should should run the user specified reload command if one is specified" do
@new_resource.reload_command("kill -9 1")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("kill -9 1")
- @provider.reload_service()
+ expect(@provider).to receive(:shell_out!).with("kill -9 1", default_env: false)
+ @provider.reload_service
end
end
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..9c44f727dc 100644
--- a/spec/unit/provider/service/solaris_smf_service_spec.rb
+++ b/spec/unit/provider/service/solaris_smf_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Toomas Pelberg (<toomasp@gmx.net>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -74,19 +74,19 @@ describe Chef::Provider::Service::Solaris do
].join("\n")
# shell_out! return value for a service that is running
- @enabled_svc_status = double("Status", :exitstatus => 0, :stdout => enabled_svc_stdout, :stdin => "", :stderr => "")
+ @enabled_svc_status = double("Status", exitstatus: 0, stdout: enabled_svc_stdout, stdin: "", stderr: "")
# shell_out! return value for a service that is disabled
- @disabled_svc_status = double("Status", :exitstatus => 0, :stdout => disabled_svc_stdout, :stdin => "", :stderr => "")
+ @disabled_svc_status = double("Status", exitstatus: 0, stdout: disabled_svc_stdout, stdin: "", stderr: "")
# shell_out! return value for a service that is in maintenance mode
- @maintenance_svc_status = double("Status", :exitstatus => 0, :stdout => maintenance_svc_stdout, :stdin => "", :stderr => "")
+ @maintenance_svc_status = double("Status", exitstatus: 0, stdout: maintenance_svc_stdout, stdin: "", stderr: "")
# shell_out! return value for a service that does not exist
- @no_svc_status = double("Status", :exitstatus => 1, :stdout => "", :stdin => "", :stderr => "svcs: Pattern 'chef' doesn't match any instances\n")
+ @no_svc_status = double("Status", exitstatus: 1, stdout: "", stdin: "", stderr: "svcs: Pattern 'chef' doesn't match any instances\n")
# shell_out! return value for a successful execution
- @success = double("clear", :exitstatus => 0, :stdout => "", :stdin => "", :stderr => "")
+ @success = double("clear", exitstatus: 0, stdout: "", stdin: "", stderr: "")
end
it "should raise an error if /bin/svcs and /usr/sbin/svcadm are not executable" do
@@ -116,18 +116,19 @@ describe Chef::Provider::Service::Solaris do
describe "when discovering the current service state" 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(@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
it "should return the current resource" 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!).with("/bin/svcs", "-l", "chef", { returns: [0, 1] }).and_return(@enabled_svc_status)
expect(@provider.load_current_resource).to eql(@current_resource)
end
it "should call '/bin/svcs -l service_name'" 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!).with("/bin/svcs", "-l", "chef", { returns: [0, 1] }).and_return(@enabled_svc_status)
@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)
@@ -191,20 +192,47 @@ describe Chef::Provider::Service::Solaris do
expect(@provider.enable_service).to be_truthy
# now we are enabled
- 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!).with("/bin/svcs", "-l", "chef", { returns: [0, 1] }).and_return(@enabled_svc_status)
@provider.load_current_resource
expect(@current_resource.enabled).to be_truthy
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
end
it "should call svcadm disable -s chef" 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("/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.disable_service).to be_truthy
@@ -212,10 +240,19 @@ describe Chef::Provider::Service::Solaris do
end
it "should call svcadm disable -s chef for stop_service" 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("/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
@@ -224,7 +261,7 @@ describe Chef::Provider::Service::Solaris do
describe "when reloading the service" do
before(:each) do
@provider.current_resource = @current_resource
- allow(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@enabled_svc_status)
+ allow(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { returns: [0, 1] }).and_return(@enabled_svc_status)
end
it "should call svcadm refresh chef" do
@@ -237,7 +274,7 @@ describe Chef::Provider::Service::Solaris do
describe "when the service doesn't exist" do
before(:each) do
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { :returns => [0, 1] }).and_return(@no_svc_status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", { returns: [0, 1] }).and_return(@no_svc_status)
end
it "should be marked not running" do
diff --git a/spec/unit/provider/service/systemd_service_spec.rb b/spec/unit/provider/service/systemd_service_spec.rb
index 4e25f499f6..34b8094974 100644
--- a/spec/unit/provider/service/systemd_service_spec.rb
+++ b/spec/unit/provider/service/systemd_service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Stephen Haynes (<sh@nomitor.com>)
# Author:: Davide Cavalca (<dcavalca@fb.com>)
-# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,39 +21,33 @@ require "spec_helper"
describe Chef::Provider::Service::Systemd do
- let(:node) do
- node = Chef::Node.new
- node.default["etc"] = Hash.new
- node.default["etc"]["passwd"] = {
- "joe" => {
- "uid" => 10000,
- },
- }
- node
- end
+ 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) }
let(:provider) { Chef::Provider::Service::Systemd.new(new_resource, run_context) }
let(:shell_out_success) do
- double("shell_out_with_systems_locale", :exitstatus => 0, :error? => false)
+ double("shell_out", exitstatus: 0, error?: false, stdout: "")
end
let(:shell_out_failure) do
- double("shell_out_with_systems_locale", :exitstatus => 1, :error? => true)
+ double("shell_out", exitstatus: 1, error?: true, stdout: "")
end
let(:current_resource) { Chef::Resource::Service.new(service_name) }
before(:each) do
allow(Chef::Resource::Service).to receive(:new).with(service_name).and_return(current_resource)
+ allow(Etc).to receive(:getpwnam).and_return(OpenStruct.new(uid: 10000))
end
describe "load_current_resource" do
@@ -62,6 +56,7 @@ describe Chef::Provider::Service::Systemd do
allow(provider).to receive(:is_active?).and_return(false)
allow(provider).to receive(:is_enabled?).and_return(false)
allow(provider).to receive(:is_masked?).and_return(false)
+ allow(provider).to receive(:is_indirect?).and_return(false)
end
it "should create a current resource with the name of the new resource" do
@@ -176,19 +171,19 @@ describe Chef::Provider::Service::Systemd do
it "should call the start command if one is specified" do
allow(new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally")
+ expect(provider).to receive(:shell_out!).with("/sbin/rsyslog startyousillysally", default_env: false)
provider.start_service
end
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_compacted!).with(systemctl_path, "--system", "start", service_name, default_env: false, timeout: 900).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_compacted!).with(systemctl_path, "--system", "start", service_name, timeout: 900)
provider.start_service
end
end
@@ -196,14 +191,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_compacted!).with(systemctl_path, "--user", "start", service_name, environment: { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, user: "joe", default_env: false, timeout: 900).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_compacted!).with(systemctl_path, "--user", "start", service_name, environment: { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, user: "joe", timeout: 900)
provider.start_service
end
end
@@ -211,13 +206,13 @@ describe Chef::Provider::Service::Systemd do
it "should call the restart command if one is specified" do
current_resource.running(true)
allow(new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally")
+ expect(provider).to receive(:shell_out!).with("/sbin/rsyslog restartyousillysally", default_env: false)
provider.restart_service
end
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_compacted!).with(systemctl_path, "--system", "restart", service_name, default_env: false, timeout: 900).and_return(shell_out_success)
provider.restart_service
end
@@ -226,7 +221,7 @@ describe Chef::Provider::Service::Systemd do
it "should call the reload command" do
current_resource.running(true)
allow(new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally")
+ expect(provider).to receive(:shell_out!).with("/sbin/rsyslog reloadyousillysally", default_env: false)
provider.reload_service
end
end
@@ -234,7 +229,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_compacted!).with(systemctl_path, "--system", "reload", service_name, default_env: false, timeout: 900).and_return(shell_out_success)
provider.reload_service
end
@@ -249,19 +244,19 @@ describe Chef::Provider::Service::Systemd do
it "should call the stop command if one is specified" do
current_resource.running(true)
allow(new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally")
+ expect(provider).to receive(:shell_out!).with("/sbin/rsyslog stopyousillysally", default_env: false)
provider.stop_service
end
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_compacted!).with(systemctl_path, "--system", "stop", service_name, timeout: 900, default_env: false).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_compacted!).with(systemctl_path, "--system", "stop", service_name, timeout: 900)
provider.stop_service
end
end
@@ -270,16 +265,16 @@ describe Chef::Provider::Service::Systemd do
before(:each) do
provider.current_resource = current_resource
current_resource.service_name(service_name)
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
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_compacted!).with(systemctl_path, "--system", "enable", service_name, timeout: 900).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_compacted!).with(systemctl_path, "--system", "disable", service_name, timeout: 900).and_return(shell_out_success)
provider.disable_service
end
end
@@ -288,34 +283,92 @@ describe Chef::Provider::Service::Systemd do
before(:each) do
provider.current_resource = current_resource
current_resource.service_name(service_name)
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
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_compacted!).with(systemctl_path, "--system", "mask", service_name, timeout: 900).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_compacted!).with(systemctl_path, "--system", "unmask", service_name, timeout: 900).and_return(shell_out_success)
provider.unmask_service
end
end
+ enabled_and_active = <<-STDOUT
+ ActiveState=active
+ UnitFileState=enabled
+ STDOUT
+ disabled_and_inactive = <<-STDOUT
+ ActiveState=disabled
+ UnitFileState=inactive
+ STDOUT
+ # No unit known for this service, and inactive
+ nil_and_inactive = <<-STDOUT
+ ActiveState=inactive
+ UnitFileState=
+ STDOUT
+
+ def with_systemctl_show(systemctl_path, stdout)
+ systemctl_show = [systemctl_path, "--system", "show", "-p", "UnitFileState", "-p", "ActiveState", service_name]
+ expect(provider).to receive(:shell_out!).with(*systemctl_show, {}).and_return(double(stdout: stdout, exitstatus: 0, error?: false))
+ end
+
+ describe "systemd_service_status" do
+ before(:each) do
+ provider.current_resource = current_resource
+ current_resource.service_name(service_name)
+ end
+
+ it "should return status if '#{systemctl_path} --system show -p UnitFileState -p ActiveState service_name' returns 0 and has nil" do
+ nil_and_inactive_h = {
+ "ActiveState" => "inactive",
+ "UnitFileState" => nil,
+ }
+ with_systemctl_show(systemctl_path, nil_and_inactive)
+ expect(provider.systemd_service_status).to eql(nil_and_inactive_h)
+ end
+
+ it "should error if '#{systemctl_path} --system show -p UnitFileState -p ActiveState service_name' misses fields" do
+ partial_systemctl_stdout = <<-STDOUT
+ ActiveState=inactive
+ STDOUT
+ with_systemctl_show(systemctl_path, partial_systemctl_stdout)
+ expect { provider.systemd_service_status }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "should error if '#{systemctl_path} --system show -p UnitFileState -p ActiveState service_name' returns non 0" do
+ systemctl_show = [systemctl_path, "--system", "show", "-p", "UnitFileState", "-p", "ActiveState", service_name]
+ allow(provider).to receive(:shell_out!).with(*systemctl_show, {}).and_return(shell_out_failure)
+ expect { provider.systemd_service_status }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
describe "is_active?" do
before(:each) do
provider.current_resource = current_resource
current_resource.service_name(service_name)
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
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)
+ it "should return true if service is active" do
+ with_systemctl_show(systemctl_path, enabled_and_active)
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)
+ it "should return false if service is not active" do
+ with_systemctl_show(systemctl_path, disabled_and_inactive)
+ expect(provider.is_active?).to be false
+ end
+
+ it "should return false if service is activating" do
+ enabled_and_activating = <<-STDOUT
+ ActiveState=activating
+ UnitFileState=enabled
+ STDOUT
+ with_systemctl_show(systemctl_path, enabled_and_activating)
expect(provider.is_active?).to be false
end
end
@@ -324,16 +377,39 @@ describe Chef::Provider::Service::Systemd do
before(:each) do
provider.current_resource = current_resource
current_resource.service_name(service_name)
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
+ end
+
+ it "should return true if service is enabled" do
+ with_systemctl_show(systemctl_path, enabled_and_active)
+ expect(provider.is_enabled?).to be true
+ end
+
+ it "should return false if service is disabled" do
+ with_systemctl_show(systemctl_path, disabled_and_inactive)
+ expect(provider.is_enabled?).to be false
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)
+ it "should return false if service has no unit file" do
+ with_systemctl_show(systemctl_path, nil_and_inactive)
+ expect(provider.is_enabled?).to be false
+ end
+
+ it "should return true if service is static" do
+ static_and_active = <<-STDOUT
+ ActiveState=active
+ UnitFileState=static
+ STDOUT
+ with_systemctl_show(systemctl_path, static_and_active)
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)
+ it "should return false if service is enabled-runtime" do
+ enabled_runtime_and_active = <<-STDOUT
+ ActiveState=active
+ UnitFileState=enabled-runtime
+ STDOUT
+ with_systemctl_show(systemctl_path, enabled_runtime_and_active)
expect(provider.is_enabled?).to be false
end
end
@@ -342,29 +418,64 @@ describe Chef::Provider::Service::Systemd do
before(:each) do
provider.current_resource = current_resource
current_resource.service_name(service_name)
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
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))
+ it "should return true if service is masked" do
+ masked_and_inactive = <<-STDOUT
+ ActiveState=inactive
+ UnitFileState=masked
+ STDOUT
+ with_systemctl_show(systemctl_path, masked_and_inactive)
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.is_masked?).to be true
+ it "should return false if service is masked-runtime" do
+ masked_runtime_and_inactive = <<-STDOUT
+ ActiveState=inactive
+ UnitFileState=masked-runtime
+ STDOUT
+ with_systemctl_show(systemctl_path, masked_runtime_and_inactive)
+ expect(provider.is_masked?).to be false
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))
+ it "should return false if service is enabled" do
+ with_systemctl_show(systemctl_path, enabled_and_active)
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))
+ it "should return false if service has no known unit file" do
+ with_systemctl_show(systemctl_path, nil_and_inactive)
expect(provider.is_masked?).to be false
end
end
+
+ describe "is_indirect?" do
+ before(:each) do
+ provider.current_resource = current_resource
+ current_resource.service_name(service_name)
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
+ end
+
+ it "should return true if service is indirect" do
+ indirect_and_inactive = <<-STDOUT
+ ActiveState=inactive
+ UnitFileState=indirect
+ STDOUT
+ with_systemctl_show(systemctl_path, indirect_and_inactive)
+ expect(provider.is_indirect?).to be true
+ end
+
+ it "should return false if service not indirect" do
+ with_systemctl_show(systemctl_path, enabled_and_active)
+ expect(provider.is_indirect?).to be false
+ end
+
+ it "should return false if service has no known unit file" do
+ with_systemctl_show(systemctl_path, nil_and_inactive)
+ expect(provider.is_indirect?).to be false
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/service/upstart_service_spec.rb b/spec/unit/provider/service/upstart_service_spec.rb
index fb5a418684..20cbef11ce 100644
--- a/spec/unit/provider/service/upstart_service_spec.rb
+++ b/spec/unit/provider/service/upstart_service_spec.rb
@@ -20,7 +20,7 @@ require "spec_helper"
describe Chef::Provider::Service::Upstart do
let(:shell_out_success) do
- double("shell_out_with_systems_locale", :exitstatus => 0, :error? => false)
+ double("shell_out", exitstatus: 0, error?: false)
end
before(:each) do
@@ -43,7 +43,7 @@ describe Chef::Provider::Service::Upstart do
it "should return /etc/event.d as the upstart job directory when running on Ubuntu 9.04" do
@node.automatic_attrs[:platform_version] = "9.04"
- #Chef::Platform.stub(:find_platform_and_version).and_return([ "ubuntu", "9.04" ])
+ # Chef::Platform.stub(:find_platform_and_version).and_return([ "ubuntu", "9.04" ])
@provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
expect(@provider.instance_variable_get(:@upstart_job_dir)).to eq("/etc/event.d")
expect(@provider.instance_variable_get(:@upstart_conf_suffix)).to eq("")
@@ -66,19 +66,15 @@ describe Chef::Provider::Service::Upstart do
describe "load_current_resource" do
before(:each) do
- @node.automatic_attrs[:command] = { :ps => "ps -ax" }
+ @node.automatic_attrs[:command] = { ps: "ps -ax" }
@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(:exist?).and_return(true)
allow(::File).to receive(:open).and_return(true)
end
@@ -100,28 +96,25 @@ 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
it "should set running to true if the goal state is 'start'" do
- @stdout = StringIO.new("rsyslog start/running")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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 goal state is 'start' but current state is not 'running'" do
- @stdout = StringIO.new("rsyslog start/starting")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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 goal state is 'stop'" do
- @stdout = StringIO.new("rsyslog stop/waiting")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ expect(@status).to receive(:stdout).and_return("rsyslog stop/waiting")
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
end
@@ -129,22 +122,19 @@ describe Chef::Provider::Service::Upstart do
describe "when the status command uses the new format with an instance" do
it "should set running to true if the goal state is 'start'" do
- @stdout = StringIO.new("rsyslog (test) start/running, process 100")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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 goal state is 'start' but current state is not 'running'" do
- @stdout = StringIO.new("rsyslog (test) start/starting, process 100")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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 goal state is 'stop'" do
- @stdout = StringIO.new("rsyslog (test) stop/waiting, process 100")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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
@@ -152,22 +142,19 @@ describe Chef::Provider::Service::Upstart do
describe "when the status command uses the old format" do
it "should set running to true if the goal state is 'start'" do
- @stdout = StringIO.new("rsyslog (start) running, process 32225")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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
- @stdout = StringIO.new("rsyslog (start) starting, process 32225")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ 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 goal state is 'stop'" do
- @stdout = StringIO.new("rsyslog (stop) waiting")
- allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ expect(@status).to receive(:stdout).and_return("rsyslog (stop) waiting")
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
end
@@ -180,34 +167,34 @@ describe Chef::Provider::Service::Upstart do
end
it "should set enabled to true when it finds 'starts on'" do
- @lines = double("start on filesystem", :gets => "start on filesystem")
+ @lines = double("start on filesystem", gets: "start on filesystem")
allow(::File).to receive(:open).and_yield(@lines)
expect(@current_resource).to receive(:running).with(false)
@provider.load_current_resource
end
it "should set enabled to false when it finds '#starts on'" do
- @lines = double("start on filesystem", :gets => "#start on filesystem")
+ @lines = double("start on filesystem", gets: "#start on filesystem")
allow(::File).to receive(:open).and_yield(@lines)
expect(@current_resource).to receive(:running).with(false)
@provider.load_current_resource
end
it "should assume disable when no job configuration file is found" do
- allow(::File).to receive(:exists?).and_return(false)
+ allow(::File).to receive(:exist?).and_return(false)
expect(@current_resource).to receive(:running).with(false)
@provider.load_current_resource
end
it "should track state when the upstart configuration file fails to load" do
- expect(File).to receive(:exists?).and_return false
+ expect(File).to receive(:exist?).and_return false
@provider.load_current_resource
expect(@provider.instance_variable_get("@config_file_found")).to eq(false)
end
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
@@ -255,7 +242,7 @@ describe Chef::Provider::Service::Upstart do
allow(@current_resource).to receive(:enabled).and_return(false)
expect(@file).to receive(:search_file_replace)
expect(@file).to receive(:write_file)
- @provider.enable_service()
+ @provider.enable_service
end
it "should disable the service if it is enabled" do
@@ -264,7 +251,7 @@ describe Chef::Provider::Service::Upstart do
allow(@current_resource).to receive(:enabled).and_return(true)
expect(@file).to receive(:search_file_replace)
expect(@file).to receive(:write_file)
- @provider.disable_service()
+ @provider.disable_service
end
end
@@ -278,20 +265,22 @@ 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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally")
- @provider.start_service()
+ @provider.upstart_service_running = false
+ @new_resource.start_command("/sbin/rsyslog startyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/sbin/rsyslog startyousillysally", default_env: false)
+ @provider.start_service
end
it "should call '/sbin/start service_name' if no start command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(shell_out_success)
- @provider.start_service()
+ @provider.upstart_service_running = false
+ expect(@provider).to receive(:shell_out!).with("/sbin/start #{@new_resource.service_name}", default_env: false).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)
- expect(@provider).not_to receive(:shell_out_with_systems_locale!)
- @provider.start_service()
+ @provider.upstart_service_running = true
+ expect(@provider).not_to receive(:shell_out!)
+ @provider.start_service
end
it "should pass parameters to the start command if they are provided" do
@@ -299,59 +288,64 @@ describe Chef::Provider::Service::Upstart do
@new_resource.parameters({ "OSD_ID" => "2" })
@provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start rsyslog OSD_ID=2").and_return(shell_out_success)
- @provider.start_service()
+ expect(@provider).to receive(:shell_out!).with("/sbin/start rsyslog OSD_ID=2", default_env: false).and_return(shell_out_success)
+ @provider.start_service
end
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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally")
- @provider.restart_service()
+ @new_resource.restart_command("/sbin/rsyslog restartyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/sbin/rsyslog restartyousillysally", default_env: false)
+ @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)
- @provider.restart_service()
+ 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()
+ expect(@provider).to receive(:shell_out!).with("/sbin/start #{@new_resource.service_name}", default_env: false).and_return(shell_out_success)
+ @provider.restart_service
end
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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally")
- @provider.reload_service()
+ @new_resource.reload_command("/sbin/rsyslog reloadyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/sbin/rsyslog reloadyousillysally", default_env: false)
+ @provider.reload_service
end
it "should call '/sbin/reload service_name' if no reload command is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/reload #{@new_resource.service_name}").and_return(shell_out_success)
- @provider.reload_service()
+ expect(@provider).to receive(:shell_out!).with("/sbin/reload #{@new_resource.service_name}", default_env: false).and_return(shell_out_success)
+ @provider.reload_service
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")
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally")
- @provider.stop_service()
+ @provider.upstart_service_running = true
+ @new_resource.stop_command("/sbin/rsyslog stopyousillysally")
+ expect(@provider).to receive(:shell_out!).with("/sbin/rsyslog stopyousillysally", default_env: false)
+ @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)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}").and_return(shell_out_success)
- @provider.stop_service()
+ @provider.upstart_service_running = true
+ expect(@provider).to receive(:shell_out!).with("/sbin/stop #{@new_resource.service_name}", default_env: false).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(@provider).not_to receive(:shell_out!).with("/sbin/stop #{@new_resource.service_name}", default_env: false)
+ @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 d4c451511d..b3a85715a8 100644
--- a/spec/unit/provider/service/windows_spec.rb
+++ b/spec/unit/provider/service/windows_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Nuo Yan <nuo@chef.io>
# Author:: Seth Chisamore <schisamo@chef.io>
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,18 +18,75 @@
#
require "spec_helper"
-require "mixlib/shellout"
describe Chef::Provider::Service::Windows, "load_current_resource" do
include_context "Win32"
- let(:new_resource) { Chef::Resource::WindowsService.new("chef") }
+ before do
+ stub_const("Chef::ReservedNames::Win32::Security", Class.new) unless windows?
+ end
+
+ 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
@@ -41,13 +98,18 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
Win32::Service::DEMAND_START = 0x00000003
Win32::Service::DISABLED = 0x00000004
+ allow(Win32::Service).to receive(:start).with(any_args).and_return(Win32::Service)
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"))
+ double("StatusStruct", current_state: "running")
+ )
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name)
+ .and_return(chef_service_config_info)
+
+ allow(Win32::Service).to receive(:delayed_start).with(chef_service_name).and_return(1)
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,121 +120,563 @@ 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(Win32::Service).to receive(:delayed_start).with(chef_service_name).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(Win32::Service).to receive(:delayed_start).with(chef_service_name).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
+
+ # properties 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
+
+ # properties 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 "does not configure service when run_as_user case is different" do
+ provider.current_resource.run_as_user = "JohnDoe"
+ provider.new_resource.run_as_user = "johndoe"
+ expect(Win32::Service).not_to receive(:configure)
+ provider.action_configure
+
+ provider.current_resource.run_as_user = "johndoe"
+ provider.new_resource.run_as_user = "JohnDoe"
+ expect(Win32::Service).not_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
+ let(:missing_service_warning_message) { "windows_service[#{chef_service_name}] does not exist. Maybe you need to prepend action :create" }
+
+ before do
+ allow(Win32::Service).to receive(:exists?).with(chef_service_name).and_return(false)
+
+ # This prevents warnings being logged during unit tests which adds to
+ # developer confusion when they aren't familiar with this specific test
+ allow(logger).to receive(:warn).with(missing_service_warning_message)
+ end
+
+ it "logs warning" do
+ expect(logger).to receive(:warn).with(missing_service_warning_message)
+ provider.action_configure
+ end
+
+ it "does not converge" do
+ provider.action_configure
+ expect(provider.resource_updated?).to be false
+ end
+
+ it "does not configure 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
+
+ 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
before(:each) do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "stopped"),
- double("StatusStruct", :current_state => "running"))
+ double("StatusStruct", current_state: "stopped"),
+ double("StatusStruct", current_state: "running")
+ )
end
- it "calls the start command if one is specified" do
- new_resource.start_command "sc start chef"
- 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
+ context "run_as_user user is specified" do
+ let(:run_as_user) { provider.new_resource.class.properties[:run_as_user].default }
+
+ before do
+ provider.new_resource.run_as_user run_as_user
+ end
+
+ it "configures service run_as_user and run_as_password" do
+ expect(provider).to receive(:configure_service_run_as_properties).and_call_original
+ expect(Win32::Service).to receive(:configure)
+ provider.start_service
+ end
end
- it "uses the built-in command if no start command is specified" do
- expect(Win32::Service).to receive(:start).with(new_resource.service_name)
- provider.start_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ context "run_as_user user is not specified" do
+ before do
+ expect(provider.new_resource.property_is_set?(:run_as_user)).to be false
+ end
+
+ it "does not configure service run_as_user and run_as_password" do
+ expect(Win32::Service).not_to receive(:configure)
+ provider.start_service
+ end
+ end
+
+ context "start_command is specified" do
+ let(:start_command) { "sc start #{chef_service_name}" }
+
+ before do
+ new_resource.start_command start_command
+ allow(provider).to receive(:shell_out!).with(start_command)
+ end
+
+ it "shells out the start_command" do
+ expect(provider).to receive(:shell_out!).with(start_command)
+ provider.start_service
+ end
+
+ it "does not call Win32::Service.start" do
+ expect(Win32::Service).not_to receive(:start)
+ provider.start_service
+ end
+
+ it "is updated by last action" do
+ provider.start_service
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "start_command is not specified" do
+ before do
+ expect(new_resource.start_command).to be_nil
+ end
+
+ it "uses the built-in command" do
+ expect(Win32::Service).to receive(:start).with(new_resource.service_name)
+ provider.start_service
+ end
+
+ it "does not shell out the start_command" do
+ expect(provider).not_to receive(:shell_out!)
+ provider.start_service
+ end
+
+ it "is updated by last action" do
+ provider.start_service
+ expect(new_resource).to be_updated_by_last_action
+ end
end
it "does nothing if the service does not exist" do
allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
- expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ expect(Win32::Service).not_to receive(:start)
provider.start_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).not_to be_updated_by_last_action
end
it "does nothing if the service is running" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "running"))
+ double("StatusStruct", current_state: "running")
+ )
provider.load_current_resource
expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
provider.start_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).not_to be_updated_by_last_action
end
- it "raises an error if the service is paused" do
- allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "paused"))
- provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
- expect { provider.start_service }.to raise_error( Chef::Exceptions::Service )
- expect(new_resource.updated_by_last_action?).to be_falsey
+ context "service is paused" do
+ before do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
+ double("StatusStruct", current_state: "paused")
+ )
+ provider.load_current_resource
+ end
+
+ it "raises error" do
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "does not start service" do
+ expect(Win32::Service).not_to receive(:start)
+ expect(provider).not_to receive(:shell_out!)
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "is not updated by last action" do
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
end
- it "waits and continues if the service is in start_pending" do
- allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "start pending"),
- double("StatusStruct", :current_state => "start pending"),
- double("StatusStruct", :current_state => "running"))
- provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
- provider.start_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ context "service is in start_pending" do
+ before do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
+ double("StatusStruct", current_state: "start pending"),
+ double("StatusStruct", current_state: "start pending"),
+ double("StatusStruct", current_state: "running")
+ )
+ provider.load_current_resource
+ end
+
+ it "waits until service is running" do
+ expect(provider).to receive(:wait_for_state).with(Chef::Provider::Service::Windows::RUNNING)
+ provider.start_service
+ end
+
+ it "does not start service" do
+ expect(Win32::Service).not_to receive(:start)
+ expect(provider).not_to receive(:shell_out!)
+ provider.start_service
+ end
+
+ it "is not updated by last action" do
+ provider.start_service
+ expect(new_resource).not_to be_updated_by_last_action
+ end
end
- it "fails if the service is in stop_pending" do
- allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "stop pending"))
- provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
- expect { provider.start_service }.to raise_error( Chef::Exceptions::Service )
- expect(new_resource.updated_by_last_action?).to be_falsey
+ context "service is in stop_pending" do
+ before do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
+ double("StatusStruct", current_state: "stop pending")
+ )
+ provider.load_current_resource
+ end
+
+ it "raises error" do
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "does not start service" do
+ expect(Win32::Service).not_to receive(:start)
+ expect(provider).not_to receive(:shell_out!)
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "is not updated by last action" do
+ expect { provider.start_service }.to raise_error(Chef::Exceptions::Service)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
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 do
new_resource.run_as_user(".\\wallace")
new_resource.run_as_password("Wensleydale")
end
- after do
- 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)
+ it "calls #grant_service_logon if the :run_as_user and :run_as_password properties are present" do
expect(provider).to receive(:grant_service_logon).and_return(true)
provider.start_service
end
it "does not grant user SeServiceLogonRight if it already has it" do
- expect(Win32::Service).to receive(:start)
expect(Chef::ReservedNames::Win32::Security).to receive(:get_account_right).with("wallace").and_return([service_right])
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(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
@@ -180,77 +684,83 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
before(:each) do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "running"),
- double("StatusStruct", :current_state => "stopped"))
+ double("StatusStruct", current_state: "running"),
+ double("StatusStruct", current_state: "stopped")
+ )
end
it "calls the stop command if one is specified" do
- new_resource.stop_command "sc stop chef"
- expect(provider).to receive(:shell_out!).with("#{new_resource.stop_command}").and_return("Stopping custom service")
+ new_resource.stop_command "sc stop #{chef_service_name}"
+ expect(provider).to receive(:shell_out!).with((new_resource.stop_command).to_s).and_return("Stopping custom service")
provider.stop_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "uses the built-in command if no stop command is specified" do
expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
provider.stop_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "does nothing if the service does not exist" do
allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
provider.stop_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
it "does nothing if the service is stopped" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "stopped"))
+ double("StatusStruct", current_state: "stopped")
+ )
provider.load_current_resource
expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
provider.stop_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
it "raises an error if the service is paused" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "paused"))
+ double("StatusStruct", current_state: "paused")
+ )
provider.load_current_resource
expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
expect { provider.stop_service }.to raise_error( Chef::Exceptions::Service )
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
it "waits and continue if the service is in stop_pending" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "stop pending"),
- double("StatusStruct", :current_state => "stop pending"),
- double("StatusStruct", :current_state => "stopped"))
+ double("StatusStruct", current_state: "stop pending"),
+ double("StatusStruct", current_state: "stop pending"),
+ double("StatusStruct", current_state: "stopped")
+ )
provider.load_current_resource
expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
provider.stop_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
it "fails if the service is in start_pending" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "start pending"))
+ double("StatusStruct", current_state: "start pending")
+ )
provider.load_current_resource
expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
expect { provider.stop_service }.to raise_error( Chef::Exceptions::Service )
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
it "passes custom timeout to the stop command if provided" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "running"))
+ double("StatusStruct", current_state: "running")
+ )
new_resource.timeout 1
expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
Timeout.timeout(2) do
expect { provider.stop_service }.to raise_error(Timeout::Error)
end
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
end
@@ -259,31 +769,33 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
it "calls the restart command if one is specified" do
new_resource.restart_command "sc restart"
- expect(provider).to receive(:shell_out!).with("#{new_resource.restart_command}")
+ expect(provider).to receive(:shell_out!).with((new_resource.restart_command).to_s)
provider.restart_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "stops then starts the service if it is running" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "running"),
- double("StatusStruct", :current_state => "stopped"),
- double("StatusStruct", :current_state => "stopped"),
- double("StatusStruct", :current_state => "running"))
+ double("StatusStruct", current_state: "running"),
+ double("StatusStruct", current_state: "stopped"),
+ double("StatusStruct", current_state: "stopped"),
+ double("StatusStruct", current_state: "running")
+ )
expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
expect(Win32::Service).to receive(:start).with(new_resource.service_name)
provider.restart_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "just starts the service if it is stopped" do
allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
- double("StatusStruct", :current_state => "stopped"),
- double("StatusStruct", :current_state => "stopped"),
- double("StatusStruct", :current_state => "running"))
+ double("StatusStruct", current_state: "stopped"),
+ double("StatusStruct", current_state: "stopped"),
+ double("StatusStruct", current_state: "running")
+ )
expect(Win32::Service).to receive(:start).with(new_resource.service_name)
provider.restart_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "does nothing if the service does not exist" do
@@ -291,7 +803,7 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
provider.restart_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
end
@@ -299,34 +811,37 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
describe Chef::Provider::Service::Windows, "enable_service" do
before(:each) do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "disabled"))
+ double("ConfigStruct", start_type: "disabled")
+ )
end
it "enables service" do
- expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::AUTO_START)
+ expect(Win32::Service).to receive(:configure).with(service_name: new_resource.service_name, start_type: Win32::Service::AUTO_START)
provider.enable_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "does nothing if the service does not exist" do
allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
expect(Win32::Service).not_to receive(:configure)
provider.enable_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
end
describe Chef::Provider::Service::Windows, "action_enable" do
it "does nothing if the service is enabled" do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "auto start"))
+ double("ConfigStruct", start_type: "auto start")
+ )
expect(provider).not_to receive(:enable_service)
provider.action_enable
end
it "enables the service if it is not set to automatic start" do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "disabled"))
+ double("ConfigStruct", start_type: "disabled")
+ )
expect(provider).to receive(:enable_service)
provider.action_enable
end
@@ -335,14 +850,16 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
describe Chef::Provider::Service::Windows, "action_disable" do
it "does nothing if the service is disabled" do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "disabled"))
+ double("ConfigStruct", start_type: "disabled")
+ )
expect(provider).not_to receive(:disable_service)
provider.action_disable
end
it "disables the service if it is not set to disabled" do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "auto start"))
+ double("ConfigStruct", start_type: "auto start")
+ )
expect(provider).to receive(:disable_service)
provider.action_disable
end
@@ -351,54 +868,60 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
describe Chef::Provider::Service::Windows, "disable_service" do
before(:each) do
allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
- double("ConfigStruct", :start_type => "auto start"))
+ double("ConfigStruct", start_type: "auto start")
+ )
end
it "disables service" do
expect(Win32::Service).to receive(:configure)
provider.disable_service
- expect(new_resource.updated_by_last_action?).to be_truthy
+ expect(new_resource).to be_updated_by_last_action
end
it "does nothing if the service does not exist" do
allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
expect(Win32::Service).not_to receive(:configure)
provider.disable_service
- expect(new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource).to_not be_updated_by_last_action
end
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
end
+
+ it "calls converge_delayed_start" do
+ expect(provider).to receive(:converge_delayed_start)
+ provider.action_configure_startup
+ end
end
describe Chef::Provider::Service::Windows, "set_start_type" do
it "when called with :automatic it calls Win32::Service#configure with Win32::Service::AUTO_START" do
- expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::AUTO_START)
+ expect(Win32::Service).to receive(:configure).with(service_name: new_resource.service_name, start_type: Win32::Service::AUTO_START)
provider.send(:set_startup_type, :automatic)
end
it "when called with :manual it calls Win32::Service#configure with Win32::Service::DEMAND_START" do
- expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::DEMAND_START)
+ expect(Win32::Service).to receive(:configure).with(service_name: new_resource.service_name, start_type: Win32::Service::DEMAND_START)
provider.send(:set_startup_type, :manual)
end
it "when called with :disabled it calls Win32::Service#configure with Win32::Service::DISABLED" do
- expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::DISABLED)
+ expect(Win32::Service).to receive(:configure).with(service_name: new_resource.service_name, start_type: Win32::Service::DISABLED)
provider.send(:set_startup_type, :disabled)
end
diff --git a/spec/unit/provider/service_spec.rb b/spec/unit/provider/service_spec.rb
index d775297658..93bcbfbb09 100644
--- a/spec/unit/provider/service_spec.rb
+++ b/spec/unit/provider/service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -99,7 +99,7 @@ describe Chef::Provider::Service do
describe "action_restart" do
before do
- @current_resource.supports(:restart => true)
+ @current_resource.supports(restart: true)
end
it "should restart the service if it's supported and set the resource as updated" do
@@ -118,12 +118,12 @@ describe Chef::Provider::Service do
describe "action_reload" do
before do
- @new_resource.supports(:reload => true)
+ @new_resource.supports(reload: true)
end
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.supports(reload: 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..0ce9a19ec2 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,12 +32,22 @@ 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 properties to options for shell_out" do
expect(@provider.run_options).to eq({})
@resource.user "deployninja"
- expect(@provider.run_options).to eq({ :user => "deployninja" })
+ expect(@provider).to receive(:get_homedir).and_return("/home/deployninja")
+ expect(@provider.run_options).to eq({ user: "deployninja", environment: { "HOME" => "/home/deployninja" } })
end
context "determining the revision of the currently deployed code" do
@@ -64,18 +74,18 @@ describe Chef::Provider::Subversion do
"Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision
"Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n"
expect(::File).to receive(:exist?).at_least(1).times.with("/my/deploy/dir/.svn").and_return(true)
- expected_command = ["svn info", { :cwd => "/my/deploy/dir", :returns => [0, 1] }]
- expect(@provider).to receive(:shell_out!).with(*expected_command).
- and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
+ expected_command = ["svn info", { cwd: "/my/deploy/dir", returns: [0, 1] }]
+ expect(@provider).to receive(:shell_out!).with(*expected_command)
+ .and_return(double("ShellOut result", stdout: example_svn_info, stderr: ""))
expect(@provider.find_current_revision).to eql("11410")
end
it "gives nil as the current revision if the deploy dir isn't a SVN working copy" do
example_svn_info = "svn: '/tmp/deploydir' is not a working copy\n"
expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(true)
- expected_command = ["svn info", { :cwd => "/my/deploy/dir", :returns => [0, 1] }]
- expect(@provider).to receive(:shell_out!).with(*expected_command).
- and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
+ expected_command = ["svn info", { cwd: "/my/deploy/dir", returns: [0, 1] }]
+ expect(@provider).to receive(:shell_out!).with(*expected_command)
+ .and_return(double("ShellOut result", stdout: example_svn_info, stderr: ""))
expect(@provider.find_current_revision).to be_nil
end
@@ -119,18 +129,18 @@ describe Chef::Provider::Subversion do
"Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision
"Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n"
@resource.revision "HEAD"
- expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", { :cwd => "/my/deploy/dir", :returns => [0, 1] }]
- expect(@provider).to receive(:shell_out!).with(*expected_command).
- and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
+ expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", { cwd: "/my/deploy/dir", returns: [0, 1] }]
+ expect(@provider).to receive(:shell_out!).with(*expected_command)
+ .and_return(double("ShellOut result", stdout: example_svn_info, stderr: ""))
expect(@provider.revision_int).to eql("11410")
end
it "returns a helpful message if data from `svn info` can't be parsed" do
example_svn_info = "some random text from an error message\n"
@resource.revision "HEAD"
- expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", { :cwd => "/my/deploy/dir", :returns => [0, 1] }]
- expect(@provider).to receive(:shell_out!).with(*expected_command).
- and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
+ expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", { cwd: "/my/deploy/dir", returns: [0, 1] }]
+ expect(@provider).to receive(:shell_out!).with(*expected_command)
+ .and_return(double("ShellOut result", stdout: example_svn_info, stderr: ""))
expect { @provider.revision_int }.to raise_error(RuntimeError, "Could not parse `svn info` data: some random text from an error message\n")
end
@@ -212,7 +222,8 @@ describe Chef::Provider::Subversion do
@resource.user "whois"
@resource.group "thisis"
expected_cmd = "svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(expected_cmd, { user: "whois", group: "thisis" })
+ expect(@provider).to receive(:get_homedir).and_return("/home/whois")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, { user: "whois", group: "thisis", environment: { "HOME" => "/home/whois" } })
@provider.run_action(:checkout)
expect(@resource).to be_updated
end
@@ -260,37 +271,38 @@ describe Chef::Provider::Subversion do
end
context "selects the correct svn binary" do
- before do
- end
-
it "selects 'svn' as the binary by default" do
@resource.svn_binary nil
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
expect(@provider).to receive(:svn_binary).and_return("svn")
expect(@provider.export_command).to eql(
- "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir")
+ "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
+ )
end
it "selects an svn binary with an exe extension on windows" do
@resource.svn_binary nil
- allow(ChefConfig).to receive(:windows?) { true }
+ allow(ChefUtils).to receive(:windows?) { true }
expect(@provider).to receive(:svn_binary).and_return("svn.exe")
expect(@provider.export_command).to eql(
- "svn.exe export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir")
+ "svn.exe export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
+ )
end
it "uses a custom svn binary as part of the svn command" do
@resource.svn_binary "teapot"
expect(@provider).to receive(:svn_binary).and_return("teapot")
expect(@provider.export_command).to eql(
- "teapot export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir")
+ "teapot export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
+ )
end
it "wraps custom svn binary with quotes if it contains whitespace" do
@resource.svn_binary "c:/program files (x86)/subversion/svn.exe"
expect(@provider).to receive(:svn_binary).and_return("c:/program files (x86)/subversion/svn.exe")
expect(@provider.export_command).to eql(
- '"c:/program files (x86)/subversion/svn.exe" export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir')
+ '"c:/program files (x86)/subversion/svn.exe" export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir'
+ )
end
end
@@ -307,11 +319,11 @@ describe Chef::Provider::Subversion do
let(:http_proxy_uri) { "http://somehost:1" }
let(:http_no_proxy) { "svn.example.org" }
- before (:all) do
+ before(:all) do
@original_env = ENV.to_hash
end
- after (:all) do
+ after(:all) do
ENV.clear
ENV.update(@original_env)
end
diff --git a/spec/unit/provider/systemd_unit_spec.rb b/spec/unit/provider/systemd_unit_spec.rb
index 7f2907c982..d31d5f0919 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");
@@ -18,27 +18,18 @@
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
+describe Chef::Provider::SystemdUnit, :linux_only do
+ 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(: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_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" }
@@ -58,33 +49,42 @@ 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",
},
}
end
let(:shell_out_success) do
- double("shell_out_with_systems_locale", :exitstatus => 0, :error? => false)
+ double("shell_out", exitstatus: 0, error?: false)
end
let(:shell_out_failure) do
- double("shell_out_with_systems_locale", :exitstatus => 1, :error? => true)
+ double("shell_out", exitstatus: 1, error?: true)
end
let(:shell_out_masked) do
- double("shell_out_with_systems_locale", :exit_status => 0, :error? => false, :stdout => "masked")
+ double("shell_out", exitstatus: 0, error?: false, stdout: "masked")
end
let(:shell_out_static) do
- double("shell_out_with_systems_locale", :exit_status => 0, :error? => false, :stdout => "static")
+ double("shell_out", exitstatus: 0, error?: false, stdout: "static")
+ end
+
+ let(:shell_out_disabled) do
+ double("shell_out", exitstatus: 1, error?: true, stdout: "disabled")
+ end
+
+ let(:shell_out_indirect) do
+ double("shell_out", exitstatus: 0, error?: true, stdout: "indirect")
end
before(:each) do
+ allow(Etc).to receive(:getpwnam).and_return(OpenStruct.new(uid: 1000))
allow(Chef::Resource::SystemdUnit).to receive(:new)
- .with(unit_name)
- .and_return(current_resource)
+ .with(unit_name)
+ .and_return(current_resource)
end
describe "define_resource_requirements" do
@@ -121,8 +121,8 @@ describe Chef::Provider::SystemdUnit do
it "should create a current resource with the name of the new resource" do
expect(Chef::Resource::SystemdUnit).to receive(:new)
- .with(unit_name)
- .and_return(current_resource)
+ .with(unit_name)
+ .and_return(current_resource)
provider.load_current_resource
end
@@ -196,55 +196,59 @@ describe Chef::Provider::SystemdUnit do
it "loads the system unit content if the file exists and user is not set" do
allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(true)
+ .with(unit_path_system)
+ .and_return(true)
allow(File).to receive(:read)
- .with(unit_path_system)
- .and_return(unit_content_string)
+ .with(unit_path_system)
+ .and_return(unit_content_string)
expect(File).to receive(:exist?)
- .with(unit_path_system)
+ .with(unit_path_system)
expect(File).to receive(:read)
- .with(unit_path_system)
+ .with(unit_path_system)
provider.load_current_resource
expect(current_resource.content).to eq(unit_content_string)
end
it "does not load the system unit content if the unit file is not present and the user is not set" do
allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(false)
+ .with(unit_path_system)
+ .and_return(false)
expect(File).to_not receive(:read)
- .with(unit_path_system)
+ .with(unit_path_system)
provider.load_current_resource
expect(current_resource.content).to eq(nil)
end
- it "loads the user unit content if the file exists and user is set" do
- new_resource.user("joe")
- allow(File).to receive(:exist?)
- .with(unit_path_user)
- .and_return(true)
- allow(File).to receive(:read)
- .with(unit_path_user)
- .and_return(unit_content_string)
- expect(File).to receive(:exist?)
- .with(unit_path_user)
- expect(File).to receive(:read)
- .with(unit_path_user)
- provider.load_current_resource
- expect(current_resource.content).to eq(unit_content_string)
- end
+ # A password is required when specifying a user on Windows. Since systemd resources
+ # won't actually run on Windows, skip these tests rather than code a workaround.
+ unless windows?
+ it "loads the user unit content if the file exists and user is set" do
+ new_resource.user("joe")
+ allow(File).to receive(:exist?)
+ .with(unit_path_user)
+ .and_return(true)
+ allow(File).to receive(:read)
+ .with(unit_path_user)
+ .and_return(unit_content_string)
+ expect(File).to receive(:exist?)
+ .with(unit_path_user)
+ expect(File).to receive(:read)
+ .with(unit_path_user)
+ provider.load_current_resource
+ expect(current_resource.content).to eq(unit_content_string)
+ end
- it "does not load the user unit if the file does not exist and user is set" do
- new_resource.user("joe")
- allow(File).to receive(:exist?)
- .with(unit_path_user)
- .and_return(false)
- expect(File).to_not receive(:read)
- .with(unit_path_user)
- provider.load_current_resource
- expect(current_resource.content).to eq(nil)
+ it "does not load the user unit if the file does not exist and user is set" do
+ new_resource.user("joe")
+ allow(File).to receive(:exist?)
+ .with(unit_path_user)
+ .and_return(false)
+ expect(File).to_not receive(:read)
+ .with(unit_path_user)
+ provider.load_current_resource
+ expect(current_resource.content).to eq(nil)
+ end
end
end
@@ -253,27 +257,27 @@ describe Chef::Provider::SystemdUnit do
before(:each) do
provider.current_resource = current_resource
allow(provider).to receive(:which)
- .with("systemctl")
- .and_return(systemctl_path)
+ .with("systemctl")
+ .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)
- .and_return(true)
+ .with(:create)
+ .and_return(true)
allow(provider).to receive(:daemon_reload)
- .and_return(true)
+ .and_return(true)
expect(provider).to receive(:manage_unit_file).with(:create)
provider.action_create
end
it "creates the file when the unit content is different" do
allow(provider).to receive(:manage_unit_file)
- .with(:create)
- .and_return(true)
+ .with(:create)
+ .and_return(true)
allow(provider).to receive(:daemon_reload)
- .and_return(true)
+ .and_return(true)
expect(provider).to receive(:manage_unit_file).with(:create)
provider.action_create
end
@@ -282,62 +286,66 @@ describe Chef::Provider::SystemdUnit do
current_resource.content(unit_content_string)
allow(provider).to receive(:manage_unit_file).with(:create)
allow(provider).to receive(:daemon_reload)
- .and_return(true)
+ .and_return(true)
expect(provider).to_not receive(:manage_unit_file)
provider.action_create
end
- it "triggers a daemon-reload when creating a unit with triggers_reload" do
- allow(provider).to receive(:manage_unit_file).with(:create)
- expect(new_resource.triggers_reload).to eq true
- allow(provider).to receive(:shell_out_with_systems_locale!)
- expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} daemon-reload")
- provider.action_create
- end
+ context "when a user is specified" do
+ it "triggers a daemon-reload when creating a unit with triggers_reload" do
+ new_resource.user("joe")
+ allow(provider).to receive(:manage_unit_file).with(:create)
+ expect(new_resource.triggers_reload).to eq true
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "daemon-reload", **user_cmd_opts, default_env: false)
+ provider.action_create
+ end
- it "triggers a daemon-reload when deleting a unit with triggers_reload" do
- allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(true)
- allow(provider).to receive(:manage_unit_file).with(:delete)
- expect(new_resource.triggers_reload).to eq true
- allow(provider).to receive(:shell_out_with_systems_locale!)
- expect(provider).to receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} daemon-reload")
- provider.action_delete
- end
+ it "triggers a daemon-reload when deleting a unit with triggers_reload" do
+ new_resource.user("joe")
+ allow(File).to receive(:exist?)
+ .with(unit_path_user)
+ .and_return(true)
+ allow(provider).to receive(:manage_unit_file).with(:delete)
+ expect(new_resource.triggers_reload).to eq true
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "daemon-reload", **user_cmd_opts, default_env: false)
+ provider.action_delete
+ end
- it "does not trigger a daemon-reload when creating a unit without triggers_reload" do
- new_resource.triggers_reload(false)
- allow(provider).to receive(:manage_unit_file).with(:create)
- allow(provider).to receive(:shell_out_with_systems_locale!)
- expect(provider).to_not receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} daemon-reload")
- provider.action_create
- end
+ it "does not trigger a daemon-reload when creating a unit without triggers_reload" do
+ new_resource.user("joe")
+ new_resource.triggers_reload(false)
+ allow(provider).to receive(:manage_unit_file).with(:create)
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to_not receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "daemon-reload", **user_cmd_opts, default_env: false)
+ provider.action_create
+ end
- it "does not trigger a daemon-reload when deleting a unit without triggers_reload" do
- new_resource.triggers_reload(false)
- allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(true)
- allow(provider).to receive(:manage_unit_file).with(:delete)
- allow(provider).to receive(:shell_out_with_systems_locale!)
- expect(provider).to_not receive(:shell_out_with_systems_locale!)
- .with("#{systemctl_path} daemon-reload")
- provider.action_delete
- end
+ it "does not trigger a daemon-reload when deleting a unit without triggers_reload" do
+ new_resource.user("joe")
+ new_resource.triggers_reload(false)
+ allow(File).to receive(:exist?)
+ .with(unit_path_user)
+ .and_return(true)
+ allow(provider).to receive(:manage_unit_file).with(:delete)
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to_not receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "daemon-reload", **user_cmd_opts, default_env: false)
+ provider.action_delete
+ end
- context "when a user is specified" do
it "deletes the file when it exists" do
new_resource.user("joe")
allow(File).to receive(:exist?)
- .with(unit_path_user)
- .and_return(true)
+ .with(unit_path_user)
+ .and_return(true)
allow(provider).to receive(:manage_unit_file)
- .with(:delete)
- .and_return(true)
+ .with(:delete)
+ .and_return(true)
allow(provider).to receive(:daemon_reload)
expect(provider).to receive(:manage_unit_file).with(:delete)
provider.action_delete
@@ -346,21 +354,78 @@ describe Chef::Provider::SystemdUnit do
it "does not delete the file when it is absent" do
new_resource.user("joe")
allow(File).to receive(:exist?)
- .with(unit_path_user)
- .and_return(false)
+ .with(unit_path_user)
+ .and_return(false)
allow(provider).to receive(:manage_unit_file).with(:delete)
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_compacted!)
+ .with(systemctl_path, "--user", "preset", unit_name, **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_compacted!)
+ .with(systemctl_path, "--user", "revert", unit_name, **user_cmd_opts)
+ .and_return(shell_out_success)
+ provider.action_revert
+ end
end
context "when no user is specified" do
+ it "triggers a daemon-reload when creating a unit with triggers_reload" do
+ allow(provider).to receive(:manage_unit_file).with(:create)
+ expect(new_resource.triggers_reload).to eq true
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "daemon-reload", default_env: false)
+ provider.action_create
+ end
+
+ it "triggers a daemon-reload when deleting a unit with triggers_reload" do
+ allow(File).to receive(:exist?)
+ .with(unit_path_system)
+ .and_return(true)
+ allow(provider).to receive(:manage_unit_file).with(:delete)
+ expect(new_resource.triggers_reload).to eq true
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "daemon-reload", default_env: false)
+ provider.action_delete
+ end
+
+ it "does not trigger a daemon-reload when creating a unit without triggers_reload" do
+ new_resource.triggers_reload(false)
+ allow(provider).to receive(:manage_unit_file).with(:create)
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to_not receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "daemon-reload", default_env: false)
+ provider.action_create
+ end
+
+ it "does not trigger a daemon-reload when deleting a unit without triggers_reload" do
+ new_resource.triggers_reload(false)
+ allow(File).to receive(:exist?)
+ .with(unit_path_system)
+ .and_return(true)
+ allow(provider).to receive(:manage_unit_file).with(:delete)
+ allow(provider).to receive(:shell_out_compacted!)
+ expect(provider).to_not receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "daemon-reload", default_env: false)
+ provider.action_delete
+ end
it "deletes the file when it exists" do
allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(true)
+ .with(unit_path_system)
+ .and_return(true)
allow(provider).to receive(:manage_unit_file)
- .with(:delete)
+ .with(:delete)
allow(provider).to receive(:daemon_reload)
expect(provider).to receive(:manage_unit_file).with(:delete)
provider.action_delete
@@ -368,104 +433,133 @@ describe Chef::Provider::SystemdUnit do
it "does not delete the file when it is absent" do
allow(File).to receive(:exist?)
- .with(unit_path_system)
- .and_return(false)
+ .with(unit_path_system)
+ .and_return(false)
allow(provider).to receive(:manage_unit_file).with(:delete)
allow(provider).to receive(:daemon_reload)
expect(provider).to_not receive(:manage_unit_file)
provider.action_delete
end
+
+ it "presets the unit" do
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "preset", unit_name)
+ .and_return(shell_out_success)
+ provider.action_preset
+ end
+
+ it "reverts the unit" do
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "revert", unit_name)
+ .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_compacted!)
+ .with(systemctl_path, "--user", "reenable", unit_name, **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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "enable", unit_name, **user_cmd_opts)
+ .and_return(shell_out_success)
provider.action_enable
end
it "does not enable the unit when it is enabled" do
current_resource.user(user_name)
current_resource.enabled(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_enable
end
it "does not enable the unit when it is static" do
current_resource.user(user_name)
current_resource.static(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_enable
end
it "disables the unit when it is enabled" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "disable", unit_name, **user_cmd_opts)
+ .and_return(shell_out_success)
provider.action_disable
end
it "does not disable the unit when it is disabled" do
current_resource.user(user_name)
current_resource.enabled(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_disable
end
it "does not disable the unit when it is static" do
current_resource.user(user_name)
current_resource.static(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_disable
end
end
context "when no user is specified" do
+ it "reenables the unit" do
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "reenable", unit_name)
+ .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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "enable", unit_name)
+ .and_return(shell_out_success)
provider.action_enable
end
it "does not enable the unit when it is enabled" do
current_resource.enabled(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_enable
end
it "does not enable the unit when it is static" do
current_resource.static(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_enable
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "disable", unit_name)
+ .and_return(shell_out_success)
provider.action_disable
end
it "does not disable the unit when it is disabled" do
current_resource.enabled(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_disable
end
it "does not disable the unit when it is static" do
current_resource.user(user_name)
current_resource.static(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_disable
end
end
@@ -476,32 +570,32 @@ describe Chef::Provider::SystemdUnit do
it "masks the unit when it is unmasked" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "mask", unit_name, **user_cmd_opts)
+ .and_return(shell_out_success)
provider.action_mask
end
it "does not mask the unit when it is masked" do
current_resource.user(user_name)
current_resource.masked(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_mask
end
it "unmasks the unit when it is masked" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "unmask", unit_name, **user_cmd_opts)
+ .and_return(shell_out_success)
provider.action_unmask
end
it "does not unmask the unit when it is unmasked" do
current_resource.user(user_name)
current_resource.masked(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_unmask
end
end
@@ -509,29 +603,29 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" 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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "mask", unit_name)
+ .and_return(shell_out_success)
provider.action_mask
end
it "does not mask the unit when it is masked" do
current_resource.masked(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_mask
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "unmask", unit_name)
+ .and_return(shell_out_success)
provider.action_unmask
end
it "does not unmask the unit when it is unmasked" do
current_resource.masked(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_unmask
end
end
@@ -542,32 +636,32 @@ describe Chef::Provider::SystemdUnit do
it "starts the unit when it is inactive" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "start", unit_name, **user_cmd_opts, default_env: false)
+ .and_return(shell_out_success)
provider.action_start
end
it "does not start the unit when it is active" do
current_resource.user(user_name)
current_resource.active(true)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_start
end
it "stops the unit when it is active" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "stop", unit_name, **user_cmd_opts, default_env: false)
+ .and_return(shell_out_success)
provider.action_stop
end
it "does not stop the unit when it is inactive" do
current_resource.user(user_name)
current_resource.active(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_stop
end
end
@@ -575,29 +669,29 @@ describe Chef::Provider::SystemdUnit do
context "when no user is specified" 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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "start", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_start
end
it "does not start the unit when it is active" do
current_resource.active(true)
- expect(provider).to_not receive(:shell_out_with_systems_locale!)
+ expect(provider).to_not receive(:shell_out_compacted!)
provider.action_start
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "stop", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_stop
end
it "does not stop the unit when it is inactive" do
current_resource.active(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_stop
end
end
@@ -607,48 +701,48 @@ describe Chef::Provider::SystemdUnit do
context "when a user is specified" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "restart", unit_name, **user_cmd_opts, default_env: false)
+ .and_return(shell_out_success)
provider.action_restart
end
it "reloads the unit if active" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "reload", unit_name, **user_cmd_opts, default_env: false)
+ .and_return(shell_out_success)
provider.action_reload
end
it "does not reload if the unit is inactive" do
current_resource.user(user_name)
current_resource.active(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_reload
end
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "restart", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_restart
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "reload", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_reload
end
it "does not reload the unit if inactive" do
current_resource.active(false)
- expect(provider).not_to receive(:shell_out_with_systems_locale!)
+ expect(provider).not_to receive(:shell_out_compacted!)
provider.action_reload
end
end
@@ -658,18 +752,18 @@ describe Chef::Provider::SystemdUnit 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}", user_cmd_opts)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "try-restart", unit_name, **user_cmd_opts, default_env: false)
+ .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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "try-restart", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_try_restart
end
end
@@ -679,18 +773,18 @@ describe Chef::Provider::SystemdUnit 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}", user_cmd_opts)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "reload-or-restart", unit_name, **user_cmd_opts, default_env: false)
+ .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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "reload-or-restart", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_reload_or_restart
end
end
@@ -700,18 +794,18 @@ describe Chef::Provider::SystemdUnit 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}", user_cmd_opts)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--user", "reload-or-try-restart", unit_name, **user_cmd_opts, default_env: false)
+ .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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted!)
+ .with(systemctl_path, "--system", "reload-or-try-restart", unit_name, default_env: false)
+ .and_return(shell_out_success)
provider.action_reload_or_try_restart
end
end
@@ -720,39 +814,39 @@ describe Chef::Provider::SystemdUnit do
describe "#active?" do
before(:each) do
provider.current_resource = current_resource
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
end
context "when a user is specified" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-active", unit_name, user_cmd_opts)
+ .and_return(shell_out_success)
expect(provider.active?).to be true
end
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)
- .and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-active", unit_name, user_cmd_opts)
+ .and_return(shell_out_failure)
expect(provider.active?).to be false
end
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-active", unit_name)
+ .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}", {})
- .and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-active", unit_name)
+ .and_return(shell_out_failure)
expect(provider.active?).to be false
end
end
@@ -761,39 +855,39 @@ describe Chef::Provider::SystemdUnit do
describe "#enabled?" do
before(:each) do
provider.current_resource = current_resource
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
end
context "when a user is specified" 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)
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_success)
expect(provider.enabled?).to be true
end
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)
- .and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_disabled)
expect(provider.enabled?).to be false
end
end
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}", {})
- .and_return(shell_out_success)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .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}", {})
- .and_return(shell_out_failure)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .and_return(shell_out_disabled)
expect(provider.enabled?).to be false
end
end
@@ -802,39 +896,39 @@ describe Chef::Provider::SystemdUnit do
describe "#masked?" do
before(:each) do
provider.current_resource = current_resource
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
end
context "when a user is specified" 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)
- .and_return(shell_out_masked)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "status", unit_name, user_cmd_opts)
+ .and_return(shell_out_masked)
expect(provider.masked?).to be true
end
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)
- .and_return(shell_out_static)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "status", unit_name, user_cmd_opts)
+ .and_return(shell_out_static)
expect(provider.masked?).to be false
end
end
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}", {})
- .and_return(shell_out_masked)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "status", unit_name)
+ .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}", {})
- .and_return(shell_out_static)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "status", unit_name)
+ .and_return(shell_out_static)
expect(provider.masked?).to be false
end
end
@@ -843,43 +937,84 @@ describe Chef::Provider::SystemdUnit do
describe "#static?" do
before(:each) do
provider.current_resource = current_resource
- allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}")
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
end
context "when a user is specified" 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)
- .and_return(shell_out_static)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_static)
expect(provider.static?).to be true
end
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)
- .and_return(shell_out_masked)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_masked)
expect(provider.static?).to be false
end
end
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}", {})
- .and_return(shell_out_static)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .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}", {})
- .and_return(shell_out_masked)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .and_return(shell_out_masked)
expect(provider.static?).to be false
end
end
end
+
+ describe "#indirect?" do
+ before(:each) do
+ provider.current_resource = current_resource
+ allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path.to_s)
+ end
+
+ context "when a user is specified" do
+ it "returns true when the unit is indirect" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_indirect)
+ expect(provider.indirect?).to be true
+ end
+
+ it "returns false when the unit is not indirect" do
+ current_resource.user(user_name)
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--user", "is-enabled", unit_name, user_cmd_opts)
+ .and_return(shell_out_static)
+ expect(provider.indirect?).to be false
+ end
+ end
+
+ context "when no user is specified" do
+ it "returns true when the unit is indirect" do
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .and_return(shell_out_indirect)
+ expect(provider.indirect?).to be true
+ end
+
+ it "returns false when the unit is not indirect" do
+ expect(provider).to receive(:shell_out_compacted)
+ .with(systemctl_path, "--system", "is-enabled", unit_name)
+ .and_return(shell_out_static)
+ expect(provider.indirect?).to be false
+ end
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 8f30d8f868..46e5c947f3 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,20 +30,20 @@ describe Chef::Provider::Template::Content do
let(:new_resource) do
double("Chef::Resource::Template (new)",
- :cookbook_name => "openldap",
- :recipe_name => "default",
- :source_line => "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
- :source_line_file => "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb",
- :source_line_number => "2",
- :source => "openldap_stuff.conf.erb",
- :name => "openldap_stuff.conf",
- :path => resource_path,
- :local => false,
- :cookbook => nil,
- :variables => {},
- :inline_helper_blocks => {},
- :inline_helper_modules => [],
- :helper_modules => [])
+ cookbook_name: "openldap",
+ recipe_name: "default",
+ source_line: "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
+ source_line_file: "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb",
+ source_line_number: "2",
+ source: "openldap_stuff.conf.erb",
+ name: "openldap_stuff.conf",
+ path: resource_path,
+ local: false,
+ cookbook: nil,
+ variables: {},
+ inline_helper_blocks: {},
+ inline_helper_modules: [],
+ helper_modules: [])
end
let(:rendered_file_locations) do
@@ -58,7 +58,7 @@ describe Chef::Provider::Template::Content do
cl.load_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cl)
node = Chef::Node.new
- double("Chef::Resource::RunContext", :node => node, :cookbook_collection => cookbook_collection)
+ double("Chef::Resource::RunContext", node: node, cookbook_collection: cookbook_collection)
end
let(:content) do
@@ -130,39 +130,39 @@ describe Chef::Provider::Template::Content do
describe "when using location helpers" do
let(:new_resource) do
double("Chef::Resource::Template (new)",
- :cookbook_name => "openldap",
- :recipe_name => "default",
- :source_line => CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
- :source_line_file => CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb",
- :source_line_number => "2",
- :source => "helpers.erb",
- :name => "helpers.erb",
- :path => CHEF_SPEC_DATA + "/cookbooks/openldap/templates/default/helpers.erb",
- :local => false,
- :cookbook => nil,
- :variables => {},
- :inline_helper_blocks => {},
- :inline_helper_modules => [],
- :helper_modules => [])
+ cookbook_name: "openldap",
+ recipe_name: "default",
+ source_line: CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
+ source_line_file: CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb",
+ source_line_number: "2",
+ source: "helpers.erb",
+ name: "helpers.erb",
+ path: CHEF_SPEC_DATA + "/cookbooks/openldap/templates/default/helpers.erb",
+ local: false,
+ cookbook: nil,
+ variables: {},
+ inline_helper_blocks: {},
+ inline_helper_modules: [],
+ helper_modules: [])
end
it "creates the template with the rendered content" do
- expect(IO.read(content.tempfile.path)).to eql <<EOF
-openldap
-default
-#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
-#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
-2
-helpers.erb
-#{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
-openldap
-default
-#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
-#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
-2
-helpers.erb
-#{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
-EOF
+ expect(IO.read(content.tempfile.path)).to eql <<~EOF
+ openldap
+ default
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
+ 2
+ helpers.erb
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
+ openldap
+ default
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
+ 2
+ helpers.erb
+ #{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
+ EOF
end
end
diff --git a/spec/unit/provider/template_spec.rb b/spec/unit/provider/template_spec.rb
index 306fd6ea71..84cbfc5d08 100644
--- a/spec/unit/provider/template_spec.rb
+++ b/spec/unit/provider/template_spec.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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,9 +24,9 @@ require "ostruct"
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(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:enclosing_directory) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
@@ -49,7 +49,7 @@ describe Chef::Provider::Template do
end
let(:content) do
- content = double("Chef::Provider::File::Content::Template", :template_location => "/foo/bar/baz")
+ content = double("Chef::Provider::File::Content::Template", template_location: "/foo/bar/baz")
allow(File).to receive(:exists?).with("/foo/bar/baz").and_return(true)
content
end
@@ -58,9 +58,6 @@ 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) do
canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
end
diff --git a/spec/unit/provider/user/aix_spec.rb b/spec/unit/provider/user/aix_spec.rb
new file mode 100644
index 0000000000..eb7cda1bfb
--- /dev/null
+++ b/spec/unit/provider/user/aix_spec.rb
@@ -0,0 +1,96 @@
+# Copyright:: Copyright (c) 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::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_compacted!).with("echo 'adam:Ostagazuzulum' | chpasswd -c -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_compacted!).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_compacted!).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_compacted!).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_compacted!).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 7b8be02f3a..5652ae6868 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Dreamcat4 (<dreamcat4@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,32 +18,31 @@
require "spec_helper"
require "ostruct"
-require "mixlib/shellout"
describe Chef::Provider::User::Dscl do
before do
- allow(ChefConfig).to receive(:windows?) { false }
- end
- let(:shellcmdresult) do
- Struct.new(:stdout, :stderr, :exitstatus)
- 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
+ allow(ChefUtils).to receive(:windows?) { false }
end
- let(:events) do
- Chef::EventDispatch::Dispatcher.new
- end
+ let(:shellcmdresult) { Struct.new(:stdout, :stderr, :exitstatus) }
+
+ let(:password) { nil }
+ let(:salt) { nil }
+ let(:iterations) { nil }
- let(:run_context) do
- Chef::RunContext.new(node, {}, events)
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:node) do
+ Chef::Node.new.tap do |node|
+ node.automatic["os"] = "darwin"
+ node.automatic["platform_version"] = "10.13.0"
+ end
end
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
let(:new_resource) do
- r = Chef::Resource::User::DsclUser.new("toor")
+ r = Chef::Resource::User::DsclUser.new("toor", run_context)
r.password(password)
r.salt(salt)
r.iterations(iterations)
@@ -54,14 +53,6 @@ describe Chef::Provider::User::Dscl do
Chef::Provider::User::Dscl.new(new_resource, run_context)
end
- let(:mac_version) do
- "10.9.1"
- end
-
- let(:password) { nil }
- let(:salt) { nil }
- let(:iterations) { nil }
-
let(:salted_sha512_password) do
"0f543f021c63255e64e121a3585601b8ecfedf6d2\
705ddac69e682a33db5dbcdb9b56a2520bc8fff63a\
@@ -116,51 +107,51 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
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_compacted).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_compacted).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_compacted).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_compacted).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_compacted).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
+ describe "when the system property is set to true" do
before do
new_resource.system(true)
end
- it "should return the first unused uid number on or above 500" do
+ it "should return the first unused uid number on or above 200" do
expect(provider.get_free_uid).to eq(202)
end
end
- it "should return the first unused uid number on or above 200" do
+ it "should return the first unused uid number on or above 500" do
expect(provider.get_free_uid).to eq(502)
end
@@ -177,7 +168,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 +189,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,21 +198,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) do
new_resource.dup
end
before do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- new_resource.supports({ :manage_home => true })
+ new_resource.manage_home true
new_resource.home("/Users/toor")
provider.current_resource = current_resource
@@ -229,7 +244,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
@@ -239,40 +254,33 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
it "moves the users home to the new location if it exists and the target location is different" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- 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)
- 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(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'")
+ it "should run createhomedir to create the user's new home folder" do
+ expect(provider).to receive(:shell_out_compacted!).with("/usr/sbin/createhomedir", "-c", "-u", "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
@@ -283,8 +291,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
@@ -309,69 +317,29 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
end
end
- describe "when on Mac 10.6" do
- let(:mac_version) do
- "10.6.5"
- end
+ describe "when password is SALTED-SHA512" do
+ let(:password) { salted_sha512_password }
it "should raise an error" do
expect { run_requirements }.to raise_error(Chef::Exceptions::User)
end
end
- describe "when on Mac 10.7" do
- let(:mac_version) do
- "10.7.5"
- end
-
- describe "when password is SALTED-SHA512" do
- let(:password) { salted_sha512_password }
-
- it "should not raise an error" do
- expect { run_requirements }.not_to raise_error
- end
- end
-
- describe "when password is SALTED-SHA512-PBKDF2" do
- let(:password) { salted_sha512_pbkdf2_password }
+ describe "when password is SALTED-SHA512-PBKDF2" do
+ let(:password) { salted_sha512_pbkdf2_password }
+ describe "when salt and iteration is not set" do
it "should raise an error" do
expect { run_requirements }.to raise_error(Chef::Exceptions::User)
end
end
- end
-
- [ "10.9", "10.10"].each do |version|
- describe "when on Mac #{version}" do
- let(:mac_version) do
- "#{version}.2"
- end
-
- describe "when password is SALTED-SHA512" do
- let(:password) { salted_sha512_password }
-
- it "should raise an error" do
- expect { run_requirements }.to raise_error(Chef::Exceptions::User)
- end
- end
- describe "when password is SALTED-SHA512-PBKDF2" do
- let(:password) { salted_sha512_pbkdf2_password }
+ describe "when salt and iteration is set" do
+ let(:salt) { salted_sha512_pbkdf2_salt }
+ let(:iterations) { salted_sha512_pbkdf2_iterations }
- describe "when salt and iteration is not set" do
- it "should raise an error" do
- expect { run_requirements }.to raise_error(Chef::Exceptions::User)
- end
- end
-
- describe "when salt and iteration is set" do
- let(:salt) { salted_sha512_pbkdf2_salt }
- let(:iterations) { salted_sha512_pbkdf2_iterations }
-
- it "should not raise an error" do
- expect { run_requirements }.not_to raise_error
- end
- end
+ it "should not raise an error" do
+ expect { run_requirements }.not_to raise_error
end
end
end
@@ -382,8 +350,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_compacted).with("dscacheutil", "-flushcache")
+ expect(provider).to receive(:shell_out_compacted).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
@@ -391,7 +359,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
@@ -415,211 +383,76 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
describe "when user is there" do
let(:password) { "something" } # Load password during load_current_resource
- describe "on 10.7" do
- let(:mac_version) do
- "10.7.5"
- end
-
- let(:user_plist_file) { "10.7" }
-
- it "collects the user data correctly" do
- provider.load_current_resource
- expect(provider.current_resource.comment).to eq("vagrant")
- expect(provider.current_resource.uid).to eq("501")
- expect(provider.current_resource.gid).to eq("80")
- expect(provider.current_resource.home).to eq("/Users/vagrant")
- expect(provider.current_resource.shell).to eq("/bin/bash")
- expect(provider.current_resource.password).to eq(vagrant_sha_512)
- end
-
- describe "when a plain password is set that is same" do
- let(:password) { "vagrant" }
-
- it "diverged_password? should report false" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_falsey
- end
- end
-
- describe "when a plain password is set that is different" do
- let(:password) { "not_vagrant" }
-
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
- end
-
- describe "when iterations change" do
- let(:password) { vagrant_sha_512 }
- let(:iterations) { 12345 }
-
- it "diverged_password? should report false" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_falsey
- end
- end
-
- describe "when shadow hash changes" do
- let(:password) { salted_sha512_password }
-
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
- end
-
- describe "when salt change" do
- let(:password) { vagrant_sha_512 }
- let(:salt) { "SOMETHINGRANDOM" }
+ let(:user_plist_file) { "10.9" }
- it "diverged_password? should report false" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_falsey
- end
- end
+ it "collects the user data correctly" do
+ provider.load_current_resource
+ expect(provider.current_resource.comment).to eq("vagrant")
+ expect(provider.current_resource.uid).to eq("501")
+ expect(provider.current_resource.gid).to eq("80")
+ expect(provider.current_resource.home).to eq("/Users/vagrant")
+ expect(provider.current_resource.shell).to eq("/bin/bash")
+ expect(provider.current_resource.password).to eq(vagrant_sha_512_pbkdf2)
+ expect(provider.current_resource.salt).to eq(vagrant_sha_512_pbkdf2_salt)
+ expect(provider.current_resource.iterations).to eq(vagrant_sha_512_pbkdf2_iterations)
end
- describe "on 10.8" do
- let(:mac_version) do
- "10.8.3"
- end
-
- let(:user_plist_file) { "10.8" }
+ describe "when a plain password is set that is same" do
+ let(:password) { "vagrant" }
- it "collects the user data correctly" do
+ it "diverged_password? should report false" do
provider.load_current_resource
- expect(provider.current_resource.comment).to eq("vagrant")
- expect(provider.current_resource.uid).to eq("501")
- expect(provider.current_resource.gid).to eq("80")
- expect(provider.current_resource.home).to eq("/Users/vagrant")
- expect(provider.current_resource.shell).to eq("/bin/bash")
- expect(provider.current_resource.password).to eq("ea4c2d265d801ba0ec0dfccd\
-253dfc1de91cbe0806b4acc1ed7fe22aebcf6beb5344d0f442e590\
-ffa04d679075da3afb119e41b72b5eaf08ee4aa54693722646d5\
-19ee04843deb8a3e977428d33f625e83887913e5c13b70035961\
-5e00ad7bc3e7a0c98afc3e19d1360272454f8d33a9214d2fbe8b\
-e68d1f9821b26689312366")
- expect(provider.current_resource.salt).to eq("f994ef2f73b7c5594ebd1553300976b20733ce0e24d659783d87f3d81cbbb6a9")
- expect(provider.current_resource.iterations).to eq(39840)
+ expect(provider.diverged_password?).to be_falsey
end
end
- describe "on 10.7 upgraded to 10.8" do
- # In this scenario user password is still in 10.7 format
- let(:mac_version) do
- "10.8.3"
- end
-
- let(:user_plist_file) { "10.7-8" }
+ describe "when a plain password is set that is different" do
+ let(:password) { "not_vagrant" }
- it "collects the user data correctly" do
+ it "diverged_password? should report true" do
provider.load_current_resource
- expect(provider.current_resource.comment).to eq("vagrant")
- expect(provider.current_resource.uid).to eq("501")
- expect(provider.current_resource.gid).to eq("80")
- expect(provider.current_resource.home).to eq("/Users/vagrant")
- expect(provider.current_resource.shell).to eq("/bin/bash")
- expect(provider.current_resource.password).to eq("6f75d7190441facc34291ebbea1fc756b242d4f\
-e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
-ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
- end
-
- describe "when a plain text password is set" do
- it "reports password needs to be updated" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
- end
-
- describe "when a salted-sha512-pbkdf2 shadow is set" do
- let(:password) { salted_sha512_pbkdf2_password }
- let(:salt) { salted_sha512_pbkdf2_salt }
- let(:iterations) { salted_sha512_pbkdf2_iterations }
-
- it "reports password needs to be updated" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
+ expect(provider.diverged_password?).to be_truthy
end
end
- describe "on 10.9" do
- let(:mac_version) do
- "10.9.1"
- end
-
- let(:user_plist_file) { "10.9" }
+ describe "when iterations change" do
+ let(:password) { vagrant_sha_512_pbkdf2 }
+ let(:salt) { vagrant_sha_512_pbkdf2_salt }
+ let(:iterations) { 12345 }
- it "collects the user data correctly" do
+ it "diverged_password? should report true" do
provider.load_current_resource
- expect(provider.current_resource.comment).to eq("vagrant")
- expect(provider.current_resource.uid).to eq("501")
- expect(provider.current_resource.gid).to eq("80")
- expect(provider.current_resource.home).to eq("/Users/vagrant")
- expect(provider.current_resource.shell).to eq("/bin/bash")
- expect(provider.current_resource.password).to eq(vagrant_sha_512_pbkdf2)
- expect(provider.current_resource.salt).to eq(vagrant_sha_512_pbkdf2_salt)
- expect(provider.current_resource.iterations).to eq(vagrant_sha_512_pbkdf2_iterations)
- end
-
- describe "when a plain password is set that is same" do
- let(:password) { "vagrant" }
-
- it "diverged_password? should report false" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_falsey
- end
- end
-
- describe "when a plain password is set that is different" do
- let(:password) { "not_vagrant" }
-
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
- end
-
- describe "when iterations change" do
- let(:password) { vagrant_sha_512_pbkdf2 }
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
- let(:iterations) { 12345 }
-
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
+ expect(provider.diverged_password?).to be_truthy
end
+ end
- describe "when shadow hash changes" do
- let(:password) { salted_sha512_pbkdf2_password }
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
+ describe "when shadow hash changes" do
+ let(:password) { salted_sha512_pbkdf2_password }
+ let(:salt) { vagrant_sha_512_pbkdf2_salt }
+ let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
+ it "diverged_password? should report true" do
+ provider.load_current_resource
+ expect(provider.diverged_password?).to be_truthy
end
+ end
- describe "when salt change" do
- let(:password) { vagrant_sha_512_pbkdf2 }
- let(:salt) { salted_sha512_pbkdf2_salt }
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
+ describe "when salt change" do
+ let(:password) { vagrant_sha_512_pbkdf2 }
+ let(:salt) { salted_sha512_pbkdf2_salt }
+ let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
- it "diverged_password? should report true" do
- provider.load_current_resource
- expect(provider.diverged_password?).to be_truthy
- end
+ it "diverged_password? should report true" do
+ provider.load_current_resource
+ expect(provider.diverged_password?).to be_truthy
end
+ end
- describe "when salt isn't found" do
- it "diverged_password? should report true" do
- provider.load_current_resource
- provider.current_resource.salt(nil)
- expect(provider.diverged_password?).to be_truthy
- end
+ describe "when salt isn't found" do
+ it "diverged_password? should report true" do
+ provider.load_current_resource
+ provider.current_resource.salt(nil)
+ expect(provider.diverged_password?).to be_truthy
end
end
end
@@ -648,71 +481,34 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
end
describe "prepare_password_shadow_info" do
- describe "when on Mac 10.7" do
- let(:mac_version) do
- "10.7.1"
- end
-
- describe "when the password is plain text" do
- let(:password) { "vagrant" }
-
- it "password_shadow_info should have salted-sha-512 format" do
- shadow_info = provider.prepare_password_shadow_info
- expect(shadow_info).to have_key("SALTED-SHA512")
- info = shadow_info["SALTED-SHA512"].string.unpack("H*").first
- expect(provider.salted_sha512?(info)).to be_truthy
- end
- end
-
- describe "when the password is salted-sha-512" do
- let(:password) { vagrant_sha_512 }
+ describe "when the password is plain text" do
+ let(:password) { "vagrant" }
- it "password_shadow_info should have salted-sha-512 format" do
- shadow_info = provider.prepare_password_shadow_info
- expect(shadow_info).to have_key("SALTED-SHA512")
- info = shadow_info["SALTED-SHA512"].string.unpack("H*").first
- expect(provider.salted_sha512?(info)).to be_truthy
- expect(info).to eq(vagrant_sha_512)
- end
+ it "password_shadow_info should have salted-sha-512 format" do
+ shadow_info = provider.prepare_password_shadow_info
+ expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations")
+ info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first
+ expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy
end
end
- ["10.8", "10.9", "10.10"].each do |version|
- describe "when on Mac #{version}" do
- let(:mac_version) do
- "#{version}.1"
- end
+ describe "when the password is salted-sha-512" do
+ let(:password) { vagrant_sha_512_pbkdf2 }
+ let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
+ let(:salt) { vagrant_sha_512_pbkdf2_salt }
- describe "when the password is plain text" do
- let(:password) { "vagrant" }
-
- it "password_shadow_info should have salted-sha-512 format" do
- shadow_info = provider.prepare_password_shadow_info
- expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations")
- info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first
- expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy
- end
- end
-
- describe "when the password is salted-sha-512" do
- let(:password) { vagrant_sha_512_pbkdf2 }
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
-
- it "password_shadow_info should have salted-sha-512 format" do
- shadow_info = provider.prepare_password_shadow_info
- expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt")
- expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations")
- info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first
- expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy
- expect(info).to eq(vagrant_sha_512_pbkdf2)
- end
- end
+ it "password_shadow_info should have salted-sha-512 format" do
+ shadow_info = provider.prepare_password_shadow_info
+ expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt")
+ expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations")
+ info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first
+ expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy
+ expect(info).to eq(vagrant_sha_512_pbkdf2)
end
end
end
@@ -726,7 +522,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_compacted).and_return(mock_shellout)
expect(provider).to receive(:read_user_info)
expect(provider).to receive(:dscl_set)
expect(provider).to receive(:sleep).with(3)
@@ -754,29 +550,28 @@ 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", "toor").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
@@ -788,21 +583,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_compacted).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
@@ -850,8 +645,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_compacted).with("dscacheutil", "-flushcache")
+ expect(provider).to receive(:shell_out_compacted).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
@@ -859,14 +654,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
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- 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
@@ -889,7 +683,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
@@ -897,7 +691,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
index 1c487c0de9..1f22d963da 100644
--- a/spec/unit/provider/user/linux_spec.rb
+++ b/spec/unit/provider/user/linux_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
#
# License:: Apache License, Version 2.0
#
@@ -19,7 +19,6 @@
#
require "spec_helper"
-require "chef/provider/user/useradd"
describe Chef::Provider::User::Linux do
@@ -45,50 +44,18 @@ describe Chef::Provider::User::Linux do
@current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
end
- it "supports manage_home does not exist", chef: ">= 13" do
- expect( @new_resource.supports.key?(:manage_home) ).to be false
+ 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 "supports non_unique does not exist", chef: ">= 13" do
- expect( @new_resource.supports.key?(:non_unique) ).to be false
- end
-
- # supports is a method on the superclass so can't totally be removed, but we should aggressively NOP it to decisively break it
- it "disables the supports API", chef: ">= 13" do
- @new_resource.supports( { manage_home: true } )
- expect( @new_resource.supports.key?(:manage_home) ).to be false
- end
-
- it "sets supports manage_home to false" do
- expect( @new_resource.supports[:manage_home] ).to be false
- end
-
- it "sets supports non-unique to false" do
- expect( @new_resource.supports[:non_unique] ).to be false
- end
-
- it "throws a deprecation warning on setting supports[:manage_home]" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- expect(Chef).to receive(:log_deprecation).with("supports { manage_home: true } on the user resource is deprecated and will be removed in Chef 13, set manage_home: true instead")
- @new_resource.supports( { :manage_home => true } )
+ 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 "supports[:manage_home] (incorectly) acts like manage_home" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- @new_resource.supports({ manage_home: true })
- expect( provider.useradd_options ).to eql(["-m"])
- end
-
- it "supports[:manage_home] does not change behavior of manage_home: false", chef: ">= 13" do
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- @new_resource.supports({ manage_home: true })
- expect( provider.useradd_options ).to eql(["-M"])
- end
-
it "by default manage_home is false and we use -M" do
expect( provider.useradd_options ).to eql(["-M"])
end
diff --git a/spec/unit/provider/user/mac_spec.rb b/spec/unit/provider/user/mac_spec.rb
new file mode 100644
index 0000000000..f27a92a0f9
--- /dev/null
+++ b/spec/unit/provider/user/mac_spec.rb
@@ -0,0 +1,38 @@
+#
+# Author:: Ryan Cragun (<ryan@chef.io>)
+# Copyright:: Copyright (c) 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::User::MacUser do
+ before do
+ allow(ChefConfig).to receive(:windows?) { false }
+ end
+
+ let(:new_resource) { Chef::Resource::User::MacUser.new("jane") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ described_class.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/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb
index fb7c9211a1..c1d3ae2953 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,9 +32,7 @@ describe Chef::Provider::User::Pw do
@new_resource.shell "/usr/bin/zsh"
@new_resource.password "abracadabra"
- # XXX: rip out in Chef-13
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- @new_resource.supports :manage_home => true
+ @new_resource.manage_home true
@current_resource = Chef::Resource::User::PwUser.new("adam")
@current_resource.comment "Adam Jacob"
@@ -56,44 +54,39 @@ describe Chef::Provider::User::Pw do
"uid" => "-u",
"shell" => "-s",
}
- field_list.each do |attribute, option|
- it "should check for differences in #{attribute} between the new and current resources" do
- expect(@current_resource).to receive(attribute)
- expect(@new_resource).to receive(attribute)
+ field_list.each do |property, option|
+ it "should check for differences in #{property} between the new and current resources" do
+ expect(@current_resource).to receive(property)
+ expect(@new_resource).to receive(property)
@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("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)}'")
+ it "should set the option for #{property} if the new resources #{property} is not null" do
+ allow(@new_resource).to receive(property).and_return("hola")
+ expect(@provider.set_options).to eql([ @new_resource.username, option, @new_resource.send(property), "-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|
- allow(@new_resource).to receive(attribute).and_return("hola")
- match_string << " #{option} 'hola'"
+ match_array = [ "adam" ]
+ field_list.sort_by { |a| a[0] }.each do |property, option|
+ allow(@new_resource).to receive(property).and_return("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_compacted!).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_compacted!).with( "pw", "useradd", "adam", "-m").and_return(true)
@provider.create_user
end
@@ -105,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_compacted!).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_compacted!).with( "pw", "usermod", "adam", "-m").and_return(true)
@provider.manage_user
end
@@ -122,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_compacted!).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_compacted!).with( "pw", "userdel", @new_resource.username, "-r").and_return(true)
@provider.remove_user
end
end
@@ -147,28 +140,27 @@ 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_compacted!).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_compacted!).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_compacted!).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
@@ -178,19 +170,19 @@ describe Chef::Provider::User::Pw do
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
@@ -201,7 +193,7 @@ describe Chef::Provider::User::Pw do
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
@@ -210,24 +202,16 @@ describe Chef::Provider::User::Pw do
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_compacted!).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_compacted!).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
@@ -239,12 +223,12 @@ describe Chef::Provider::User::Pw do
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 860c9e41dd..4c6768cda5 100644
--- a/spec/unit/provider/user/solaris_spec.rb
+++ b/spec/unit/provider/user/solaris_spec.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Dave Eddy (<dave@daveeddy.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2015-2016, Dave Eddy
#
# License:: Apache License, Version 2.0
@@ -20,7 +20,6 @@
# limitations under the License.
#
-require "mixlib/shellout"
require "spec_helper"
describe Chef::Provider::User::Solaris do
@@ -29,31 +28,36 @@ describe Chef::Provider::User::Solaris 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::SolarisUser.new("adam", @run_context)
- @current_resource = Chef::Resource::User::SolarisUser.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
expect(provider).to receive(:write_shadow_file)
- allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:shell_out_compacted!).and_return(true)
provider.manage_user
end
@@ -63,59 +67,93 @@ describe Chef::Provider::User::Solaris do
password_file = Tempfile.new("shadow")
password_file.puts "adam:existingpassword:15441::::::"
password_file.close
- provider.password_file = password_file.path
- allow(provider).to receive(:shell_out!).and_return(true)
+ stub_const("Chef::Provider::User::Solaris::PASSWORD_FILE", password_file.path)
+ allow(provider).to receive(:shell_out_compacted!).and_return(true)
# 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_compacted!).with( "useradd", "adam")
+ provider.create_user
+ end
+ end
- @new_resource = Chef::Resource::User::SolarisUser.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_compacted!).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 +161,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_compacted!).with("passwd", "-l", "adam").and_return(shell_return)
provider.lock_user
end
end
@@ -131,7 +169,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_compacted!).with("passwd", "-u", "adam").and_return(shell_return)
provider.unlock_user
end
end
diff --git a/spec/unit/provider/user/windows_spec.rb b/spec/unit/provider/user/windows_spec.rb
index 324b5f503f..b5c6954575 100644
--- a/spec/unit/provider/user/windows_spec.rb
+++ b/spec/unit/provider/user/windows_spec.rb
@@ -28,11 +28,14 @@ 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::WindowsUser.new("monkey")
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
+ allow(@run_context).to receive(:logger).and_return(logger)
@current_resource = Chef::Resource::User::WindowsUser.new("monkey")
@net_user = double("Chef::Util::Windows::NetUser")
@@ -48,9 +51,10 @@ describe Chef::Provider::User::Windows do
@provider = Chef::Provider::User::Windows.new(@new_resource, @run_context)
end
- describe "when comparing the user's current attributes to the desired attributes" do
+ describe "when comparing the user's current properties to the desired properties" do
before do
- @new_resource.comment "Adam Jacob"
+ @new_resource.full_name "Adam Jacob"
+ @new_resource.comment "Some comments"
@new_resource.uid 1000
@new_resource.gid 1000
@new_resource.home "/home/adam"
@@ -60,9 +64,9 @@ describe Chef::Provider::User::Windows do
@provider.current_resource = @new_resource.clone
end
- describe "and the attributes match" do
+ describe "and the properties match" do
it "doesn't set the comment field to be updated" do
- expect(@provider.set_options).not_to have_key(:full_name)
+ expect(@provider.set_options).not_to have_key(:comment)
end
it "doesn't set the home directory to be updated" do
@@ -85,12 +89,16 @@ describe Chef::Provider::User::Windows do
expect(@provider.set_options).not_to have_key(:password)
end
+ it "doesn't set the full_name to be updated" do
+ expect(@provider.set_options).not_to have_key(:full_name)
+ end
end
- describe "and the attributes do not match" do
+ describe "and the properties do not match" do
before do
@current_resource = Chef::Resource::User::WindowsUser.new("adam")
- @current_resource.comment "Adam Jacob-foo"
+ @current_resource.full_name "Adam Jacob-foo"
+ @current_resource.comment "some comments"
@current_resource.uid 1111
@current_resource.gid 1111
@current_resource.home "/home/adam-foo"
@@ -103,23 +111,27 @@ describe Chef::Provider::User::Windows do
expect(@provider.set_options[:full_name]).to eq("Adam Jacob")
end
- it "marks the home_dir attribute to be updated" do
+ it "marks the comment field to be updated" do
+ expect(@provider.set_options[:comment]).to eq("Some comments")
+ end
+
+ it "marks the home_dir property to be updated" do
expect(@provider.set_options[:home_dir]).to eq("/home/adam")
end
- it "ignores the primary_group_id attribute" do
+ it "ignores the primary_group_id property" do
expect(@provider.set_options[:primary_group_id]).to eq(nil)
end
- it "marks the user_id attribute to be updated" do
+ it "marks the user_id property to be updated" do
expect(@provider.set_options[:user_id]).to eq(1000)
end
- it "marks the script_path attribute to be updated" do
+ it "marks the script_path property to be updated" do
expect(@provider.set_options[:script_path]).to eq("/usr/bin/zsh")
end
- it "marks the password attribute to be updated" do
+ it "marks the password property to be updated" do
expect(@provider.set_options[:password]).to eq("abracadabra")
end
end
@@ -127,19 +139,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 1a8ad6ad1b..16428de1a9 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,18 +27,18 @@ describe Chef::Provider::User do
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::User.new("adam")
- @new_resource.comment "Adam Jacob"
+ @new_resource = Chef::Resource::User.new("notarealuser")
+ @new_resource.comment "Nota Realuser"
@new_resource.uid 1000
@new_resource.gid 1000
- @new_resource.home "/home/adam"
+ @new_resource.home "/home/notarealuser"
@new_resource.shell "/usr/bin/zsh"
- @current_resource = Chef::Resource::User.new("adam")
- @current_resource.comment "Adam Jacob"
+ @current_resource = Chef::Resource::User.new("notarealuser")
+ @current_resource.comment "Nota Realuser"
@current_resource.uid 1000
@current_resource.gid 1000
- @current_resource.home "/home/adam"
+ @current_resource.home "/home/notarealuser"
@current_resource.shell "/usr/bin/zsh"
@provider = Chef::Provider::User.new(@new_resource, @run_context)
@@ -58,24 +58,24 @@ 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",
+ # :username => "notarealuser",
+ # :comment => "Nota Realuser",
# :uid => 1000,
# :gid => 1000,
- # :home => "/home/adam",
+ # :home => "/home/notarealuser",
# :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"
+ @pw_user.name = "notarealuser"
@pw_user.gid = 1000
@pw_user.uid = 1000
- @pw_user.gecos = "Adam Jacob"
- @pw_user.dir = "/home/adam"
+ @pw_user.gecos = "Nota Realuser"
+ @pw_user.dir = "/home/notarealuser"
@pw_user.shell = "/usr/bin/zsh"
@pw_user.passwd = "*"
allow(Etc).to receive(:getpwnam).and_return(@pw_user)
@@ -83,7 +83,7 @@ describe Chef::Provider::User do
it "should create a current resource with the same name as the new resource" do
@provider.load_current_resource
- expect(@provider.current_resource.name).to eq("adam")
+ expect(@provider.current_resource.name).to eq("notarealuser")
end
it "should set the username of the current resource to the username of the new resource" do
@@ -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(nil)
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,16 +159,16 @@ 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)
- expect(Shadow::Passwd).to receive(:getspnam).with("adam").and_return(passwd_info)
+ 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("notarealuser").and_return(passwd_info)
@provider.load_current_resource
@provider.define_resource_requirements
@provider.process_resource_requirements
@@ -192,28 +190,28 @@ describe Chef::Provider::User do
describe "compare_user" do
let(:mapping) do
{
- "username" => %w{adam Adam},
- "comment" => ["Adam Jacob", "adam jacob"],
+ "username" => %w{notarealuser notarealuser},
+ "comment" => ["Nota Realuser", "Not a Realuser"],
"uid" => [1000, 1001],
"gid" => [1000, 1001],
- "home" => ["/home/adam", "/Users/adam"],
+ "home" => ["/home/notarealuser", "/Users/notarealuser"],
"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
- @new_resource.send(attribute, mapping[attribute][0])
- @current_resource.send(attribute, mapping[attribute][1])
+ %w{uid gid comment home shell password}.each do |property|
+ it "should return true if #{property} doesn't match" do
+ @new_resource.send(property, mapping[property][0])
+ @current_resource.send(property, mapping[property][1])
expect(@provider.compare_user).to eql(true)
end
end
- %w{uid gid}.each do |attribute|
- it "should return false if string #{attribute} matches fixnum" do
- @new_resource.send(attribute, "100")
- @current_resource.send(attribute, 100)
+ %w{uid gid}.each do |property|
+ it "should return false if string #{property} matches fixnum" do
+ @new_resource.send(property, "100")
+ @current_resource.send(property, 100)
expect(@provider.compare_user).to eql(false)
end
end
@@ -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/notarealuser"
+ @current_resource.home "/home/notarealuser/"
+ expect(@provider.compare_user).to eql(false)
+ end
end
describe "action_create" do
@@ -228,11 +232,11 @@ describe Chef::Provider::User do
allow(@provider).to receive(:load_current_resource)
# @current_resource = double("Chef::Resource::User",
# :null_object => true,
- # :username => "adam",
- # :comment => "Adam Jacob",
+ # :username => "notarealuser",
+ # :comment => "Nota Realuser",
# :uid => 1000,
# :gid => 1000,
- # :home => "/home/adam",
+ # :home => "/home/notarealuser",
# :shell => "/usr/bin/zsh",
# :password => nil,
# :updated => nil
@@ -252,9 +256,10 @@ describe Chef::Provider::User do
expect(@new_resource).to be_updated
end
- it "should call manage_user if the user exists and has mismatched attributes" do
+ it "should call manage_user if the user exists and has mismatched properties" do
@provider.user_exists = true
allow(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
expect(@provider).to receive(:manage_user).and_return(true)
@provider.action_create
end
@@ -262,6 +267,7 @@ describe Chef::Provider::User do
it "should set the new_resources updated flag when it creates the user if we call manage_user" do
@provider.user_exists = true
allow(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
allow(@provider).to receive(:manage_user).and_return(true)
@provider.action_create
@provider.set_updated_status
@@ -311,14 +317,16 @@ describe Chef::Provider::User do
# @provider.stub(:manage_user).and_return(true)
end
- it "should run manage_user if the user exists and has mismatched attributes" do
+ it "should call manage_user if the user exists and has mismatched properties" do
expect(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
expect(@provider).to receive(:manage_user).and_return(true)
@provider.action_manage
end
it "should set the new resources updated flag to true if manage_user is called" do
allow(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
allow(@provider).to receive(:manage_user).and_return(true)
@provider.action_manage
@provider.set_updated_status
@@ -331,7 +339,7 @@ describe Chef::Provider::User do
@provider.action_manage
end
- it "should not run manage_user if the user exists but has no differing attributes" do
+ it "should not run manage_user if the user exists but has no differing properties" do
expect(@provider).to receive(:compare_user).and_return(false)
expect(@provider).not_to receive(:manage_user)
@provider.action_manage
@@ -354,21 +362,23 @@ describe Chef::Provider::User do
# @provider.stub(:manage_user).and_return(true)
end
- it "should run manage_user if the user exists and has mismatched attributes" do
+ it "should run manage_user if the user exists and has mismatched properties" do
expect(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
expect(@provider).to receive(:manage_user).and_return(true)
@provider.action_modify
end
it "should set the new resources updated flag to true if manage_user is called" do
allow(@provider).to receive(:compare_user).and_return(true)
+ allow(@provider).to receive(:change_desc).and_return([ ])
allow(@provider).to receive(:manage_user).and_return(true)
@provider.action_modify
@provider.set_updated_status
expect(@new_resource).to be_updated
end
- it "should not run manage_user if the user exists but has no differing attributes" do
+ it "should not run manage_user if the user exists but has no differing properties" do
expect(@provider).to receive(:compare_user).and_return(false)
expect(@provider).not_to receive(:manage_user)
@provider.action_modify
@@ -440,35 +450,51 @@ describe Chef::Provider::User do
describe "convert_group_name" do
before do
- @new_resource.gid("999")
@group = EtcGrnamIsh.new("wheel", "*", 999, [])
end
- it "should lookup the group name locally" do
- expect(Etc).to receive(:getgrnam).with("999").and_return(@group)
- expect(@provider.convert_group_name).to eq(999)
- end
+ context "when user passes group name in gid" do
+ before do
+ @new_resource.gid("wheel")
+ end
- it "should raise an error if we can't translate the group name during resource assertions" do
- expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
- @provider.action = :create
- @provider.define_resource_requirements
- @provider.convert_group_name
- expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::User)
- end
+ it "should lookup the group name locally" do
+ expect(Etc).to receive(:getgrnam).with("wheel").and_return(@group)
+ expect(@provider.convert_group_name).to eq(999)
+ end
- it "does not raise an error if we can't translate the group name during resource assertions if we are removing the user" do
- expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
- @provider.action = :remove
- @provider.define_resource_requirements
- @provider.convert_group_name
- expect { @provider.process_resource_requirements }.not_to raise_error
+ it "should raise an error if we can't translate the group name during resource assertions" do
+ expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
+ @provider.action = :create
+ @provider.define_resource_requirements
+ @provider.convert_group_name
+ expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::User)
+ end
+
+ it "does not raise an error if we can't translate the group name during resource assertions if we are removing the user" do
+ expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
+ @provider.action = :remove
+ @provider.define_resource_requirements
+ @provider.convert_group_name
+ expect { @provider.process_resource_requirements }.not_to raise_error
+ end
+
+ it "should set the new resources gid to the integerized version if available" do
+ expect(Etc).to receive(:getgrnam).with("wheel").and_return(@group)
+ @provider.convert_group_name
+ expect(@new_resource.gid).to eq(999)
+ end
end
- it "should set the new resources gid to the integerized version if available" do
- expect(Etc).to receive(:getgrnam).with("999").and_return(@group)
- @provider.convert_group_name
- expect(@new_resource.gid).to eq(999)
+ context "when user passes group id in gid" do
+ before do
+ @new_resource.gid(999)
+ end
+
+ it "should not call getgrnam" do
+ expect(Etc).not_to receive(:getgrnam)
+ @provider.convert_group_name
+ end
end
end
end
diff --git a/spec/unit/provider/windows_env_spec.rb b/spec/unit/provider/windows_env_spec.rb
new file mode 100644
index 0000000000..7f64622d20
--- /dev/null
+++ b/spec/unit/provider/windows_env_spec.rb
@@ -0,0 +1,385 @@
+#
+# 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 "spec_helper"
+
+describe "windows_env provider", :windows_only do
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ before do
+ @new_resource = Chef::Resource::WindowsEnv.new("FOO", run_context)
+ @new_resource.value("bar")
+ @new_resource.user("<System>")
+ @provider = @new_resource.provider_for_action(:create)
+ end
+
+ describe "when loading the current status" do
+ before do
+ # @current_resource = @new_resource.clone
+ # Chef::Resource::Env.stub(:new).and_return(@current_resource)
+ @provider.current_resource = @current_resource
+ allow(@provider).to receive(:env_value).with("FOO").and_return("bar")
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ end
+
+ it "should create a current resource with the same name as the new resource" do
+ @provider.load_current_resource
+ 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 return the current resource" do
+ expect(@provider.load_current_resource).to be_a_kind_of(Chef::Resource::WindowsEnv)
+ end
+ end
+
+ describe "action_create" do
+ before do
+ allow(@provider).to receive(:key_exists?).and_return(false)
+ allow(@provider).to receive(:create_env).and_return(true)
+ allow(@provider).to receive(:modify_env).and_return(true)
+ end
+
+ 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
+
+ it "should set the new_resources updated flag when it creates the key" do
+ @provider.action_create
+ expect(@new_resource).to be_updated
+ end
+
+ it "should check to see if the values are the same if the key exists" do
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ expect(@provider).to receive(:requires_modify_or_create?).and_return(false)
+ @provider.action_create
+ end
+
+ it "should call modify_env if the key exists with provided user and values are not equal" do
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ allow(@provider).to receive(:requires_modify_or_create?).and_return(true)
+ expect(@provider).to receive(:modify_env).and_return(true)
+ @provider.action_create
+ end
+
+ it "should set the new_resources updated flag when it updates an existing value" do
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ allow(@provider).to receive(:requires_modify_or_create?).and_return(true)
+ allow(@provider).to receive(:modify_env).and_return(true)
+ @provider.action_create
+ expect(@new_resource).to be_updated
+ end
+ end
+
+ describe "action_delete" do
+ before(:each) do
+ @provider.current_resource = @current_resource
+ allow(@provider).to receive(:key_exists?).and_return(false)
+ allow(@provider).to receive(:delete_element).and_return(false)
+ allow(@provider).to receive(:delete_env).and_return(true)
+ end
+
+ it "should not call delete_env if the key does not exist" do
+ expect(@provider).not_to receive(:delete_env)
+ @provider.action_delete
+ end
+
+ it "should not call delete_element if the key does not exist" do
+ expect(@provider).not_to receive(:delete_element)
+ @provider.action_delete
+ end
+
+ it "should call delete_env if the key exists" do
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ expect(@provider).to receive(:delete_env)
+ @provider.action_delete
+ end
+
+ it "should set the new_resources updated flag to true if the key is deleted" do
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ @provider.action_delete
+ expect(@new_resource).to be_updated
+ end
+ end
+
+ describe "action_modify" do
+ before(:each) do
+ @provider.current_resource = @current_resource
+ allow(@provider).to receive(:key_exists?).and_return(true)
+ allow(@provider).to receive(:modify_env).and_return(true)
+ end
+
+ it "should call modify_group if the key exists and values 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 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)
+ @provider.action_modify
+ expect(@new_resource).to be_updated
+ end
+
+ 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::WindowsEnv if the key doesn't exist" do
+ allow(@provider).to receive(:key_exists?).and_return(false)
+ expect { @provider.action_modify }.to raise_error(Chef::Exceptions::WindowsEnv)
+ end
+ end
+
+ describe "delete_element" do
+ before(:each) do
+ @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
+ @new_resource.value("C:/baz/bin")
+ expect(@provider.delete_element).to eql(true)
+ end
+
+ it "should return false if the delim not defined" do
+ @new_resource.delim(nil)
+ expect(@provider.delete_element).to eql(false)
+ end
+
+ it "should return true if the element is deleted" do
+ @new_resource.value("C:/foo/bin")
+ expect(@provider).to receive(:create_env)
+ expect(@provider.delete_element).to eql(true)
+ expect(@new_resource).to be_updated
+ end
+
+ context "when new_resource's value contains the delimiter" do
+ it "should return false if all the elements are deleted" do
+ # This indicates that the entire key needs to be deleted
+ @new_resource.value("C:/foo/bin;C:/bar/bin")
+ expect(@provider.delete_element).to eql(false)
+ expect(@new_resource).not_to be_updated # This will be updated in action_delete
+ end
+
+ it "should return true if any, but not all, of the elements are deleted" do
+ @new_resource.value("C:/foo/bin;C:/notbaz/bin")
+ expect(@provider).to receive(:create_env)
+ expect(@provider.delete_element).to eql(true)
+ expect(@new_resource).to be_updated
+ end
+
+ it "should return true if none of the elements are deleted" do
+ @new_resource.value("C:/notfoo/bin;C:/notbaz/bin")
+ expect(@provider.delete_element).to eql(true)
+ expect(@new_resource).not_to be_updated
+ end
+ end
+ end
+
+ describe "requires_modify_or_create?" do
+ before(:each) do
+ @new_resource.value("C:/bar")
+ @current_resource = @new_resource.clone
+ @provider.current_resource = @current_resource
+ end
+
+ it "should return false if the values are equal" do
+ expect(@provider.requires_modify_or_create?).to be_falsey
+ end
+
+ it "should return true if the values not are equal" do
+ @new_resource.value("C:/elsewhere")
+ expect(@provider.requires_modify_or_create?).to be_truthy
+ end
+
+ it "should return false if the current value contains the element" do
+ @new_resource.delim(";")
+ @current_resource.value("C:/bar;C:/foo;C:/baz")
+
+ expect(@provider.requires_modify_or_create?).to be_falsey
+ end
+
+ it "should return true if the current value does not contain the element" do
+ @new_resource.delim(";")
+ @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
+ expect(@provider.requires_modify_or_create?).to be_truthy
+ end
+
+ context "when new_resource's value contains the delimiter" do
+ it "should return false if all the current values are contained in specified order" do
+ @new_resource.value("C:/biz;C:/baz")
+ @new_resource.delim(";")
+ @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
+ expect(@provider.requires_modify_or_create?).to be_falsey
+ end
+
+ it "should return true if any of the new values are not contained" do
+ @new_resource.value("C:/biz;C:/baz;C:/bin")
+ @new_resource.delim(";")
+ @current_resource.value("C:/biz;C:/foo/bin;C:/baz")
+ expect(@provider.requires_modify_or_create?).to be_truthy
+ end
+
+ it "should return true if values are contained in different order" do
+ @new_resource.value("C:/biz;C:/baz")
+ @new_resource.delim(";")
+ @current_resource.value("C:/baz;C:/foo/bin;C:/biz")
+ expect(@provider.requires_modify_or_create?).to be_truthy
+ end
+ end
+ end
+
+ describe "modify_env" do
+ before(:each) do
+ allow(@provider).to receive(:create_env).and_return(true)
+ @new_resource.delim ";"
+
+ @current_resource = Chef::Resource::WindowsEnv.new("FOO")
+ @current_resource.value "C:/foo/bin"
+ @provider.current_resource = @current_resource
+ end
+
+ it "should not modify the variable passed to the resource" do
+ new_value = "C:/bar/bin"
+ passed_value = new_value.dup
+ @new_resource.value(passed_value)
+ @provider.modify_env
+ expect(passed_value).to eq(new_value)
+ end
+
+ it "should only add values not already contained" do
+ @new_resource.value("C:/foo;C:/bar;C:/baz")
+ @current_resource.value("C:/bar;C:/baz;C:/foo/bar")
+ @provider.modify_env
+ expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
+ end
+
+ it "should reorder values to keep order which asked" do
+ @new_resource.value("C:/foo;C:/bar;C:/baz")
+ @current_resource.value("C:/foo/bar;C:/baz;C:/bar")
+ @provider.modify_env
+ 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", run_context)
+ new_resource.value("foo")
+ new_resource
+ end
+ let(:provider) do
+ provider = new_resource.provider_for_action(:create)
+ 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")
+ allow(provider).to receive(:key_exists?).and_return(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", run_context)
+ new_resource.value(system_root)
+ new_resource
+ end
+ let(:provider) do
+ provider = new_resource.provider_for_action(:create)
+ 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..0c6c590ece
--- /dev/null
+++ b/spec/unit/provider/windows_path_spec.rb
@@ -0,0 +1,60 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "windows_path provider", :windows_only do
+ before(:all) do
+ @old_path = ENV["PATH"].dup
+ end
+
+ after(:all) do
+ ENV["PATH"] = @old_path
+ end
+
+ let(:run_context) { Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new) }
+ let(:new_resource) { Chef::Resource::WindowsPath.new("some_path", run_context) }
+ let(:provider) { new_resource.provider_for_action(:add) }
+
+ 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", hash_including)
+ 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", hash_including)
+ 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..dce898154b
--- /dev/null
+++ b/spec/unit/provider/windows_task_spec.rb
@@ -0,0 +1,436 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "windows_task provider", :windows_only do
+ let(:new_resource) { Chef::Resource::WindowsTask.new("sample_task", run_context) }
+ let(:current_resource) { Chef::Resource::WindowsTask.new }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:provider) do
+ new_resource.provider_for_action(:create)
+ 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_command_and_arguments" do
+ it "sets the command arguments if command has arguments passed in it" do
+ new_resource.command = "chef-client -W"
+ provider.send(:set_command_and_arguments)
+ expect(new_resource.command).to eq("chef-client")
+ expect(new_resource.command_arguments).to eq("-W")
+ 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 string 1" do
+ new_resource.day "1"
+ expect(provider.send(:days_of_month)).to eq(1)
+ end
+
+ it "returns the binary value 1 if day is set as integer 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.user = "Administrator"
+ 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
index 5b019f7d3e..aa7737699e 100644
--- a/spec/unit/provider/yum_repository_spec.rb
+++ b/spec/unit/provider/yum_repository_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/unit/provider/zypper_repository_spec.rb b/spec/unit/provider/zypper_repository_spec.rb
new file mode 100644
index 0000000000..f0686874e6
--- /dev/null
+++ b/spec/unit/provider/zypper_repository_spec.rb
@@ -0,0 +1,176 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ZypperRepository do
+ # Output of the command:
+ # => rpm -qa gpg-pubkey*
+ ZYPPER_RPM_KEYS = <<~EOF.freeze
+ gpg-pubkey-307e3d54-4be01a65
+ gpg-pubkey-3dbdc284-53674dd4
+ EOF
+
+ # Output of the command:
+ # => gpg --with-fingerprint [FILE]
+ ZYPPER_GPG_20 = <<~EOF.freeze
+ 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
+
+ # Output of the command:
+ # => gpg --import-options import-show --dry-run --import --with-colons [FILE]
+ ZYPPER_GPG_22 = <<~EOF.freeze
+ pub:-:2048:1:ABF5BD827BD9BF62:1313747554:1718374819::-:::scSC::::::23::0:
+ fpr:::::::::573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62:
+ uid:-::::1466086904::F18C4DBBFCB45099ABB59088DB6B252FA7E9FB41::nginx signing key <signing-key@nginx.com>::::::::::0:
+ gpg: Total number processed: 1
+ EOF
+
+ # Output of the command:
+ # -> gpg --version
+ ZYPPER_GPG_VERSION = <<~EOF.freeze
+ gpg (GnuPG) 2.2.20
+ libgcrypt 1.8.5
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
+ This is free software: you are free to change and redistribute it.
+ There is NO WARRANTY, to the extent permitted by law.
+
+ Home: /Users/tsmith/.gnupg
+ Supported algorithms:
+ Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
+ Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
+ CAMELLIA128, CAMELLIA192, CAMELLIA256
+ Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
+ Compression: Uncompressed, ZIP, ZLIB, BZIP2
+ EOF
+
+ 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", stdout: ZYPPER_RPM_KEYS, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_20) do
+ double("shell_out", stdout: ZYPPER_GPG_20, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_22) do
+ double("shell_out", stdout: ZYPPER_GPG_22, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_ver) do
+ double("shell_out", stdout: ZYPPER_GPG_VERSION, 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("/bin/rpm -qa gpg-pubkey*").and_return(rpm_key_finger)
+ end
+
+ it "returns true if the key is installed" do
+ expect(provider).to receive(:short_key_id).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(:short_key_id).and_return("BOGUS")
+ expect(provider.key_installed?("/foo/nginx.key")).to be_falsey
+ end
+ end
+
+ describe "#gpg_version" do
+ it "returns the gpg version by shelling out to gpg" do
+ expect(provider).to receive(:shell_out!).with("gpg --version").and_return(gpg_ver)
+ expect(provider.gpg_version).to eq(Gem::Version.new("2.2.20"))
+ end
+ end
+
+ describe "#short_key_id" do
+ it "returns the short key ID via running a dry-run import on gpg 2.2+" do
+ expect(provider).to receive(:gpg_version).and_return(Gem::Version.new("2.2"))
+ expect(provider).to receive(:shell_out!).with("gpg --import-options import-show --dry-run --import --with-colons /foo/nginx.key").and_return(gpg_22)
+ expect(provider.short_key_id("/foo/nginx.key")).to eq("7bd9bf62")
+ end
+
+ it "returns the short key ID via --with-fingerpint on gpg < 2.2" do
+ expect(provider).to receive(:gpg_version).and_return(Gem::Version.new("2.0"))
+ expect(provider).to receive(:shell_out!).with("gpg --with-fingerprint /foo/nginx.key").and_return(gpg_20)
+ expect(provider.short_key_id("/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
deleted file mode 100644
index 5ba5ddae03..0000000000
--- a/spec/unit/provider_resolver_spec.rb
+++ /dev/null
@@ -1,935 +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 "chef/mixin/convert_to_class_name"
-require "chef/provider_resolver"
-require "chef/platform/service_helpers"
-require "support/shared/integration/integration_helper"
-require "tmpdir"
-require "fileutils"
-
-include Chef::Mixin::ConvertToClassName
-
-# Open up Provider so we can write things down easier in here
-#module Chef::Provider
-
-describe Chef::ProviderResolver do
- include IntegrationSupport
-
- # Root the filesystem under a temp directory so Chef.path_to will point at it
- when_the_repository "is empty" do
- before do
- allow(Chef).to receive(:path_to) { |path| File.join(path_to(""), path) }
- end
-
- let(:resource_name) { :service }
- let(:provider) { nil }
- let(:action) { :start }
-
- let(:node) do
- node = Chef::Node.new
- node.automatic[:os] = os
- node.automatic[:platform_family] = platform_family
- node.automatic[:platform] = platform
- node.automatic[:platform_version] = platform_version
- node.automatic[:kernel] = { machine: "i386" }
- node
- end
- let(:run_context) { Chef::RunContext.new(node, nil, nil) }
-
- let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) }
- let(:resolved_provider) do
- begin
- resource ? resource.provider_for_action(action).class : nil
- rescue Chef::Exceptions::ProviderNotFound
- nil
- end
- end
-
- let(:service_name) { "test" }
- let(:resource) do
- resource_class = Chef::ResourceResolver.resolve(resource_name, node: node)
- if resource_class
- resource = resource_class.new(service_name, run_context)
- resource.provider = provider if provider
- end
- resource
- end
-
- def self.on_platform(platform, *tags,
- platform_version: "11.0.1",
- platform_family: nil,
- os: nil,
- &block)
- Array(platform).each do |platform|
- Array(platform_version).each do |platform_version|
- on_one_platform(platform, platform_version, platform_family || platform, os || platform_family || platform, *tags, &block)
- end
- end
- end
-
- def self.on_one_platform(platform, platform_version, platform_family, os, *tags, &block)
- describe "on #{platform} #{platform_version}, platform_family: #{platform_family}, os: #{os}", *tags do
- let(:os) { os }
- let(:platform) { platform }
- let(:platform_family) { platform_family }
- let(:platform_version) { platform_version }
-
- define_singleton_method(:os) { os }
- define_singleton_method(:platform) { platform }
- define_singleton_method(:platform_family) { platform_family }
- define_singleton_method(:platform_version) { platform_version }
-
- instance_eval(&block)
- end
- end
-
- def self.expect_providers(**providers)
- providers.each do |name, expected|
- describe name.to_s do
- let(:resource_name) { name }
-
- tags = []
- expected_provider = nil
- expected_resource = nil
- Array(expected).each do |p|
- if p.is_a?(Class) && p <= Chef::Provider
- expected_provider = p
- elsif p.is_a?(Class) && p <= Chef::Resource
- expected_resource = p
- else
- tags << p
- end
- end
-
- if expected_resource && expected_provider
- it "'#{name}' resolves to resource #{expected_resource} and provider #{expected_provider}", *tags do
- expect(resource.class).to eql(expected_resource)
- provider = double(expected_provider, class: expected_provider)
- expect(provider).to receive(:action=).with(action)
- expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
- expect(resolved_provider).to eql(expected_provider)
- end
- elsif expected_provider
- it "'#{name}' resolves to provider #{expected_provider}", *tags do
- provider = double(expected_provider)
- expect(provider).to receive(:action=).with(action)
- expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
- expect(resolved_provider).to eql(expected_provider)
- end
- else
- it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
- expect(resolved_provider).to be_nil
- end
- end
- end
- end
- end
-
- describe "resolving service resource" do
- def stub_service_providers(*services)
- services.each do |service|
- case service
- when :debian
- file "usr/sbin/update-rc.d", ""
- when :invokercd
- file "usr/sbin/invoke-rc.d", ""
- when :insserv
- file "sbin/insserv", ""
- when :upstart
- file "sbin/initctl", ""
- when :redhat
- file "sbin/chkconfig", ""
- when :systemd
- file "proc/1/comm", "systemd\n"
- else
- raise ArgumentError, service
- end
- end
- end
-
- def stub_service_configs(*configs)
- configs.each do |config|
- case config
- when :initd
- file "etc/init.d/#{service_name}", ""
- when :upstart
- file "etc/init/#{service_name}.conf", ""
- when :xinetd
- file "etc/xinetd.d/#{service_name}", ""
- when :etc_rcd
- file "etc/rc.d/#{service_name}", ""
- when :usr_local_etc_rcd
- file "usr/local/etc/rc.d/#{service_name}", ""
- when :systemd
- file "proc/1/comm", "systemd\n"
- file "etc/systemd/system/#{service_name}.service", ""
- else
- raise ArgumentError, config
- end
- end
- end
-
- shared_examples_for "an ubuntu platform with upstart, update-rc.d and systemd" do
- before do
- stub_service_providers(:debian, :invokercd, :upstart, :systemd)
- end
-
- it "when only the SysV init 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
- 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
- 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
- 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
- stub_service_configs(:initd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
- end
-
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
- stub_service_configs(:initd, :upstart)
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
-
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
- stub_service_configs(:upstart)
- 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
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
- end
- end
-
- shared_examples_for "an ubuntu platform with upstart and update-rc.d" do
- before do
- stub_service_providers(:debian, :invokercd, :upstart)
- end
-
- # needs to be handled by the highest priority init.d handler
- context "when only the SysV init script exists" do
- before do
- stub_service_configs(:initd)
- end
-
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart
- )
- end
-
- it "supports all the enabled handlers except for upstart" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd
- )
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Upstart
- )
- end
-
- it "returns a Service::Debian provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
- end
- end
-
- # on ubuntu this must be handled by upstart, the init script will exit 1 and fail
- context "when both SysV and Upstart scripts exist" do
- before do
- stub_service_configs(:initd, :upstart)
- end
-
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart
- )
- end
-
- it "supports all the enabled handlers" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart
- )
- end
-
- it "returns a Service::Upstart provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
-
- # this case is a pure-upstart script which is easy
- context "when only the Upstart script exists" do
- before do
- stub_service_configs(:upstart)
- end
-
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart
- )
- end
-
- it "supports only the upstart handler" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Upstart
- )
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd
- )
- end
-
- it "returns a Service::Upstart provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
-
- # this case is important to get correct for why-run when no config is setup
- context "when both do not exist" do
- before do
- stub_service_configs
- end
-
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart
- )
- end
-
- it "no providers claim to support the resource" do
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Upstart,
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd
- )
- end
-
- it "returns a Debian Provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
- end
-
- shared_examples_for "a debian platform using the insserv provider" do
- context "with a default install" do
- before do
- stub_service_providers(:debian, :invokercd, :insserv)
- end
-
- it "uses the Service::Insserv Provider to manage sysv init scripts" do
- stub_service_configs(:initd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
- end
-
- it "uses the Service::Insserv Provider when there is no config" do
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
- end
- end
-
- context "when the user has installed upstart" do
- before do
- stub_service_providers(:debian, :invokercd, :insserv, :upstart)
- end
-
- it "when only the SysV init script exists, it returns an Insserv provider" do
- stub_service_configs(:initd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
- end
-
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
- stub_service_configs(:initd, :upstart)
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
-
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
- stub_service_configs(:upstart)
- 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
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
- end
-
- on_platform "ubuntu", platform_version: "15.10", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
-
- it "when the unit-files are missing and system-ctl list-unit-files returns an error" 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)
- end
- end
-
- on_platform "ubuntu", platform_version: "14.10", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
- end
-
- on_platform "ubuntu", platform_version: "14.04", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart and update-rc.d"
- end
-
- on_platform "ubuntu", platform_version: "10.04", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart and update-rc.d"
- end
-
- # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???)
- on_platform "debian", platform_version: "4.0", os: "linux" do
- #it_behaves_like "a debian platform using the debian provider"
- end
-
- # Debian replaced the debian provider with insserv in the FIXME:VERSION distro
- on_platform "debian", platform_version: "7.0", os: "linux" do
- it_behaves_like "a debian platform using the insserv provider"
- end
-
- on_platform %w{solaris2 openindiana opensolaris nexentacore omnios smartos}, os: "solaris2", platform_version: "5.11" do
- it "returns a Solaris provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
- end
-
- it "always returns a Solaris provider" do
- # no matter what we stub on the next two lines we should get a Solaris provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
- end
- end
-
- on_platform %w{mswin mingw32 windows}, platform_family: "windows", platform_version: "5.11" do
- it "returns a Windows provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
- end
-
- it "always returns a Windows provider" do
- # no matter what we stub on the next two lines we should get a Windows provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
- end
- end
-
- on_platform %w{mac_os_x mac_os_x_server}, os: "darwin", platform_family: "mac_os_x", platform_version: "10.9.2" do
- it "returns a Macosx provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
- end
-
- it "always returns a Macosx provider" do
- # no matter what we stub on the next two lines we should get a Macosx provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
- end
- end
-
- 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)
- 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
-
- 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 ],
- aix_user: [ Chef::Resource::User::AixUser, Chef::Provider::User::Aix ],
- dscl_user: [ Chef::Resource::User::DsclUser, Chef::Provider::User::Dscl ],
- linux_user: [ Chef::Resource::User::LinuxUser, Chef::Provider::User::Linux ],
- pw_user: [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
- solaris_user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
- windows_user: [ Chef::Resource::User::WindowsUser, Chef::Provider::User::Windows ],
- 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 ],
-
- "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 ],
- },
- },
- },
-
- "arch" => {
- # TODO should be Chef::Resource::PacmanPackage
- package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
-
- "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 ],
- },
- },
- "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" => {
- },
- },
- },
-
- "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 ],
-
- %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" => {
- "freebsd" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
- user: [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
-
- "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::DsclUser, Chef::Provider::User::Dscl ],
-
- "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::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::AixUser, Chef::Provider::User::Aix ],
- # service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
-
- "aix" => {
- "aix" => {
- "5.6" => {
- },
- },
- },
- },
-
- "hpux" => {
- "hpux" => {
- "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 ],
- },
- },
- },
- },
-
- "openbsd" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
- package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
-
- "openbsd" => {
- "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 ],
-
- "smartos" => {
- "3.1.4" => {
- },
- },
- },
-
- "solaris2" => {
- "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" => {
- },
- },
- },
- },
-
- "exherbo" => {
- "exherbo" => {
- "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) })
- providers.each do |key, value|
- if !key.is_a?(Symbol)
- next_test = test.merge({ filter => key })
- next_filter =
- case filter
- when :os
- :platform_family
- when :platform_family
- :platform
- when :platform
- :platform_version
- when :platform_version
- nil
- else
- raise "Hash too deep; only os, platform_family, platform and platform_version supported"
- end
- create_provider_tests(value, next_test, expected, next_filter)
- end
- end
- # If there is no filter, we're as deep as we need to go
- if !filter
- on_platform test.delete(:platform), test do
- expect_providers(expected)
- end
- end
- end
-
- create_provider_tests(PROVIDERS, {}, {}, :os)
- end
-end
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index 2bc2ae7c88..a8f0ede94e 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,12 +20,12 @@ require "spec_helper"
class NoWhyrunDemonstrator < Chef::Provider
attr_reader :system_state_altered
+
def whyrun_supported?
false
end
- def load_current_resource
- end
+ def load_current_resource; end
def action_foo
@system_state_altered = true
@@ -39,8 +39,7 @@ class ConvergeActionDemonstrator < Chef::Provider
true
end
- def load_current_resource
- end
+ def load_current_resource; end
def action_foo
converge_by("running a state changing action") do
@@ -75,10 +74,6 @@ describe Chef::Provider do
expect(@provider.respond_to?(:shell_out!)).to be true
end
- it "should mixin shell_out_with_systems_locale" do
- expect(@provider.respond_to?(:shell_out_with_systems_locale)).to be true
- end
-
it "should store the resource passed to new as new_resource" do
expect(@provider.new_resource).to eql(@resource)
end
@@ -91,8 +86,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 +190,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 f42b7563f5..ffa7025e3f 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 (c) 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/platform/resource_priority_map"
describe Chef::Recipe do
let(:cookbook_collection) do
- cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
+ cookbook_repo = File.expand_path(File.join(__dir__, "..", "data", "cookbooks"))
cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
cookbook_loader.load_cookbooks
Chef::CookbookCollection.new(cookbook_loader)
@@ -74,12 +74,6 @@ describe Chef::Recipe do
expect { recipe.not_home("not_home_resource") }.to raise_error(NameError)
end
- it "should require a name argument" do
- expect do
- recipe.cat
- end.to raise_error(ArgumentError)
- end
-
it "should allow regular errors (not NameErrors) to pass unchanged" do
expect do
recipe.cat("felix") { raise ArgumentError, "You Suck" }
@@ -100,7 +94,7 @@ describe Chef::Recipe do
end
end
- expect(run_context.resource_collection.map { |r| r.name }).to eql(%w{monkey dog cat})
+ expect(run_context.resource_collection.map(&:name)).to eql(%w{monkey dog cat})
end
it "should return the new resource after creating it" do
@@ -116,12 +110,12 @@ describe Chef::Recipe do
it "locate resource for particular platform" do
ShaunTheSheep = Class.new(Chef::Resource)
ShaunTheSheep.resource_name :shaun_the_sheep
- ShaunTheSheep.provides :laughter, :platform => ["television"]
+ ShaunTheSheep.provides :laughter, platform: ["television"]
node.automatic[:platform] = "television"
node.automatic[:platform_version] = "123"
res = recipe.laughter "timmy"
expect(res.name).to eql("timmy")
- res.kind_of?(ShaunTheSheep)
+ res.is_a?(ShaunTheSheep)
end
it "locate a resource for all platforms" do
@@ -130,7 +124,7 @@ describe Chef::Recipe do
YourMom.provides :love_and_caring
res = recipe.love_and_caring "mommy"
expect(res.name).to eql("mommy")
- res.kind_of?(YourMom)
+ res.is_a?(YourMom)
end
describe "when there is more than one resource that resolves on a node" 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"
@@ -189,111 +183,40 @@ describe Chef::Recipe do
it "does not add the resource to the resource collection" do
zm_resource # force let binding evaluation
- expect { run_context.resource_collection.resources(:zen_master => "klopp") }.to raise_error(Chef::Exceptions::ResourceNotFound)
+ expect { run_context.resource_collection.resources(zen_master: "klopp") }.to raise_error(Chef::Exceptions::ResourceNotFound)
end
end
- describe "when cloning resources" do
- def expect_warning
- expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
+ 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 "vbv"
- 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"
end
- expect_warning
+ not_expect_warning
recipe.zen_master "klopp" do
- something "bvb"
- peace true
- end
- end
-
- it "should emit a 3694 warning when attributes change" do
- recipe.zen_master "klopp" do
- something "bvb"
- peace true
- end
- 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
- recipe.zen_master "klopp" do
- something "bvb"
- end
- expect_warning
+ it "should not copy attributes from a prior resource" do
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).to_not receive(:log_deprecation)
+ 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).to_not receive(:log_deprecation)
- 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).to_not receive(:log_deprecation)
- recipe.zen_master "klopp" do
- action :nothing
- end
- end
-
- class Coerced < Chef::Resource
- resource_name :coerced
- provides :coerced
- default_action :whatever
- property :package_name, [String, Array], coerce: proc { |x| [x].flatten }, name_property: true
- def after_created
- Array(action).each do |action|
- run_action(action)
- end
- end
- action :whatever do
- package_name # unlazy the package_name
- end
- end
-
- it "does not emit 3694 when the name_property is unlazied by running it at compile_time" do
- recipe.coerced "string"
- expect(Chef).to_not receive(:log_deprecation)
- recipe.coerced "string"
- end
-
- it "validating resources via build_resource" do
- expect do
- recipe.build_resource(:remote_file, "klopp") do
- source Chef::DelayedEvaluator.new { "http://chef.io" }
- end end.to_not raise_error
- end
-
end
describe "creating resources via declare_resource" do
@@ -315,23 +238,15 @@ describe Chef::Recipe do
it "adds the resource to the resource collection" do
zm_resource # force let binding evaluation
- expect(run_context.resource_collection.resources(:zen_master => "klopp")).to eq(zm_resource)
+ expect(run_context.resource_collection.resources(zen_master: "klopp")).to eq(zm_resource)
end
it "will insert another resource if create_if_missing is not set (cloned resource as of Chef-12)" do
- expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
zm_resource
recipe.declare_resource(:zen_master, "klopp")
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
@@ -386,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
@@ -395,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
@@ -421,62 +336,10 @@ 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(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
- expect(duplicated_resource.something).to eq("bvb09")
- end
-
- it "does not copy the action from the first resource" do
- expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
- 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
- expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
- # 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(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
- 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(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
- 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
- crow_define.define :crow, :peace => false, :something => true do
+ crow_define.define :crow, peace: false, something: true do
zen_master "lao tzu" do
peace params[:peace]
something params[:something]
@@ -486,13 +349,13 @@ describe Chef::Recipe do
recipe.crow "mine" do
peace true
end
- expect(run_context.resource_collection.resources(:zen_master => "lao tzu").name).to eql("lao tzu")
- expect(run_context.resource_collection.resources(:zen_master => "lao tzu").something).to eql(true)
+ expect(run_context.resource_collection.resources(zen_master: "lao tzu").name).to eql("lao tzu")
+ expect(run_context.resource_collection.resources(zen_master: "lao tzu").something).to eql(true)
end
it "should set the node on defined resources" do
crow_define = Chef::ResourceDefinition.new
- crow_define.define :crow, :peace => false, :something => true do
+ crow_define.define :crow, peace: false, something: true do
zen_master "lao tzu" do
peace params[:peace]
something params[:something]
@@ -503,12 +366,12 @@ describe Chef::Recipe do
recipe.crow "mine" do
something node[:foo]
end
- expect(recipe.resources(:zen_master => "lao tzu").something).to eql(false)
+ expect(recipe.resources(zen_master: "lao tzu").something).to eql(false)
end
it "should return the last statement in the definition as the retval" do
crow_define = Chef::ResourceDefinition.new
- crow_define.define :crow, :peace => false, :something => true do
+ crow_define.define :crow, peace: false, something: true do
"the return val"
end
run_context.definitions[:crow] = crow_define
@@ -527,9 +390,9 @@ describe Chef::Recipe do
zen_master "gnome" do
peace = true
end
- CODE
+ CODE
expect { recipe.instance_eval(code) }.not_to raise_error
- expect(recipe.resources(:zen_master => "gnome").name).to eql("gnome")
+ expect(recipe.resources(zen_master: "gnome").name).to eql("gnome")
end
end
@@ -545,7 +408,7 @@ describe Chef::Recipe do
describe "from_file" do
it "should load a resource from a ruby file" do
recipe.from_file(File.join(CHEF_SPEC_DATA, "recipes", "test.rb"))
- res = recipe.resources(:file => "/etc/nsswitch.conf")
+ res = recipe.resources(file: "/etc/nsswitch.conf")
expect(res.name).to eql("/etc/nsswitch.conf")
expect(res.action).to eql([:create])
expect(res.owner).to eql("root")
@@ -563,7 +426,7 @@ describe Chef::Recipe do
expect(node).to receive(:loaded_recipe).with(:openldap, "gigantor")
allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false)
run_context.include_recipe "openldap::gigantor"
- res = run_context.resource_collection.resources(:cat => "blanket")
+ res = run_context.resource_collection.resources(cat: "blanket")
expect(res.name).to eql("blanket")
expect(res.pretty_kitty).to eql(false)
end
@@ -572,7 +435,7 @@ describe Chef::Recipe do
expect(node).to receive(:loaded_recipe).with(:openldap, "default")
allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false)
run_context.include_recipe "openldap"
- res = run_context.resource_collection.resources(:cat => "blanket")
+ res = run_context.resource_collection.resources(cat: "blanket")
expect(res.name).to eql("blanket")
expect(res.pretty_kitty).to eql(true)
end
@@ -713,12 +576,75 @@ describe Chef::Recipe do
end
end
- describe "included DSL" do
- it "should include features from Chef::DSL::Audit" do
- expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit)
- expect(recipe.respond_to?(:control_group)).to be true
+ describe "from_yaml_file" do
+ it "raises ArgumentError if the YAML file contains multiple documents" do
+ filename = "multiple_docs.yaml"
+ yaml = "---\n- resources:\n - type: false\n---\n-resources:\n - type: false\n"
+ allow(File).to receive(:file?).and_call_original
+ allow(File).to receive(:readable?).and_call_original
+ allow(IO).to receive(:read).and_call_original
+ allow(File).to receive(:file?).with(filename).and_return(true)
+ allow(File).to receive(:readable?).with(filename).and_return(true)
+ allow(IO).to receive(:read).with(filename).and_return(yaml)
+ expect { recipe.from_yaml_file(filename) }.to raise_error(ArgumentError, /contains multiple documents/)
+ end
+
+ it "raises IOError if the file does not exist" do
+ filename = "/nonexistent"
+ allow(File).to receive(:file?).and_call_original
+ allow(File).to receive(:file?).with(filename).and_return(false)
+ expect { recipe.from_yaml_file(filename) }.to raise_error(IOError, /Cannot open or read/)
+ end
+ end
+
+ describe "from_yaml" do
+ it "raises ArgumentError if the YAML is not a top-level hash" do
+ yaml = <<~YAML
+ ---
+ - one
+ - resources
+ - three
+ YAML
+ expect { recipe.from_yaml(yaml) }.to raise_error(ArgumentError, /must contain a top-level 'resources' hash/)
+ end
+
+ it "raises ArgumentError if the YAML does not contain a resources hash" do
+ yaml = <<~YAML
+ ---
+ - airplanes:
+ - type: "execute"
+ command: "whoami"
+ YAML
+ expect { recipe.from_yaml(yaml) }.to raise_error(ArgumentError, /must contain a top-level 'resources' hash/)
+ end
+
+ it "does not raise if the YAML contains a resources hash" do
+ yaml = <<~YAML
+ ---
+ resources:
+ - type: "execute"
+ command: "whoami"
+ YAML
+ expect(recipe).to receive(:from_hash).with({ "resources" => [{ "command" => "whoami", "type" => "execute" }] })
+ recipe.from_yaml(yaml)
end
+ end
+ describe "from_hash" do
+ it "declares resources from a hash" do
+ resources = { "resources" => [
+ { "name" => "running some commands", "type" => "execute", "command" => "whoami" },
+ { "name" => "preparing the bits", "type" => "service", "action" => "start", "service_name" => "bit_launcher" },
+ ] }
+
+ recipe.from_hash(resources)
+ expect(recipe.resources(execute: "running some commands").command).to eql("whoami")
+ expect(recipe.resources(service: "preparing the bits").service_name).to eql("bit_launcher")
+ expect(recipe.resources(service: "preparing the bits").action).to eql([:start])
+ end
+ end
+
+ describe "included DSL" do
it "should respond to :ps_credential from Chef::DSL::Powershell" do
expect(recipe.respond_to?(:ps_credential)).to be true
end
diff --git a/spec/unit/resource/alternatives_spec.rb b/spec/unit/resource/alternatives_spec.rb
new file mode 100644
index 0000000000..9f8d8dcfbb
--- /dev/null
+++ b/spec/unit/resource/alternatives_spec.rb
@@ -0,0 +1,120 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::Alternatives do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Alternatives.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:install) }
+
+ let(:alternatives_display_exists) do
+ double("shellout", stdout: <<-STDOUT)
+ java - auto mode
+ link best version is /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+ link currently points to /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+ link java is /usr/bin/java
+ slave java.1.gz is /usr/share/man/man1/java.1.gz
+/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java - priority 1081
+ slave java.1.gz: /usr/lib/jvm/java-8-openjdk-amd64/jre/man/man1/java.1.gz
+ STDOUT
+ end
+
+ let(:alternatives_display_does_not_exist) do
+ double("shellout", stdout: "update-alternatives: error: no alternatives for fakey_fakerton")
+ end
+
+ it "the link_name property is the name_property" do
+ expect(resource.link_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "coerces priority value to an Integer" do
+ resource.priority("1")
+ expect(resource.priority).to eql(1)
+ end
+
+ it "builds a default value for link based on link_name value" do
+ expect(resource.link).to eql("/usr/bin/fakey_fakerton")
+ end
+
+ it "supports :install, :auto, :refresh, and :remove actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :auto }.not_to raise_error
+ expect { resource.action :refresh }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ describe "#path_exists?" do
+ it "returns true if the path exists according to alternatives --display" do
+ allow(provider).to receive(:shell_out).with("alternatives", "--display", "fakey_fakerton").and_return(alternatives_display_exists)
+ expect(provider.path_exists?).to eql(true)
+ end
+
+ it "returns false if alternatives --display does not find a path" do
+ allow(provider).to receive(:shell_out).with("alternatives", "--display", "fakey_fakerton").and_return(alternatives_display_does_not_exist)
+ expect(provider.path_exists?).to eql(false)
+ end
+ end
+
+ describe "#current_path" do
+ it "extracts the current path by running alternatives --display" do
+ allow(provider).to receive(:shell_out).with("alternatives", "--display", "fakey_fakerton").and_return(alternatives_display_exists)
+ expect(provider.current_path).to eql("/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java")
+ end
+ end
+
+ describe "#path_priority" do
+ it "extracts the path priority by running alternatives --display" do
+ allow(provider).to receive(:shell_out).with("alternatives", "--display", "fakey_fakerton").and_return(alternatives_display_exists)
+ expect(provider.path_priority).to eql(1081)
+ end
+ end
+
+ describe "#alternatives_cmd" do
+ it "returns alternatives on fedora" do
+ node.automatic_attrs[:platform_family] = "fedora"
+ expect(provider.alternatives_cmd).to eql("alternatives")
+ end
+
+ it "returns alternatives on amazon" do
+ node.automatic_attrs[:platform_family] = "amazon"
+ expect(provider.alternatives_cmd).to eql("alternatives")
+ end
+
+ it "returns alternatives on suse" do
+ node.automatic_attrs[:platform_family] = "suse"
+ expect(provider.alternatives_cmd).to eql("alternatives")
+ end
+
+ it "returns alternatives on redhat" do
+ node.automatic_attrs[:platform_family] = "rhel"
+ expect(provider.alternatives_cmd).to eql("alternatives")
+ end
+
+ it "returns update-alternatives on debian" do
+ node.automatic_attrs[:platform_family] = "debian"
+ expect(provider.alternatives_cmd).to eql("update-alternatives")
+ end
+ end
+end
diff --git a/spec/unit/resource/apt_package_spec.rb b/spec/unit/resource/apt_package_spec.rb
index 78eccfb444..4fc1b903d1 100644
--- a/spec/unit/resource/apt_package_spec.rb
+++ b/spec/unit/resource/apt_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,8 +31,36 @@ describe Chef::Resource::AptPackage, "initialize" do
let(:resource) { Chef::Resource::AptPackage.new("foo") }
- it "should support default_release" do
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ 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
+
+ it "accepts a string for the response file" do
+ resource.response_file "something"
+ expect(resource.response_file).to eql("something")
+ end
+
+ 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
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..9a30c1198e
--- /dev/null
+++ b/spec/unit/resource/apt_preference_spec.rb
@@ -0,0 +1,39 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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("fakey_fakerton", run_context) }
+
+ it "the package_name property is the name_property" do
+ expect(resource.package_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/apt_repository_spec.rb b/spec/unit/resource/apt_repository_spec.rb
index 0b0c0c5d26..23016c9abf 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:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,31 +20,55 @@ require "spec_helper"
describe Chef::Resource::AptRepository do
let(:node) { Chef::Node.new }
- let(:events) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, events) }
- let(:resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
-
- 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)
+ 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("fakey_fakerton", run_context) }
- it "the default keyserver should be keyserver.ubuntu.com" do
+ it "keyserver defaults to keyserver.ubuntu.com" do
expect(resource.keyserver).to eql("keyserver.ubuntu.com")
end
- it "the default distribution should be nillable" do
- expect(resource.distribution(nil)).to eql(nil)
- expect(resource.distribution).to eql(nil)
+ it "the repo_name property is the name_property" do
+ expect(resource.repo_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ 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 "should resolve to a Noop class when apt-get is not found" do
- expect(Chef::Provider::AptRepository).to receive(:which).with("apt-get").and_return(false)
- expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+ 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 "should resolve to a AptRepository class when apt-get is found" do
- expect(Chef::Provider::AptRepository).to receive(:which).with("apt-get").and_return(true)
- expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptRepository)
+ 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
end
diff --git a/spec/unit/resource/apt_update_spec.rb b/spec/unit/resource/apt_update_spec.rb
index dd72b18063..c14d909a48 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:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,27 +24,21 @@ describe Chef::Resource::AptUpdate do
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:resource) { Chef::Resource::AptUpdate.new("update", run_context) }
- 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)
+ it "sets the default action as :periodic" do
+ expect(resource.action).to eql([:periodic])
end
- it "the default frequency should be 1 day" do
+ it "supports :periodic, :update actions" do
+ expect { resource.action :periodic }.not_to raise_error
+ expect { resource.action :update }.not_to raise_error
+ end
+
+ 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 "should resolve to a Noop class when apt-get is not found" do
- expect(Chef::Provider::AptUpdate).to receive(:which).with("apt-get").and_return(false)
- expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
- end
-
- it "should resolve to a AptUpdate class when apt-get is found" do
- expect(Chef::Provider::AptUpdate).to receive(:which).with("apt-get").and_return(true)
- expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptUpdate)
- end
end
diff --git a/spec/unit/resource/archive_file_spec.rb b/spec/unit/resource/archive_file_spec.rb
new file mode 100644
index 0000000000..6effe550db
--- /dev/null
+++ b/spec/unit/resource/archive_file_spec.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright (c) 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::ArchiveFile do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ArchiveFile.new("foo", run_context) }
+ let(:provider) { resource.provider_for_action(:extract) }
+
+ it "has a resource name of :archive_file" do
+ expect(resource.resource_name).to eql(:archive_file)
+ end
+
+ it "has a name property of path" do
+ expect(resource.path).to match(/.*foo$/)
+ end
+
+ it "sets the default action as :extract" do
+ expect(resource.action).to eql([:extract])
+ end
+
+ it "supports :extract action" do
+ expect { resource.action :extract }.not_to raise_error
+ end
+
+ it "mode property defaults to '755'" do
+ expect(resource.mode).to eql("755")
+ end
+
+ it "mode property throws a deprecation warning if Integers are passed" do
+ expect(Chef::Log).to receive(:deprecation)
+ resource.mode 755
+ provider.define_resource_requirements
+ end
+
+ it "options property defaults to [:time]" do
+ expect(resource.options).to eql([:time])
+ end
+end
diff --git a/spec/unit/resource/bash_spec.rb b/spec/unit/resource/bash_spec.rb
index 56c36df1ce..204e720e71 100644
--- a/spec/unit/resource/bash_spec.rb
+++ b/spec/unit/resource/bash_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,21 +20,26 @@ require "spec_helper"
describe Chef::Resource::Bash do
- before(:each) do
- @resource = Chef::Resource::Bash.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::Bash.new("fakey_fakerton") }
+
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
+ end
+
+ it "has a resource name of :bash" do
+ expect(resource.resource_name).to eql(:bash)
end
- 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 "has an interpreter of bash" do
+ expect(resource.interpreter).to eql("bash")
end
- it "should have a resource name of :bash" do
- expect(@resource.resource_name).to eql(:bash)
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
- it "should have an interpreter of bash" do
- expect(@resource.interpreter).to eql("bash")
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
end
end
diff --git a/spec/unit/resource/batch_spec.rb b/spec/unit/resource/batch_spec.rb
index e19ea15585..a008cfda04 100644
--- a/spec/unit/resource/batch_spec.rb
+++ b/spec/unit/resource/batch_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,26 +20,23 @@ require "spec_helper"
describe Chef::Resource::Batch do
- before(:each) do
+ let(:resource) do
node = Chef::Node.new
-
- node.default["kernel"] = Hash.new
+ node.default["kernel"] = {}
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)
-
+ Chef::Resource::Batch.new("batch_unit_test", run_context)
end
- it "should create a new Chef::Resource::Batch" do
- expect(@resource).to be_a_kind_of(Chef::Resource::Batch)
+ it "creates a new Chef::Resource::Batch" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Batch)
end
context "windows script" do
- let(:resource_instance) { @resource }
- let(:resource_instance_name ) { @resource.command }
+ let(:windows_script_resource) { resource }
+ let(:resource_instance_name ) { resource.command }
let(:resource_name) { :batch }
let(:interpreter_file_name) { "cmd.exe" }
diff --git a/spec/unit/resource/bff_package_spec.rb b/spec/unit/resource/bff_package_spec.rb
new file mode 100644
index 0000000000..53e2b18a81
--- /dev/null
+++ b/spec/unit/resource/bff_package_spec.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::BffPackage, "initialize" do
+
+ static_provider_resolution(
+ resource: Chef::Resource::BffPackage,
+ provider: Chef::Provider::Package::Bff,
+ name: :bff_package,
+ action: :install,
+ os: "linux",
+ platform_family: "aix"
+ )
+
+end
+
+describe Chef::Resource::BffPackage, "defaults" do
+ let(:resource) { Chef::Resource::BffPackage.new("fakey_fakerton") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb
index a5b27bae16..4b1aed6f8e 100644
--- a/spec/unit/resource/breakpoint_spec.rb
+++ b/spec/unit/resource/breakpoint_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@kallistec.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 "allows the action :break" do
- expect(@breakpoint.allowed_actions).to include(:break)
+ 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 "defaults to the break action" do
- expect(@breakpoint.action).to eq([:break])
+ 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 "sets the default action as :break" do
+ expect(resource.action).to eql([:break])
+ end
+
+ it "supports :break action" do
+ expect { resource.action :break }.not_to raise_error
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/build_essential_spec.rb b/spec/unit/resource/build_essential_spec.rb
new file mode 100644
index 0000000000..f366ceb557
--- /dev/null
+++ b/spec/unit/resource/build_essential_spec.rb
@@ -0,0 +1,77 @@
+#
+# Copyright:: Copyright (c) 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::BuildEssential do
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::BuildEssential.new("foo", run_context) }
+ let(:provider) { resource.provider_for_action(:install) }
+
+ let(:softwareupdate_catalina_and_later) do
+ double("shell_out", exitstatus: 0, error!: nil, stdout: "Software Update Tool\n\nFinding available software\nSoftware Update found the following new or updated software:\n* Label: Command Line Tools for Xcode-11.0\n\tTitle: Command Line Tools for Xcode, Version: 11.0, Size: 224868K, Recommended: YES, \n")
+ end
+
+ let(:softwareupdate_catalina_and_later_no_cli) do
+ double("shell_out", exitstatus: 0, error!: nil, stdout: "Software Update Tool\n\nFinding available software\nSoftware Update found the following new or updated software:\n* Label: Chef Infra Client\n\tTitle: Chef Infra Client, Version: 17.0.208, Size: 224868K, Recommended: YES, \n")
+ end
+
+ let(:softwareupdate_pre_catalina) do
+ double("shell_out", exitstatus: 0, error!: nil, stdout: "Software Update Tool\n\nFinding available software\nSoftware Update found the following new or updated software:\n * Command Line Tools (macOS High Sierra version 10.13) for Xcode-10.0\n")
+ end
+
+ it "has a resource name of :build_essential" do
+ expect(resource.resource_name).to eql(:build_essential)
+ 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
+ end
+
+ 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
+
+ describe "#xcode_cli_package_label" do
+ it "returns a package name on macOS < 10.15" do
+ allow(provider).to receive(:shell_out).with("softwareupdate", "--list").and_return(softwareupdate_pre_catalina)
+ expect(provider.xcode_cli_package_label).to eql("Command Line Tools (macOS High Sierra version 10.13) for Xcode-10.0")
+ end
+
+ it "returns a package name on macOS 10.15+" do
+ allow(provider).to receive(:shell_out).with("softwareupdate", "--list").and_return(softwareupdate_catalina_and_later)
+ expect(provider.xcode_cli_package_label).to eql("Command Line Tools for Xcode-11.0")
+ end
+
+ it "returns nil if no update is listed" do
+ allow(provider).to receive(:shell_out).with("softwareupdate", "--list").and_return(softwareupdate_catalina_and_later_no_cli)
+ expect(provider.xcode_cli_package_label).to be_nil
+ 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..19f7f91e63
--- /dev/null
+++ b/spec/unit/resource/cab_package_spec.rb
@@ -0,0 +1,64 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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_client_config_spec.rb b/spec/unit/resource/chef_client_config_spec.rb
new file mode 100644
index 0000000000..7e6d7a5c5c
--- /dev/null
+++ b/spec/unit/resource/chef_client_config_spec.rb
@@ -0,0 +1,137 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ChefClientConfig do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientConfig.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:create) }
+
+ 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
+ end
+
+ describe "ssl_verify_mode property" do
+ it "coerces String to Symbol" do
+ resource.ssl_verify_mode "verify_peer"
+ expect(resource.ssl_verify_mode).to eql(:verify_peer)
+ end
+
+ it "coerces Symbol-like String to Symbol" do
+ resource.ssl_verify_mode ":verify_peer"
+ expect(resource.ssl_verify_mode).to eql(:verify_peer)
+ end
+
+ it "raises an error if it is not an allowed value" do
+ expect { resource.ssl_verify_mode("foo") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.ssl_verify_mode(:verify_none) }.not_to raise_error
+ expect { resource.ssl_verify_mode(:verify_peer) }.not_to raise_error
+ end
+ end
+
+ describe "no_proxy property" do
+ it "coerces Array into comma separated list" do
+ resource.no_proxy ["something.com", "example.com"]
+ expect(resource.no_proxy).to eql("something.com,example.com")
+ end
+
+ it "accepts String of comma separated values" do
+ resource.no_proxy "something.com,example.com"
+ expect(resource.no_proxy).to eql("something.com,example.com")
+ end
+ end
+
+ describe "ohai_disabled_plugins property" do
+ it "coerces String values into capitalized symbols" do
+ resource.ohai_disabled_plugins %w{foo Bar}
+ expect(resource.ohai_disabled_plugins).to eql(%i{Foo Bar})
+ end
+
+ it "coerces symbol-like string values into capitalized Symbols" do
+ resource.ohai_disabled_plugins [":foo", ":Bar"]
+ expect(resource.ohai_disabled_plugins).to eql(%i{Foo Bar})
+ end
+
+ it "coerces Symbol values into capitalized Symbols" do
+ resource.ohai_disabled_plugins %i{foo Bar}
+ expect(resource.ohai_disabled_plugins).to eql(%i{Foo Bar})
+ end
+ end
+
+ describe "ohai_optional_plugins property" do
+ it "coerces String values into capitalized symbols" do
+ resource.ohai_optional_plugins %w{foo Bar}
+ expect(resource.ohai_optional_plugins).to eql(%i{Foo Bar})
+ end
+
+ it "coerces symbol-like string values into capitalized Symbols" do
+ resource.ohai_optional_plugins [":foo", ":Bar"]
+ expect(resource.ohai_optional_plugins).to eql(%i{Foo Bar})
+ end
+
+ it "coerces Symbol values into capitalized Symbols" do
+ resource.ohai_optional_plugins %i{foo Bar}
+ expect(resource.ohai_optional_plugins).to eql(%i{Foo Bar})
+ end
+ end
+
+ describe "log_level property" do
+ it "accepts auto trace debug info warn fatal" do
+ expect { resource.log_level(:auto) }.not_to raise_error
+ expect { resource.log_level(:trace) }.not_to raise_error
+ expect { resource.log_level(:debug) }.not_to raise_error
+ expect { resource.log_level(:info) }.not_to raise_error
+ expect { resource.log_level(:warn) }.not_to raise_error
+ end
+
+ it "raises an error if an invalid value is passed" do
+ expect { resource.log_level(":foo") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
+ describe "log_location property" do
+ it "accepts a String logfile location" do
+ expect { resource.log_location("/foo/bar/") }.not_to raise_error
+ end
+
+ it "accepts a String form of STDOUT/STDERR" do
+ expect { resource.log_location("STDOUT") }.not_to raise_error
+ expect { resource.log_location("STDERR") }.not_to raise_error
+ end
+
+ it "accepts :syslog or :win_evt Symbols" do
+ expect { resource.log_location(:syslog) }.not_to raise_error
+ expect { resource.log_location(:win_evt) }.not_to raise_error
+ expect { resource.log_location(:nope) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
+ describe "#format_handler" do
+ it "provides an array of handler object creation code" do
+ expect(provider.format_handler([{ "class" => "Foo", "arguments" => ["'one'", "two", "three"] }])).to eql(["Foo.new('one',two,three)"])
+ end
+ end
+end
diff --git a/spec/unit/resource/chef_client_cron_spec.rb b/spec/unit/resource/chef_client_cron_spec.rb
new file mode 100644
index 0000000000..121758ac73
--- /dev/null
+++ b/spec/unit/resource/chef_client_cron_spec.rb
@@ -0,0 +1,156 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ChefClientCron do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientCron.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:add) }
+
+ 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
+ end
+
+ it "coerces splay to an Integer" do
+ resource.splay "10"
+ expect(resource.splay).to eql(10)
+ end
+
+ it "raises an error if splay is not a positive number" do
+ expect { resource.splay("-10") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "builds a default value for chef_binary_path dist values" do
+ expect(resource.chef_binary_path).to eql("/opt/chef/bin/chef-client")
+ end
+
+ it "raises an error if nice is less than -20" do
+ expect { resource.nice(-21) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "raises an error if nice is greater than 19" do
+ expect { resource.nice(20) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "coerces nice to an Integer" do
+ resource.nice "10"
+ expect(resource.nice).to eql(10)
+ end
+
+ it "log_directory is /Library/Logs/Chef on macOS systems" do
+ node.automatic_attrs[:platform_family] = "mac_os_x"
+ node.automatic_attrs[:platform] = "mac_os_x"
+ expect(resource.log_directory).to eql("/Library/Logs/Chef")
+ end
+
+ it "log_directory is /var/log/chef on non-macOS systems" do
+ node.automatic_attrs[:platform_family] = "ubuntu"
+ expect(resource.log_directory).to eql("/var/log/chef")
+ end
+
+ describe "#splay_sleep_time" do
+ it "uses shard_seed attribute if present" do
+ node.automatic_attrs[:shard_seed] = "73399073"
+ expect(provider.splay_sleep_time(300)).to satisfy { |v| v >= 0 && v <= 300 }
+ end
+
+ it "uses a hex conversion of a md5 hash of the splay if present" do
+ node.automatic_attrs[:shard_seed] = nil
+ allow(node).to receive(:name).and_return("test_node")
+ expect(provider.splay_sleep_time(300)).to satisfy { |v| v >= 0 && v <= 300 }
+ end
+ end
+
+ describe "#client_command" do
+ before do
+ allow(provider).to receive(:splay_sleep_time).and_return("123")
+ end
+
+ let(:root_path) { windows? ? "C:\\chef/client.rb" : "/etc/chef/client.rb" }
+
+ it "creates a valid command if using all default properties" do
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /var/log/chef/client.log"
+ )
+ end
+
+ it "uses daemon_options if set" do
+ resource.daemon_options ["--foo 1", "--bar 2"]
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client --foo 1 --bar 2 -c #{root_path} -L /var/log/chef/client.log"
+ )
+ end
+
+ it "uses custom config dir if set" do
+ resource.config_directory "/etc/some_other_dir"
+ expect(provider.client_command).to eql("/bin/sleep 123; /opt/chef/bin/chef-client -c /etc/some_other_dir/client.rb -L /var/log/chef/client.log")
+ end
+
+ it "uses custom log files / paths if set" do
+ resource.log_file_name "my-client.log"
+ resource.log_directory "/var/log/my-chef/"
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /var/log/my-chef/my-client.log"
+ )
+ end
+
+ it "uses mailto if set" do
+ resource.mailto "bob@example.com"
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /var/log/chef/client.log || echo \"Chef Infra Client execution failed\""
+ )
+ end
+
+ it "uses custom chef-client binary if set" do
+ resource.chef_binary_path "/usr/local/bin/chef-client"
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /usr/local/bin/chef-client -c #{root_path} -L /var/log/chef/client.log"
+ )
+ end
+
+ it "appends to the log file appending if set to false" do
+ resource.append_log_file false
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} > /var/log/chef/client.log 2>&1"
+ )
+ end
+
+ it "sets the license acceptance flag if set" do
+ resource.accept_chef_license true
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} --chef-license accept -L /var/log/chef/client.log"
+ )
+ end
+
+ it "uses nice if set" do
+ allow(provider).to receive(:which).with("nice").and_return("/usr/bin/nice")
+ resource.nice(-15)
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /usr/bin/nice -n -15 /opt/chef/bin/chef-client -c #{root_path} -L /var/log/chef/client.log"
+ )
+ end
+ end
+end
diff --git a/spec/unit/resource/chef_client_launchd_spec.rb b/spec/unit/resource/chef_client_launchd_spec.rb
new file mode 100644
index 0000000000..1d0015cb0d
--- /dev/null
+++ b/spec/unit/resource/chef_client_launchd_spec.rb
@@ -0,0 +1,127 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ChefClientLaunchd do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientLaunchd.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:enable) }
+
+ it "sets the default action as :enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "supports :enable and :disable actions" do
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ end
+
+ it "coerces splay to an Integer" do
+ resource.splay "10"
+ expect(resource.splay).to eql(10)
+ end
+
+ it "raises an error if splay is not a positive number" do
+ expect { resource.splay("-10") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "builds a default value for chef_binary_path dist values" do
+ expect(resource.chef_binary_path).to eql("/opt/chef/bin/chef-client")
+ end
+
+ it "raises an error if interval is not a positive number" do
+ expect { resource.interval("-10") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "coerces interval to an Integer" do
+ resource.interval "10"
+ expect(resource.interval).to eql(10)
+ end
+
+ it "raises an error if nice is less than -20" do
+ expect { resource.nice(-21) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "raises an error if nice is greater than 19" do
+ expect { resource.nice(20) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "coerces nice to an Integer" do
+ resource.nice "10"
+ expect(resource.nice).to eql(10)
+ end
+
+ describe "#splay_sleep_time" do
+ it "uses shard_seed attribute if present" do
+ node.automatic_attrs[:shard_seed] = "73399073"
+ expect(provider.splay_sleep_time(300)).to satisfy { |v| v >= 0 && v <= 300 }
+ end
+
+ it "uses a hex conversion of a md5 hash of the splay if present" do
+ node.automatic_attrs[:shard_seed] = nil
+ allow(node).to receive(:name).and_return("test_node")
+ expect(provider.splay_sleep_time(300)).to satisfy { |v| v >= 0 && v <= 300 }
+ end
+ end
+
+ describe "#client_command" do
+ before do
+ allow(provider).to receive(:splay_sleep_time).and_return("123")
+ end
+
+ let(:root_path) { windows? ? "C:\\chef/client.rb" : "/etc/chef/client.rb" }
+
+ it "creates a valid command if using all default properties" do
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /Library/Logs/Chef/client.log"
+ )
+ end
+
+ it "adds custom daemon options from daemon_options property" do
+ resource.daemon_options %w{foo bar}
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client foo bar -c #{root_path} -L /Library/Logs/Chef/client.log"
+ )
+ end
+
+ it "adds license acceptance flags if the property is set" do
+ resource.accept_chef_license true
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /Library/Logs/Chef/client.log --chef-license accept"
+ )
+ end
+
+ it "uses custom config dir if set" do
+ resource.config_directory "/etc/some_other_dir"
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c /etc/some_other_dir/client.rb -L /Library/Logs/Chef/client.log"
+ )
+ end
+
+ it "uses custom log files / paths if set" do
+ resource.log_file_name "my-client.log"
+ resource.log_directory "/var/log/my-chef/"
+ expect(provider.client_command).to eql(
+ "/bin/sleep 123; /opt/chef/bin/chef-client -c #{root_path} -L /var/log/my-chef/my-client.log"
+ )
+ end
+ end
+end
diff --git a/spec/unit/resource/chef_client_scheduled_task_spec.rb b/spec/unit/resource/chef_client_scheduled_task_spec.rb
new file mode 100644
index 0000000000..b3c663cdae
--- /dev/null
+++ b/spec/unit/resource/chef_client_scheduled_task_spec.rb
@@ -0,0 +1,112 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ChefClientScheduledTask do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientScheduledTask.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:add) }
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "coerces splay to an Integer" do
+ resource.splay "10"
+ expect(resource.splay).to eql(10)
+ end
+
+ it "raises an error if splay is not a positive number" do
+ expect { resource.splay("-10") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "coerces frequency_modifier to an Integer" do
+ resource.frequency_modifier "10"
+ expect(resource.frequency_modifier).to eql(10)
+ end
+
+ it "expects default frequency modifier to be 30 when frequency is set to 'minute'" do
+ resource.frequency "minute"
+ expect(resource.frequency_modifier).to eql(30)
+ end
+
+ it "expects default frequency modifier to be 1 when frequency is set to 'daily'" do
+ resource.frequency "daily"
+ expect(resource.frequency_modifier).to eql(1)
+ end
+
+ it "validates the start_time property input" do
+ expect { resource.start_time("8:00 am") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.start_time("8:00") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.start_time("08:00") }.not_to raise_error
+ end
+
+ it "validates the start_date property input" do
+ expect { resource.start_date("2/1/20") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.start_date("02/01/20") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.start_date("02/01/2020") }.not_to raise_error
+ end
+
+ it "raises an error if frequency_modifier is not a positive number" do
+ expect { resource.frequency_modifier("-10") }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "builds a default value for chef_binary_path dist values" do
+ expect(resource.chef_binary_path).to eql("C:/opscode/chef/bin/chef-client")
+ end
+
+ it "supports :add and :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ describe "#client_cmd" do
+ it "creates a valid command if using all default properties" do
+ expect(provider.client_cmd).to eql("C:/opscode/chef/bin/chef-client -L /etc/chef/log/client.log -c /etc/chef/client.rb") | eql("C:/opscode/chef/bin/chef-client -L C:\\chef/log/client.log -c C:\\chef/client.rb")
+ end
+
+ it "uses daemon_options if set" do
+ resource.daemon_options ["--foo 1", "--bar 2"]
+ expect(provider.client_cmd).to eql("C:/opscode/chef/bin/chef-client -L /etc/chef/log/client.log -c /etc/chef/client.rb --foo 1 --bar 2") | eql("C:/opscode/chef/bin/chef-client -L C:\\chef/log/client.log -c C:\\chef/client.rb --foo 1 --bar 2")
+ end
+
+ it "uses custom config dir if set" do
+ resource.config_directory "C:/foo/bar"
+ expect(provider.client_cmd).to eql("C:/opscode/chef/bin/chef-client -L C:/foo/bar/log/client.log -c C:/foo/bar/client.rb")
+ end
+
+ it "uses custom log files / paths if set" do
+ resource.log_file_name "my-client.log"
+ resource.log_directory "C:/foo/bar"
+ expect(provider.client_cmd).to eql("C:/opscode/chef/bin/chef-client -L C:/foo/bar/my-client.log -c /etc/chef/client.rb") | eql("C:/opscode/chef/bin/chef-client -L C:/foo/bar/my-client.log -c C:\\chef/client.rb")
+ end
+
+ it "uses custom chef-client binary if set" do
+ resource.chef_binary_path "C:/foo/bar/chef-client"
+ expect(provider.client_cmd).to eql("C:/foo/bar/chef-client -L /etc/chef/log/client.log -c /etc/chef/client.rb") | eql("C:/foo/bar/chef-client -L C:\\chef/log/client.log -c C:\\chef/client.rb")
+ end
+
+ it "sets the license acceptance flag if set" do
+ resource.accept_chef_license true
+ expect(provider.client_cmd).to eql("C:/opscode/chef/bin/chef-client -L /etc/chef/log/client.log -c /etc/chef/client.rb --chef-license accept") | eql("C:/opscode/chef/bin/chef-client -L C:\\chef/log/client.log -c C:\\chef/client.rb --chef-license accept")
+ end
+ end
+end
diff --git a/spec/unit/resource/chef_client_systemd_timer_spec.rb b/spec/unit/resource/chef_client_systemd_timer_spec.rb
new file mode 100644
index 0000000000..dfe01973fb
--- /dev/null
+++ b/spec/unit/resource/chef_client_systemd_timer_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ChefClientSystemdTimer do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientSystemdTimer.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:add) }
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "user defaults to root" do
+ expect(resource.user).to eql("root")
+ end
+
+ it "validates the cpu_quota property input" do
+ expect { resource.cpu_quota(0) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.cpu_quota(50) }.not_to raise_error
+ expect { resource.cpu_quota(101) }.not_to raise_error
+ end
+
+ it "builds a default value for chef_binary_path dist values" do
+ expect(resource.chef_binary_path).to eql("/opt/chef/bin/chef-client")
+ end
+
+ it "supports :add and :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ describe "#chef_client_cmd" do
+
+ let(:root_path) { windows? ? "C:\\chef/client.rb" : "/etc/chef/client.rb" }
+
+ it "creates a valid command if using all default properties" do
+ expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client -c #{root_path}")
+ end
+
+ it "uses daemon_options if set" do
+ resource.daemon_options ["--foo 1", "--bar 2"]
+ expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client --foo 1 --bar 2 -c #{root_path}")
+ end
+
+ it "uses custom config dir if set" do
+ resource.config_directory "/etc/some_other_dir"
+ expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client -c /etc/some_other_dir/client.rb")
+ end
+
+ it "uses custom chef-client binary if set" do
+ resource.chef_binary_path "/usr/local/bin/chef-client"
+ expect(provider.chef_client_cmd).to eql("/usr/local/bin/chef-client -c #{root_path}")
+ end
+
+ it "sets the license acceptance flag if set" do
+ resource.accept_chef_license true
+ expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client --chef-license accept -c #{root_path}")
+ end
+ end
+
+ describe "#service_content" do
+ it "does not set ConditionACPower if run_on_battery property is set to true (the default)" do
+ expect(provider.service_content["Service"]).not_to have_key("ConditionACPower")
+ end
+
+ it "sets ConditionACPower if run_on_battery property is set to false" do
+ resource.run_on_battery false
+ expect(provider.service_content["Service"]["ConditionACPower"]).to eq("true")
+ end
+
+ it "does not set Environment if environment property is empty" do
+ expect(provider.service_content["Service"]).not_to have_key("Environment")
+ end
+
+ it "sets Environment if environment property is set" do
+ resource.environment({ "foo" => "bar" })
+ expect(provider.service_content["Service"]["Environment"]).to eq(["\"foo=bar\""])
+ end
+
+ it "does not set CPUQuota if cpu_quota property is not set" do
+ expect(provider.service_content["Service"]).not_to have_key("CPUQuota")
+ end
+
+ it "sets CPUQuota if cpu_quota property is set" do
+ resource.cpu_quota 50
+ expect(provider.service_content["Service"]["CPUQuota"]).to eq(50)
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/chef_client_trusted_certificate_spec.rb b/spec/unit/resource/chef_client_trusted_certificate_spec.rb
new file mode 100644
index 0000000000..558e737555
--- /dev/null
+++ b/spec/unit/resource/chef_client_trusted_certificate_spec.rb
@@ -0,0 +1,54 @@
+#
+# Copyright:: Copyright (c) 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::ChefClientTrustedCertificate do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChefClientTrustedCertificate.new("foo", run_context) }
+ let(:provider) { resource.provider_for_action(:add) }
+
+ it "has a resource name of :chef_client_trusted_certificate" do
+ expect(resource.resource_name).to eql(:chef_client_trusted_certificate)
+ end
+
+ it "has a name property of cert_name" do
+ expect(resource.cert_name).to eql("foo")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :remove action" do
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ describe "#cert_path" do
+ it "appends .pem to new_resource.cert_name value" do
+ resource.cert_name "something"
+ expect(provider.cert_path).to match(%r{trusted_certs/something.pem$})
+ end
+
+ it "does not append .pem if cert_name already ends in .pem" do
+ resource.cert_name "something.pem"
+ expect(provider.cert_path).to match(%r{trusted_certs/something.pem$})
+ end
+ end
+end
diff --git a/spec/unit/resource/chef_gem_spec.rb b/spec/unit/resource/chef_gem_spec.rb
index c98b447582..4a16c9fd41 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,16 +34,30 @@ 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 "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ 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
- expect(resource.gem_binary).to eql("#{RbConfig::CONFIG['bindir']}/gem")
+ 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
@@ -59,10 +73,7 @@ describe Chef::Resource::ChefGem, "gem_binary" do
Chef::Recipe.new("hjk", "test", run_context)
end
- let(:chef_gem_compile_time) { nil }
-
let(:resource) do
- Chef::Config[:chef_gem_compile_time] = chef_gem_compile_time
Chef::Resource::ChefGem.new("foo", run_context)
end
@@ -70,22 +81,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 +95,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/spec/unit/resource/chef_handler_spec.rb b/spec/unit/resource/chef_handler_spec.rb
new file mode 100644
index 0000000000..5063b73925
--- /dev/null
+++ b/spec/unit/resource/chef_handler_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright (c) 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::ChefHandler do
+
+ let(:resource) { Chef::Resource::ChefHandler.new("fakey_fakerton") }
+
+ it "has a resource name of :chef_handler" do
+ expect(resource.resource_name).to eql(:chef_handler)
+ end
+
+ it "the class_name property is the name_property" do
+ expect(resource.class_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "supports :disable, :enable actions" do
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/chef_sleep_spec.rb b/spec/unit/resource/chef_sleep_spec.rb
new file mode 100644
index 0000000000..ac4387cede
--- /dev/null
+++ b/spec/unit/resource/chef_sleep_spec.rb
@@ -0,0 +1,30 @@
+#
+# Copyright:: Copyright (c) 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::ChefSleep do
+ let(:resource) { Chef::Resource::ChefSleep.new("30") }
+
+ it "sets the default action as :sleep" do
+ expect(resource.action).to eql([:sleep])
+ end
+
+ it "sleep is the name property and it coerces to an Integer" do
+ expect(resource.seconds).to eql(30)
+ end
+end
diff --git a/spec/unit/resource/chef_vault_secret_spec.rb b/spec/unit/resource/chef_vault_secret_spec.rb
new file mode 100644
index 0000000000..f7a0b811aa
--- /dev/null
+++ b/spec/unit/resource/chef_vault_secret_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright (c) 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::ChefVaultSecret do
+ let(:resource) { Chef::Resource::ChefVaultSecret.new("foo") }
+
+ it "has a resource name of :chef_vault_secret" do
+ expect(resource.resource_name).to eql(:chef_vault_secret)
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "id is the name property" do
+ expect(resource.id).to eql("foo")
+ end
+
+ it "supports :create, :create_if_missing, and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/chocolatey_config_spec.rb b/spec/unit/resource/chocolatey_config_spec.rb
new file mode 100644
index 0000000000..8c4ebfaecf
--- /dev/null
+++ b/spec/unit/resource/chocolatey_config_spec.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright (c) 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::ChocolateyConfig do
+
+ let(:resource) { Chef::Resource::ChocolateyConfig.new("fakey_fakerton") }
+ let(:config) do
+ <<-CONFIG
+ <?xml version="1.0" encoding="utf-8"?>
+ <chocolatey xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <config>
+ <add key="containsLegacyPackageInstalls" value="true" description="Install has packages installed prior to 0.9.9 series." />
+ </config>
+ <sources>
+ <source id="chocolatey" value="https://chocolatey.org/api/v2/" disabled="false" bypassProxy="false" selfService="false" adminOnly="false" priority="0" />
+ </sources>
+ <features>
+ <feature name="checksumFiles" enabled="true" setExplicitly="false" description="Checksum files when pulled in from internet (based on package)." />
+ </features>
+ <apiKeys />
+ </chocolatey>
+ CONFIG
+ end
+
+ # we save off the ENV and set ALLUSERSPROFILE so these specs will work on *nix and non-C drive Windows installs
+ before(:each) do
+ @original_env = ENV.to_hash
+ ENV["ALLUSERSPROFILE"] = 'C:\ProgramData'
+ end
+
+ after(:each) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "has a resource name of :chocolatey_config" do
+ expect(resource.resource_name).to eql(:chocolatey_config)
+ end
+
+ it "has a name property of config_key" do
+ expect(resource.config_key).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set and :unset actions" do
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :unset }.not_to raise_error
+ end
+
+ it "bypass_proxy property defaults to false" do
+ expect { resource.bypass_proxy.to be_false }
+ end
+
+ describe "#fetch_config_element" do
+ it "raises and error if the config file cannot be found" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(false)
+ expect { resource.fetch_config_element("foo") }.to raise_error(RuntimeError)
+ end
+
+ it "returns the value if present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_config_element("containsLegacyPackageInstalls")).to eq("true")
+ expect { resource.fetch_config_element("foo") }.not_to raise_error
+ end
+
+ it "returns nil if the element is not present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_config_element("foo")).to be_nil
+ expect { resource.fetch_config_element("foo") }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/unit/resource/chocolatey_feature_spec.rb b/spec/unit/resource/chocolatey_feature_spec.rb
new file mode 100644
index 0000000000..0a3ec8e314
--- /dev/null
+++ b/spec/unit/resource/chocolatey_feature_spec.rb
@@ -0,0 +1,89 @@
+#
+# Copyright:: Copyright (c) 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::ChocolateyFeature do
+
+ let(:resource) { Chef::Resource::ChocolateyFeature.new("fakey_fakerton") }
+ let(:config) do
+ <<-CONFIG
+ <?xml version="1.0" encoding="utf-8"?>
+ <chocolatey xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <config>
+ <add key="containsLegacyPackageInstalls" value="true" description="Install has packages installed prior to 0.9.9 series." />
+ </config>
+ <sources>
+ <source id="chocolatey" value="https://chocolatey.org/api/v2/" disabled="false" bypassProxy="false" selfService="false" adminOnly="false" priority="0" />
+ </sources>
+ <features>
+ <feature name="checksumFiles" enabled="true" setExplicitly="false" description="Checksum files when pulled in from internet (based on package)." />
+ </features>
+ <apiKeys />
+ </chocolatey>
+ CONFIG
+ end
+
+ # we save off the ENV and set ALLUSERSPROFILE so these specs will work on *nix and non-C drive Windows installs
+ before(:each) do
+ @original_env = ENV.to_hash
+ ENV["ALLUSERSPROFILE"] = 'C:\ProgramData'
+ end
+
+ after(:each) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "has a resource name of :chocolatey_feature" do
+ expect(resource.resource_name).to eql(:chocolatey_feature)
+ end
+
+ it "has a name property of feature_name" do
+ expect(resource.feature_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "supports :enable and :disable actions" do
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ end
+
+ describe "#fetch_feature_element" do
+ it "raises and error if the config file cannot be found" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(false)
+ expect { resource.fetch_feature_element("foo") }.to raise_error(RuntimeError)
+ end
+
+ it "returns the value if present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_feature_element("checksumFiles")).to eq("true")
+ expect { resource.fetch_feature_element("foo") }.not_to raise_error
+ end
+
+ it "returns nil if the element is not present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_feature_element("foo")).to be_nil
+ expect { resource.fetch_feature_element("foo") }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/unit/resource/chocolatey_package_spec.rb b/spec/unit/resource/chocolatey_package_spec.rb
index e43803a65f..b998e48c71 100644
--- a/spec/unit/resource/chocolatey_package_spec.rb
+++ b/spec/unit/resource/chocolatey_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,46 +22,85 @@ 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 :chocolatey_package" do
expect(resource.resource_name).to eql(:chocolatey_package)
end
- it "should coerce its name to a package_name array" do
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ 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 "sets the list_options" do
+ resource.list_options("--local-only")
+ expect(resource.list_options).to eql("--local-only")
+ end
+
+ it "sets the user" do
+ resource.user("ubuntu")
+ expect(resource.user).to eql("ubuntu")
+ end
+
+ it "sets the password" do
+ resource.password("ubuntu@123")
+ expect(resource.password).to eql("ubuntu@123")
+ end
+
+ it "the default returns are 0 and 2" do
+ expect(resource.returns).to eql([0, 2])
+ 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/chocolatey_source_spec.rb b/spec/unit/resource/chocolatey_source_spec.rb
new file mode 100644
index 0000000000..d0066d3ccb
--- /dev/null
+++ b/spec/unit/resource/chocolatey_source_spec.rb
@@ -0,0 +1,151 @@
+#
+# Copyright:: Copyright (c) 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::ChocolateySource do
+
+ let(:node) { Chef::Node.new }
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::ChocolateySource.new("fakey_fakerton", run_context) }
+ let(:disable_provider) { resource.provider_for_action(:disable) }
+ let(:enable_provider) { resource.provider_for_action(:enable) }
+ let(:current_resource) { Chef::Resource::ChocolateySource.new("fakey_fakerton") }
+ let(:config) do
+ <<-CONFIG
+ <?xml version="1.0" encoding="utf-8"?>
+ <chocolatey xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <config>
+ <add key="containsLegacyPackageInstalls" value="true" description="Install has packages installed prior to 0.9.9 series." />
+ </config>
+ <sources>
+ <source id="chocolatey" value="https://chocolatey.org/api/v2/" disabled="false" bypassProxy="false" selfService="false" adminOnly="false" priority="0" />
+ </sources>
+ <features>
+ <feature name="checksumFiles" enabled="true" setExplicitly="false" description="Checksum files when pulled in from internet (based on package)." />
+ </features>
+ <apiKeys />
+ </chocolatey>
+ CONFIG
+ end
+
+ # we save off the ENV and set ALLUSERSPROFILE so these specs will work on *nix and non-C drive Windows installs
+ before(:each) do
+ disable_provider # vivify before mocking
+ enable_provider
+ current_resource
+ allow(resource).to receive(:provider_for_action).and_return(disable_provider)
+ allow(resource).to receive(:provider_for_action).and_return(enable_provider)
+ allow(resource.class).to receive(:new).and_return(current_resource)
+ @original_env = ENV.to_hash
+ ENV["ALLUSERSPROFILE"] = 'C:\ProgramData'
+ end
+
+ after(:each) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "has a resource name of :chocolatey_source" do
+ expect(resource.resource_name).to eql(:chocolatey_source)
+ end
+
+ it "has a name property of source_name" do
+ expect(resource.source_name).to eql("fakey_fakerton")
+ 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 :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ end
+
+ it "bypass_proxy property defaults to false" do
+ expect { resource.bypass_proxy.to be_false }
+ end
+
+ it "priority property defaults to 0" do
+ expect { resource.priority.to eq(0) }
+ end
+
+ it "admin_only property defaults to false" do
+ expect { resource.admin_only.to be_false }
+ end
+
+ it "allow_self_service property defaults to false" do
+ expect { resource.allow_self_service.to be_false }
+ end
+
+ describe "#load_current_resource" do
+ it "sets disabled to true when the XML disabled property is true" do
+ allow(current_resource).to receive(:fetch_source_element).with("fakey_fakerton").and_return(OpenStruct.new(disabled: "true"))
+ disable_provider.load_current_resource
+ expect(current_resource.disabled).to be true
+ end
+
+ it "sets disabled to false when the XML disabled property is false" do
+ allow(current_resource).to receive(:fetch_source_element).with("fakey_fakerton").and_return(OpenStruct.new(disabled: "false"))
+ enable_provider.load_current_resource
+ expect(current_resource.disabled).to be false
+ end
+ end
+
+ describe "run_action(:enable)" do
+ it "when source is disabled, it enables it correctly" do
+ resource.disabled true
+ allow(current_resource).to receive(:fetch_source_element).with("fakey_fakerton").and_return(OpenStruct.new(disabled: "true"))
+ expect(enable_provider).to receive(:shell_out!).with("C:\\ProgramData\\chocolatey\\bin\\choco source enable -n \"fakey_fakerton\"")
+ resource.run_action(:enable)
+ expect(resource.updated_by_last_action?).to be true
+ end
+
+ it "when source is enabled, it is idempotent when trying to enable" do
+ resource.disabled false
+ allow(current_resource).to receive(:fetch_source_element).with("fakey_fakerton").and_return(OpenStruct.new(disabled: "false"))
+ resource.run_action(:enable)
+ expect(resource.updated_by_last_action?).to be false
+ end
+ end
+
+ describe "#fetch_source_element" do
+ it "raises and error if the config file cannot be found" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(false)
+ expect { resource.fetch_source_element("foo") }.to raise_error(RuntimeError)
+ end
+
+ it "returns the value if present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_source_element("chocolatey")["value"]).to eq("https://chocolatey.org/api/v2/")
+ expect { resource.fetch_source_element("foo") }.not_to raise_error
+ end
+
+ it "returns nil if the element is not present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_source_element("foo")).to be_nil
+ expect { resource.fetch_source_element("foo") }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb
index e84b0980c4..5f99238919 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,9 +22,9 @@ require "ostruct"
describe Chef::Resource::Conditional do
before do
allow_any_instance_of(Mixlib::ShellOut).to receive(:run_command).and_return(nil)
- @status = OpenStruct.new(:success? => true)
+ @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
@@ -99,7 +99,7 @@ describe Chef::Resource::Conditional 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
@@ -202,7 +202,7 @@ describe Chef::Resource::Conditional 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
diff --git a/spec/unit/resource/cookbook_file_spec.rb b/spec/unit/resource/cookbook_file_spec.rb
index 6886ce1f31..572ba4c2f2 100644
--- a/spec/unit/resource/cookbook_file_spec.rb
+++ b/spec/unit/resource/cookbook_file_spec.rb
@@ -1,8 +1,8 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software, Inc.
-#p License:: Apache License, Version 2.0
+# Copyright:: Copyright (c) Chef Software Inc.
+# p License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,55 +20,60 @@
require "spec_helper"
describe Chef::Resource::CookbookFile do
- before do
- @cookbook_file = Chef::Resource::CookbookFile.new("sourcecode_tarball.tgz")
+ let(:resource) { Chef::Resource::CookbookFile.new("/foo/bar/sourcecode_tarball.tgz") }
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
end
- it "uses the name parameter for the source parameter" do
- expect(@cookbook_file.name).to eq("sourcecode_tarball.tgz")
+ it "supports :create, :create_if_missing, :delete, :touch actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :touch }.not_to raise_error
end
- 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 resource name 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.source("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")
+ if ChefUtils.windows?
+ 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
- if Chef::Platform.windows?
+ state = resource.state_for_resource_reporter
+ if ChefUtils.windows?
puts state
- expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }])
- expect(state[:deny_rights]).to eq([{ :permissions => :full_control, :principals => "Clumsy_Sam" }])
+ expect(state[:rights]).to eq([{ permissions: :read, principals: "Everyone" }])
+ expect(state[:deny_rights]).to eq([{ permissions: :full_control, principals: "Clumsy_Sam" }])
else
expect(state[:group]).to eq("wheel")
expect(state[:mode]).to eq("0664")
@@ -78,10 +83,10 @@ describe Chef::Resource::CookbookFile do
end
it "returns the path as its identity" do
- if Chef::Platform.windows?
- expect(@cookbook_file.identity).to eq("C:/temp/origin/file.txt")
+ if ChefUtils.windows?
+ 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_access_spec.rb b/spec/unit/resource/cron_access_spec.rb
new file mode 100644
index 0000000000..f7c84632c5
--- /dev/null
+++ b/spec/unit/resource/cron_access_spec.rb
@@ -0,0 +1,36 @@
+#
+# Copyright:: Copyright (c) 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::CronAccess do
+ let(:resource) { Chef::Resource::CronAccess.new("bob") }
+
+ it "has a default action of [:deny]" do
+ expect(resource.action).to eql([:allow])
+ end
+
+ it "accepts create or delete for action" do
+ expect { resource.action :allow }.not_to raise_error
+ expect { resource.action :deny }.not_to raise_error
+ expect { resource.action :lolcat }.to raise_error(ArgumentError)
+ end
+
+ it "the user property is the name_property" do
+ expect(resource.user).to eql("bob")
+ end
+end
diff --git a/spec/unit/resource/cron_d_spec.rb b/spec/unit/resource/cron_d_spec.rb
new file mode 100644
index 0000000000..c798265d06
--- /dev/null
+++ b/spec/unit/resource/cron_d_spec.rb
@@ -0,0 +1,48 @@
+#
+# Copyright:: Copyright (c) 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::CronD do
+ let(:resource) { Chef::Resource::CronD.new("cronify") }
+
+ it "has a default action of [:create]" do
+ expect(resource.action).to eql([:create])
+ end
+
+ 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 "the cron_name property is the name_property" do
+ expect(resource.cron_name).to eql("cronify")
+ end
+
+ it "the mode property defaults to '0600'" do
+ expect(resource.mode).to eql("0600")
+ end
+
+ it "the user property defaults to 'root'" do
+ expect(resource.user).to eql("root")
+ end
+
+ it "the command property is required" do
+ expect { resource.command nil }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+end
diff --git a/spec/unit/resource/cron_spec.rb b/spec/unit/resource/cron_spec.rb
index 6e867b75e1..c9dbef06c6 100644
--- a/spec/unit/resource/cron_spec.rb
+++ b/spec/unit/resource/cron_spec.rb
@@ -20,157 +20,138 @@
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 "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
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 "supports :create, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
end
- it "should have a name" do
- expect(@resource.name).to eql("cronify")
+ it "allows you to set a command" do
+ resource.command "/bin/true"
+ expect(resource.command).to eql("/bin/true")
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "allows you to set a user" do
+ resource.user "daemon"
+ expect(resource.user).to eql("daemon")
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 specify the minute" do
+ resource.minute "30"
+ expect(resource.minute).to eql("30")
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 hour" do
+ resource.hour "6"
+ expect(resource.hour).to eql("6")
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 day" do
+ resource.day "10"
+ expect(resource.day).to eql("10")
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 month" do
+ resource.month "10"
+ expect(resource.month).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 weekday" do
+ resource.weekday "2"
+ expect(resource.weekday).to eql("2")
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 mailto variable" do
+ resource.mailto "test@example.com"
+ expect(resource.mailto).to eql("test@example.com")
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 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 weekday" do
- @resource.weekday "2"
- expect(@resource.weekday).to eql("2")
+ 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 mailto variable" do
- @resource.mailto "test@example.com"
- expect(@resource.mailto).to eql("test@example.com")
+ 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 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 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(Chef::Exceptions::ValidationFailed)
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(Chef::Exceptions::ValidationFailed)
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(Chef::Exceptions::ValidationFailed)
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(Chef::Exceptions::ValidationFailed)
end
describe "weekday" do
- it "should reject 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 weekday over 7" do
+ expect { resource.weekday "8" }.to raise_error(Chef::Exceptions::ValidationFailed)
end
- end
-
- it "should convert integer schedule values to a string" do
- %w{minute hour day month weekday}.each do |x|
- expect(@resource.send(x, 5)).to eql("5")
+ it "rejects any symbols which don't represent day of week" do
+ expect { resource.weekday :foo }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
- describe "when it has a time (minute, hour, day, month, weeekend) and user" do
+ describe "when it has a time (minute, hour, day, month, weekend) 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 +161,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..b4ac946ad9 100644
--- a/spec/unit/resource/csh_spec.rb
+++ b/spec/unit/resource/csh_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,21 +20,26 @@ require "spec_helper"
describe Chef::Resource::Csh do
- before(:each) do
- @resource = Chef::Resource::Csh.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::Csh.new("fakey_fakerton") }
+
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
+ end
+
+ it "has a resource name of :csh" do
+ expect(resource.resource_name).to eql(:csh)
end
- 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 "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
- it "should have a resource name of :csh" do
- expect(@resource.resource_name).to eql(:csh)
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
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_revision_spec.rb b/spec/unit/resource/deploy_revision_spec.rb
deleted file mode 100644
index aa12b9595d..0000000000
--- a/spec/unit/resource/deploy_revision_spec.rb
+++ /dev/null
@@ -1,42 +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"
-require "support/shared/unit/resource/static_provider_resolution"
-
-describe Chef::Resource::DeployRevision do
-
- static_provider_resolution(
- resource: Chef::Resource::DeployRevision,
- provider: Chef::Provider::Deploy::Revision,
- name: :deploy_revision,
- action: :deploy
- )
-
-end
-
-describe Chef::Resource::DeployBranch do
-
- static_provider_resolution(
- resource: Chef::Resource::DeployBranch,
- provider: Chef::Provider::Deploy::Revision,
- name: :deploy_branch,
- action: :deploy
- )
-
-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..605cc9d9e7 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,63 +20,50 @@
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 "the path property is the name_property" do
+ expect(resource.path).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 "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
end
- it "should have a name" do
- expect(@resource.name).to eql("fakey_fakerton")
+ it "supports :create, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ 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({}) }.to raise_error(ArgumentError)
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)
- 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..de3e79e6fc
--- /dev/null
+++ b/spec/unit/resource/dmg_package_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :dmg_package" do
+ expect(resource.resource_name).to eql(:dmg_package)
+ end
+
+ it "the app property is the name_property" do
+ expect(resource.app).to eql("fakey_fakerton")
+ 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
+ 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..c74c46d19b
--- /dev/null
+++ b/spec/unit/resource/dnf_package_spec.rb
@@ -0,0 +1,108 @@
+#
+# Copyright:: Copyright (c) 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, "defaults" 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
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :flush_cache, :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :flush_cache }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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 = %i{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/dpkg_package_spec.rb b/spec/unit/resource/dpkg_package_spec.rb
index 66ad86b861..acd879597d 100644
--- a/spec/unit/resource/dpkg_package_spec.rb
+++ b/spec/unit/resource/dpkg_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,4 +29,32 @@ describe Chef::Resource::DpkgPackage, "initialize" do
os: "linux"
)
+ describe Chef::Resource::DpkgPackage, "defaults" do
+ let(:resource) { Chef::Resource::DpkgPackage.new("fakey_fakerton") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "accepts a string for the response file" do
+ resource.response_file "something"
+ expect(resource.response_file).to eql("something")
+ end
+
+ 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 "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+ end
+
end
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
index b610c262cc..6d6b99bd10 100644
--- a/spec/unit/resource/dsc_resource_spec.rb
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
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 }
@@ -39,26 +40,31 @@ describe Chef::Resource::DscResource do
expect(dsc_test_resource.action).to eq([:run])
end
- it "has an ed_actions attribute with only the `:run` and `:nothing` attributes" do
- expect(dsc_test_resource.allowed_actions.to_set).to eq([:run, :nothing].to_set)
+ it "has an ed_actions property with only the `:run` and `:nothing` properties" do
+ expect(dsc_test_resource.allowed_actions.to_set).to eq(%i{run nothing}.to_set)
end
- it "allows the resource attribute to be set" do
+ it "allows the resource property to be set" do
dsc_test_resource.resource(dsc_test_resource_name)
expect(dsc_test_resource.resource).to eq(dsc_test_resource_name)
end
- it "allows the module_name attribute to be set" do
+ it "allows the module_name property to be set" do
dsc_test_resource.module_name(dsc_test_resource_name)
expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name)
end
- it "allows the reboot_action attribute to be set" do
+ it "allows the module_version property 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 property 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)
end
- it "allows the timeout attribute to be set" do
+ it "allows the timeout property to be set" do
dsc_test_resource.timeout(dsc_test_timeout)
expect(dsc_test_resource.timeout).to eq(dsc_test_timeout)
end
@@ -87,7 +93,7 @@ describe Chef::Resource::DscResource do
end
end
- context "Powershell DSL methods" do
+ context "PowerShell DSL methods" do
it "responds to :ps_credential" do
expect(dsc_test_resource.respond_to?(:ps_credential)).to be true
end
diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb
index f0c81e43b5..b61a6fcede 100644
--- a/spec/unit/resource/dsc_script_spec.rb
+++ b/spec/unit/resource/dsc_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
describe Chef::Resource::DscScript do
- let(:dsc_test_resource_name) { "DSCTest" }
+ let(:resource_name) { "DSCTest" }
context "when Powershell supports Dsc" do
let(:dsc_test_run_context) do
@@ -28,8 +28,8 @@ describe Chef::Resource::DscScript do
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
end
- let(:dsc_test_resource) do
- Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
+ let(:resource) do
+ Chef::Resource::DscScript.new(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" }
@@ -37,41 +37,41 @@ describe Chef::Resource::DscScript do
let(:configuration_data) { '@{AllNodes = @( @{ NodeName = "localhost"; PSDscAllowPlainTextPassword = $true })}' }
let(:configuration_data_script) { "c:/myconfigs/data/safedata.psd1" }
- it "has a default action of `:run`" do
- expect(dsc_test_resource.action).to eq([:run])
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
- it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
- expect(dsc_test_resource.allowed_actions.to_set).to eq([:run, :nothing].to_set)
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
end
- it "allows the code attribute to be set" do
- dsc_test_resource.code(configuration_code)
- expect(dsc_test_resource.code).to eq(configuration_code)
+ it "allows the code property to be set" do
+ resource.code(configuration_code)
+ expect(resource.code).to eq(configuration_code)
end
- it "allows the command attribute to be set" do
- dsc_test_resource.command(configuration_path)
- expect(dsc_test_resource.command).to eq(configuration_path)
+ it "allows the command property to be set" do
+ resource.command(configuration_path)
+ expect(resource.command).to eq(configuration_path)
end
- it "allows the configuration_name attribute to be set" do
- dsc_test_resource.configuration_name(configuration_name)
- expect(dsc_test_resource.configuration_name).to eq(configuration_name)
+ it "allows the configuration_name property to be set" do
+ resource.configuration_name(configuration_name)
+ expect(resource.configuration_name).to eq(configuration_name)
end
- it "allows the configuration_data attribute to be set" do
- dsc_test_resource.configuration_data(configuration_data)
- expect(dsc_test_resource.configuration_data).to eq(configuration_data)
+ it "allows the configuration_data property to be set" do
+ resource.configuration_data(configuration_data)
+ expect(resource.configuration_data).to eq(configuration_data)
end
- it "allows the configuration_data_script attribute to be set" do
- dsc_test_resource.configuration_data_script(configuration_data_script)
- expect(dsc_test_resource.configuration_data_script).to eq(configuration_data_script)
+ it "allows the configuration_data_script property to be set" do
+ resource.configuration_data_script(configuration_data_script)
+ expect(resource.configuration_data_script).to eq(configuration_data_script)
end
it "has the ps_credential helper method" do
- expect(dsc_test_resource).to respond_to(:ps_credential)
+ expect(resource).to respond_to(:ps_credential)
end
context "when calling imports" do
@@ -80,55 +80,55 @@ describe Chef::Resource::DscScript do
let(:dsc_resources) { %w{ResourceA ResourceB} }
it "allows an arbitrary number of resources to be set for a module to be set" do
- dsc_test_resource.imports module_name, *dsc_resources
- module_imports = dsc_test_resource.imports[module_name]
+ resource.imports module_name, *dsc_resources
+ module_imports = resource.imports[module_name]
expect(module_imports).to eq(dsc_resources)
end
it "adds * to the imports when no resources are set for a moudle" do
- dsc_test_resource.imports module_name
- module_imports = dsc_test_resource.imports[module_name]
+ resource.imports module_name
+ module_imports = resource.imports[module_name]
expect(module_imports).to eq(["*"])
end
it "allows an arbitrary number of modules" do
- dsc_test_resource.imports module_name
- dsc_test_resource.imports module_name_b
- expect(dsc_test_resource.imports).to have_key(module_name)
- expect(dsc_test_resource.imports).to have_key(module_name_b)
+ resource.imports module_name
+ resource.imports module_name_b
+ expect(resource.imports).to have_key(module_name)
+ expect(resource.imports).to have_key(module_name_b)
end
it "allows resources to be added for a module" do
- dsc_test_resource.imports module_name, dsc_resources[0]
- dsc_test_resource.imports module_name, dsc_resources[1]
- module_imports = dsc_test_resource.imports[module_name]
+ resource.imports module_name, dsc_resources[0]
+ resource.imports module_name, dsc_resources[1]
+ module_imports = resource.imports[module_name]
expect(module_imports).to eq(dsc_resources)
end
end
- it "raises an ArgumentError exception if an attempt is made to set the code attribute when the command attribute is already set" do
- dsc_test_resource.command(configuration_path)
- expect { dsc_test_resource.code(configuration_code) }.to raise_error(ArgumentError)
+ it "raises an ArgumentError exception if an attempt is made to set the code property when the command property is already set" do
+ resource.command(configuration_path)
+ expect { resource.code(configuration_code) }.to raise_error(ArgumentError)
end
- it "raises an ArgumentError exception if an attempt is made to set the command attribute when the code attribute is already set" do
- dsc_test_resource.code(configuration_code)
- expect { dsc_test_resource.command(configuration_path) }.to raise_error(ArgumentError)
+ it "raises an ArgumentError exception if an attempt is made to set the command property when the code property is already set" do
+ resource.code(configuration_code)
+ expect { resource.command(configuration_path) }.to raise_error(ArgumentError)
end
- it "raises an ArgumentError exception if an attempt is made to set the configuration_name attribute when the code attribute is already set" do
- dsc_test_resource.code(configuration_code)
- expect { dsc_test_resource.configuration_name(configuration_name) }.to raise_error(ArgumentError)
+ it "raises an ArgumentError exception if an attempt is made to set the configuration_name property when the code property is already set" do
+ resource.code(configuration_code)
+ expect { resource.configuration_name(configuration_name) }.to raise_error(ArgumentError)
end
- it "raises an ArgumentError exception if an attempt is made to set the configuration_data attribute when the configuration_data_script attribute is already set" do
- dsc_test_resource.configuration_data_script(configuration_data_script)
- expect { dsc_test_resource.configuration_data(configuration_data) }.to raise_error(ArgumentError)
+ it "raises an ArgumentError exception if an attempt is made to set the configuration_data property when the configuration_data_script property is already set" do
+ resource.configuration_data_script(configuration_data_script)
+ expect { resource.configuration_data(configuration_data) }.to raise_error(ArgumentError)
end
- it "raises an ArgumentError exception if an attempt is made to set the configuration_data_script attribute when the configuration_data attribute is already set" do
- dsc_test_resource.configuration_data(configuration_data)
- expect { dsc_test_resource.configuration_data_script(configuration_data_script) }.to raise_error(ArgumentError)
+ it "raises an ArgumentError exception if an attempt is made to set the configuration_data_script property when the configuration_data property is already set" do
+ resource.configuration_data(configuration_data)
+ expect { resource.configuration_data_script(configuration_data_script) }.to raise_error(ArgumentError)
end
end
end
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/env_spec.rb b/spec/unit/resource/env_spec.rb
deleted file mode 100644
index cff862b69e..0000000000
--- a/spec/unit/resource/env_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Doug MacEachern (<dougm@vmware.com>)
-# Author:: Tyler Cloke (<tyler@chef.io>)
-# 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 "spec_helper"
-
-describe Chef::Resource::Env do
-
- before(:each) do
- @resource = Chef::Resource::Env.new("FOO")
- end
-
- 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)
- end
-
- it "should have 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])
- 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)
- else
- 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")
- end
-
- it "should accept 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)
- end
-
- it "should allow 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")
- end
-
- it "describes its state" do
- state = @resource.state
- expect(state[:value]).to eq("level7")
- end
-
- it "returns the key name as its identity" do
- expect(@resource.identity).to eq("charmander")
- end
- 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..78dff6a120 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,262 @@ require "spec_helper"
describe Chef::Resource::Execute do
let(:resource_instance_name) { "some command" }
- let(:execute_resource) { Chef::Resource::Execute.new(resource_instance_name) }
+ let(:resource) { Chef::Resource::Execute.new(resource_instance_name) }
it_behaves_like "an execute resource"
- it "default guard interpreter should be :execute interpreter" do
- expect(execute_resource.guard_interpreter).to be(:execute)
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
+ end
+
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
+ end
+
+ it "default guard interpreter is :execute interpreter" do
+ expect(resource.guard_interpreter).to be(:execute)
end
it "defaults to not being a guard interpreter" do
- expect(execute_resource.is_guard_interpreter).to eq(false)
+ expect(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 = 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 = 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 an integer" do
+ let(:username) { 499 }
+
+ it "correctly parses the user and domain" do
+ identity = resource.qualify_user(username, password, domain)
+ expect(identity[:domain]).to eq(nil)
+ expect(identity[:user]).to eq(499)
+ end
+ end
+ end
+
+ shared_examples_for "it received valid credentials" do
+ describe "the validation method" do
+ it "does not raise an error" do
+ expect { 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 = 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 { 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 { 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 { resource.validate_identity_platform(username, password, domain) }.to raise_error(Chef::Exceptions::UnsupportedPlatform)
+ end
+ end
+ end
+
+ context "when running on Windows" do
+ before do
+ allow(resource).to receive(:windows?).and_return(true)
+ 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(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
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..e26e870891
--- /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 (c) 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..d1fe863891 100644
--- a/spec/unit/resource/file/verification_spec.rb
+++ b/spec/unit/resource/file/verification_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (<steve@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,7 +48,7 @@ describe Chef::Resource::File::Verification do
it "accepts an options hash" do
v = Chef::Resource::File::Verification.new(parent_resource, nil, {}) {}
- expect { v.verify("/foo/bar", { :future => true }) }.to_not raise_error
+ expect { v.verify("/foo/bar", { future: true }) }.to_not raise_error
end
context "with a verification block" do
@@ -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
@@ -113,26 +112,35 @@ describe Chef::Resource::File::Verification do
end
it "returns true if the command succeeds" do
- v = Chef::Resource::File::Verification.new(parent_resource, "true", {})
+ test_command = platform_specific_verify_command("path")
+ v = Chef::Resource::File::Verification.new(parent_resource, test_command, {})
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
+ 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..0dba33236b 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 (c) 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 "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "sets the default action as :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 "supports :create, :create_if_missing, :delete, :touch actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :touch }.not_to raise_error
end
- it "should be set to back up 5 files by default" do
- expect(@resource.backup).to eql(5)
+ it "has a default content of nil" do
+ expect(resource.content).to be_nil
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 "is set to back up 5 files by default" do
+ expect(resource.backup).to eql(5)
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 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 accept a sha256 for checksum" do
- expect { @resource.checksum "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa" }.not_to raise_error
- expect { @resource.checksum "monkey!" }.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 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 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 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({}) }.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
+ 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
- expect(state[:rights]).to eq([ { :permissions => :read, :principals => "Everyone" },
- { :permissions => :full_control, :principals => "DOMAIN\User" } ])
+ it "describes its state including windows ACL properties" do
+ 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 4edc3dbc78..913fc0e0b5 100644
--- a/spec/unit/resource/freebsd_package_spec.rb
+++ b/spec/unit/resource/freebsd_package_spec.rb
@@ -1,7 +1,7 @@
#
# Authors:: AJ Christensen (<aj@chef.io>)
# Richard Manyanza (<liseki@nyikacraftsmen.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2014-2016, Richard Manyanza.
# License:: Apache License, Version 2.0
#
@@ -22,65 +22,55 @@ 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)
- end
+ describe "Actions" do
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
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)
- end
- end
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
end
+ end
- describe "if pkgng enabled" do
- it "should be 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)
+ describe "Assigning provider after creation" do
+ describe "if ports specified as source" do
+ 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 less than 1000017 and pkgng not enabled" do
- it "should be 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)
-
- [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
+ describe "if ports is not specified" do
+ it "is Freebsd::Pkgng" do
+ resource.after_created
+ expect(resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
end
end
end
diff --git a/spec/unit/resource/gem_package_spec.rb b/spec/unit/resource/gem_package_spec.rb
index a1571ab9bb..d61caa5521 100644
--- a/spec/unit/resource/gem_package_spec.rb
+++ b/spec/unit/resource/gem_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,12 +31,37 @@ describe Chef::Resource::GemPackage, "initialize" do
end
describe Chef::Resource::GemPackage, "gem_binary" do
- before(:each) do
- @resource = Chef::Resource::GemPackage.new("foo")
+ let(:resource) { Chef::Resource::GemPackage.new("foo") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ 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
+
+describe Chef::Resource::GemPackage, "clear_sources" do
+ let(:resource) { Chef::Resource::GemPackage.new("foo") }
+
+ it "is nil by default" do
+ expect(resource.clear_sources).to be_nil
end
- 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 default of clear_sources to the config value" do
+ Chef::Config[:clear_gem_sources] = true
+ expect(resource.clear_sources).to be true
end
end
diff --git a/spec/unit/resource/git_spec.rb b/spec/unit/resource/git_spec.rb
deleted file mode 100644
index 15c1e54f25..0000000000
--- a/spec/unit/resource/git_spec.rb
+++ /dev/null
@@ -1,50 +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::Git do
-
- static_provider_resolution(
- resource: Chef::Resource::Git,
- provider: Chef::Provider::Git,
- name: :git,
- action: :sync
- )
-
- before(:each) do
- @git = Chef::Resource::Git.new("my awesome webapp")
- end
-
- 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)
- end
-
- it "uses aliases revision as branch" do
- @git.branch "HEAD"
- expect(@git.revision).to eql("HEAD")
- end
-
- it "aliases revision as reference" do
- @git.reference "v1.0 tag"
- expect(@git.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..cb1d7f9a47 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:: AJ Christensen (<aj@junglistheavy.industries>)
# Author:: Tyler Cloke (<tyler@chef.io>);
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,143 +20,146 @@
require "spec_helper"
describe Chef::Resource::Group, "initialize" do
- before(:each) do
- @resource = Chef::Resource::Group.new("admin")
- end
+ let(:resource) { Chef::Resource::Group.new("fakey_fakerton") }
- 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)
+ it "sets the resource_name to :group" do
+ expect(resource.resource_name).to eql(:group)
end
- it "should set the resource_name to :group" do
- expect(@resource.resource_name).to eql(:group)
+ it "the group_name property is the name_property" do
+ expect(resource.group_name).to eql("fakey_fakerton")
end
- it "should set the group_name equal to the argument to initialize" do
- expect(@resource.group_name).to eql("admin")
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
end
- it "should default gid to nil" do
- expect(@resource.gid).to eql(nil)
+ it "supports :create, :manage, :modify, :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :manage }.not_to raise_error
+ expect { resource.action :modify }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
end
- it "should default members to an empty array" do
- expect(@resource.members).to eql([])
+ it "defaults gid to nil" do
+ expect(resource.gid).to eql(nil)
end
- it "should alias users to members, also an empty array" do
- expect(@resource.users).to eql([])
+ it "defaults members to an empty array" do
+ expect(resource.members).to eql([])
end
- it "should set action to :create" do
- expect(@resource.action).to eql([:create])
+ it "defaults comment to be nil" do
+ expect(resource.comment).to eql(nil)
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)
- end
+ it "aliases users to members, also an empty array" do
+ expect(resource.users).to eql([])
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("fakey_fakerton") }
- 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, { some_other_user: "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("fakey_fakerton") }
- 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, { some_other_user: "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("fakey_fakerton") }
- [ :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"])
+ %i{users members}.each do |method|
+ it "(#{method}) allows a String and coerces it to an Array" do
+ resource.send(method, "some_user")
+ expect(resource.send(method)).to eql(["some_user"])
end
- it "(#{method}) should split a string on commas" do
- @resource.send(method, "aj,adam")
- expect(@resource.send(method)).to eql( %w{aj adam} )
+ it "(#{method}) coerces a comma separated list of users to an Array" do
+ resource.send(method, "some_user, other_user ,another_user,just_one_more_user")
+ expect(resource.send(method)).to eql( %w{some_user other_user another_user just_one_more_user} )
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{some_user other_user})
+ expect(resource.send(method)).to eql( %w{some_user other_user} )
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, { some_user: "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("fakey_fakerton") }
- 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, { some_other_user: "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
+
+describe Chef::Resource::Group, "comment" do
+ let(:resource) { Chef::Resource::Group.new("fakey_fakerton") }
+
+ it "allows an string" do
+ resource.comment "this is a group comment"
+ expect(resource.comment).to eql("this is a group comment")
+ end
+
+ it "does not allow a hash" do
+ expect { resource.send(:comment, { some_other_user: "is freakin awesome" }) }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/helpers/cron_validations_spec.rb b/spec/unit/resource/helpers/cron_validations_spec.rb
new file mode 100644
index 0000000000..9ec58e8b5f
--- /dev/null
+++ b/spec/unit/resource/helpers/cron_validations_spec.rb
@@ -0,0 +1,81 @@
+#
+# Copyright:: Copyright (c) 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 "spec_helper"
+require "chef/resource/helpers/cron_validations"
+
+describe Chef::ResourceHelpers::CronValidations do
+ context "#validate_dow" do
+ it "it accepts a string day" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow("mon")).to be true
+ end
+
+ it "it accepts an integer day" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow(0)).to be true
+ end
+
+ it "it accepts the string of *" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow("*")).to be true
+ end
+
+ it "returns false for an out of range integer" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow(8)).to be false
+ end
+
+ it "it accepts the string day with full name" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow("monday")).to be true
+ end
+
+ it "returns false for an invalid string" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_dow("funday")).to be false
+ end
+ end
+
+ context "#validate_month" do
+ it "it accepts a string month" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_month("feb")).to be true
+ end
+
+ it "it accepts an integer month" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_month(2)).to be true
+ end
+
+ it "it accepts the string of *" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_month("*")).to be true
+ end
+
+ it "returns false for an out of range integer" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_month(13)).to be false
+ end
+
+ it "returns false for an invalid string (typo)" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_month("janurary")).to be false
+ end
+ end
+
+ context "#validate_numeric" do
+ it "returns true if the value is in the allowed range" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_numeric(5, 1, 100)).to be true
+ end
+
+ it "returns false if the value less than the allowed range" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_numeric(-1, 1, 100)).to be false
+ end
+
+ it "returns false if the value more than the allowed range" do
+ expect(Chef::ResourceHelpers::CronValidations.validate_numeric(101, 1, 100)).to be false
+ 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..ed68bc613f
--- /dev/null
+++ b/spec/unit/resource/homebrew_cask_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :homebrew_cask" do
+ expect(resource.resource_name).to eql(:homebrew_cask)
+ end
+
+ it "the cask_name property is the name_property" do
+ expect(resource.cask_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :remove actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/homebrew_package_spec.rb b/spec/unit/resource/homebrew_package_spec.rb
index cfcfcd9c3a..d956058b19 100644
--- a/spec/unit/resource/homebrew_package_spec.rb
+++ b/spec/unit/resource/homebrew_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Joshua Timberman (<joshua@chef.io>)
-# Copyright 2014-2016, Chef Software, Inc. <legal@chef.io>
+# Copyright:: Copyright (c) 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.
@@ -30,6 +30,24 @@ 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
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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..9b92dc79d3
--- /dev/null
+++ b/spec/unit/resource/homebrew_tap_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright (c) 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 "the tap_name property is the name_property" do
+ expect(resource.tap_name).to eql("user/mytap")
+ end
+
+ it "sets the default action as :tap" do
+ expect(resource.action).to eql([:tap])
+ end
+
+ it "supports :tap, :untap actions" do
+ expect { resource.action :tap }.not_to raise_error
+ expect { resource.action :untap }.not_to raise_error
+ 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/homebrew_update_spec.rb b/spec/unit/resource/homebrew_update_spec.rb
new file mode 100644
index 0000000000..3bf39afe9c
--- /dev/null
+++ b/spec/unit/resource/homebrew_update_spec.rb
@@ -0,0 +1,30 @@
+require "spec_helper"
+
+describe Chef::Resource::HomebrewUpdate do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::HomebrewUpdate.new("update", run_context) }
+
+ let(:stamp_dir) { Dir.mktmpdir("brew_update_periodic") }
+ let(:stamp_file) { Dir.mktmpdir("apt_update_periodic") }
+ let(:brew_update_cmd) { %w{homebrew update} }
+
+ it "sets the default action as :periodic" do
+ expect(resource.action).to eql([:periodic])
+ end
+
+ it "supports :periodic, :update actions" do
+ expect { resource.action :periodic }.not_to raise_error
+ expect { resource.action :update }.not_to raise_error
+ end
+
+ it "default frequency is set to be 1 da1y" do
+ expect(resource.frequency).to eql(86_400)
+ end
+
+ it "frequency accepts integers" do
+ resource.frequency(400)
+ expect(resource.frequency).to eql(400)
+ end
+end
diff --git a/spec/unit/resource/hostname_spec.rb b/spec/unit/resource/hostname_spec.rb
new file mode 100644
index 0000000000..3ac962f203
--- /dev/null
+++ b/spec/unit/resource/hostname_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :hostname" do
+ expect(resource.resource_name).to eql(:hostname)
+ end
+
+ it "the hostname property is the name_property" do
+ expect(resource.hostname).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set action" do
+ expect { resource.action :set }.not_to raise_error
+ 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
+end
diff --git a/spec/unit/resource/http_request_spec.rb b/spec/unit/resource/http_request_spec.rb
index 318a154b88..4c628c3003 100644
--- a/spec/unit/resource/http_request_spec.rb
+++ b/spec/unit/resource/http_request_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,39 +20,45 @@
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 the default action as :get" do
+ expect(resource.action).to eql([:get])
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 "supports :delete, :get, :head, :options, :patch, :post, :put actions" do
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :get }.not_to raise_error
+ expect { resource.action :head }.not_to raise_error
+ expect { resource.action :options }.not_to raise_error
+ expect { resource.action :patch }.not_to raise_error
+ expect { resource.action :post }.not_to raise_error
+ expect { resource.action :put }.not_to raise_error
end
- it "should set url to a string" do
- @resource.url "http://slashdot.org"
- expect(@resource.url).to eql("http://slashdot.org")
+ it "sets 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")
+ it "sets 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..bc018baee6 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
@@ -90,19 +90,11 @@ describe Chef::Resource::Ifconfig do
it_should_behave_like "being a platform based on RedHat", "redhat", "4.0"
end
- describe "when it is an old Debian platform" do
- it_should_behave_like "being a platform based on an old Debian", "debian", "6.0"
- end
-
- describe "when it is a new Debian platform" do
+ describe "when it is a Debian platform" do
it_should_behave_like "being a platform based on a recent Debian", "debian", "7.0"
end
- describe "when it is an old Ubuntu platform" do
- it_should_behave_like "being a platform based on an old Debian", "ubuntu", "11.04"
- end
-
- describe "when it is a new Ubuntu platform" do
+ describe "when it is a Ubuntu platform" do
it_should_behave_like "being a platform based on a recent Debian", "ubuntu", "11.10"
end
diff --git a/spec/unit/resource/ips_package_spec.rb b/spec/unit/resource/ips_package_spec.rb
index fd1fe2840c..12c9f223eb 100644
--- a/spec/unit/resource/ips_package_spec.rb
+++ b/spec/unit/resource/ips_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@chef.io>
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,8 @@
#
require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-
-describe Chef::Resource::IpsPackage, "initialize" do
+describe Chef::Resource::IpsPackage do
static_provider_resolution(
resource: Chef::Resource::IpsPackage,
provider: Chef::Provider::Package::Ips,
@@ -29,12 +27,28 @@ 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
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
end
end
diff --git a/spec/unit/resource/kernel_module_spec.rb b/spec/unit/resource/kernel_module_spec.rb
new file mode 100644
index 0000000000..aed31e03c8
--- /dev/null
+++ b/spec/unit/resource/kernel_module_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright (c) 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::KernelModule do
+ let(:resource) { Chef::Resource::KernelModule.new("foo") }
+
+ it "sets resource name as :kernel_module" do
+ expect(resource.resource_name).to eql(:kernel_module)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "sets the modname property as its name property" do
+ expect(resource.modname).to eql("foo")
+ end
+
+ it "supports :create and :flush actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :uninstall }.not_to raise_error
+ expect { resource.action :blacklist }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :load }.not_to raise_error
+ expect { resource.action :unload }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/ksh_spec.rb b/spec/unit/resource/ksh_spec.rb
index 6c3ba291b4..b2ee971ad8 100644
--- a/spec/unit/resource/ksh_spec.rb
+++ b/spec/unit/resource/ksh_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Nolan Davidson (<nolan.davidson@gmail.com>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,21 +20,26 @@ require "spec_helper"
describe Chef::Resource::Ksh do
- before(:each) do
- @resource = Chef::Resource::Ksh.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::Ksh.new("fakey_fakerton") }
+
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
+ end
+
+ it "has a resource name of :ksh" do
+ expect(resource.resource_name).to eql(:ksh)
end
- 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 "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
- it "should have a resource name of :ksh" do
- expect(@resource.resource_name).to eql(:ksh)
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
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 98d21a8234..62f5ad0b92 100644
--- a/spec/unit/resource/launchd_spec.rb
+++ b/spec/unit/resource/launchd_spec.rb
@@ -1,32 +1,48 @@
#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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) do
- Chef::Resource::Launchd.new(
- "io.chef.chef-client",
- run_context
- ) end
-
- 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("fakey_fakerton" ) }
- 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 "the label property is the name_property" do
+ expect(resource.label).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
expect(resource.action).to eql([:create])
end
- it "should accept enable, disable, create, and delete as actions" do
- expect { resource.action :enable }.not_to raise_error
- expect { resource.action :disable }.not_to raise_error
+ it "supports :create, :create_if_missing, :delete, :disable, :enable, :restart actions" do
expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :restart }.not_to raise_error
+ end
+
+ it "raises an error if nice is less than -20" do
+ expect { resource.nice(-21) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "raises an error if nice is greater than 19" do
+ expect { resource.nice(20) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
diff --git a/spec/unit/resource/link_spec.rb b/spec/unit/resource/link_spec.rb
index bd0976d8ea..11e5952663 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,115 +20,101 @@
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")
+ it "the target_file property is the name_property" do
+ expect(resource.target_file).to eql("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 "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
end
- it "should have a name" do
- expect(@resource.name).to eql("fakey_fakerton")
+ it "supports :create, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql([:create])
+ it "uses the object name as the target_file by default" do
+ expect(resource.target_file).to eql("fakey_fakerton")
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)
- else
- 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")
- 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({}) }.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/locale_spec.rb b/spec/unit/resource/locale_spec.rb
new file mode 100644
index 0000000000..e1275a6830
--- /dev/null
+++ b/spec/unit/resource/locale_spec.rb
@@ -0,0 +1,189 @@
+#
+# Author:: Nimesh Patni (<nimesh.patni@msystechnologies.com>)
+# Copyright:: Copyright (c) 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::Locale do
+
+ let(:resource) { Chef::Resource::Locale.new("fakey_fakerton") }
+ let(:provider) { resource.provider_for_action(:update) }
+
+ describe "default:" do
+ it "name would be locale" do
+ expect(resource.resource_name).to eq(:locale)
+ end
+ it "lang would be nil" do
+ expect(resource.lang).to be_nil
+ end
+ it "lc_env would be an empty hash" do
+ expect(resource.lc_env).to be_a(Hash)
+ expect(resource.lc_env).to be_empty
+ end
+ it "action would be :update" do
+ expect(resource.action).to eql([:update])
+ end
+ end
+
+ describe "validations:" do
+ let(:validation) { Chef::Exceptions::ValidationFailed }
+ context "lang" do
+ it "is non empty" do
+ expect { resource.lang("") }.to raise_error(validation)
+ end
+ it "does not contain any leading whitespaces" do
+ expect { resource.lang(" XX") }.to raise_error(validation)
+ end
+ end
+
+ context "lc_env" do
+ it "is non empty" do
+ expect { resource.lc_env({ "LC_TIME" => "" }) }.to raise_error(validation)
+ end
+ it "does not contain any leading whitespaces" do
+ expect { resource.lc_env({ "LC_TIME" => " XX" }) }.to raise_error(validation)
+ end
+ it "keys are valid and case sensitive" do
+ expect { resource.lc_env({ "LC_TIMES" => " XX" }) }.to raise_error(validation)
+ expect { resource.lc_env({ "Lc_Time" => " XX" }) }.to raise_error(validation)
+ expect(resource.lc_env({ "LC_TIME" => "XX" })).to eql({ "LC_TIME" => "XX" })
+ end
+ end
+ end
+
+ describe "#unavailable_locales" do
+ let(:available_locales) do
+ <<~LOC
+ C
+ C.UTF-8
+ en_AG
+ en_AG.utf8
+ en_US
+ POSIX
+ LOC
+ end
+ before do
+ dummy = Mixlib::ShellOut.new
+ allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).with("locale -a").and_return(dummy)
+ allow(dummy).to receive(:stdout).and_return(available_locales)
+ end
+ context "when all locales are available on system" do
+ context "with both properties" do
+ it "returns an empty array" do
+ resource.lang("en_US")
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_AG.utf8" })
+ expect(provider.unavailable_locales).to eq([])
+ end
+ end
+ context "without lang" do
+ it "returns an empty array" do
+ resource.lang
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_AG.utf8" })
+ expect(provider.unavailable_locales).to eq([])
+ end
+ end
+ context "without lc_env" do
+ it "returns an empty array" do
+ resource.lang("en_US")
+ resource.lc_env
+ expect(provider.unavailable_locales).to eq([])
+ end
+ end
+ context "without both" do
+ it "returns an empty array" do
+ resource.lang
+ resource.lc_env
+ expect(provider.unavailable_locales).to eq([])
+ end
+ end
+ end
+
+ context "when some locales are not available" do
+ context "with both properties" do
+ it "returns list" do
+ resource.lang("de_DE")
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_US.utf8" })
+ expect(provider.unavailable_locales).to eq(["de_DE", "en_US.utf8"])
+ end
+ end
+ context "without lang" do
+ it "returns list" do
+ resource.lang
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_US.utf8" })
+ expect(provider.unavailable_locales).to eq(["en_US.utf8"])
+ end
+ end
+ context "without lc_env" do
+ it "returns list" do
+ resource.lang("de_DE")
+ resource.lc_env
+ expect(provider.unavailable_locales).to eq(["de_DE"])
+ end
+ end
+ context "without both" do
+ it "returns an empty array" do
+ resource.lang
+ resource.lc_env
+ expect(provider.unavailable_locales).to eq([])
+ end
+ end
+ end
+ end
+
+ describe "#new_content" do
+ context "with both properties" do
+ before do
+ resource.lang("en_US")
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_AG.utf8" })
+ end
+ it "returns string" do
+ expect(provider.new_content).to be_a(String)
+ expect(provider.new_content).not_to be_empty
+ end
+ it "keys will be sorted" do
+ expect(provider.new_content.split("\n").map { |x| x.split("=") }.collect(&:first)).to eq(%w{LANG LC_MESSAGES LC_TIME})
+ end
+ it "ends with a new-line character" do
+ expect(provider.new_content[-1]).to eq("\n")
+ end
+ it "returns a valid string" do
+ expect(provider.new_content).to eq("LANG=en_US\nLC_MESSAGES=en_AG.utf8\nLC_TIME=en_AG.utf8\n")
+ end
+ end
+ context "without lang" do
+ it "returns a valid string" do
+ resource.lang
+ resource.lc_env({ "LC_TIME" => "en_AG.utf8", "LC_MESSAGES" => "en_AG.utf8" })
+ expect(provider.new_content).to eq("LC_MESSAGES=en_AG.utf8\nLC_TIME=en_AG.utf8\n")
+ end
+ end
+ context "without lc_env" do
+ it "returns a valid string" do
+ resource.lang("en_US")
+ resource.lc_env
+ expect(provider.new_content).to eq("LANG=en_US\n")
+ end
+ end
+ context "without both" do
+ it "returns string with only new-line character" do
+ resource.lang
+ resource.lc_env
+ expect(provider.new_content).to eq("\n")
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/log_spec.rb b/spec/unit/resource/log_spec.rb
index 18a1eb65bf..3afdc1c5d2 100644
--- a/spec/unit/resource/log_spec.rb
+++ b/spec/unit/resource/log_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Cary Penniman (<cary@rightscale.com>)
# Author:: Tyler Cloke (<tyler@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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)
+ it "has a name of log" do
+ expect(resource.resource_name).to eq(:log)
end
- it "should have a name of log" do
- expect(@resource.resource_name).to eq(:log)
+ it "the message property is the name_property" do
+ expect(resource.message).to eql("this is my string to log")
end
- it "should allow you to set a log string" do
- expect(@resource.name).to eq(@log_str)
+ it "sets the default action as :write" do
+ expect(resource.action).to eql([:write])
end
- it "should set the message to the first argument to new" do
- expect(@resource.message).to eq(@log_str)
+ it "supports :write action" do
+ expect { resource.action :write }.not_to raise_error
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/macos_user_defaults_spec.rb b/spec/unit/resource/macos_user_defaults_spec.rb
new file mode 100644
index 0000000000..2c643ab266
--- /dev/null
+++ b/spec/unit/resource/macos_user_defaults_spec.rb
@@ -0,0 +1,136 @@
+#
+# Copyright:: Copyright (c) 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::MacosUserDefaults do
+
+ let(:resource) { Chef::Resource::MacosUserDefaults.new("foo") }
+ let(:provider) { resource.provider_for_action(:write) }
+
+ it "has a resource name of :macos_userdefaults" do
+ expect(resource.resource_name).to eq(:macos_userdefaults)
+ end
+
+ it "the domain property defaults to NSGlobalDomain" do
+ expect(resource.domain).to eq("NSGlobalDomain")
+ end
+
+ it "the value property coerces keys in hashes to strings so we can compare them with plist data" do
+ resource.value "User": "/Library/Managed Installs/way_fake.log"
+ expect(resource.value).to eq({ "User" => "/Library/Managed Installs/way_fake.log" })
+ end
+
+ it "the host property defaults to nil" do
+ expect(resource.host).to be_nil
+ end
+
+ it "the sudo property defaults to false" do
+ expect(resource.sudo).to be false
+ end
+
+ it "sets the default action as :write" do
+ expect(resource.action).to eq([:write])
+ end
+
+ it "supports :write action" do
+ expect { resource.action :write }.not_to raise_error
+ end
+
+ describe "#defaults_export_cmd" do
+ it "exports NSGlobalDomain if no domain is set" do
+ expect(provider.defaults_export_cmd(resource)).to eq(["/usr/bin/defaults", "export", "NSGlobalDomain", "-"])
+ end
+
+ it "exports a provided domain" do
+ resource.domain "com.tim"
+ expect(provider.defaults_export_cmd(resource)).to eq(["/usr/bin/defaults", "export", "com.tim", "-"])
+ end
+
+ it "sets -currentHost if host is 'current'" do
+ resource.host "current"
+ expect(provider.defaults_export_cmd(resource)).to eq(["/usr/bin/defaults", "-currentHost", "export", "NSGlobalDomain", "-"])
+ end
+
+ it "sets -host 'tim-laptop if host is 'tim-laptop'" do
+ resource.host "tim-laptop"
+ expect(provider.defaults_export_cmd(resource)).to eq(["/usr/bin/defaults", "-host", "tim-laptop", "export", "NSGlobalDomain", "-"])
+ end
+ end
+
+ describe "#defaults_modify_cmd" do
+ # avoid needing to set these required values over and over. We'll overwrite them where necessary
+ before do
+ resource.key = "foo"
+ resource.value = "bar"
+ end
+
+ it "writes to NSGlobalDomain if domain isn't specified" do
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-string", "bar"])
+ end
+
+ it "uses the domain property if set" do
+ resource.domain = "MyCustomDomain"
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "MyCustomDomain", "foo", "-string", "bar"])
+ end
+
+ it "sets host specific values using host property" do
+ resource.host = "tims_laptop"
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "-host", "tims_laptop", "write", "NSGlobalDomain", "foo", "-string", "bar"])
+ end
+
+ it "if host is set to :current it passes CurrentHost" do
+ resource.host = :current
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "-currentHost", "write", "NSGlobalDomain", "foo", "-string", "bar"])
+ end
+
+ it "raises ArgumentError if bool is specified, but the value can't be made into a bool" do
+ resource.type "bool"
+ expect { provider.defaults_modify_cmd }.to raise_error(ArgumentError)
+ end
+
+ it "autodetects array type and passes individual values" do
+ resource.value = %w{one two three}
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-array", "one", "two", "three"])
+ end
+
+ it "autodetects string type and passes a single value" do
+ resource.value = "one"
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-string", "one"])
+ end
+
+ it "autodetects integer type and passes a single value" do
+ resource.value = 1
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-int", 1])
+ end
+
+ it "autodetects boolean type from TrueClass value and passes a 'TRUE' string" do
+ resource.value = true
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-bool", "TRUE"])
+ end
+
+ it "autodetects boolean type from FalseClass value and passes a 'FALSE' string" do
+ resource.value = false
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-bool", "FALSE"])
+ end
+
+ it "autodetects dict type from Hash value and flattens keys & values" do
+ resource.value = { "foo" => "bar" }
+ expect(provider.defaults_modify_cmd).to eq(["/usr/bin/defaults", "write", "NSGlobalDomain", "foo", "-dict", "foo", "bar"])
+ end
+ end
+end
diff --git a/spec/unit/resource/macosx_service.rb b/spec/unit/resource/macosx_service.rb
new file mode 100644
index 0000000000..b798b3bc79
--- /dev/null
+++ b/spec/unit/resource/macosx_service.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Tim Smith (tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::MacosxService do
+ let(:resource) { Chef::Resource::MacosxService.new("chef") }
+
+ it "sets the resource_name to :macosx_service" do
+ expect(resource.resource_name).to eql(:macosx_service)
+ end
+
+ it "accepts a String for the session_type property" do
+ resource.session_type "foo"
+ expect(resource.session_type).to eql("foo")
+ end
+
+ it "accepts a String for the plist property" do
+ resource.plist "foo"
+ expect(resource.plist).to eql("foo")
+ end
+end
diff --git a/spec/unit/resource/macports_package_spec.rb b/spec/unit/resource/macports_package_spec.rb
index 62346def2d..613748174e 100644
--- a/spec/unit/resource/macports_package_spec.rb
+++ b/spec/unit/resource/macports_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: David Balatero (<dbalatero@gmail.com>)
-# Copyright:: Copyright 2009-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,3 +30,22 @@ describe Chef::Resource::MacportsPackage, "initialize" do
)
end
+
+describe Chef::Resource::MacportsPackage, "defaults" do
+ let(:resource) { Chef::Resource::MacportsPackage.new("foo") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+end
diff --git a/spec/unit/resource/mdadm_spec.rb b/spec/unit/resource/mdadm_spec.rb
index fe9acf807b..62879859a2 100644
--- a/spec/unit/resource/mdadm_spec.rb
+++ b/spec/unit/resource/mdadm_spec.rb
@@ -21,86 +21,83 @@ require "spec_helper"
describe Chef::Resource::Mdadm do
- before(:each) do
- @resource = Chef::Resource::Mdadm.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Mdadm.new("fakey_fakerton") }
- 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)
+ it "has a resource name of :mdadm" do
+ expect(resource.resource_name).to eql(:mdadm)
end
- it "should have a resource name of :mdadm" do
- expect(@resource.resource_name).to eql(:mdadm)
+ it "the raid_device property is the name_property" do
+ expect(resource.raid_device).to eql("fakey_fakerton")
end
- it "should have a default action of create" do
- expect(@resource.action).to eql([:create])
+ it "sets the default action as :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 "supports :assemble, :create, :stop actions" do
+ expect { resource.action :assemble }.not_to raise_error
+ expect { resource.action :create }.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 property" 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 property" 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 property" 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 property" 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 property" 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 property" 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 property" 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 property" 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..c9800f1381 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,195 +20,172 @@
require "spec_helper"
describe Chef::Resource::Mount do
- before(:each) do
- @resource = Chef::Resource::Mount.new("filesystem")
- end
+ let(:resource) { Chef::Resource::Mount.new("fakey_fakerton") }
- 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 "the mount_point property is the name_property" do
+ expect(resource.mount_point).to eql("fakey_fakerton")
end
- it "should have a name" do
- expect(@resource.name).to eql("filesystem")
+ it "sets the default action as :mount" do
+ expect(resource.action).to eql([:mount])
end
- it "should set mount_point to the name" do
- expect(@resource.mount_point).to eql("filesystem")
+ it "supports :disable, :enable, :mount, :remount, :umount, :unmount actions" do
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :mount }.not_to raise_error
+ expect { resource.action :remount }.not_to raise_error
+ expect { resource.action :umount }.not_to raise_error
+ expect { resource.action :unmount }.not_to raise_error
end
- it "should have a default action of mount" do
- expect(@resource.action).to eql([:mount])
+ it "allows you to set the device property" do
+ resource.device "/dev/sdb3"
+ expect(resource.device).to eql("/dev/sdb3")
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 mount_point property" do
+ resource.mount_point "U:"
+ expect(resource.mount_point).to eql("U:")
end
- it "should allow you to set the device attribute" do
- @resource.device "/dev/sdb3"
- expect(@resource.device).to eql("/dev/sdb3")
+ it "splits strings passed to the mount_point property" do
+ resource.mount_point "U:"
+ expect(resource.mount_point).to eql("U:")
end
- it "should set fsck_device to '-' by default" do
- expect(@resource.fsck_device).to eql("-")
+ it "strips trailing slashes from mount_point values" do
+ resource.mount_point "//192.168.11.102/Share/backup/"
+ expect(resource.mount_point).to eql("//192.168.11.102/Share/backup")
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 "raises error when mount_point property is not set" do
+ expect { resource.mount_point nil }.to raise_error(Chef::Exceptions::ValidationFailed, "Property mount_point must be one of: String! You passed nil.")
end
- it "should allow you to set the fstype attribute" do
- @resource.fstype "nfs"
- expect(@resource.fstype).to eql("nfs")
+ it "sets fsck_device to '-' by default" do
+ expect(resource.fsck_device).to eql("-")
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 fsck_device property" do
+ resource.fsck_device "/dev/rdsk/sdb3"
+ expect(resource.fsck_device).to eql("/dev/rdsk/sdb3")
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 fstype property" do
+ resource.fstype "nfs"
+ expect(resource.fstype).to eql("nfs")
end
- it "should set the options attribute to defaults" do
- expect(@resource.options).to eql(["defaults"])
+ it "sets fstype to 'auto' by default" do
+ expect(resource.fstype).to eql("auto")
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 you to set the dump property" do
+ resource.dump 1
+ expect(resource.dump).to eql(1)
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 you to set the pass property" do
+ resource.pass 1
+ expect(resource.pass).to eql(1)
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 "sets the options property to defaults" do
+ expect(resource.options).to eql(["defaults"])
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 string, and convert to array" do
+ resource.options "rw,noexec"
+ 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 "strips whitespace around options in a comma deliminated string" do
+ resource.options "rw, noexec"
+ expect(resource.options).to eql(%w{rw noexec})
end
- it "should accept false for mounted" do
- @resource.mounted(false)
- expect(@resource.mounted).to eql(false)
+ it "allows options property as an array" do
+ resource.options %w{ro nosuid}
+ expect(resource.options).to eql(%w{ro nosuid})
end
- it "should set mounted to false by default" do
- expect(@resource.mounted).to eql(false)
+ 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 not accept a string for mounted" do
- expect { @resource.mounted("poop") }.to raise_error(ArgumentError)
+ 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 eql(%w{rw noexec})
end
- it "should accept true for enabled" do
- @resource.enabled(true)
- expect(@resource.enabled).to eql(true)
+ it "accepts true for mounted" do
+ resource.mounted(true)
+ expect(resource.mounted).to eql(true)
end
- it "should accept false for enabled" do
- @resource.enabled(false)
- expect(@resource.enabled).to eql(false)
+ it "accepts false for mounted" do
+ resource.mounted(false)
+ expect(resource.mounted).to eql(false)
end
- it "should set enabled to false by default" do
- expect(@resource.enabled).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 enabled" do
- expect { @resource.enabled("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 default all feature support to false" do
- support_hash = { :remount => false }
- expect(@resource.supports).to eq(support_hash)
+ it "accepts true for enabled" do
+ resource.enabled(true)
+ expect(resource.enabled).to eql(true)
end
- it "should allow 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)
+ it "accepts false for enabled" do
+ resource.enabled(false)
+ expect(resource.enabled).to eql(false)
end
- it "should allow you to set feature support as a hash" do
- support_hash = { :remount => true }
- @resource.supports(support_hash)
- expect(@resource.supports).to eq(support_hash)
+ it "sets enabled to false by default" do
+ expect(resource.enabled).to eql(false)
end
- it "should allow you to set username" do
- @resource.username("Administrator")
- expect(@resource.username).to eq("Administrator")
+ it "does not accept a string for enabled" do
+ expect { resource.enabled("poop") }.to raise_error(ArgumentError)
end
- it "should allow you to set password" do
- @resource.password("Jetstream123!")
- expect(@resource.password).to eq("Jetstream123!")
+ it "defaults all feature support to false" do
+ support_hash = { remount: false }
+ expect(resource.supports).to eq(support_hash)
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 feature support as an array" do
+ support_array = [ :remount ]
+ support_hash = { remount: true }
+ resource.supports(support_array)
+ expect(resource.supports).to eq(support_hash)
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
+ 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
- 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 "allows you to set username" do
+ resource.username("Administrator")
+ expect(resource.username).to eq("Administrator")
+ 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 password" do
+ resource.password("Jetstream123!")
+ expect(resource.password).to eq("Jetstream123!")
+ 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..3c7462a1a7
--- /dev/null
+++ b/spec/unit/resource/msu_package_spec.rb
@@ -0,0 +1,67 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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
+
+ it "sets timeout property to 3600 by default" do
+ expect(resource.timeout).to eql(3600)
+ end
+end
diff --git a/spec/unit/resource/notify_group_spec.rb b/spec/unit/resource/notify_group_spec.rb
new file mode 100644
index 0000000000..63c77fdab2
--- /dev/null
+++ b/spec/unit/resource/notify_group_spec.rb
@@ -0,0 +1,34 @@
+#
+# Copyright:: Copyright (c) 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::NotifyGroup do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::NotifyGroup.new("whatever", run_context) }
+
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:nothing])
+ end
+
+ it "is always updated" do
+ resource.run_action(:run)
+ expect(resource.updated_by_last_action?).to be true
+ 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..8d70e27e11
--- /dev/null
+++ b/spec/unit/resource/ohai_hint_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :ohai_hint" do
+ expect(resource.resource_name).to eql(:ohai_hint)
+ end
+
+ it "the hint_name property is the name_property" do
+ expect(resource.hint_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+
+ it "runs at compile_time by default" do
+ expect(resource.compile_time).to eql(true)
+ end
+end
diff --git a/spec/unit/resource/ohai_spec.rb b/spec/unit/resource/ohai_spec.rb
index 9669ef193d..5681a4540c 100644
--- a/spec/unit/resource/ohai_spec.rb
+++ b/spec/unit/resource/ohai_spec.rb
@@ -19,43 +19,94 @@
require "spec_helper"
describe Chef::Resource::Ohai do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Ohai.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:reload) }
- before(:each) do
- @resource = Chef::Resource::Ohai.new("ohai_reload")
+ it "has a resource name of :ohai" do
+ expect(resource.resource_name).to eql(:ohai)
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)
+ it "sets the default action as :reload" do
+ expect(resource.action).to eql([:reload])
end
- it "should have a resource name of :ohai" do
- expect(@resource.resource_name).to eql(:ohai)
+ it "supports :reload action" do
+ expect { resource.action :reload }.not_to raise_error
end
- it "should have a default action of create" 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 property" 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
+ describe "reload action" do
+ before(:each) do
+ # Copied from client_spec
+ @fqdn = "hostname.domainname"
+ @hostname = "hostname"
+ @platform = "example-platform"
+ @platform_version = "example-platform"
+ Chef::Config[:node_name] = @fqdn
+ mock_ohai = {
+ fqdn: @fqdn,
+ hostname: @hostname,
+ platform: @platform,
+ platform_version: @platform_version,
+ data: {
+ origdata: "somevalue",
+ },
+ data2: {
+ origdata: "somevalue",
+ newdata: "somevalue",
+ },
+ }
+ allow(mock_ohai).to receive(:all_plugins).and_return(true)
+ allow(mock_ohai).to receive(:data).and_return(mock_ohai[:data],
+ mock_ohai[:data2])
+ allow(Ohai::System).to receive(:new).and_return(mock_ohai)
+ allow(Chef::Platform).to receive(:find_platform_and_version).and_return({ "platform" => @platform,
+ "platform_version" => @platform_version })
+ # Fake node with a dummy save
+ node.name(@fqdn)
+ allow(node).to receive(:save).and_return(node)
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ node.consume_external_attrs(ohai.data, {})
+ node.automatic_attrs[:origdata] = "somevalue"
+ end
+
+ it "applies updated ohai data to the node" do
+ expect(node[:origdata]).to eq("somevalue")
+ expect(node[:newdata]).to be_nil
+ provider.run_action(:reload)
+ expect(node[:origdata]).to eq("somevalue")
+ expect(node[:newdata]).to eq("somevalue")
+ end
+
+ it "supports reloading a specific plugin and causes node to pick up new values" do
+ resource.plugin "someplugin"
+ provider.run_action(:reload)
+ expect(node[:origdata]).to eq("somevalue")
+ expect(node[:newdata]).to eq("somevalue")
+ end
+ end
end
diff --git a/spec/unit/resource/openbsd_package_spec.rb b/spec/unit/resource/openbsd_package_spec.rb
index 9bdc823576..4c812cef02 100644
--- a/spec/unit/resource/openbsd_package_spec.rb
+++ b/spec/unit/resource/openbsd_package_spec.rb
@@ -2,7 +2,7 @@
# Authors:: AJ Christensen (<aj@chef.io>)
# Richard Manyanza (<liseki@nyikacraftsmen.com>)
# Scott Bonds (<scott@ggr.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# Copyright:: Copyright 2014-2016, Richard Manyanza, Scott Bonds
# License:: Apache License, Version 2.0
#
@@ -20,28 +20,38 @@
#
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 "sets the resource_name to :openbsd_package" do
+ expect(resource.resource_name).to eql(:openbsd_package)
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
end
- it "should set the resource_name to :openbsd_package" do
- expect(@resource.resource_name).to eql(:openbsd_package)
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
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_spec.rb b/spec/unit/resource/openssl_dhparam_spec.rb
new file mode 100644
index 0000000000..7dd066b339
--- /dev/null
+++ b/spec/unit/resource/openssl_dhparam_spec.rb
@@ -0,0 +1,61 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :openssl_dhparam" do
+ expect(resource.resource_name).to eql(:openssl_dhparam)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ 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 "sets the mode which user provides for existing file" do
+ resource.mode "0600"
+ expect(resource.mode).to eql("0600")
+ end
+
+end
diff --git a/spec/unit/resource/openssl_ec_private_key_spec.rb b/spec/unit/resource/openssl_ec_private_key_spec.rb
new file mode 100644
index 0000000000..89c0e62e06
--- /dev/null
+++ b/spec/unit/resource/openssl_ec_private_key_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright (c) 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::OpensslEcPrivateKey do
+
+ let(:resource) { Chef::Resource::OpensslEcPrivateKey.new("fakey_fakerton") }
+
+ it "has a resource name of :openssl_ec_private_key" do
+ expect(resource.resource_name).to eql(:openssl_ec_private_key)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ 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_curve of 'prime256v1'" do
+ expect(resource.key_curve).to eql("prime256v1")
+ end
+
+ it "only accepts valid key_curve values" do
+ expect { resource.key_curve "fako" }.to raise_error(ArgumentError)
+ end
+
+ it "has a default force value of of false" do
+ expect(resource.force).to eql(false)
+ end
+
+end
diff --git a/spec/unit/resource/openssl_ec_public_key_spec.rb b/spec/unit/resource/openssl_ec_public_key_spec.rb
new file mode 100644
index 0000000000..3da9920012
--- /dev/null
+++ b/spec/unit/resource/openssl_ec_public_key_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright (c) 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::OpensslEcPublicKey do
+
+ let(:resource) { Chef::Resource::OpensslEcPublicKey.new("key") }
+
+ it "has a resource name of :openssl_ec_public_key" do
+ expect(resource.resource_name).to eql(:openssl_ec_public_key)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("key")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "has a default mode of '0640'" do
+ expect(resource.mode).to eql("0640")
+ 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..8a0465780b
--- /dev/null
+++ b/spec/unit/resource/openssl_rsa_private_key_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :openssl_rsa_private_key" do
+ expect(resource.resource_name).to eql(:openssl_rsa_private_key)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ 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
+
+end
diff --git a/spec/unit/resource/openssl_rsa_public_key_spec.rb b/spec/unit/resource/openssl_rsa_public_key_spec.rb
new file mode 100644
index 0000000000..3e272eb040
--- /dev/null
+++ b/spec/unit/resource/openssl_rsa_public_key_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright (c) 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::OpensslRsaPublicKey do
+
+ let(:resource) { Chef::Resource::OpensslRsaPublicKey.new("key") }
+
+ it "has a resource name of :openssl_rsa_public_key" do
+ expect(resource.resource_name).to eql(:openssl_rsa_public_key)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("key")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "has a default mode of '0640'" do
+ expect(resource.mode).to eql("0640")
+ end
+end
diff --git a/spec/unit/resource/openssl_x509_certificate_spec.rb b/spec/unit/resource/openssl_x509_certificate_spec.rb
new file mode 100644
index 0000000000..68e216a2e1
--- /dev/null
+++ b/spec/unit/resource/openssl_x509_certificate_spec.rb
@@ -0,0 +1,72 @@
+#
+# Copyright:: Copyright (c) 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::OpensslX509Certificate do
+
+ let(:resource) { Chef::Resource::OpensslX509Certificate.new("fakey_fakerton") }
+
+ it "has a resource name of :openssl_x509_certificate" do
+ expect(resource.resource_name).to eql(:openssl_x509_certificate)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "has a default expiration of 365" do
+ expect(resource.expire).to eql(365)
+ end
+
+ it "has a default key_type of 'rsa'" do
+ expect(resource.key_type).to eql("rsa")
+ end
+
+ it "only accepts valid key_type values" do
+ expect { resource.key_type "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 values" do
+ expect { resource.key_length 1023 }.to raise_error(ArgumentError)
+ end
+
+ it "has a default key_curve of 'prime256v1'" do
+ expect(resource.key_curve).to eql("prime256v1")
+ end
+
+ it "only accepts valid key_curve values" do
+ expect { resource.key_curve "fako" }.to raise_error(ArgumentError)
+ end
+
+ it "mode accepts both String and Integer values" do
+ expect { resource.mode "644" }.not_to raise_error
+ expect { resource.mode 644 }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/openssl_x509_crl_spec.rb b/spec/unit/resource/openssl_x509_crl_spec.rb
new file mode 100644
index 0000000000..99d64539a0
--- /dev/null
+++ b/spec/unit/resource/openssl_x509_crl_spec.rb
@@ -0,0 +1,61 @@
+#
+# Copyright:: Copyright (c) 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::OpensslX509Crl do
+
+ let(:resource) { Chef::Resource::OpensslX509Crl.new("fakey_fakerton") }
+
+ it "has a resource name of :openssl_x509_crl" do
+ expect(resource.resource_name).to eql(:openssl_x509_crl)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "has a default revocation_reason of 0" do
+ expect(resource.revocation_reason).to eql(0)
+ end
+
+ it "has a default expiration of 8" do
+ expect(resource.expire).to eql(8)
+ end
+
+ it "has a default renewal_threshold of 1" do
+ expect(resource.renewal_threshold).to eql(1)
+ end
+
+ it "serial_to_revoke accepts both String and Integer values" do
+ expect { resource.serial_to_revoke "123" }.not_to raise_error
+ expect { resource.serial_to_revoke 123 }.not_to raise_error
+ end
+
+ it "mode accepts both String and Integer values" do
+ expect { resource.mode "644" }.not_to raise_error
+ expect { resource.mode 644 }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/openssl_x509_request.rb b/spec/unit/resource/openssl_x509_request.rb
new file mode 100644
index 0000000000..31a8dde320
--- /dev/null
+++ b/spec/unit/resource/openssl_x509_request.rb
@@ -0,0 +1,68 @@
+#
+# Copyright:: Copyright (c) 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::OpensslX509Request do
+
+ let(:resource) { Chef::Resource::OpensslX509Request.new("fakey_fakerton") }
+
+ it "has a resource name of :openssl_x509_request" do
+ expect(resource.resource_name).to eql(:openssl_x509_request)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "has a default key_type of 'ec'" do
+ expect(resource.key_type).to eql("ec")
+ end
+
+ it "only accepts valid key_type values" do
+ expect { resource.key_type "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 values" do
+ expect { resource.key_length 1023 }.to raise_error(ArgumentError)
+ end
+
+ it "has a default key_curve of 'prime256v1'" do
+ expect(resource.key_curve).to eql("prime256v1")
+ end
+
+ it "only accepts valid key_curve values" do
+ expect { resource.key_curve "fako" }.to raise_error(ArgumentError)
+ end
+
+ it "mode accepts both String and Integer values" do
+ expect { resource.mode "644" }.not_to raise_error
+ expect { resource.mode 644 }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/osx_profile_spec.rb b/spec/unit/resource/osx_profile_spec.rb
index 513e570e7c..c6909a842d 100644
--- a/spec/unit/resource/osx_profile_spec.rb
+++ b/spec/unit/resource/osx_profile_spec.rb
@@ -21,42 +21,340 @@ require "spec_helper"
describe Chef::Resource::OsxProfile do
let(:resource) do
Chef::Resource::OsxProfile.new(
- "Test Profile Resource",
- run_context)
+ "fakey_fakerton"
+ )
end
- 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)
+ it "has a resource name of profile" do
+ expect(resource.resource_name).to eql(:osx_profile)
end
- it "should have a resource name of profile" do
- expect(resource.resource_name).to eql(:osx_profile)
+ it "the profile_name property is the name_property" do
+ expect(resource.profile_name).to eql("fakey_fakerton")
end
- it "should have a default action of install" do
+ it "sets the default action as :install" do
expect(resource.action).to eql([:install])
end
- it "should accept install and remove as actions" do
+ it "supports :install, :remove 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 property" 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 property 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 property to a hash" do
test_profile = { "profile" => false }
resource.profile test_profile
expect(resource.profile).to be_a(Hash)
end
+
+ let(:shell_out_success) do
+ double("shell_out", exitstatus: 0, error?: false)
+ end
+
+ describe "action_create" do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
+ let(:provider) { resource.provider_for_action(:create) }
+ let(:all_profiles) do
+ { "_computerlevel" => [{ "ProfileDisplayName" => "Finder Settings",
+ "ProfileIdentifier" => "com.apple.finder",
+ "ProfileInstallDate" => "2015-11-08 23:15:21 +0000",
+ "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.finder" => { "Forced" => [{ "mcx_preference_settings" => { "ShowExternalHardDrivesOnDesktop" => false } }] } } },
+ "PayloadDisplayName" => "Custom: (com.apple.finder)",
+ "PayloadIdentifier" => "com.apple.finder",
+ "PayloadType" => "com.apple.ManagedClient.preferences",
+ "PayloadUUID" => "a017048f-684b-4e81-baa3-43afe316d739",
+ "PayloadVersion" => 1 }],
+ "ProfileOrganization" => "Chef",
+ "ProfileRemovalDisallowed" => "false",
+ "ProfileType" => "Configuration",
+ "ProfileUUID" => "e2e09bef-e673-44a6-bcbe-ecb5f1c1b740",
+ "ProfileVerificationState" => "unsigned",
+ "ProfileVersion" => 1 },
+ { "ProfileDisplayName" => "ScreenSaver Settings",
+ "ProfileIdentifier" => "com.testprofile.screensaver",
+ "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
+ "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
+ "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
+ "PayloadIdentifier" => "com.apple.screensaver",
+ "PayloadType" => "com.apple.ManagedClient.preferences",
+ "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c110",
+ "PayloadVersion" => 1 }],
+ "ProfileOrganization" => "Chef",
+ "ProfileRemovalDisallowed" => "false",
+ "ProfileType" => "Configuration",
+ "ProfileUUID" => "6e95927c-f200-54b4-85c7-52ab99b61c47",
+ "ProfileVerificationState" => "unsigned",
+ "ProfileVersion" => 1 }],
+ }
+ end
+ let(:profile_raw_xml) do
+ <<~OUT
+ <?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>
+ <key>tsmith</key>
+ <array>
+ <dict>
+ <key>ProfileDisplayName</key>
+ <string>Screensaver Settings</string>
+ <key>ProfileIdentifier</key>
+ <string>com.company.screensaver</string>
+ <key>ProfileInstallDate</key>
+ <string>2020-09-17 17:20:49 +0000</string>
+ <key>ProfileItems</key>
+ <array>
+ <dict>
+ <key>PayloadContent</key>
+ <dict>
+ <key>PayloadContentManagedPreferences</key>
+ <dict>
+ <key>com.apple.screensaver</key>
+ <dict>
+ <key>Forced</key>
+ <array>
+ <dict>
+ <key>mcx_preference_settings</key>
+ <dict>
+ <key>idleTime</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ <key>PayloadDisplayName</key>
+ <string>com.apple.screensaver</string>
+ <key>PayloadIdentifier</key>
+ <string>com.company.screensaver</string>
+ <key>PayloadType</key>
+ <string>com.apple.ManagedClient.preferences</string>
+ <key>PayloadUUID</key>
+ <string>73fc30e0-1e57-0131-c32d-000c2944c108</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ </dict>
+ </array>
+ <key>ProfileOrganization</key>
+ <string>Chef</string>
+ <key>ProfileType</key>
+ <string>Configuration</string>
+ <key>ProfileUUID</key>
+ <string>ed5e36c8-ea0b-5960-8f49-3c7d9121687e</string>
+ <key>ProfileVersion</key>
+ <integer>1</integer>
+ </dict>
+ </array>
+ </dict>
+ </plist>
+ OUT
+ end
+ let(:shell_out_profiles) do
+ double("shell_out", exitstatus: 0, error?: false, stdout: profile_raw_xml)
+ end
+ # If anything is changed within this profile, be sure to update the
+ # ProfileUUID in all_profiles to match the new config specific UUID
+ let(:test_profile) do
+ {
+ "PayloadIdentifier" => "com.testprofile.screensaver",
+ "PayloadRemovalDisallowed" => false,
+ "PayloadScope" => "System",
+ "PayloadType" => "Configuration",
+ "PayloadUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
+ "PayloadOrganization" => "Chef",
+ "PayloadVersion" => 1,
+ "PayloadDisplayName" => "Screensaver Settings",
+ "PayloadContent" => [
+ {
+ "PayloadType" => "com.apple.ManagedClient.preferences",
+ "PayloadVersion" => 1,
+ "PayloadIdentifier" => "com.testprofile.screensaver",
+ "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c108",
+ "PayloadEnabled" => true,
+ "PayloadDisplayName" => "com.apple.screensaver",
+ "PayloadContent" => {
+ "com.apple.screensaver" => {
+ "Forced" => [
+ {
+ "mcx_preference_settings" => {
+ "idleTime" => 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ ],
+ }
+ end
+ let(:no_profiles) do
+ {}
+ end
+
+ before(:each) do
+ allow(provider).to receive(:cookbook_file_available?).and_return(true)
+ allow(provider).to receive(:cache_cookbook_profile).and_return("/tmp/test.mobileconfig.remote")
+ allow(provider).to receive(:get_new_profile_hash).and_return(test_profile)
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ allow(provider).to receive(:read_plist).and_return(all_profiles)
+ allow(::File).to receive(:unlink).and_return(true)
+ end
+
+ it "should build the get all profiles shellout command correctly" do
+ profile_name = "com.testprofile.screensaver.mobileconfig"
+ resource.profile_name profile_name
+ allow(provider).to receive(:get_installed_profiles).and_call_original
+ allow(provider).to receive(:read_plist).and_return(all_profiles)
+ expect(provider).to receive(:shell_out_compacted).with("/usr/bin/profiles", "-P", "-o", "stdout-xml").and_return(shell_out_profiles)
+ provider.load_current_resource
+ end
+
+ it "should use profile name as profile when no profile is set" do
+ profile_name = "com.testprofile.screensaver.mobileconfig"
+ resource.profile_name profile_name
+ provider.load_current_resource
+ expect(resource.profile_name).to eql(profile_name)
+ end
+
+ it "should use identifier from specified profile" do
+ resource.profile test_profile
+ provider.load_current_resource
+ expect(
+ provider.instance_variable_get(:@new_profile_identifier)
+ ).to eql(test_profile["PayloadIdentifier"])
+ end
+
+ it "should install when not installed" do
+ resource.profile test_profile
+ allow(provider).to receive(:get_installed_profiles).and_return(no_profiles)
+ provider.load_current_resource
+ expect(provider).to receive(:install_profile)
+ expect { provider.run_action(:install) }.to_not raise_error
+ end
+
+ it "does not install if the profile is already installed" do
+ resource.profile test_profile
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ provider.load_current_resource
+ expect(provider).to_not receive(:install_profile)
+ expect { provider.action_install }.to_not raise_error
+ end
+
+ it "should install when installed but uuid differs" do
+ resource.profile test_profile
+ all_profiles["_computerlevel"][1]["ProfileUUID"] = "1781fbec-3325-565f-9022-9bb39245d4dd"
+ provider.load_current_resource
+ expect(provider).to receive(:install_profile)
+ expect { provider.run_action(:install) }.to_not raise_error
+ end
+
+ it "should build the shellout install command correctly" do
+ profile_path = "/tmp/test.mobileconfig"
+ resource.profile test_profile
+ # Change the profile so it triggers an install
+ all_profiles["_computerlevel"][1]["ProfileUUID"] = "1781fbec-3325-565f-9022-9bb39245d4dd"
+ provider.load_current_resource
+ allow(provider).to receive(:write_profile_to_disk).and_return(profile_path)
+ expect(provider).to receive(:shell_out_compacted!).with("/usr/bin/profiles", "-I", "-F", profile_path).and_return(shell_out_success)
+ provider.action_install
+ end
+
+ it "should fail if there is no identifier inside the profile" do
+ test_profile.delete("PayloadIdentifier")
+ resource.profile test_profile
+ error_message = "The specified profile does not seem to be valid"
+ expect { provider.run_action(:install) }.to raise_error(RuntimeError, error_message)
+ end
+ end
+
+ describe "action_remove" do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
+ let(:provider) { resource.provider_for_action(:remove) }
+ let(:current_resource) { Chef::Resource::OsxProfile.new("Profile Test") }
+ let(:all_profiles) do
+ { "_computerlevel" => [{ "ProfileDisplayName" => "ScreenSaver Settings",
+ "ProfileIdentifier" => "com.apple.screensaver",
+ "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
+ "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
+ "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
+ "PayloadIdentifier" => "com.apple.screensaver",
+ "PayloadType" => "com.apple.ManagedClient.preferences",
+ "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c108",
+ "PayloadVersion" => 1 }],
+ "ProfileOrganization" => "Chef",
+ "ProfileRemovalDisallowed" => "false",
+ "ProfileType" => "Configuration",
+ "ProfileUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
+ "ProfileVerificationState" => "unsigned",
+ "ProfileVersion" => 1 },
+ { "ProfileDisplayName" => "ScreenSaver Settings",
+ "ProfileIdentifier" => "com.testprofile.screensaver",
+ "ProfileInstallDate" => "2015-10-05 23:15:21 +0000",
+ "ProfileItems" => [{ "PayloadContent" => { "PayloadContentManagedPreferences" => { "com.apple.screensaver" => { "Forced" => [{ "mcx_preference_settings" => { "idleTime" => 0 } }] } } },
+ "PayloadDisplayName" => "Custom: (com.apple.screensaver)",
+ "PayloadIdentifier" => "com.apple.screensaver",
+ "PayloadType" => "com.apple.ManagedClient.preferences",
+ "PayloadUUID" => "73fc30e0-1e57-0131-c32d-000c2944c110",
+ "PayloadVersion" => 1 }],
+ "ProfileOrganization" => "Chef",
+ "ProfileRemovalDisallowed" => "false",
+ "ProfileType" => "Configuration",
+ "ProfileUUID" => "1781fbec-3325-565f-9022-8aa28135c3cc",
+ "ProfileVerificationState" => "unsigned",
+ "ProfileVersion" => 1 }],
+ }
+ end
+
+ before(:each) do
+ provider.current_resource = current_resource
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ end
+
+ it "should use resource name for identifier when not specified" do
+ resource.profile_name "com.testprofile.screensaver"
+ resource.action(:remove)
+ provider.load_current_resource
+ expect(provider.instance_variable_get(:@new_profile_identifier)).to eql(resource.profile_name)
+ end
+
+ it "should use specified identifier" do
+ resource.identifier "com.testprofile.screensaver"
+ resource.action(:remove)
+ provider.load_current_resource
+ expect(provider.instance_variable_get(:@new_profile_identifier)).to eql(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
+ resource.identifier "com.testprofile.screensaver"
+ resource.action(:remove)
+ provider.load_current_resource
+ expect(provider).to receive(:shell_out_compacted!).with("/usr/bin/profiles", "-R", "-p", resource.identifier).and_return(shell_out_success)
+ provider.action_remove
+ end
+ end
end
diff --git a/spec/unit/resource/package_spec.rb b/spec/unit/resource/package_spec.rb
index dbd76d2eba..f2800a1485 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,74 +20,79 @@
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 "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
end
- it "should set the package_name to the first argument to new" do
- expect(@resource.package_name).to eql("emacs")
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
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 package name" do
+ resource.package_name "something"
+ expect(resource.package_name).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 string for the version" do
+ resource.version "something"
+ expect(resource.version).to eql("something")
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/pacman_package_spec.rb b/spec/unit/resource/pacman_package_spec.rb
index b9d2ea21f6..c69deebd7a 100644
--- a/spec/unit/resource/pacman_package_spec.rb
+++ b/spec/unit/resource/pacman_package_spec.rb
@@ -1,6 +1,7 @@
#
# Author:: Jan Zimmek (<jan.zimmek@web.de>)
# Copyright:: Copyright 2010-2016, Jan Zimmek
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +18,8 @@
#
require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-
-describe Chef::Resource::PacmanPackage, "initialize" do
+describe Chef::Resource::PacmanPackage do
static_provider_resolution(
resource: Chef::Resource::PacmanPackage,
provider: Chef::Provider::Package::Pacman,
@@ -29,4 +28,19 @@ describe Chef::Resource::PacmanPackage, "initialize" do
os: "linux"
)
+ let(:resource) { Chef::Resource::PacmanPackage.new("fakey_fakerton") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
end
diff --git a/spec/unit/resource/paludis_package_spec.rb b/spec/unit/resource/paludis_package_spec.rb
new file mode 100644
index 0000000000..3841eb026c
--- /dev/null
+++ b/spec/unit/resource/paludis_package_spec.rb
@@ -0,0 +1,36 @@
+#
+# Copyright:: Copyright (c) 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::PaludisPackage do
+ let(:resource) { Chef::Resource::PaludisPackage.new("fakey_fakerton") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/perl_spec.rb b/spec/unit/resource/perl_spec.rb
index 417d74a8c2..80326eb972 100644
--- a/spec/unit/resource/perl_spec.rb
+++ b/spec/unit/resource/perl_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,21 +20,25 @@ require "spec_helper"
describe Chef::Resource::Perl do
- before(:each) do
- @resource = Chef::Resource::Perl.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::Perl.new("fakey_fakerton") }
+
+ it "is a subclass of Chef::Resource::Script" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Script)
end
- 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 "has a resource name of :perl" do
+ expect(resource.resource_name).to eql(:perl)
end
- it "should have a resource name of :perl" do
- expect(@resource.resource_name).to eql(:perl)
+ it "has an interpreter of perl" do
+ expect(resource.interpreter).to eql("perl")
end
- it "should have an interpreter of perl" do
- expect(@resource.interpreter).to eql("perl")
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
+ end
end
diff --git a/spec/unit/resource/plist_spec.rb b/spec/unit/resource/plist_spec.rb
new file mode 100644
index 0000000000..bbecb2e022
--- /dev/null
+++ b/spec/unit/resource/plist_spec.rb
@@ -0,0 +1,130 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) Chef Software Inc.
+# Copyright:: 2017-2020, Microsoft Corporation
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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::PlistResource do
+ let(:resource) { Chef::Resource::PlistResource.new("fakey_fakerton") }
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "path is the name property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ describe "#plistbuddy_command" do
+ it "the bool arguments contain the data type" do
+ expect(resource.plistbuddy_command(:add, "FooEntry", "path/to/file.plist", true)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"FooEntry\" bool' \"path/to/file.plist\""
+ end
+
+ it "the add command only adds the data type" do
+ expect(resource.plistbuddy_command(:add, "QuuxEntry", "path/to/file.plist", 50)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"QuuxEntry\" integer' \"path/to/file.plist\""
+ end
+
+ it "the delete command is formatted properly" do
+ expect(resource.plistbuddy_command(:delete, "BarEntry", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Delete :\"BarEntry\"' \"path/to/file.plist\""
+ end
+
+ it "the set command is formatted properly" do
+ expect(resource.plistbuddy_command(:set, "BazEntry", "path/to/file.plist", false)).to eq "/usr/libexec/PlistBuddy -c 'Set :\"BazEntry\" false' \"path/to/file.plist\""
+ end
+
+ it "the print command is formatted properly" do
+ expect(resource.plistbuddy_command(:print, "QuxEntry", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"QuxEntry\"' \"path/to/file.plist\""
+ end
+
+ it "the command to set a dictionary data type is formatted properly" do
+ expect(resource.plistbuddy_command(:set, "AppleFirstWeekday", "path/to/file.plist", gregorian: 4)).to eq "/usr/libexec/PlistBuddy -c 'Set :\"AppleFirstWeekday\":gregorian 4' \"path/to/file.plist\""
+ end
+
+ it "returns the value properly formatted with double quotes when the value has spaces" do
+ expect(resource.plistbuddy_command(:print, "Foo Bar Baz", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"Foo Bar Baz\"' \"path/to/file.plist\""
+ end
+
+ it "returns the value properly formatted with double quotes when The value to be added contains spaces" do
+ expect(resource.plistbuddy_command(:add, "Foo Bar Baz", "path/to/file.plist", true)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"Foo Bar Baz\" bool' \"path/to/file.plist\""
+ end
+
+ it "returns the value properly formatted with double quotes when the plist itself contains spaces" do
+ expect(resource.plistbuddy_command(:print, "Foo Bar Baz", "Library/Preferences/com.parallels.Parallels Desktop.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"Foo Bar Baz\"' \"Library/Preferences/com.parallels.Parallels Desktop.plist\""
+ end
+ end
+
+ describe "#convert_to_data_type_from_string" do
+ it "returns true if entry is 1 and the type is boolean" do
+ expect(resource.convert_to_data_type_from_string("boolean", "1")).to eq true
+ end
+
+ it "returns false if entry is 0 and the type is boolean" do
+ expect(resource.convert_to_data_type_from_string("boolean", "0")).to eq false
+ end
+
+ it "returns the value as an integer when the type is integer and the value is 1" do
+ expect(resource.convert_to_data_type_from_string("integer", "1")).to eq 1
+ end
+
+ it "returns the value as an integer when the type is integer and the value is 0" do
+ expect(resource.convert_to_data_type_from_string("integer", "0")).to eq 0
+ end
+
+ it "returns the correct value as an integer when the type is integer and the value is 950224" do
+ expect(resource.convert_to_data_type_from_string("integer", "950224")).to eq 950224
+ end
+
+ it "returns the correct value still as a string when the type is string and the value is also a string" do
+ expect(resource.convert_to_data_type_from_string("string", "corge")).to eq "corge"
+ end
+
+ it "returns the correct value as a float when the type is float and the value is 3.14159265359" do
+ expect(resource.convert_to_data_type_from_string("float", "3.14159265359")).to eq 3.14159265359
+ end
+
+ it "returns an empty string when the type nor the value is given" do
+ expect(resource.convert_to_data_type_from_string(nil, "")).to eq ""
+ end
+ end
+
+ describe "#type_to_commandline_string" do
+ it "returns the required boolean entry type as a string" do
+ expect(resource.type_to_commandline_string(true)).to eq "bool"
+ end
+
+ it "returns the required array entry type as a string" do
+ expect(resource.type_to_commandline_string(%w{foo bar})).to eq "array"
+ end
+
+ it "returns the required dictionary entry type as a string" do
+ expect(resource.type_to_commandline_string("baz" => "qux")).to eq "dict"
+ end
+
+ it "returns the required string entry type as a string" do
+ expect(resource.type_to_commandline_string("quux")).to eq "string"
+ end
+
+ it "returns the required integer entry type as a string" do
+ expect(resource.type_to_commandline_string(1)).to eq "integer"
+ end
+
+ it "returns the required float entry type as a string" do
+ expect(resource.type_to_commandline_string(1.0)).to eq "float"
+ end
+ end
+end
diff --git a/spec/unit/resource/portage_package_spec.rb b/spec/unit/resource/portage_package_spec.rb
index d2336744bf..6dc2972b6a 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,23 +16,31 @@
# 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")
+ let(:resource) { Chef::Resource::PortagePackage.new("foo") }
+
+ it "is a subclass of Chef::Resource::Package" do
+ expect(resource).to be_a_kind_of(Chef::Resource::Package)
end
- it "should return a Chef::Resource::PortagePackage" do
- expect(@resource).to be_a_kind_of(Chef::Resource::PortagePackage)
+ it "sets the resource_name to :portage_package" do
+ expect(resource.resource_name).to eql(:portage_package)
end
- it "should set the resource_name to :portage_package" do
- expect(@resource.resource_name).to eql(:portage_package)
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
end
- it "should set the provider to Chef::Provider::Package::Portage" do
- expect(@resource.provider).to eql(Chef::Provider::Package::Portage)
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
end
end
diff --git a/spec/unit/resource/powershell_package_source_spec.rb b/spec/unit/resource/powershell_package_source_spec.rb
new file mode 100644
index 0000000000..1032902a0f
--- /dev/null
+++ b/spec/unit/resource/powershell_package_source_spec.rb
@@ -0,0 +1,219 @@
+# Author:: Tor Magnus Rakvåg (tm@intility.no)
+# Copyright:: 2018, Intility AS
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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::PowershellPackageSource do
+ let(:resource) { Chef::Resource::PowershellPackageSource.new("MyGallery") }
+ let(:provider) { resource.provider_for_action(:enable) }
+
+ it "has a resource name of :powershell_package_source" do
+ expect(resource.resource_name).to eql(:powershell_package_source)
+ end
+
+ it "the name_property is 'name'" do
+ expect(resource.source_name).to eql("MyGallery")
+ end
+
+ it "the default action is :register" do
+ expect(resource.action).to eql([:register])
+ end
+
+ it "supports :register and :unregister actions" do
+ expect { resource.action :register }.not_to raise_error
+ expect { resource.action :unregister }.not_to raise_error
+ end
+
+ it "the url property accepts strings" do
+ resource.url("https://mygallery.company.co/api/v2/")
+ expect(resource.url).to eql("https://mygallery.company.co/api/v2/")
+ end
+
+ it "the trusted property accepts true and false" do
+ resource.trusted(false)
+ expect(resource.trusted).to eql(false)
+ resource.trusted(true)
+ expect(resource.trusted).to eql(true)
+ end
+
+ it "trusted defaults to false" do
+ expect(resource.trusted).to eql(false)
+ end
+
+ it "provider_name accepts 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl', 'chocolatey'" do
+ expect { resource.provider_name("Programs") }.not_to raise_error
+ expect { resource.provider_name("msi") }.not_to raise_error
+ expect { resource.provider_name("NuGet") }.not_to raise_error
+ expect { resource.provider_name("msu") }.not_to raise_error
+ expect { resource.provider_name("PowerShellGet") }.not_to raise_error
+ expect { resource.provider_name("psl") }.not_to raise_error
+ expect { resource.provider_name("chocolatey") }.not_to raise_error
+ end
+
+ it "the publish_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/package")
+ end
+
+ it "the script_source_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/scripts")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/scripts")
+ end
+
+ it "the script_publish_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/scripts")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/scripts")
+ end
+
+ describe "#build_ps_repository_command" do
+ before do
+ resource.source_name("MyGallery")
+ resource.url("https://mygallery.company.co/api/v2/")
+ end
+
+ context "#register" do
+ it "builds a minimal command" do
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' | Out-Null")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Trusted' | Out-Null")
+ end
+
+ it "builds a command with a publish location" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -PublishLocation 'https://mygallery.company.co/api/v2/package' | Out-Null")
+ end
+
+ it "builds a command with a script source location" do
+ resource.script_source_location("https://mygallery.company.co/api/v2/scripts")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptSourceLocation 'https://mygallery.company.co/api/v2/scripts' | Out-Null")
+ end
+
+ it "builds a command with a script publish location" do
+ resource.script_publish_location("https://mygallery.company.co/api/v2/scripts/package")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptPublishLocation 'https://mygallery.company.co/api/v2/scripts/package' | Out-Null")
+ end
+ end
+
+ context "#set" do
+ it "builds a minimal command" do
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' | Out-Null")
+ end
+
+ it "builds a command to change the url" do
+ resource.url("https://othergallery.company.co/api/v2/")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://othergallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' | Out-Null")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Trusted' | Out-Null")
+ end
+
+ it "builds a command with a publish location" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -PublishLocation 'https://mygallery.company.co/api/v2/package' | Out-Null")
+ end
+
+ it "builds a command with a script source location" do
+ resource.script_source_location("https://mygallery.company.co/api/v2/scripts")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptSourceLocation 'https://mygallery.company.co/api/v2/scripts' | Out-Null")
+ end
+
+ it "builds a command with a script publish location" do
+ resource.script_publish_location("https://mygallery.company.co/api/v2/scripts/package")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptPublishLocation 'https://mygallery.company.co/api/v2/scripts/package' | Out-Null")
+ end
+ end
+ end
+
+ describe "#build_package_source_command" do
+ before do
+ resource.source_name("NuGet")
+ resource.url("http://nuget.org/api/v2/")
+ end
+
+ context "#register" do
+ it "builds a minimal command" do
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$false -ProviderName 'NuGet' | Out-Null")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$true -ProviderName 'NuGet' | Out-Null")
+ end
+
+ it "builds a command with a different provider" do
+ resource.source_name("choco")
+ resource.url("https://chocolatey.org/api/v2/")
+ resource.provider_name("chocolatey")
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'choco' -Location 'https://chocolatey.org/api/v2/' -Trusted:$false -ProviderName 'chocolatey' | Out-Null")
+ end
+ end
+
+ context "#set" do
+ it "builds a minimal command" do
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$false -ProviderName 'NuGet' | Out-Null")
+ end
+
+ it "builds a command to change the url" do
+ resource.url("https://nuget.company.co/api/v2/")
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'https://nuget.company.co/api/v2/' -Trusted:$false -ProviderName 'NuGet' | Out-Null")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$true -ProviderName 'NuGet' | Out-Null")
+ end
+
+ it "builds a command with a different provider" do
+ resource.source_name("choco")
+ resource.url("https://chocolatey.org/api/v2/")
+ resource.provider_name("chocolatey")
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'choco' -Location 'https://chocolatey.org/api/v2/' -Trusted:$false -ProviderName 'chocolatey' | Out-Null")
+ end
+ end
+ end
+
+ describe "#psrepository_cmdlet_appropriate?" do
+ it "returns true if the provider_name is 'PowerShellGet'" do
+ resource.provider_name("PowerShellGet")
+ expect(provider.psrepository_cmdlet_appropriate?).to eql(true)
+ end
+
+ it "returns false if the provider_name is something else" do
+ resource.provider_name("NuGet")
+ expect(provider.psrepository_cmdlet_appropriate?).to eql(false)
+ end
+ end
+
+ describe "#package_source_exists?" do
+ it "returns true if it exists" do
+ allow(provider).to receive(:powershell_exec!).with("(Get-PackageSource -Name 'MyGallery' -ErrorAction SilentlyContinue).Name").and_return(double("powershell_exec!", result: "MyGallery\r\n"))
+ resource.source_name("MyGallery")
+ expect(provider.package_source_exists?).to eql(true)
+ end
+
+ it "returns false if it doesn't exist" do
+ allow(provider).to receive(:powershell_exec!).with("(Get-PackageSource -Name 'MyGallery' -ErrorAction SilentlyContinue).Name").and_return(double("powershell_exec!", result: ""))
+ resource.source_name("MyGallery")
+ expect(provider.package_source_exists?).to eql(false)
+ end
+ 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..3ca4d32722
--- /dev/null
+++ b/spec/unit/resource/powershell_package_spec.rb
@@ -0,0 +1,98 @@
+#
+# Author:: Dheeraj Dubey(<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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
+
+ it "the skip_publisher_check default is false" do
+ expect(resource.skip_publisher_check).to eql(false)
+ end
+
+ it "the skip_publisher_check setter accepts booleans" do
+ resource.skip_publisher_check(true)
+ expect(resource.skip_publisher_check).to eql(true)
+ end
+end
diff --git a/spec/unit/resource/powershell_script_spec.rb b/spec/unit/resource/powershell_script_spec.rb
index 6457090608..a913583cfa 100644
--- a/spec/unit/resource/powershell_script_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,114 +20,43 @@ require "spec_helper"
describe Chef::Resource::PowershellScript do
- before(:each) do
+ let(:resource) do
node = Chef::Node.new
- node.default["kernel"] = Hash.new
+ node.default["kernel"] = {}
node.default["kernel"][:machine] = :x86_64.to_s
node.automatic[:os] = "windows"
run_context = Chef::RunContext.new(node, nil, nil)
- @resource = Chef::Resource::PowershellScript.new("powershell_unit_test", run_context)
+ Chef::Resource::PowershellScript.new("powershell_unit_test", run_context)
end
it "creates a new Chef::Resource::PowershellScript" do
- expect(@resource).to be_a_kind_of(Chef::Resource::PowershellScript)
+ expect(resource).to be_a_kind_of(Chef::Resource::PowershellScript)
end
it "sets convert_boolean_return to false by default" do
- expect(@resource.convert_boolean_return).to eq(false)
+ expect(resource.convert_boolean_return).to eq(false)
end
it "returns the value for convert_boolean_return that was set" do
- @resource.convert_boolean_return true
- expect(@resource.convert_boolean_return).to eq(true)
- @resource.convert_boolean_return false
- expect(@resource.convert_boolean_return).to eq(false)
+ resource.convert_boolean_return true
+ expect(resource.convert_boolean_return).to eq(true)
+ resource.convert_boolean_return false
+ expect(resource.convert_boolean_return).to eq(false)
end
- it "raises an error when architecture is i386 on Windows Nano Server" do
- allow(Chef::Platform).to receive(:windows_nano_server?).and_return(true)
- expect { @resource.architecture(:i386) }.to raise_error(Chef::Exceptions::Win32ArchitectureIncorrect, "cannot execute script with requested architecture 'i386' on Windows Nano Server")
- end
-
- context "when using guards" do
- let(:resource) { @resource }
- before(:each) do
- allow(resource).to receive(:run_action)
- 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
- inherited_difference = Chef::Resource::PowershellScript.guard_inherited_attributes -
- [:cwd, :environment, :group, :path, :user, :umask, :architecture ]
-
- expect(inherited_difference).to eq([])
- end
-
- it "allows guard interpreter to be set to Chef::Resource::Script" do
- resource.guard_interpreter(:script)
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
- resource.only_if("echo hi")
- end
-
- it "allows guard interpreter to be set to Chef::Resource::Bash derived from Chef::Resource::Script" do
- resource.guard_interpreter(:bash)
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
- resource.only_if("echo hi")
- end
-
- it "allows guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
- resource.guard_interpreter(:powershell_script)
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
- resource.only_if("echo hi")
- end
-
- it "enables convert_boolean_return by default for guards in the context of powershell_script when no guard params are specified" do
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true)
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
- { :convert_boolean_return => true, :code => "$true" }).and_return(Proc.new {})
- resource.only_if("$true")
- end
-
- it "enables convert_boolean_return by default for guards in non-Chef::Resource::Script derived resources when no guard params are specified" do
- node = Chef::Node.new
- run_context = Chef::RunContext.new(node, nil, nil)
- file_resource = Chef::Resource::File.new("idontexist", run_context)
- file_resource.guard_interpreter :powershell_script
-
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
- { :convert_boolean_return => true, :code => "$true" }).and_return(Proc.new {})
- resource.only_if("$true")
- end
-
- it "enables convert_boolean_return by default for guards in the context of powershell_script when guard params are specified" do
- guard_parameters = { :cwd => "/etc/chef", :architecture => :x86_64 }
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
- { :convert_boolean_return => true, :code => "$true" }.merge(guard_parameters)).and_return(Proc.new {})
- resource.only_if("$true", guard_parameters)
- end
-
- it "passes convert_boolean_return as true if it was specified as true in a guard parameter" do
- guard_parameters = { :cwd => "/etc/chef", :convert_boolean_return => true, :architecture => :x86_64 }
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
- { :convert_boolean_return => true, :code => "$true" }.merge(guard_parameters)).and_return(Proc.new {})
- resource.only_if("$true", guard_parameters)
- end
+ it "inherits exactly the :cwd, :domain, :environment, :group, :password, :path, :user, :umask, :architecture, :elevated, :interpreter properties from a parent resource class" do
+ inherited_difference = Chef::Resource::PowershellScript.guard_inherited_attributes -
+ %i{cwd domain environment group password path user umask architecture elevated interpreter}
- it "passes convert_boolean_return as false if it was specified as true in a guard parameter" do
- other_guard_parameters = { :cwd => "/etc/chef", :architecture => :x86_64 }
- parameters_with_boolean_disabled = other_guard_parameters.merge({ :convert_boolean_return => false, :code => "$true" })
- allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
- parameters_with_boolean_disabled).and_return(Proc.new {})
- resource.only_if("$true", parameters_with_boolean_disabled)
- end
+ expect(inherited_difference).to eq([])
end
context "as a script running in Windows-based scripting language" do
- let(:resource_instance) { @resource }
- let(:resource_instance_name ) { @resource.command }
+ let(:windows_script_resource) { resource }
+ let(:resource_instance_name ) { resource.command }
let(:resource_name) { :powershell_script }
let(:interpreter_file_name) { "powershell.exe" }
diff --git a/spec/unit/resource/python_spec.rb b/spec/unit/resource/python_spec.rb
index aba84c4000..3414fcccd4 100644
--- a/spec/unit/resource/python_spec.rb
+++ b/spec/unit/resource/python_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,21 +20,21 @@ require "spec_helper"
describe Chef::Resource::Python do
- before(:each) do
- @resource = Chef::Resource::Python.new("fakey_fakerton")
- end
+ let(:resource) { Chef::Resource::Python.new("fakey_fakerton") }
- 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)
+ it "has a resource name of :python" do
+ expect(resource.resource_name).to eql(:python)
end
- it "should have a resource name of :python" do
- expect(@resource.resource_name).to eql(:python)
+ it "has an interpreter of python" do
+ expect(resource.interpreter).to eql("python")
end
- it "should have an interpreter of python" do
- expect(@resource.interpreter).to eql("python")
+ it "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
+ end
end
diff --git a/spec/unit/resource/reboot_spec.rb b/spec/unit/resource/reboot_spec.rb
new file mode 100644
index 0000000000..6f0e4a1a1e
--- /dev/null
+++ b/spec/unit/resource/reboot_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright (c) 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 "sets the default action as :nothing" do
+ expect(resource.action).to eql([:nothing])
+ end
+
+ it "supports :cancel, :reboot_now, :request_reboot actions" do
+ expect { resource.action :cancel }.not_to raise_error
+ expect { resource.action :reboot_now }.not_to raise_error
+ expect { resource.action :request_reboot }.not_to raise_error
+ 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..86bd41ef92 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,181 +19,197 @@
require "spec_helper"
describe Chef::Resource::RegistryKey, "initialize" do
- before(:each) do
- @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ let(:resource) { Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') }
+
+ it "sets the resource_name to :registry_key" do
+ expect(resource.resource_name).to eql(:registry_key)
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)
+ it "the key property is the name_property" do
+ expect(resource.key).to eql('HKCU\Software\Raxicoricofallapatorius')
end
- it "should set the resource_name to :registry_key" do
- expect(@resource.resource_name).to eql(:registry_key)
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
end
- it "should set the key equal to the argument to initialize" do
- expect(@resource.key).to eql('HKCU\Software\Raxicoricofallapatorius')
+ it "supports :create, :create_if_missing, :delete, :delete_key actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :delete_key }.not_to raise_error
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
+
+ it "does not raise an exception if keys are in string format" do
+ expect { resource.values([ { "name" => "123", "type" => :string, "data" => "carmen" } ]) }.to_not raise_error
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(%i{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)
+ %i{i386 x86_64 machine}.each do |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(%i{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
- key_values = [ { :name => "poosh", :type => :binary, :data => 255.chr * 1 } ]
- @resource.values(key_values)
- expect(@resource.unscrubbed_values).to eql(key_values)
+ 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)
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..62851dd306 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,78 +20,99 @@ require "spec_helper"
describe Chef::Resource::RemoteDirectory do
- before(:each) do
- @resource = Chef::Resource::RemoteDirectory.new("/etc/dunk")
+ let(:resource) { Chef::Resource::RemoteDirectory.new("/etc/dunk") }
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("/etc/dunk")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :create_if_missing, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+
+ it "accepts a String for the cookbook property" do
+ resource.cookbook "foo"
+ expect(resource.cookbook).to eql("foo")
+ end
+
+ it "accepts a String for the source property" do
+ resource.source "foo"
+ expect(resource.source).to eql("foo")
end
- 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 "uses the basename of the pat property as the default value of the source property" do
+ resource.path "/foo/bar"
+ expect(resource.source).to eql("bar")
end
- it "should set the path to the first argument to new" do
- expect(@resource.path).to eql("/etc/dunk")
+ it "files_backup property defaults to 5" do
+ expect(resource.files_backup).to eql(5)
end
- it "should accept a string for the remote directory source" do
- @resource.source "foo"
- expect(@resource.source).to eql("foo")
+ it "accepts an Integer for the files_backup property" do
+ resource.files_backup 1
+ expect(resource.files_backup).to eql(1)
end
- it "should have the basename of the remote directory resource as the default source" do
- expect(@resource.source).to eql("dunk")
+ it "accepts false for the files_backup property" do
+ resource.files_backup false
+ expect(resource.files_backup).to eql(false)
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 3 or 4 digits for the files_mode property" 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 false for the remote files backup" do
- @resource.files_backup false
- expect(@resource.files_backup).to eql(false)
+ it "accepts a String or number for the files_group property" 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 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_owner property" 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 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 "overwrite property has the default value of true" do
+ expect(resource.overwrite).to be true
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 "recursive property has the default value of true" do
+ expect(resource.recursive).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/")
+ it "returns the path as its identity" do
+ 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 5fac457ebf..94254750d4 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,173 +21,186 @@ require "spec_helper"
describe Chef::Resource::RemoteFile do
- before(:each) do
- @resource = Chef::Resource::RemoteFile.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::RemoteFile.new("fakey_fakerton") }
+
+ describe "name_property" do
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+ end
+
+ describe "Actions" do
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :create_if_missing, :delete, :touch actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :touch }.not_to raise_error
+ end
end
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 evaluator (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
+ 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/" })
+ 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
+ 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/" }])
+ 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 do
- @resource.source(Chef::DelayedEvaluator.new { "not-a-uri" })
- @resource.source
+ 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
+ it "disables etags individually" do
+ 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
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")
+ if ChefUtils.windows?
+ 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
- if Chef::Platform.windows?
+ state = resource.state_for_resource_reporter
+ if ChefUtils.windows?
puts state
- expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }])
- expect(state[:deny_rights]).to eq([{ :permissions => :full_control, :principals => "Clumsy_Sam" }])
+ expect(state[:rights]).to eq([{ permissions: :read, principals: "Everyone" }])
+ expect(state[:deny_rights]).to eq([{ permissions: :full_control, principals: "Clumsy_Sam" }])
else
expect(state[:group]).to eq("pokemon")
expect(state[:mode]).to eq("0664")
@@ -197,10 +210,10 @@ describe Chef::Resource::RemoteFile do
end
it "returns the path as its identity" do
- if Chef::Platform.windows?
- expect(@resource.identity).to eq("C:/temp/origin/file.txt")
+ if ChefUtils.windows?
+ 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/resource_notification_spec.rb b/spec/unit/resource/resource_notification_spec.rb
index e141e01fcf..cbe6257a2f 100644
--- a/spec/unit/resource/resource_notification_spec.rb
+++ b/spec/unit/resource/resource_notification_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tyler Ball (<tball@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -65,7 +65,7 @@ describe Chef::Resource::Notification do
end
it "resolves a lazy reference to a resource" do
- notification.resource = { :cat => "keyboard_cat" }
+ notification.resource = { cat: "keyboard_cat" }
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
@@ -78,7 +78,7 @@ describe Chef::Resource::Notification do
it "resolves a lazy reference to its notifying resource" do
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
notification.resource = @keyboard_cat
- notification.notifying_resource = { :cat => "long_cat" }
+ notification.notifying_resource = { cat: "long_cat" }
@long_cat = Chef::Resource::Cat.new("long_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @long_cat
@@ -87,11 +87,11 @@ describe Chef::Resource::Notification do
end
it "resolves lazy references to both its resource and its notifying resource" do
- notification.resource = { :cat => "keyboard_cat" }
+ notification.resource = { cat: "keyboard_cat" }
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
- notification.notifying_resource = { :cat => "long_cat" }
+ notification.notifying_resource = { cat: "long_cat" }
@long_cat = Chef::Resource::Cat.new("long_cat")
@resource_collection << @long_cat
notification.resolve_resource_reference(@resource_collection)
@@ -100,7 +100,7 @@ describe Chef::Resource::Notification do
end
it "raises a RuntimeError if you try to reference multiple resources" do
- notification.resource = { :cat => %w{keyboard_cat cheez_cat} }
+ notification.resource = { cat: %w{keyboard_cat cheez_cat} }
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@@ -112,7 +112,7 @@ describe Chef::Resource::Notification do
end
it "raises a RuntimeError if you try to reference multiple notifying resources" do
- notification.notifying_resource = { :cat => %w{long_cat cheez_cat} }
+ notification.notifying_resource = { cat: %w{long_cat cheez_cat} }
@long_cat = Chef::Resource::Cat.new("long_cat")
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@@ -124,7 +124,7 @@ describe Chef::Resource::Notification do
end
it "raises a RuntimeError if it can't find a resource in the resource collection when resolving a lazy reference" do
- notification.resource = { :cat => "keyboard_cat" }
+ notification.resource = { cat: "keyboard_cat" }
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @cheez_cat
@@ -134,7 +134,7 @@ describe Chef::Resource::Notification do
end
it "raises a RuntimeError if it can't find a notifying resource in the resource collection when resolving a lazy reference" do
- notification.notifying_resource = { :cat => "long_cat" }
+ notification.notifying_resource = { cat: "long_cat" }
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @cheez_cat
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..5502fd9e2d
--- /dev/null
+++ b/spec/unit/resource/rhsm_errata_level_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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 "the errata_level property is the name_property" do
+ expect(resource.errata_level).to eql("moderate")
+ 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
+ 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..c470663f11
--- /dev/null
+++ b/spec/unit/resource/rhsm_errata_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "has a resource name of :rhsm_errata" do
+ expect(resource.resource_name).to eql(:rhsm_errata)
+ end
+
+ it "the errata_id property is the name_property" do
+ expect(resource.errata_id).to eql("fakey_fakerton")
+ 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
+ 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..7ce76ad908
--- /dev/null
+++ b/spec/unit/resource/rhsm_register_spec.rb
@@ -0,0 +1,266 @@
+#
+# Copyright:: Copyright (c) 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 "sets the default action as :register" do
+ expect(resource.action).to eql([:register])
+ end
+
+ it "supports :register, :unregister actions" do
+ expect { resource.action :register }.not_to raise_error
+ expect { resource.action :unregister }.not_to raise_error
+ 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
+ context "when the output contains katello-ca-consumer" do
+ let(:with_katello) { double("shell_out", stdout: <<~RPM) }
+ libevent-2.0.21-4.el7.x86_64
+ gettext-libs-0.19.8.1-3.el7.x86_64
+ yum-metadata-parser-1.1.4-10.el7.x86_64
+ pyliblzma-0.5.3-11.el7.x86_64
+ python-IPy-0.75-6.el7.noarch
+ grubby-8.28-26.el7.x86_64
+ fipscheck-lib-1.4.1-6.el7.x86_64
+ centos-logos-70.0.6-3.el7.centos.noarch
+ nss-tools-3.44.0-7.el7_7.x86_64
+ katello-ca-consumer-somehostname-1.0-1.el7.x86_64
+ rpm-4.11.3-43.el7.x86_64
+ gpgme-1.3.2-5.el7.x86_64
+ libnfsidmap-0.25-19.el7.x86_64
+ RPM
+
+ it "returns true" do
+ allow(provider).to receive(:shell_out).and_return(with_katello)
+ expect(provider.katello_cert_rpm_installed?).to eq(true)
+ end
+ end
+
+ context "when the output does not contain katello-ca-consumer" do
+ let(:without_katello) { double("shell_out", stdout: "") }
+
+ it "returns false" do
+ allow(provider).to receive(:shell_out).and_return(without_katello)
+ 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 a system_name is provided" do
+ it "returns a command containing the system name" do
+ allow(resource).to receive(:organization).and_return("myorg")
+ allow(resource).to receive(:system_name).and_return("myname")
+ expect(provider.register_command).to match("--name=myname")
+ end
+ end
+
+ context "when a system_name is not provided" do
+ it "returns a command containing the system name" do
+ allow(resource).to receive(:organization).and_return("myorg")
+ allow(resource).to receive(:system_name).and_return(nil)
+ expect(provider.register_command).not_to match("--name")
+ 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 a system_name is provided" do
+ it "returns a command containing the system name" do
+ allow(resource).to receive(:system_name).and_return("myname")
+ expect(provider.register_command).to match("--name=myname")
+ end
+ end
+
+ context "when a system_name is not provided" do
+ it "returns a command containing the system name" do
+ allow(resource).to receive(:system_name).and_return(nil)
+ expect(provider.register_command).not_to match("--name")
+ 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 "#ca_consumer_package_source" do
+ let(:satellite_host) { "sat-host" }
+
+ before do
+ resource.satellite_host = satellite_host
+ end
+
+ context "when https_for_ca_consumer is true" do
+ before { resource.https_for_ca_consumer true }
+
+ it "returns url with https" do
+ expect(provider.ca_consumer_package_source).to eq("https://#{satellite_host}/pub/katello-ca-consumer-latest.noarch.rpm")
+ end
+ end
+
+ context "when https_for_ca_consumer is false" do
+ before { resource.https_for_ca_consumer false }
+
+ it "returns url with http" do
+ expect(provider.ca_consumer_package_source).to eq("http://#{satellite_host}/pub/katello-ca-consumer-latest.noarch.rpm")
+ end
+ end
+ end
+
+ describe "#registered_with_rhsm?" do
+ context "when the status is Unknown" do
+ let(:unknown_status) { double("shell_out", stdout: "Overall Status: Unknown") }
+
+ it "returns false" do
+ allow(provider).to receive(:shell_out).and_return(unknown_status)
+ expect(provider.registered_with_rhsm?).to eq(false)
+ end
+ end
+
+ context "when the status is anything else" do
+ let(:known_status) { double("shell_out", stdout: "Overall Status: Insufficient") }
+
+ it "returns true" do
+ allow(provider).to receive(:shell_out).and_return(known_status)
+ 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..b26280fb4d
--- /dev/null
+++ b/spec/unit/resource/rhsm_repo_spec.rb
@@ -0,0 +1,70 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+ 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 "the repo_name property is the name_property" do
+ expect(resource.repo_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "supports :disable, :enable actions" do
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ 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
+
+ context "when user pass wildcard" do
+ it "returns true" do
+ expect(provider.repo_enabled?("*")).to eq(true)
+ 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..7f81cec497
--- /dev/null
+++ b/spec/unit/resource/rhsm_subscription_spec.rb
@@ -0,0 +1,98 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+ 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 "the pool_id property is the name_property" do
+ expect(resource.pool_id).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :attach" do
+ expect(resource.action).to eql([:attach])
+ end
+
+ it "supports :attach, :remove actions" do
+ expect { resource.action :attach }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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..dd79d04024 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 "the target property is the name_property" do
+ expect(resource.target).to eql("10.0.0.10")
end
- it "should have a default action of 'add'" do
- expect(@resource.action).to eql([:add])
+ it "sets the default action as :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 "supports :add, :delete actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
end
- it "should use the object name as the target by default" do
- expect(@resource.target).to eql("10.0.0.10")
+ 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 netmask" do
- @resource.netmask "255.255.255.0"
- expect(@resource.netmask).to eql("255.255.255.0")
+ 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 gateway" do
- @resource.gateway "10.0.0.1"
- expect(@resource.gateway).to eql("10.0.0.1")
+ 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 metric" do
- @resource.metric 10
- expect(@resource.metric).to eql(10)
+ 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 device" do
- @resource.device "eth0"
- expect(@resource.device).to eql("eth0")
+ it "allows you to specify the route type as a symbol" do
+ resource.route_type :host
+ expect(resource.route_type).to eql(:host)
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 as a string" 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..eeeddd9bee 100644
--- a/spec/unit/resource/rpm_package_spec.rb
+++ b/spec/unit/resource/rpm_package_spec.rb
@@ -34,13 +34,29 @@ 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 "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
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..8410077dae 100644
--- a/spec/unit/resource/ruby_block_spec.rb
+++ b/spec/unit/resource/ruby_block_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,41 +21,38 @@ require "spec_helper"
describe Chef::Resource::RubyBlock do
- before(:each) do
- @resource = Chef::Resource::RubyBlock.new("fakey_fakerton")
+ let(:resource) { Chef::Resource::RubyBlock.new("fakey_fakerton") }
+
+ it "has a resource name of :ruby_block" do
+ expect(resource.resource_name).to eql(:ruby_block)
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)
+ it "the block_name property is the name_property" do
+ expect(resource.block_name).to eql("fakey_fakerton")
end
- it "should have a default action of 'run'" do
- expect(@resource.action).to eql([:run])
+ it "sets the default action as :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 "supports :create, :run actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :run }.not_to raise_error
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])
- 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..1c877bbbd6 100644
--- a/spec/unit/resource/ruby_spec.rb
+++ b/spec/unit/resource/ruby_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,22 +19,21 @@
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 "sets the default action as :run" do
+ expect(resource.action).to eql([:run])
end
- it "should have a resource name of :ruby" do
- expect(@resource.resource_name).to eql(:ruby)
+ it "supports :run action" do
+ expect { resource.action :run }.not_to raise_error
end
- it "should have an interpreter of ruby" do
- expect(@resource.interpreter).to eql("ruby")
+ it "has an interpreter of ruby" do
+ expect(resource.interpreter).to eql("ruby")
end
-
end
diff --git a/spec/unit/resource/scm/git_spec.rb b/spec/unit/resource/scm/git_spec.rb
new file mode 100644
index 0000000000..706b7c370b
--- /dev/null
+++ b/spec/unit/resource/scm/git_spec.rb
@@ -0,0 +1,110 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 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_relative "scm"
+
+describe Chef::Resource::Git do
+
+ static_provider_resolution(
+ resource: Chef::Resource::Git,
+ provider: Chef::Provider::Git,
+ name: :git,
+ action: :sync
+ )
+
+ let(:resource) { Chef::Resource::Git.new("fakey_fakerton") }
+
+ it_behaves_like "an SCM resource"
+
+ 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)
+ end
+
+ it "defaults to nil depth for a full clone" do
+ 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)
+ end
+
+ it "defaults to not enabling submodules" do
+ 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)
+ end
+
+ it "defaults to enabling checkout" do
+ 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)
+ end
+
+ it "defaults to ``origin'' for the remote" do
+ 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")
+ end
+
+ it "defaults to nil for the ssh wrapper" do
+ expect(resource.ssh_wrapper).to be_nil
+ end
+
+ it "uses aliases revision as branch" do
+ resource.branch "HEAD"
+ expect(resource.revision).to eql("HEAD")
+ end
+
+ it "aliases revision as reference" do
+ resource.reference "v1.0 tag"
+ expect(resource.revision).to eql("v1.0 tag")
+ end
+
+ it "the destination property is the name_property" do
+ expect(resource.destination).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :sync" do
+ expect(resource.action).to eql([:sync])
+ end
+
+ it "supports :checkout, :diff, :export, :log, :sync actions" do
+ expect { resource.action :checkout }.not_to raise_error
+ expect { resource.action :diff }.not_to raise_error
+ expect { resource.action :export }.not_to raise_error
+ expect { resource.action :log }.not_to raise_error
+ expect { resource.action :sync }.not_to raise_error
+ end
+
+end
diff --git a/spec/unit/resource/scm/scm.rb b/spec/unit/resource/scm/scm.rb
new file mode 100644
index 0000000000..28c3f73136
--- /dev/null
+++ b/spec/unit/resource/scm/scm.rb
@@ -0,0 +1,122 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 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"
+
+shared_examples_for "an SCM resource" do
+ it "the destination property is the name_property" do
+ expect(resource.destination).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :sync" do
+ expect(resource.action).to eql([:sync])
+ end
+
+ it "supports :checkout, :diff, :export, :log, :sync actions" do
+ expect { resource.action :checkout }.not_to raise_error
+ expect { resource.action :diff }.not_to raise_error
+ expect { resource.action :export }.not_to raise_error
+ expect { resource.action :log }.not_to raise_error
+ expect { resource.action :sync }.not_to raise_error
+ 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")
+ 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")
+ end
+
+ it "takes a string for the revision" do
+ resource.revision "abcdef"
+ expect(resource.revision).to eql("abcdef")
+ end
+
+ it "defaults to the ``HEAD'' revision" do
+ 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")
+ end
+
+ it "also takes an integer for the user to run as" do
+ 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")
+ end
+
+ it "also takes an integer for the group to run as" do
+ resource.group 23
+ expect(resource.group).to eq(23)
+ end
+
+ it "defaults to nil for the environment" do
+ expect(resource.environment).to be_nil
+ end
+
+ describe "when it has a timeout property" 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 property" do
+ 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")
+ end
+
+ it "describes its state" do
+ 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")
+ end
+ end
+
+ describe "when it has a environment property" do
+ let(:test_environment) { { "CHEF_ENV" => "/tmp" } }
+ before { resource.environment(test_environment) }
+ it "stores this environment" do
+ expect(resource.environment).to eq(test_environment)
+ end
+ end
+end
diff --git a/spec/unit/resource/scm/subversion_spec.rb b/spec/unit/resource/scm/subversion_spec.rb
new file mode 100644
index 0000000000..394ed6be9f
--- /dev/null
+++ b/spec/unit/resource/scm/subversion_spec.rb
@@ -0,0 +1,90 @@
+#
+# Author:: Daniel DeLeo (<dan@kallistec.com>)
+# Copyright:: Copyright (c) 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_relative "scm"
+
+describe Chef::Resource::Subversion do
+ static_provider_resolution(
+ resource: Chef::Resource::Subversion,
+ provider: Chef::Provider::Subversion,
+ name: :subversion,
+ action: :install
+ )
+
+ let(:resource) { Chef::Resource::Subversion.new("fakey_fakerton") }
+
+ it_behaves_like "an SCM resource"
+
+ it "the destination property is the name_property" do
+ expect(resource.destination).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :sync" do
+ expect(resource.action).to eql([:sync])
+ end
+
+ it "supports :checkout, :diff, :export, :force_export, :log, :sync actions" do
+ expect { resource.action :checkout }.not_to raise_error
+ expect { resource.action :diff }.not_to raise_error
+ expect { resource.action :export }.not_to raise_error
+ expect { resource.action :force_export }.not_to raise_error
+ expect { resource.action :log }.not_to raise_error
+ expect { resource.action :sync }.not_to raise_error
+ end
+
+ it "sets svn info arguments to --no-auth-cache by default" do
+ 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
+ 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(resource.svn_arguments).to eq("--no-auth-cache")
+ end
+
+ it "sets svn binary to nil by default" do
+ expect(resource.svn_binary).to be_nil
+ end
+
+ it "resets svn arguments to nil when given false in the setter" do
+ resource.svn_arguments(false)
+ expect(resource.svn_arguments).to be_nil
+ end
+
+ it "has a svn_arguments String property" 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 property" 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
+ 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/scm_spec.rb b/spec/unit/resource/scm_spec.rb
deleted file mode 100644
index f39334348e..0000000000
--- a/spec/unit/resource/scm_spec.rb
+++ /dev/null
@@ -1,193 +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.
-#
-
-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
-
- 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)
- 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")
- 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")
- end
-
- it "takes a string for the revision" do
- @resource.revision "abcdef"
- expect(@resource.revision).to eql("abcdef")
- end
-
- it "defaults to the ``HEAD'' revision" do
- 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")
- end
-
- it "also takes an integer for the user to run as" do
- @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")
- end
-
- it "also takes an integer for the group to run as" do
- @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")
- 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")
- 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)
- end
-
- it "defaults to nil depth for a full clone" do
- 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)
- end
-
- it "defaults to not enabling submodules" do
- 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)
- end
-
- it "defaults to enabling checkout" do
- 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)
- end
-
- it "defaults to ``origin'' for the remote" do
- 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")
- end
-
- it "defaults to nil for the ssh wrapper" do
- expect(@resource.ssh_wrapper).to be_nil
- end
-
- it "defaults to nil for the environment" do
- expect(@resource.environment).to be_nil
- 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 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")
- end
-
- it "describes its state" do
- state = @resource.state
- expect(state[:revision]).to eq("1.2.3")
- end
-
- it "returns the destination as its identity" do
- 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) }
- it "stores this environment" do
- 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..010e9b4b09 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,10 +21,23 @@ require "spec_helper"
describe Chef::Resource::Script do
let(:resource_instance_name) { "fakey_fakerton" }
- let(:script_resource) { Chef::Resource::Script.new(resource_instance_name) }
let(:resource_name) { :script }
- it "should accept a string for the interpreter" do
+ let(:script_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, nil, nil)
+
+ Chef::Resource::Script.new(resource_instance_name, run_context)
+ end
+
+ it "sets the default action as :run" do
+ expect(script_resource.action).to eql([:run])
+ end
+
+ it "supports :run action" do
+ expect { script_resource.action :run }.not_to raise_error
+ end
+
+ 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 7aadc55532..24155e3b8f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,160 +20,184 @@
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 property as the name_property" 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 default action as :nothing" do
+ expect(resource.action).to eql([:nothing])
end
- it "should set the service_name to the first argument to new" do
- expect(@resource.service_name).to eql("chef")
+ it "supports :disable, :enable, :mask, :reload, :restart, :start, :stop, :unmask actions" do
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :mask }.not_to raise_error
+ expect { resource.action :reload }.not_to raise_error
+ expect { resource.action :restart }.not_to raise_error
+ expect { resource.action :start }.not_to raise_error
+ expect { resource.action :stop }.not_to raise_error
+ expect { resource.action :unmask }.not_to raise_error
end
- it "should set the pattern to be the service name by default" do
- expect(@resource.pattern).to eql("chef")
+ it "Uses the service_name property as the default for the pattern property" do
+ resource.service_name "something"
+ expect(resource.pattern).to eql("something")
end
- it "should accept a string for the service name" do
- @resource.service_name "something"
- expect(@resource.service_name).to eql("something")
+ it "accepts a String for the service name property" do
+ resource.service_name "something"
+ expect(resource.service_name).to eql("something")
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 pattern" do
+ resource.pattern ".*"
+ expect(resource.pattern).to eql(".*")
end
- it "should not accept a regexp for the service pattern" do
+ it "does not accept a regexp for the service pattern" do
expect do
- @resource.pattern /.*/
+ resource.pattern(/.*/)
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 user property" do
+ resource.user "fakey_fakerton"
+ expect(resource.user).to eql("fakey_fakerton")
end
- it "should not accept a regexp for the service start command" do
- expect do
- @resource.start_command /.*/
- end.to raise_error(ArgumentError)
+ it "accepts an Array for the run_levels property" do
+ resource.run_levels ["foo"]
+ expect(resource.run_levels).to eql(["foo"])
+ end
+
+ it "accepts a Hash for the parameters property" do
+ param_hash = { something: nil }
+ resource.parameters param_hash
+ expect(resource.parameters).to eql(param_hash)
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 init_command property" 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 stop command" do
+ it "does not accept a regexp for the init_command property" do
expect do
- @resource.stop_command /.*/
+ resource.init_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 an array for options property" do
+ resource.options ["-r", "-s"]
+ expect(resource.options).to eql(["-r", "-s"])
end
- it "should not accept a regexp for the service status command" do
- expect do
- @resource.status_command /.*/
- end.to raise_error(ArgumentError)
+ it "accepts a String for options property" do
+ resource.options "-r"
+ expect(resource.options).to eql(["-r"])
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 with multiple flags for options property" do
+ resource.options "-r -s"
+ expect(resource.options).to eql(["-r", "-s"])
end
- it "should not accept a regexp for the service restart command" do
+ it "does not accept a boolean for options property" do
expect do
- @resource.restart_command /.*/
+ resource.options true
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")
+ %w{restart_command start_command stop_command status_command reload_command}.each do |prop|
+ it "accepts a String for the #{prop} property" do
+ resource.send(prop, "service foo bar")
+ expect(resource.send(prop)).to eql("service foo bar")
+ end
+
+ it "accepts false for #{prop} property" do
+ resource.send(prop, false)
+ expect(resource.send(prop)).to eql(false)
+ end
+
+ it "does not accept a regexp for the #{prop} property" do
+ expect { resource.send(prop, /.*/) }.to raise_error(ArgumentError)
+ end
end
- it "should not accept a regexp for the service reload command" do
- expect do
- @resource.reload_command /.*/
- end.to raise_error(ArgumentError)
+ it "accepts a String for priority property" do
+ resource.priority "1"
+ expect(resource.priority).to eql("1")
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 Integer for priority property" do
+ resource.priority 1
+ expect(resource.priority).to eql(1)
end
- it "should not accept a regexp for the service init command" do
- expect do
- @resource.init_command /.*/
- end.to raise_error(ArgumentError)
+ it "accepts an Integer for timeout property" do
+ resource.timeout 1
+ expect(resource.timeout).to eql(1)
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)
- end
+ it "defaults the timeout property to 900 (seconds)" do
+ expect(resource.timeout).to eql(900)
+ end
- it "should accept false for #{attrib}" do
- @resource.send(attrib, false)
- expect(@resource.send(attrib)).to eql(false)
+ %w{enabled running}.each do |prop|
+ it "accepts true for #{prop} property" do
+ resource.send(prop, true)
+ expect(resource.send(prop)).to eql(true)
end
- it "should not accept a string for #{attrib}" do
- expect { @resource.send(attrib, "poop") }.to raise_error(ArgumentError)
+ it "accepts false for #{prop} property" do
+ resource.send(prop, false)
+ expect(resource.send(prop)).to eql(false)
end
- it "should default all the feature support to nil" do
- support_hash = { :status => nil, :restart => nil, :reload => nil }
- expect(@resource.supports).to eq(support_hash)
+ it "does not accept a String for #{prop} property" do
+ expect { resource.send(prop, "poop") }.to raise_error(ArgumentError)
end
+ end
- it "should allow 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)
- end
+ it "defaults all the feature support to nil" do
+ support_hash = { status: nil, restart: nil, reload: nil }
+ 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)
- end
+ it "allows you to set what features this resource supports as an array" do
+ support_array = %i{status restart}
+ support_hash = { status: true, restart: true }
+ resource.supports(support_array)
+ expect(resource.supports).to eq(support_hash)
+ end
+
+ 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
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")
+ it "returns the service_name property as its identity" do
+ 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..bd9a7dd7ec 100644
--- a/spec/unit/resource/smartos_package_spec.rb
+++ b/spec/unit/resource/smartos_package_spec.rb
@@ -1,6 +1,7 @@
#
# Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
# Copyright:: Copyright 2010-2016, Thomas Bishop
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +18,8 @@
#
require "spec_helper"
-require "support/shared/unit/resource/static_provider_resolution"
-describe Chef::Resource::SmartosPackage, "initialize" do
+describe Chef::Resource::SmartosPackage do
static_provider_resolution(
resource: Chef::Resource::SmartosPackage,
@@ -30,4 +30,23 @@ describe Chef::Resource::SmartosPackage, "initialize" do
platform_family: "smartos"
)
+ let(:resource) { Chef::Resource::SmartosPackage.new("fakey_fakerton") }
+
+ it "sets the package_name to the name provided" do
+ expect(resource.package_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
end
diff --git a/spec/unit/resource/snap_package_spec.rb b/spec/unit/resource/snap_package_spec.rb
new file mode 100644
index 0000000000..c94831e344
--- /dev/null
+++ b/spec/unit/resource/snap_package_spec.rb
@@ -0,0 +1,60 @@
+#
+# Author:: S.Cavallo (<smcavallo@hotmail.com>)
+# Copyright:: Copyright (c) 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/snap_package"
+require "chef/provider/package/snap"
+require "support/shared/unit/resource/static_provider_resolution"
+
+describe Chef::Resource::SnapPackage, "initialize" do
+
+ static_provider_resolution(
+ resource: Chef::Resource::SnapPackage,
+ provider: Chef::Provider::Package::Snap,
+ name: :snap_package,
+ action: :install,
+ os: "linux"
+ )
+
+ let(:resource) { Chef::Resource::SnapPackage.new("foo") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ it "channel defaults to stable" do
+ expect(resource.channel).to eql("stable")
+ end
+
+ it "supports all channel values" do
+ expect { resource.channel "stable" }.not_to raise_error
+ expect { resource.channel "edge" }.not_to raise_error
+ expect { resource.channel "beta" }.not_to raise_error
+ expect { resource.channel "candidate" }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/solaris_package_spec.rb b/spec/unit/resource/solaris_package_spec.rb
index 1f1ef4da30..e663ab5684 100644
--- a/spec/unit/resource/solaris_package_spec.rb
+++ b/spec/unit/resource/solaris_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,22 +21,32 @@ require "support/shared/unit/resource/static_provider_resolution"
describe Chef::Resource::SolarisPackage, "initialize" do
- %w{solaris2 nexentacore}.each do |platform_family|
- static_provider_resolution(
- resource: Chef::Resource::SolarisPackage,
- provider: Chef::Provider::Package::Solaris,
- name: :solaris_package,
- action: :install,
- os: "solaris2",
- platform_family: platform_family
- )
+ static_provider_resolution(
+ resource: Chef::Resource::SolarisPackage,
+ provider: Chef::Provider::Package::Solaris,
+ name: :solaris_package,
+ action: :install,
+ os: "solaris2",
+ platform_family: "solaris2"
+ )
+
+ let(:resource) { Chef::Resource::SolarisPackage.new("foo") }
+
+ it "sets the package_name to the name provided" do
+ expect(resource.package_name).to eql("foo")
end
- before(:each) do
- @resource = Chef::Resource::SolarisPackage.new("foo")
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
end
- it "should set the package_name to the name provided" do
- expect(@resource.package_name).to eql("foo")
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
end
end
diff --git a/spec/unit/resource/ssh_known_hosts_entry_spec.rb b/spec/unit/resource/ssh_known_hosts_entry_spec.rb
new file mode 100644
index 0000000000..6471231906
--- /dev/null
+++ b/spec/unit/resource/ssh_known_hosts_entry_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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::SshKnownHostsEntry do
+ let(:node) { Chef::Node.new }
+ let(:run_context) do
+ node.automatic[:root_group] = "superduper"
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+ let(:resource) { Chef::Resource::SshKnownHostsEntry.new("example.com", run_context) }
+
+ it "sets resource name as :ssh_known_hosts_entry" do
+ expect(resource.resource_name).to eql(:ssh_known_hosts_entry)
+ end
+
+ it "sets group property to node['root_group'] by default" do
+ expect(resource.group).to eql("superduper")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "sets the host property as its name property" do
+ expect(resource.host).to eql("example.com")
+ end
+
+ it "supports :create and :flush actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :flush }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/subversion_spec.rb b/spec/unit/resource/subversion_spec.rb
deleted file mode 100644
index a2901bf53b..0000000000
--- a/spec/unit/resource/subversion_spec.rb
+++ /dev/null
@@ -1,71 +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::Subversion do
-
- static_provider_resolution(
- resource: Chef::Resource::Subversion,
- provider: Chef::Provider::Subversion,
- name: :subversion,
- action: :install
- )
-
- before do
- @svn = Chef::Resource::Subversion.new("ohai, svn project!")
- end
-
- 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)
- end
-
- it "allows the force_export action" do
- expect(@svn.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")
- 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
- end
-
- it "sets svn arguments to --no-auth-cache by default" do
- expect(@svn.svn_arguments).to eq("--no-auth-cache")
- end
-
- it "sets svn binary to nil by default" do
- expect(@svn.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
- 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
- end
-end
diff --git a/spec/unit/resource/sudo_spec.rb b/spec/unit/resource/sudo_spec.rb
new file mode 100644
index 0000000000..41ce883357
--- /dev/null
+++ b/spec/unit/resource/sudo_spec.rb
@@ -0,0 +1,99 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton", run_context) }
+
+ it "has a resource name of :sudo" do
+ expect(resource.resource_name).to eql(:sudo)
+ end
+
+ it "the filename property is the name_property" do
+ expect(resource.filename).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :delete, :install, :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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..dfc637675c
--- /dev/null
+++ b/spec/unit/resource/swap_file_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :swap_file" do
+ expect(resource.resource_name).to eql(:swap_file)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/sysctl_spec.rb b/spec/unit/resource/sysctl_spec.rb
new file mode 100644
index 0000000000..47556f5ee8
--- /dev/null
+++ b/spec/unit/resource/sysctl_spec.rb
@@ -0,0 +1,76 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+ let(:provider) { resource.provider_for_action(:create) }
+
+ it "sets resource name as :sysctl" do
+ expect(resource.resource_name).to eql(:sysctl)
+ end
+
+ it "the key property is the name_property" do
+ expect(resource.key).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :apply" do
+ expect(resource.action).to eql([:apply])
+ end
+
+ it "supports :apply, :remove actions" do
+ expect { resource.action :apply }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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
+
+ context "#contruct_sysctl_content" do
+ before do
+ resource.key("foo")
+ resource.value("bar")
+ end
+
+ context "when comment is a String" do
+ it "Returns content for use with a file resource" do
+ resource.comment("This sets foo / bar on our system")
+ expect(provider.contruct_sysctl_content).to eql("# This sets foo / bar on our system\nfoo = bar")
+ end
+ end
+
+ context "when comment is an Array" do
+ it "Returns content for use with a file resource" do
+ resource.comment(["This sets foo / bar on our system", "We need for baz"])
+ expect(provider.contruct_sysctl_content).to eql("# This sets foo / bar on our system\n# We need for baz\nfoo = bar")
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/systemd_unit_spec.rb b/spec/unit/resource/systemd_unit_spec.rb
index 7e46872525..f05418eb34 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,16 +19,13 @@
require "spec_helper"
describe Chef::Resource::SystemdUnit do
- before(:each) do
- @resource = Chef::Resource::SystemdUnit.new("sysstat-collect.timer")
- end
-
- 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(:resource) { Chef::Resource::SystemdUnit.new("sysstat-collect.timer") }
+ let(:unit_content_string) { "[Unit]\nDescription = Run system activity accounting tool every 10 minutes\nDocumentation = foo\nDocumentation = bar\n\n[Timer]\nOnCalendar = *:00/10\n\n[Install]\nWantedBy = sysstat.service\n" }
let(:unit_content_hash) do
{
"Unit" => {
"Description" => "Run system activity accounting tool every 10 minutes",
+ "Documentation" => %w{foo bar},
},
"Timer" => {
"OnCalendar" => "*:00/10",
@@ -39,77 +36,77 @@ 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 "the unit_name property is the name_property" do
+ expect(resource.unit_name).to eql("sysstat-collect.timer")
end
- it "has a default action of nothing" do
- expect(@resource.action).to eql([:nothing])
+ it "sets the default action as :nothing" do
+ 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)
+ it "supports :create, :delete, :disable, :enable, :mask, :preset, :reenable, :reload, :reload_or_restart, :reload_or_try_restart, :restart, :revert, :start, :stop, :try_restart, :unmask actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :mask }.not_to raise_error
+ expect { resource.action :preset }.not_to raise_error
+ expect { resource.action :reenable }.not_to raise_error
+ expect { resource.action :reload }.not_to raise_error
+ expect { resource.action :reload_or_restart }.not_to raise_error
+ expect { resource.action :reload_or_try_restart }.not_to raise_error
+ expect { resource.action :restart }.not_to raise_error
+ expect { resource.action :revert }.not_to raise_error
+ expect { resource.action :start }.not_to raise_error
+ expect { resource.action :stop }.not_to raise_error
+ expect { resource.action :try_restart }.not_to raise_error
+ expect { resource.action :unmask }.not_to raise_error
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 +115,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..1ad3f5004f 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,76 +19,90 @@
require "spec_helper"
describe Chef::Resource::Template do
+ let(:resource) { Chef::Resource::Template.new("fakey_fakerton") }
- before(:each) do
- @resource = Chef::Resource::Template.new("fakey_fakerton")
+ describe "initialize" do
+ it "is a subclass of Chef::Resource::File" do
+ expect(resource).to be_a_kind_of(Chef::Resource::File)
+ end
end
- 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)
+ describe "name" do
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+ end
+
+ describe "Actions" do
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :create_if_missing, :delete, :touch actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :create_if_missing }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :touch }.not_to raise_error
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")
@@ -97,12 +111,12 @@ describe Chef::Resource::Template do
end
context "on windows", :windows_only do
- # according to Chef::Resource::File, windows state attributes are rights + deny_rights
+ # according to Chef::Resource::File, windows state properties are rights + deny_rights
skip "it describes its state"
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 +129,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 +147,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, &: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 +186,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/timestamped_deploy_spec.rb b/spec/unit/resource/timestamped_deploy_spec.rb
deleted file mode 100644
index 5a2dc8ae02..0000000000
--- a/spec/unit/resource/timestamped_deploy_spec.rb
+++ /dev/null
@@ -1,32 +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::Resource::TimestampedDeploy, "initialize" do
-
- static_provider_resolution(
- resource: Chef::Resource::TimestampedDeploy,
- provider: Chef::Provider::Deploy::Timestamped,
- name: :timestamped_deploy,
- action: :deploy,
- os: "linux",
- platform_family: "rhel"
- )
-
-end
diff --git a/spec/unit/resource/timezone_spec.rb b/spec/unit/resource/timezone_spec.rb
new file mode 100644
index 0000000000..9e1b7dbfbe
--- /dev/null
+++ b/spec/unit/resource/timezone_spec.rb
@@ -0,0 +1,102 @@
+#
+# Copyright:: Copyright (c) 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::Timezone do
+ let(:resource) { Chef::Resource::Timezone.new("fakey_fakerton") }
+
+ let(:shellout_tzutil) do
+ double("shell_out", stdout: "UTC\n", exitstatus: 0, error?: false)
+ end
+
+ # note: This weird indention is correct
+ let(:shellout_timedatectl) do
+ double("shell_out", exitstatus: 0, error?: false, stdout: <<-OUTPUT)
+ Local time: Tue 2020-08-18 20:55:05 UTC
+ Universal time: Tue 2020-08-18 20:55:05 UTC
+ RTC time: Tue 2020-08-18 20:55:05
+ Time zone: Etc/UTC (UTC, +0000)
+System clock synchronized: yes
+systemd-timesyncd.service active: yes
+ RTC in local TZ: no
+ OUTPUT
+ end
+
+ let(:shellout_systemsetup_fail) do
+ double("shell_out!", stdout: "You need administrator access to run this tool... exiting!", exitstatus: 0, error?: false) # yes it's a non-error exit
+ end
+
+ let(:shellout_systemsetup) do
+ double("shell_out!", stdout: "Time Zone: UTC", exitstatus: 0, error?: false)
+ end
+
+ it "sets resource name as :timezone" do
+ expect(resource.resource_name).to eql(:timezone)
+ end
+
+ it "the timezone property is the name_property" do
+ expect(resource.timezone).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports the :set action only" do
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :unset }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ describe "#current_macos_tz" do
+ context "with admin privs" do
+ it "returns the TZ" do
+ expect(resource).to receive(:shell_out!).and_return(shellout_systemsetup)
+ expect(resource.current_macos_tz).to eql("UTC")
+ end
+ end
+
+ context "without admin privs" do
+ it "returns the TZ" do
+ expect(resource).to receive(:shell_out!).and_return(shellout_systemsetup_fail)
+ expect { resource.current_macos_tz }.to raise_error(RuntimeError, "The timezone resource requires administrative privileges to run on macOS hosts!")
+ end
+ end
+ end
+
+ describe "#current_systemd_tz" do
+ it "returns the TZ" do
+ expect(resource).to receive(:shell_out).and_return(shellout_timedatectl)
+ expect(resource.current_systemd_tz).to eql("Etc/UTC")
+ end
+ end
+
+ describe "#current_windows_tz" do
+ it "returns the TZ" do
+ expect(resource).to receive(:shell_out).and_return(shellout_tzutil)
+ expect(resource.current_windows_tz).to eql("UTC")
+ end
+ end
+
+ describe "#current_rhel_tz" do
+ it "returns the TZ" do
+ allow(File).to receive(:exist?).with("/etc/sysconfig/clock").and_return true
+ expect(File).to receive(:read).with("/etc/sysconfig/clock").and_return 'ZONE="UTC"'
+ expect(resource.current_rhel_tz).to eql("UTC")
+ end
+ end
+end
diff --git a/spec/unit/resource/user/windows_user_spec.rb b/spec/unit/resource/user/windows_user_spec.rb
new file mode 100644
index 0000000000..32f8e69d99
--- /dev/null
+++ b/spec/unit/resource/user/windows_user_spec.rb
@@ -0,0 +1,36 @@
+#
+# Copyright:: Copyright (c) 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::User::WindowsUser, "#uid" do
+ let(:resource) { Chef::Resource::User::WindowsUser.new("notarealuser") }
+
+ it "allows a string" do
+ resource.uid "100"
+ expect(resource.uid).to eql(100)
+ end
+
+ it "allows an integer" do
+ resource.uid 100
+ expect(resource.uid).to eql(100)
+ end
+
+ it "does not allow a hash" do
+ expect { resource.uid({ woot: "i found it" }) }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb
index 138ffb1bfe..58c17024af 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,115 +19,103 @@
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("notarealuser") }
- it "should set the resource_name to :user" do
- expect(@resource.resource_name).to eql(:user_resource_abstract_base_class)
+ it "sets the resource_name to nil" do
+ expect(resource.resource_name).to eql(nil)
end
- it "should set the username equal to the argument to initialize" do
- expect(@resource.username).to eql("adam")
+ it "username property is the name property" do
+ expect(resource.username).to eql("notarealuser")
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)
+ %w{comment uid gid home shell password}.each do |prop|
+ it "sets #{prop} to nil" do
+ expect(resource.send(prop)).to eql(nil)
end
end
- it "should set 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)
- end
-
- it "should set supports[:non_unique] to false" do
- expect(@resource.supports[:non_unique]).to eql(false)
+ it "sets action to :create" do
+ expect(resource.action).to eql([:create])
end
- it "should set force to false" do
- expect(@resource.force).to eql(false)
+ %w{manage_home non_unique force system}.each do |prop|
+ it "sets #{prop} to false" do
+ expect(resource.send(prop)).to eql(false)
+ end
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 "group is an alias for the gid property" do
+ resource.group(1234)
+ expect(resource.gid).to eql(1234)
+ end
+
+ 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("notarealuser") }
- 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, "something")
+ expect(resource.send(attrib)).to eql("something")
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("notarealuser") }
- 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/user_ulimit_spec.rb b/spec/unit/resource/user_ulimit_spec.rb
new file mode 100644
index 0000000000..f451870ac1
--- /dev/null
+++ b/spec/unit/resource/user_ulimit_spec.rb
@@ -0,0 +1,53 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::UserUlimit do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::UserUlimit.new("fakey_fakerton", run_context) }
+
+ it "the username property is the name_property" do
+ expect(resource.username).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "coerces filename value to end in .conf" do
+ resource.filename("foo")
+ expect(resource.filename).to eql("foo.conf")
+ end
+
+ it "if username is * then the filename defaults to 00_all_limits.conf" do
+ resource.username("*")
+ expect(resource.filename).to eql("00_all_limits.conf")
+ end
+
+ it "if username is NOT * then the filename defaults to USERNAME_limits.conf" do
+ expect(resource.filename).to eql("fakey_fakerton_limits.conf")
+ end
+
+ it "supports :create and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/windows_ad_join_spec.rb b/spec/unit/resource/windows_ad_join_spec.rb
new file mode 100644
index 0000000000..d5f7d88f02
--- /dev/null
+++ b/spec/unit/resource/windows_ad_join_spec.rb
@@ -0,0 +1,55 @@
+#
+# Copyright:: Copyright (c) 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 "the domain_name property is the name_property" do
+ expect(resource.domain_name).to eql("example.com")
+ end
+
+ it "sets the default action as :join" do
+ expect(resource.action).to eql([:join])
+ end
+
+ it "supports :join action" do
+ expect { resource.action :join }.not_to raise_error
+ end
+
+ it "supports :leave action" do
+ expect { resource.action :leave }.not_to raise_error
+ end
+
+ it "only accepts FQDNs for the domain_name property" do
+ expect { resource.domain_name "example" }.to raise_error(ArgumentError)
+ end
+
+ it "accepts :immediate, :reboot_now, :request_reboot, :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 :reboot_now }.not_to raise_error
+ expect { resource.reboot :request_reboot }.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_audit_policy_spec.rb b/spec/unit/resource/windows_audit_policy_spec.rb
new file mode 100644
index 0000000000..80a92f2656
--- /dev/null
+++ b/spec/unit/resource/windows_audit_policy_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright (c) 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::WindowsAuditPolicy do
+ let(:resource) { Chef::Resource::WindowsAuditPolicy.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_audit_policy" do
+ expect(resource.resource_name).to eql(:windows_audit_policy)
+ end
+
+ it "expects crash_on_audit_fail to have a true or false value if entered" do
+ expect { resource.crash_on_audit_fail "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "expects full_privilege_auditing to have a true or false value if entered" do
+ expect { resource.full_privilege_auditing "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "expects audit_base_objects to have a true or false value if entered" do
+ expect { resource.audit_base_objects "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "expects audit_base_directories to have a true or false value if entered" do
+ expect { resource.audit_base_directories "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "expects success property to have a true or false value if entered" do
+ expect { resource.success "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "expects failure property to have a true or false value if entered" do
+ expect { resource.failure "not_a_true_or_false" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ Chef::Resource::WindowsAuditPolicy::WIN_AUDIT_SUBCATEGORIES.each do |val|
+ it "the subcategory property accepts :#{val}" do
+ expect { resource.subcategory val }.not_to raise_error
+ end
+ end
+
+ it "the resource raises an ArgumentError if invalid subcategory property is set" do
+ expect { resource.subcategory "Logount" }.to raise_error(ArgumentError)
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ 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..2e9f4a4f33
--- /dev/null
+++ b/spec/unit/resource/windows_auto_run_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_auto_run" do
+ expect(resource.resource_name).to eql(:windows_auto_run)
+ end
+
+ it "the program_name property is the name_property" do
+ expect(resource.program_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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 "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/windows_certificate_spec.rb b/spec/unit/resource/windows_certificate_spec.rb
new file mode 100644
index 0000000000..71ef8a9498
--- /dev/null
+++ b/spec/unit/resource/windows_certificate_spec.rb
@@ -0,0 +1,95 @@
+#
+# Copyright:: Copyright (c) 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::WindowsCertificate do
+ let(:resource) { Chef::Resource::WindowsCertificate.new("foobar") }
+
+ it "sets resource name as :windows_certificate" do
+ expect(resource.resource_name).to eql(:windows_certificate)
+ end
+
+ it "the source property is the name_property" do
+ expect(resource.source).to eql("foobar")
+ end
+
+ it "the store_name property defaults to 'MY'" do
+ expect(resource.store_name).to eql("MY")
+ end
+
+ it 'the store_name property accepts "TRUSTEDPUBLISHER", "TrustedPublisher", "CLIENTAUTHISSUER", "REMOTE DESKTOP", "ROOT", "TRUSTEDDEVICES", "WEBHOSTING", "CA", "AUTHROOT", "TRUSTEDPEOPLE", "MY", "SMARTCARDROOT", "TRUST", or "DISALLOWED"' do
+ expect { resource.store_name("TRUSTEDPUBLISHER") }.not_to raise_error
+ expect { resource.store_name("TrustedPublisher") }.not_to raise_error
+ expect { resource.store_name("CLIENTAUTHISSUER") }.not_to raise_error
+ expect { resource.store_name("REMOTE DESKTOP") }.not_to raise_error
+ expect { resource.store_name("ROOT") }.not_to raise_error
+ expect { resource.store_name("TRUSTEDDEVICES") }.not_to raise_error
+ expect { resource.store_name("WEBHOSTING") }.not_to raise_error
+ expect { resource.store_name("CA") }.not_to raise_error
+ expect { resource.store_name("AUTHROOT") }.not_to raise_error
+ expect { resource.store_name("TRUSTEDPEOPLE") }.not_to raise_error
+ expect { resource.store_name("MY") }.not_to raise_error
+ expect { resource.store_name("SMARTCARDROOT") }.not_to raise_error
+ expect { resource.store_name("TRUST") }.not_to raise_error
+ expect { resource.store_name("DISALLOWED") }.not_to raise_error
+ end
+
+ it "the resource is marked sensitive if pfx_password is specified" do
+ resource.pfx_password("1234")
+ expect(resource.sensitive).to be true
+ end
+
+ it "the user_store property defaults to false" do
+ expect(resource.user_store).to be false
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :acl_add, :delete, and :verify actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :acl_add }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :verify }.not_to raise_error
+ end
+
+ it "sets sensitive to true if the pfx_password property is set" do
+ resource.pfx_password "foo"
+ expect(resource.sensitive).to be_truthy
+ end
+
+ it "doesn't raise error if pfx_password contains special characters" do
+ resource.pfx_password "chef$123"
+ resource.source "C:\\certs\\test-cert.pfx"
+ resource.store_name "MY"
+ expect { resource.action :create }.not_to raise_error
+ end
+
+ it "the exportable property defaults to false" do
+ expect(resource.exportable).to be false
+ end
+
+ it "doesn't raise error if exportable option is passed" do
+ resource.pfx_password "chef$123"
+ resource.source "C:\\certs\\test-cert.pfx"
+ resource.store_name "MY"
+ resource.exportable true
+ expect { resource.action :create }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/windows_dfs_folder_spec.rb b/spec/unit/resource/windows_dfs_folder_spec.rb
new file mode 100644
index 0000000000..387e40b95e
--- /dev/null
+++ b/spec/unit/resource/windows_dfs_folder_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) 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::WindowsDfsFolder do
+ let(:resource) { Chef::Resource::WindowsDfsFolder.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_dfs_folder" do
+ expect(resource.resource_name).to eql(:windows_dfs_folder)
+ end
+
+ it "the folder_path property is the name_property" do
+ expect(resource.folder_path).to eql("fakey_fakerton")
+ 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
+ end
+end
diff --git a/spec/unit/resource/windows_dfs_namespace_spec.rb b/spec/unit/resource/windows_dfs_namespace_spec.rb
new file mode 100644
index 0000000000..18f3331ff4
--- /dev/null
+++ b/spec/unit/resource/windows_dfs_namespace_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) 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::WindowsDfsNamespace do
+ let(:resource) { Chef::Resource::WindowsDfsNamespace.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_dfs_namespace" do
+ expect(resource.resource_name).to eql(:windows_dfs_namespace)
+ end
+
+ it "the namespace_name property is the name_property" do
+ expect(resource.namespace_name).to eql("fakey_fakerton")
+ 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
+ end
+end
diff --git a/spec/unit/resource/windows_dfs_server_spec.rb b/spec/unit/resource/windows_dfs_server_spec.rb
new file mode 100644
index 0000000000..eeb27839ed
--- /dev/null
+++ b/spec/unit/resource/windows_dfs_server_spec.rb
@@ -0,0 +1,34 @@
+#
+# Copyright:: Copyright (c) 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::WindowsDfsServer do
+ let(:resource) { Chef::Resource::WindowsDfsServer.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_dfs_server" do
+ expect(resource.resource_name).to eql(:windows_dfs_server)
+ end
+
+ it "sets the default action as :configure" do
+ expect(resource.action).to eql([:configure])
+ end
+
+ it "supports :configure action" do
+ expect { resource.action :configure }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/windows_dns_record_spec.rb b/spec/unit/resource/windows_dns_record_spec.rb
new file mode 100644
index 0000000000..acf8824e89
--- /dev/null
+++ b/spec/unit/resource/windows_dns_record_spec.rb
@@ -0,0 +1,55 @@
+#
+# Copyright:: Copyright (c) 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::WindowsDnsRecord do
+ let(:resource) { Chef::Resource::WindowsDnsRecord.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_dns_record" do
+ expect(resource.resource_name).to eql(:windows_dns_record)
+ end
+
+ it "the record_name property is the name_property" do
+ expect(resource.record_name).to eql("fakey_fakerton")
+ end
+
+ it "the record_type property accepts 'CNAME'" do
+ expect { resource.record_type "CNAME" }.not_to raise_error
+ end
+
+ it "the record_type property accepts 'ARecord'" do
+ expect { resource.record_type "ARecord" }.not_to raise_error
+ end
+
+ it "the record_type property accepts 'PTR'" do
+ expect { resource.record_type "PTR" }.not_to raise_error
+ end
+
+ it "the resource raises an ArgumentError if invalid record_type is set" do
+ expect { resource.record_type "NOPE" }.to raise_error(ArgumentError)
+ 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
+ end
+end
diff --git a/spec/unit/resource/windows_dns_zone_spec.rb b/spec/unit/resource/windows_dns_zone_spec.rb
new file mode 100644
index 0000000000..806c565b81
--- /dev/null
+++ b/spec/unit/resource/windows_dns_zone_spec.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright (c) 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::WindowsDnsZone do
+ let(:resource) { Chef::Resource::WindowsDnsZone.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_dns_zone" do
+ expect(resource.resource_name).to eql(:windows_dns_zone)
+ end
+
+ it "the zone_name property is the name_property" do
+ expect(resource.zone_name).to eql("fakey_fakerton")
+ end
+
+ it "the server_type property accepts 'Standalone'" do
+ expect { resource.server_type "Standalone" }.not_to raise_error
+ end
+
+ it "the server_type property accepts 'Domain'" do
+ expect { resource.server_type "Domain" }.not_to raise_error
+ end
+
+ it "the resource raises an ArgumentError if invalid server_type is set" do
+ expect { resource.server_type "NOPE" }.to raise_error(ArgumentError)
+ 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
+ end
+end
diff --git a/spec/unit/resource/windows_env_spec.rb b/spec/unit/resource/windows_env_spec.rb
new file mode 100644
index 0000000000..3ba5b18881
--- /dev/null
+++ b/spec/unit/resource/windows_env_spec.rb
@@ -0,0 +1,75 @@
+#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# 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 "spec_helper"
+
+describe Chef::Resource::WindowsEnv do
+
+ let(:resource) { Chef::Resource::WindowsEnv.new("fakey_fakerton") }
+
+ 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 "the key_name property is the name_property" do
+ expect(resource.key_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :delete, :modify actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :modify }.not_to raise_error
+ end
+
+ it "accepts a string as the env value via 'value'" do
+ expect { resource.value "bar" }.not_to raise_error
+ end
+
+ it "does not accept a Hash for the env value via 'to'" do
+ expect { resource.value({}) }.to raise_error(ArgumentError)
+ end
+
+ 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")
+ end
+
+ it "describes its state" do
+ 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")
+ end
+ end
+
+end
diff --git a/spec/unit/resource/windows_feature_dism_spec.rb b/spec/unit/resource/windows_feature_dism_spec.rb
new file mode 100644
index 0000000000..d600f611a3
--- /dev/null
+++ b/spec/unit/resource/windows_feature_dism_spec.rb
@@ -0,0 +1,57 @@
+#
+# Copyright:: Copyright (c) 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(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::WindowsFeatureDism.new(%w{SNMP DHCP}, run_context) }
+
+ 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 "the feature_name property is the name_property" do
+ expect(resource.feature_name).to eql(%w{snmp dhcp})
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :delete, :install, :remove actions" do
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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
+end
diff --git a/spec/unit/resource/windows_feature_powershell_spec.rb b/spec/unit/resource/windows_feature_powershell_spec.rb
new file mode 100644
index 0000000000..5b4062f34c
--- /dev/null
+++ b/spec/unit/resource/windows_feature_powershell_spec.rb
@@ -0,0 +1,83 @@
+#
+# Copyright:: Copyright (c) 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(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::WindowsFeaturePowershell.new(%w{SNMP DHCP}, run_context) }
+ let(:provider) { resource.provider_for_action(:install) }
+
+ 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 "the feature_name property is the name_property" do
+ expect(resource.feature_name).to eql(%w{snmp dhcp})
+ end
+
+ it "supports :delete, :install, :remove actions" do
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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 "install a single feature" do
+ resource.feature_name "snmp"
+ expect { resource.action :install }.not_to raise_error
+ end
+
+ it "install multi feature" do
+ resource.feature_name "SNMP, DHCP"
+ expect { resource.action :install }.not_to raise_error
+ end
+
+ it "does not attempt to install features that have been removed" do
+ node.default["powershell_features_cache"] ||= {}
+ node.default["powershell_features_cache"]["disabled"] = ["dhcp"]
+ node.default["powershell_features_cache"]["removed"] = ["snmp"]
+ resource.feature_name "dhcp, snmp"
+
+ expect(provider.features_to_install).to eq(["dhcp"])
+ end
+
+ it "attempts to install features that have been removed when source is set" do
+ node.default["powershell_features_cache"] ||= {}
+ node.default["powershell_features_cache"]["disabled"] = ["dhcp"]
+ node.default["powershell_features_cache"]["removed"] = ["snmp"]
+ resource.feature_name "dhcp, snmp"
+ resource.source 'D:\\sources\\sxs'
+
+ expect(provider.features_to_install).to eq(%w{dhcp snmp})
+ end
+end
diff --git a/spec/unit/resource/windows_feature_spec.rb b/spec/unit/resource/windows_feature_spec.rb
new file mode 100644
index 0000000000..752fb8ceb4
--- /dev/null
+++ b/spec/unit/resource/windows_feature_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_feature" do
+ expect(resource.resource_name).to eql(:windows_feature)
+ end
+
+ it "the feature_name property is the name_property" do
+ expect(resource.feature_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :delete, :install, :remove actions" do
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ it "all property defaults to false" do
+ expect(resource.all).to eql(false)
+ end
+
+ it "management_tools property defaults to false" do
+ expect(resource.management_tools).to eql(false)
+ end
+
+ it "timeout property defaults to 600" do
+ expect(resource.timeout).to eql(600)
+ end
+
+ it "install_method property defaults to :windows_feature_dism" do
+ expect(resource.install_method).to eql(:windows_feature_dism)
+ end
+
+ it "install_method accepts :windows_feature_dism, :windows_feature_powershell, and :windows_feature_servermanagercmd" do
+ expect { resource.install_method :windows_feature_dism }.not_to raise_error
+ expect { resource.install_method :windows_feature_powershell }.not_to raise_error
+ expect { resource.install_method :windows_feature_servermanagercmd }.not_to raise_error
+ expect { resource.install_method "windows_feature_servermanagercmd" }.to raise_error(ArgumentError)
+ end
+
+end
diff --git a/spec/unit/resource/windows_firewall_profile_spec.rb b/spec/unit/resource/windows_firewall_profile_spec.rb
new file mode 100644
index 0000000000..086ac78998
--- /dev/null
+++ b/spec/unit/resource/windows_firewall_profile_spec.rb
@@ -0,0 +1,77 @@
+#
+# Copyright:: Copyright (c) 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::WindowsFirewallProfile do
+ let(:resource) { Chef::Resource::WindowsFirewallProfile.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_firewall_profile" do
+ expect(resource.resource_name).to eql(:windows_firewall_profile)
+ end
+
+ %w{ Domain Private Public }.each do |this_profile|
+ it "The profile accepts values for the \"#{this_profile}\" Profile" do
+ expect { resource.profile this_profile }.not_to raise_error
+ end
+ end
+
+ it "the profile property does not accept bad profile names" do
+ expect { resource.profile "Special" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "the resource's default_inbound_action property only strings Block, Allow, or NotConfigured" do
+ expect { resource.default_inbound_action "AllowSome" }.to raise_error(ArgumentError)
+ expect { resource.default_inbound_action "Block" }.not_to raise_error
+ end
+ it "the resource's default_outbound_action property only accepts strings Block, Allow, or NotConfigured" do
+ expect { resource.default_outbound_action "BlockMost" }.to raise_error(ArgumentError)
+ expect { resource.default_outbound_action "Allow" }.not_to raise_error
+ end
+ it "the resource's allow_inbound_rules property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_inbound_rules "Yes" }.to raise_error(ArgumentError)
+ expect { resource.allow_inbound_rules true }.not_to raise_error
+ end
+ it "the resource's allow_local_firewall_rules property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_local_firewall_rules "No" }.to raise_error(ArgumentError)
+ expect { resource.allow_local_firewall_rules false }.not_to raise_error
+ end
+ it "the resource's allow_local_ipsec_rules property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_local_ipsec_rules "Yes" }.to raise_error(ArgumentError)
+ expect { resource.allow_local_ipsec_rules true }.not_to raise_error
+ end
+ it "the resource's allow_user_apps property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_user_apps "No" }.to raise_error(ArgumentError)
+ expect { resource.allow_user_apps false }.not_to raise_error
+ end
+ it "the resource's allow_user_ports property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_user_ports "Nope" }.to raise_error(ArgumentError)
+ expect { resource.allow_user_ports "NotConfigured" }.not_to raise_error
+ end
+ it "the resource's allow_unicast_response property only accepts strings true, false, or NotConfigured" do
+ expect { resource.allow_unicast_response "True" }.to raise_error(ArgumentError)
+ expect { resource.allow_unicast_response true }.not_to raise_error
+ end
+ it "the resource's display_notification property only accepts strings true, false, or NotConfigured" do
+ expect { resource.display_notification "False" }.to raise_error(ArgumentError)
+ expect { resource.display_notification false }.not_to raise_error
+ end
+
+ it "sets the default action as :configure" do
+ expect(resource.action).to eql([:enable])
+ end
+end
diff --git a/spec/unit/resource/windows_firewall_rule_spec.rb b/spec/unit/resource/windows_firewall_rule_spec.rb
new file mode 100644
index 0000000000..f4dfea1e0a
--- /dev/null
+++ b/spec/unit/resource/windows_firewall_rule_spec.rb
@@ -0,0 +1,505 @@
+# Author:: Tor Magnus Rakvåg (tor.magnus@outlook.com)
+# Copyright:: 2018, Intility AS
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES 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::WindowsFirewallRule do
+ let(:resource) { Chef::Resource::WindowsFirewallRule.new("rule") }
+ let(:provider) { resource.provider_for_action(:create) }
+
+ it "has a resource name of :windows_firewall_rule" do
+ expect(resource.resource_name).to eql(:windows_firewall_rule)
+ end
+
+ it "the name_property is 'rule_name'" do
+ expect(resource.rule_name).to eql("rule")
+ end
+
+ it "the default action is :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
+ end
+
+ it "the rule_name property accepts strings" do
+ resource.rule_name("rule2")
+ expect(resource.rule_name).to eql("rule2")
+ end
+
+ it "the description property accepts strings" do
+ resource.description("firewall rule")
+ expect(resource.description).to eql("firewall rule")
+ end
+
+ it "the group property accepts strings" do
+ resource.group("New group")
+ expect(resource.group).to eql("New group")
+ end
+
+ it "the local_address property accepts strings" do
+ resource.local_address("192.168.1.1")
+ expect(resource.local_address).to eql("192.168.1.1")
+ end
+
+ it "the local_port property accepts integers" do
+ resource.local_port(8080)
+ expect(resource.local_port).to eql(["8080"])
+ end
+
+ it "the local_port property accepts strings" do
+ resource.local_port("8080")
+ expect(resource.local_port).to eql(["8080"])
+ end
+
+ it "the local_port property accepts comma separated lists without spaces" do
+ resource.local_port("8080,8081")
+ expect(resource.local_port).to eql(%w{8080 8081})
+ end
+
+ it "the local_port property accepts comma separated lists with spaces" do
+ resource.local_port("8080, 8081")
+ expect(resource.local_port).to eql(%w{8080 8081})
+ end
+
+ it "the local_port property accepts arrays and coerces to a sorta array of strings" do
+ resource.local_port([8081, 8080])
+ expect(resource.local_port).to eql(%w{8080 8081})
+ end
+
+ it "the remote_address property accepts strings" do
+ resource.remote_address("8.8.4.4")
+ expect(resource.remote_address).to eql("8.8.4.4")
+ end
+
+ it "the remote_port property accepts strings" do
+ resource.remote_port("8081")
+ expect(resource.remote_port).to eql(["8081"])
+ end
+
+ it "the remote_port property accepts integers" do
+ resource.remote_port(8081)
+ expect(resource.remote_port).to eql(["8081"])
+ end
+
+ it "the remote_port property accepts comma separated lists without spaces" do
+ resource.remote_port("8080,8081")
+ expect(resource.remote_port).to eql(%w{8080 8081})
+ end
+
+ it "the remote_port property accepts comma separated lists with spaces" do
+ resource.remote_port("8080, 8081")
+ expect(resource.remote_port).to eql(%w{8080 8081})
+ end
+
+ it "the remote_port property accepts arrays and coerces to a sorta array of strings" do
+ resource.remote_port([8081, 8080])
+ expect(resource.remote_port).to eql(%w{8080 8081})
+ end
+
+ it "the direction property accepts :inbound and :outbound" do
+ resource.direction(:inbound)
+ expect(resource.direction).to eql(:inbound)
+ resource.direction(:outbound)
+ expect(resource.direction).to eql(:outbound)
+ end
+
+ it "the direction property coerces strings to symbols" do
+ resource.direction("Inbound")
+ expect(resource.direction).to eql(:inbound)
+ end
+
+ it "the protocol property accepts strings" do
+ resource.protocol("TCP")
+ expect(resource.protocol).to eql("TCP")
+ end
+
+ it "the icmp_type property accepts strings" do
+ resource.icmp_type("Any")
+ expect(resource.icmp_type).to eql("Any")
+ end
+
+ it "the icmp_type property accepts integers" do
+ resource.icmp_type(8)
+ expect(resource.icmp_type).to eql(8)
+ end
+
+ it "the firewall_action property accepts :allow, :block and :notconfigured" do
+ resource.firewall_action(:allow)
+ expect(resource.firewall_action).to eql(:allow)
+ resource.firewall_action(:block)
+ expect(resource.firewall_action).to eql(:block)
+ resource.firewall_action(:notconfigured)
+ expect(resource.firewall_action).to eql(:notconfigured)
+ end
+
+ it "the firewall_action property coerces strings to symbols" do
+ resource.firewall_action("Allow")
+ expect(resource.firewall_action).to eql(:allow)
+ end
+
+ it "the profile property accepts :public, :private, :domain, :any and :notapplicable" do
+ resource.profile(:public)
+ expect(resource.profile).to eql([:public])
+ resource.profile(:private)
+ expect(resource.profile).to eql([:private])
+ resource.profile(:domain)
+ expect(resource.profile).to eql([:domain])
+ resource.profile(:any)
+ expect(resource.profile).to eql([:any])
+ resource.profile(:notapplicable)
+ expect(resource.profile).to eql([:notapplicable])
+ end
+
+ it "the profile property raises on any unknown values" do
+ expect { resource.profile(:other) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.profile(%i{public other}) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "the profile property coerces strings to symbols" do
+ resource.profile("Public")
+ expect(resource.profile).to eql([:public])
+ resource.profile([:private, "Public"])
+ expect(resource.profile).to eql(%i{private public})
+ end
+
+ it "the profile property supports multiple profiles" do
+ resource.profile(%w{Private Public})
+ expect(resource.profile).to eql(%i{private public})
+ end
+
+ it "the program property accepts strings" do
+ resource.program("C:/Test/test.exe")
+ expect(resource.program).to eql("C:/Test/test.exe")
+ end
+
+ it "the service property accepts strings" do
+ resource.service("Spooler")
+ expect(resource.service).to eql("Spooler")
+ end
+
+ it "the interface_type property accepts :any, :wireless, :wired and :remoteaccess" do
+ resource.interface_type(:any)
+ expect(resource.interface_type).to eql(:any)
+ resource.interface_type(:wireless)
+ expect(resource.interface_type).to eql(:wireless)
+ resource.interface_type(:wired)
+ expect(resource.interface_type).to eql(:wired)
+ resource.interface_type(:remoteaccess)
+ expect(resource.interface_type).to eql(:remoteaccess)
+ end
+
+ it "the interface_type property coerces strings to symbols" do
+ resource.interface_type("Any")
+ expect(resource.interface_type).to eql(:any)
+ end
+
+ it "the enabled property accepts true and false" do
+ resource.enabled(true)
+ expect(resource.enabled).to eql(true)
+ resource.enabled(false)
+ expect(resource.enabled).to eql(false)
+ end
+
+ it "aliases :localip to :local_address" do
+ resource.localip("192.168.30.30")
+ expect(resource.local_address).to eql("192.168.30.30")
+ end
+
+ it "aliases :remoteip to :remote_address" do
+ resource.remoteip("8.8.8.8")
+ expect(resource.remote_address).to eql("8.8.8.8")
+ end
+
+ it "aliases :localport to :local_port" do
+ resource.localport("80")
+ expect(resource.local_port).to eql(["80"])
+ end
+
+ it "aliases :remoteport to :remote_port" do
+ resource.remoteport("8080")
+ expect(resource.remote_port).to eql(["8080"])
+ end
+
+ it "aliases :interfacetype to :interface_type" do
+ resource.interfacetype(:any)
+ expect(resource.interface_type).to eql(:any)
+ end
+
+ describe "#firewall_command" do
+ before do
+ resource.rule_name("test_rule")
+ end
+
+ context "#new" do
+ it "build a minimal command" do
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets a description" do
+ resource.description("New description")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Description 'New description' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets a displayname" do
+ resource.displayname("New displayname")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'New displayname' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets a group" do
+ resource.group("New groupname")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Group 'New groupname' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalAddress" do
+ resource.local_address("127.0.0.1")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -LocalAddress '127.0.0.1' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalPort" do
+ resource.local_port("80")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -LocalPort '80' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalPort with int" do
+ resource.local_port(80)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -LocalPort '80' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets multiple LocalPorts" do
+ resource.local_port(%w{80 RPC})
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -LocalPort '80', 'RPC' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemoteAddress" do
+ resource.remote_address("8.8.8.8")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -RemoteAddress '8.8.8.8' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemotePort" do
+ resource.remote_port("443")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -RemotePort '443' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemotePort with int" do
+ resource.remote_port(443)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -RemotePort '443' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets multiple RemotePorts" do
+ resource.remote_port(%w{443 445})
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -RemotePort '443', '445' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Direction" do
+ resource.direction(:outbound)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'outbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Protocol" do
+ resource.protocol("UDP")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'UDP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets ICMP Protocol with type 8" do
+ resource.protocol("ICMPv6")
+ resource.icmp_type(8)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'ICMPv6' -IcmpType '8' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Action" do
+ resource.firewall_action(:block)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'block' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Profile" do
+ resource.profile(:private)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'private' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets multiple Profiles (must be comma-plus-space delimited for PowerShell to treat as an array)" do
+ resource.profile(%i{private public})
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'private', 'public' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Program" do
+ resource.program("C:/calc.exe")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -Program 'C:/calc.exe' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Service" do
+ resource.service("Spooler")
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -Service 'Spooler' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets InterfaceType" do
+ resource.interface_type(:wired)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'wired' -Enabled 'true'")
+ end
+
+ it "sets Enabled" do
+ resource.enabled(false)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule' -DisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'false'")
+ end
+
+ it "sets all properties UDP" do
+ resource.rule_name("test_rule_the_second")
+ resource.displayname("some cool display name")
+ resource.description("some other rule")
+ resource.group("new group")
+ resource.local_address("192.168.40.40")
+ resource.local_port("80")
+ resource.remote_address("8.8.4.4")
+ resource.remote_port("8081")
+ resource.direction(:outbound)
+ resource.protocol("UDP")
+ resource.icmp_type("Any")
+ resource.firewall_action(:notconfigured)
+ resource.profile(:domain)
+ resource.program('%WINDIR%\System32\lsass.exe')
+ resource.service("SomeService")
+ resource.interface_type(:remoteaccess)
+ resource.enabled(false)
+ expect(provider.firewall_command("New")).to eql("New-NetFirewallRule -Name 'test_rule_the_second' -DisplayName 'some cool display name' -Group 'new group' -Description 'some other rule' -LocalAddress '192.168.40.40' -LocalPort '80' -RemoteAddress '8.8.4.4' -RemotePort '8081' -Direction 'outbound' -Protocol 'UDP' -IcmpType 'Any' -Action 'notconfigured' -Profile 'domain' -Program '%WINDIR%\\System32\\lsass.exe' -Service 'SomeService' -InterfaceType 'remoteaccess' -Enabled 'false'")
+ end
+ end
+
+ context "#set" do
+ it "build a minimal command" do
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets a displayname" do
+ resource.displayname("New displayname")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'New displayname' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets a description" do
+ resource.description("New description")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Description 'New description' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalAddress" do
+ resource.local_address("127.0.0.1")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -LocalAddress '127.0.0.1' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalPort" do
+ resource.local_port("80")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -LocalPort '80' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets LocalPort with int" do
+ resource.local_port(80)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -LocalPort '80' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets multiple LocalPorts (must be comma-plus-space delimited for PowerShell to treat as an array)" do
+ resource.local_port(%w{80 8080})
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -LocalPort '80', '8080' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemoteAddress" do
+ resource.remote_address("8.8.8.8")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -RemoteAddress '8.8.8.8' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemotePort" do
+ resource.remote_port("443")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -RemotePort '443' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets RemotePort with int" do
+ resource.remote_port(443)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -RemotePort '443' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets multiple RemotePorts (must be comma-plus-space delimited for PowerShell to treat as an array)" do
+ resource.remote_port(%w{443 445})
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -RemotePort '443', '445' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Direction" do
+ resource.direction(:outbound)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'outbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Protocol" do
+ resource.protocol("UDP")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'UDP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets ICMP Protocol with type 8" do
+ resource.protocol("ICMPv6")
+ resource.icmp_type(8)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'ICMPv6' -IcmpType '8' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Action" do
+ resource.firewall_action(:block)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'block' -Profile 'any' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Profile" do
+ resource.profile(:private)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'private' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Program" do
+ resource.program("C:/calc.exe")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -Program 'C:/calc.exe' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets Service" do
+ resource.service("Spooler")
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -Service 'Spooler' -InterfaceType 'any' -Enabled 'true'")
+ end
+
+ it "sets InterfaceType" do
+ resource.interface_type(:wired)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'wired' -Enabled 'true'")
+ end
+
+ it "sets Enabled" do
+ resource.enabled(false)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule' -NewDisplayName 'test_rule' -Direction 'inbound' -Protocol 'TCP' -IcmpType 'Any' -Action 'allow' -Profile 'any' -InterfaceType 'any' -Enabled 'false'")
+ end
+
+ it "sets all properties" do
+ resource.rule_name("test_rule_the_second")
+ resource.description("some other rule")
+ resource.displayname("some cool display name")
+ resource.local_address("192.168.40.40")
+ resource.local_port("80")
+ resource.remote_address("8.8.4.4")
+ resource.remote_port("8081")
+ resource.direction(:outbound)
+ resource.protocol("UDP")
+ resource.icmp_type("Any")
+ resource.firewall_action(:notconfigured)
+ resource.profile(:domain)
+ resource.program('%WINDIR%\System32\lsass.exe')
+ resource.service("SomeService")
+ resource.interface_type(:remoteaccess)
+ resource.enabled(false)
+ expect(provider.firewall_command("Set")).to eql("Set-NetFirewallRule -Name 'test_rule_the_second' -NewDisplayName 'some cool display name' -Description 'some other rule' -LocalAddress '192.168.40.40' -LocalPort '80' -RemoteAddress '8.8.4.4' -RemotePort '8081' -Direction 'outbound' -Protocol 'UDP' -IcmpType 'Any' -Action 'notconfigured' -Profile 'domain' -Program '%WINDIR%\\System32\\lsass.exe' -Service 'SomeService' -InterfaceType 'remoteaccess' -Enabled 'false'")
+ end
+ end
+ 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..eeefa244b7
--- /dev/null
+++ b/spec/unit/resource/windows_font_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_font" do
+ expect(resource.resource_name).to eql(:windows_font)
+ end
+
+ it "the font_name property is the name_property" do
+ expect(resource.font_name).to eql("fakey_fakerton")
+ 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
+ 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..cbe3a06d87 100644
--- a/spec/unit/resource/windows_package_spec.rb
+++ b/spec/unit/resource/windows_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,17 +33,33 @@ 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
- expect(resource.resource_name).to eql(:windows_package)
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
end
- it "supports setting installer_type as a symbol" do
- resource.installer_type(:msi)
- expect(resource.installer_type).to eql(:msi)
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ end
+
+ it "supports setting installer_type to :custom :inno :installshield :msi :nsis or :wise only" do
+ expect { resource.installer_type :custom }.not_to raise_error
+ expect { resource.installer_type :inno }.not_to raise_error
+ expect { resource.installer_type :installshield }.not_to raise_error
+ expect { resource.installer_type :msi }.not_to raise_error
+ expect { resource.installer_type :nsis }.not_to raise_error
+ expect { resource.installer_type :wise }.not_to raise_error
+ expect { resource.installer_type :something_else_entirely }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { resource.installer_type "msi" }.to raise_error(Chef::Exceptions::ValidationFailed)
end
# String, Integer
@@ -62,7 +78,7 @@ describe Chef::Resource::WindowsPackage, "initialize" do
end
end
- it "coverts a source to an absolute path" do
+ it "converts a source to an absolute path" do
allow(::File).to receive(:absolute_path).and_return("c:\\files\\frost.msi")
resource.source("frost.msi")
expect(resource.source).to eql "c:\\files\\frost.msi"
@@ -74,13 +90,27 @@ describe Chef::Resource::WindowsPackage, "initialize" do
expect(resource.source).to eql "c:\\frost.msi"
end
+ it "defaults returns to [0, 3010]" do
+ expect(resource.returns).to eq([0, 3010])
+ end
+
+ it "does not accept a string for the package_name property" do
+ expect { resource.package_name(%w{this should break}) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ # even though we don't do anything with arrays of versions we need them for current_value
+ it "accepts both Strings and Arrays for the version property" do
+ expect { resource.version "1.2.3" }.not_to raise_error
+ expect { resource.version ["1.2.3", "1.2.3.4"] }.not_to raise_error
+ end
+
it "defaults source to the resource name" do
# it's a little late to stub out File.absolute_path
expect(resource.source).to include("solitaire.msi")
end
- it "supports the checksum attribute" do
- resource.checksum("somechecksum")
+ it "lowercases values provided in the checksum property" do
+ resource.checksum("SOMECHECKSUM")
expect(resource.checksum).to eq("somechecksum")
end
@@ -88,7 +118,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..9f12a74ca8
--- /dev/null
+++ b/spec/unit/resource/windows_pagefile_spec.rb
@@ -0,0 +1,49 @@
+#
+# Copyright:: Copyright (c) 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 "the path property is the name_property" do
+ 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 :delete, :set actions" do
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :set }.not_to raise_error
+ 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 "automatic_managed property defaults to false" do
+ expect(resource.automatic_managed).to eql(false)
+ 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..50f4a4fb82
--- /dev/null
+++ b/spec/unit/resource/windows_path_spec.rb
@@ -0,0 +1,40 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_path" do
+ expect(resource.resource_name).to eql(:windows_path)
+ end
+
+ it "the path property is the name_property" do
+ expect(resource.path).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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..a3dba914ad
--- /dev/null
+++ b/spec/unit/resource/windows_printer_port_spec.rb
@@ -0,0 +1,62 @@
+#
+# Copyright:: Copyright (c) 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 "the ipv4_address property is the name_property" 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, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+
+ it "port_number property defaults to 9100" do
+ expect(resource.port_number).to eql(9100)
+ end
+
+ it "snmp_enabled property defaults to false" do
+ expect(resource.snmp_enabled).to eql(false)
+ end
+
+ it "port_protocol property defaults to 1" do
+ expect(resource.port_protocol).to eql(1)
+ end
+
+ it "raises an error if port_protocol isn't in 1 or 2" do
+ expect { resource.port_protocol 1 }.not_to raise_error
+ expect { resource.port_protocol 2 }.not_to raise_error
+ expect { resource.port_protocol 3 }.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..20b12bed51
--- /dev/null
+++ b/spec/unit/resource/windows_printer_spec.rb
@@ -0,0 +1,52 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_printer" do
+ expect(resource.resource_name).to eql(:windows_printer)
+ end
+
+ it "the device_id property is the name_property" do
+ expect(resource.device_id).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+
+ it "default property defaults to false" do
+ expect(resource.default).to eql(false)
+ end
+
+ it "shared property defaults to false" do
+ expect(resource.shared).to eql(false)
+ 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..1a35e1ad52 100644
--- a/spec/unit/resource/windows_service_spec.rb
+++ b/spec/unit/resource/windows_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,31 +19,104 @@
require "spec_helper"
describe Chef::Resource::WindowsService, "initialize" do
- static_provider_resolution(
- resource: Chef::Resource::WindowsService,
- provider: Chef::Provider::Service::Windows,
- os: "windows",
- name: :windows_service,
- action: :start
- )
+ let(:resource) { Chef::Resource::WindowsService.new("fakey_fakerton") }
- let(:resource) { Chef::Resource::WindowsService.new("BITS") }
+ it "sets the resource_name to :windows_service" do
+ expect(resource.resource_name).to eql(:windows_service)
+ end
- it "returns a Chef::Resource::WindowsService" do
- expect(resource).to be_a_kind_of(Chef::Resource::WindowsService)
+ it "the service_name property is the name_property" do
+ expect(resource.service_name).to eql("fakey_fakerton")
end
- it "sets the resource_name to :windows_service" do
- expect(resource.resource_name).to eql(:windows_service)
+ it "sets the default action as :nothing" do
+ expect(resource.action).to eql([:nothing])
+ end
+
+ it "supports :configure, :configure_startup, :create, :delete, :disable, :enable, :mask, :reload, :restart, :start, :stop, :unmask actions" do
+ expect { resource.action :configure }.not_to raise_error
+ expect { resource.action :configure_startup }.not_to raise_error
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :mask }.not_to raise_error
+ expect { resource.action :reload }.not_to raise_error
+ expect { resource.action :restart }.not_to raise_error
+ expect { resource.action :start }.not_to raise_error
+ expect { resource.action :stop }.not_to raise_error
+ expect { resource.action :unmask }.not_to raise_error
+ end
+
+ it "accepts an Integer for timeout property" do
+ resource.timeout 1
+ expect(resource.timeout).to eql(1)
+ end
+
+ it "defaults the timeout property to 60 (seconds)" do
+ expect(resource.timeout).to eql(60)
+ end
+
+ %i{automatic manual disabled}.each do |type|
+ it "supports setting startup_type property to #{type.inspect}" do
+ resource.startup_type type
+ expect(resource.startup_type).to eql(type)
+ end
+ end
+
+ { 2 => :automatic, 3 => :manual, 4 => :disabled }.each_pair do |k, v|
+ it "it coerces startup_type property #{k} to #{v.inspect}" do
+ resource.startup_type k
+ expect(resource.startup_type).to eql(v)
+ end
end
- it "supports setting startup_type" do
- resource.startup_type(:manual)
- expect(resource.startup_type).to eql(:manual)
+ %w{automatic manual disabled}.each do |type|
+ it "it coerces startup_type property #{type} to :#{type}" do
+ resource.startup_type type
+ expect(resource.startup_type).to eql(type.to_sym)
+ end
+ end
+
+ %i{automatic manual disabled}.each do |type|
+ it "supports setting startup_type property to #{type.inspect}" do
+ resource.startup_type type
+ expect(resource.startup_type).to eql(type)
+ end
end
it "allows the action to be 'configure_startup'" do
resource.action :configure_startup
expect(resource.action).to eq([:configure_startup])
end
+
+ # Properties 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} property with a String" do
+ resource.send("#{prop}=", "some value")
+ expect(resource.send(prop)).to eq("some value")
+ end
+ end
+
+ # Properties that are Integers
+ %i{desired_access error_control service_type}.each do |prop|
+ it "support setting #{prop} property with an Integer" do
+ resource.send("#{prop}=", 1)
+ expect(resource.send(prop)).to eq(1)
+ end
+ end
+
+ # Properties 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
+
+ it "lowercases run_as_user" do
+ resource.run_as_user = "JohnDoe"
+ expect(resource.run_as_user).to eq("johndoe")
+ end
end
diff --git a/spec/unit/resource/windows_share_spec.rb b/spec/unit/resource/windows_share_spec.rb
new file mode 100644
index 0000000000..1a551e081b
--- /dev/null
+++ b/spec/unit/resource/windows_share_spec.rb
@@ -0,0 +1,48 @@
+#
+# Copyright:: Copyright (c) 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::WindowsShare do
+ let(:resource) { Chef::Resource::WindowsShare.new("foobar") }
+
+ it "sets resource name as :windows_share" do
+ expect(resource.resource_name).to eql(:windows_share)
+ end
+
+ it "the share_name property is the name_property" do
+ expect(resource.share_name).to eql("foobar")
+ 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
+ end
+
+ it "coerces path to a single path separator format" do
+ resource.path("C:/chef".freeze)
+ expect(resource.path).to eql("C:\\chef")
+ resource.path("C:\\chef")
+ expect(resource.path).to eql("C:\\chef")
+ resource.path("C:/chef".dup)
+ expect(resource.path).to eql("C:\\chef")
+ 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..d41cbfc4f8
--- /dev/null
+++ b/spec/unit/resource/windows_shortcut_spec.rb
@@ -0,0 +1,38 @@
+#
+# Copyright:: Copyright (c) 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("fakey_fakerton") }
+
+ it "sets resource name as :windows_shortcut" do
+ expect(resource.resource_name).to eql(:windows_shortcut)
+ end
+
+ it "the shortcut_name property is the name_property" do
+ expect(resource.shortcut_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action" do
+ expect { resource.action :create }.not_to raise_error
+ 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..b991cb89de
--- /dev/null
+++ b/spec/unit/resource/windows_task_spec.rb
@@ -0,0 +1,403 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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
+
+ it "sets the default value for disallow_start_if_on_batteries as false" do
+ expect(resource.disallow_start_if_on_batteries).to eql(false)
+ end
+
+ it "sets the default value for stop_if_going_on_batteries as false" do
+ expect(resource.stop_if_going_on_batteries).to eql(false)
+ end
+
+ it "sets the default value for start_when_available as false" do
+ expect(resource.start_when_available).to eql(false)
+ 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
+
+ describe "#validate_user_and_password" do
+ context "a System User" do
+ before do
+ resource.frequency :hourly
+ resource.user 'NT AUTHORITY\SYSTEM'
+ end
+
+ context "for an interactive task" do
+ before { resource.interactive_enabled true }
+ it "does not require a password" do
+ expect { resource.after_created }.to_not raise_error
+ end
+ it "raises an error when a password is given" do
+ resource.password "XXXX"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Password is not required for system users.")
+ end
+ it "does not raises an error even when user is in lowercase" do
+ resource.user 'nt authority\system'
+ expect { resource.after_created }.to_not raise_error
+ end
+ end
+
+ context "for a non-interactive task" do
+ before { resource.interactive_enabled false }
+ it "does not require a password" do
+ expect { resource.after_created }.to_not raise_error
+ end
+ it "raises an error when a password is given" do
+ resource.password "XXXX"
+ expect { resource.after_created }.to raise_error(ArgumentError, "Password is not required for system users.")
+ end
+ it "does not raises an error even when user is in lowercase" do
+ resource.user 'nt authority\system'
+ expect { resource.after_created }.to_not raise_error
+ end
+ end
+ end
+
+ context "a Non-System User" do
+ before do
+ resource.frequency :hourly
+ resource.user "bob"
+ end
+ context "for an interactive task" do
+ before { resource.interactive_enabled true }
+ it "does not require a password" do
+ expect { resource.after_created }.to_not raise_error
+ end
+ it "does not raises an error when a password is given" do
+ resource.password "XXXX"
+ expect { resource.after_created }.to_not raise_error
+ end
+ end
+
+ context "for a non-interactive task" do
+ before { resource.interactive_enabled false }
+ it "require a password" do
+ expect { resource.after_created }.to raise_error(ArgumentError, %q{Please provide a password or check if this task needs to be interactive! Valid passwordless users are: 'SYSTEM', 'NT AUTHORITY\SYSTEM', 'LOCAL SERVICE', 'NT AUTHORITY\LOCAL SERVICE', 'NETWORK SERVICE', 'NT AUTHORITY\NETWORK SERVICE', 'ADMINISTRATORS', 'BUILTIN\ADMINISTRATORS', 'USERS', 'BUILTIN\USERS', 'GUESTS', 'BUILTIN\GUESTS'})
+ end
+ it "does not raises an error when a password is given" do
+ resource.password "XXXX"
+ expect { resource.after_created }.to_not raise_error
+ end
+ end
+ 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
+ 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 "priority" do
+ it "default value is 7" do
+ expect(resource.priority).to eq(7)
+ end
+
+ it "raise error when priority value less than 0" do
+ expect { resource.priority(-1) }.to raise_error(Chef::Exceptions::ValidationFailed, "Option priority's value -1 should be in range of 0 to 10!")
+ end
+
+ it "raise error when priority values is greater than 10" do
+ expect { resource.priority 11 }.to raise_error(Chef::Exceptions::ValidationFailed, "Option priority's value 11 should be in range of 0 to 10!")
+ 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_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
+ %i{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
+ %i{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/windows_uac_spec.rb b/spec/unit/resource/windows_uac_spec.rb
new file mode 100644
index 0000000000..48f2f33a16
--- /dev/null
+++ b/spec/unit/resource/windows_uac_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright (c) 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::WindowsUac do
+ let(:resource) { Chef::Resource::WindowsUac.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_uac" do
+ expect(resource.resource_name).to eql(:windows_uac)
+ end
+
+ %i{no_prompt secure_prompt_for_creds secure_prompt_for_consent prompt_for_creds prompt_for_consent prompt_for_consent_non_windows_binaries}.each do |val|
+ it "the consent_behavior_admins property accepts :#{val}" do
+ expect { resource.consent_behavior_admins val }.not_to raise_error
+ end
+ end
+
+ it "the resource raises an ArgumentError if invalid consent_behavior_admins is set" do
+ expect { resource.consent_behavior_admins :bogus }.to raise_error(ArgumentError)
+ end
+
+ %i{auto_deny secure_prompt_for_creds prompt_for_creds}.each do |val|
+ it "the consent_behavior_users property accepts :#{val}" do
+ expect { resource.consent_behavior_users val }.not_to raise_error
+ end
+ end
+
+ it "the resource raises an ArgumentError if invalid consent_behavior_users is set" do
+ expect { resource.consent_behavior_users :bogus }.to raise_error(ArgumentError)
+ end
+
+ it "sets the default action as :configure" do
+ expect(resource.action).to eql([:configure])
+ end
+end
diff --git a/spec/unit/resource/windows_user_privilege_spec.rb b/spec/unit/resource/windows_user_privilege_spec.rb
new file mode 100644
index 0000000000..73c800c8bd
--- /dev/null
+++ b/spec/unit/resource/windows_user_privilege_spec.rb
@@ -0,0 +1,55 @@
+#
+# Copyright:: Copyright (c) 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::WindowsUserPrivilege do
+ let(:resource) { Chef::Resource::WindowsUserPrivilege.new("fakey_fakerton") }
+
+ it "sets resource name as :windows_user_privilege" do
+ expect(resource.resource_name).to eql(:windows_user_privilege)
+ end
+
+ it "the principal property is the name_property" do
+ expect(resource.principal).to eql("fakey_fakerton")
+ end
+
+ it "the users property coerces to an array" do
+ resource.users "Administrator"
+ expect(resource.users).to eql(["Administrator"])
+ end
+
+ it "the privilege property coerces to an array" do
+ resource.privilege "SeDenyRemoteInteractiveLogonRight"
+ expect(resource.privilege).to eql(["SeDenyRemoteInteractiveLogonRight"])
+ end
+
+ it "the privilege property validates inputs against the allowed list of privs" do
+ expect { resource.privilege "invalidPriv" }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add, :set, :clear, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :clear }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+end
diff --git a/spec/unit/resource/windows_workgroup_spec.rb b/spec/unit/resource/windows_workgroup_spec.rb
new file mode 100644
index 0000000000..3f7d964ce0
--- /dev/null
+++ b/spec/unit/resource/windows_workgroup_spec.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright (c) 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::WindowsWorkgroup do
+ let(:resource) { Chef::Resource::WindowsWorkgroup.new("example") }
+ let(:provider) { resource.provider_for_action(:join) }
+
+ it "sets resource name as :windows_workgroup" do
+ expect(resource.resource_name).to eql(:windows_workgroup)
+ end
+
+ it "the workgroup_name property is the name_property" do
+ expect(resource.workgroup_name).to eql("example")
+ end
+
+ it "converts the legacy :immediate reboot property to :reboot_now" do
+ resource.reboot(:immediate)
+ expect(resource.reboot).to eql(:reboot_now)
+ end
+
+ it "converts the legacy :delayed reboot property to :request_reboot" do
+ resource.reboot(:delayed)
+ expect(resource.reboot).to eql(:request_reboot)
+ end
+
+ it "sets the default action as :join" do
+ expect(resource.action).to eql([:join])
+ end
+
+ it "supports :join action" do
+ expect { resource.action :join }.not_to raise_error
+ end
+
+ it "accepts :immediate, :reboot_now, :request_reboot, :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 :reboot_now }.not_to raise_error
+ expect { resource.reboot :request_reboot }.not_to raise_error
+ expect { resource.reboot :never }.not_to raise_error
+ expect { resource.reboot :nopenope }.to raise_error(ArgumentError)
+ end
+
+ describe "#join_command" do
+ context "if password property is not specified" do
+ it "constructs a command without credentials" do
+ expect(provider.join_command).to eql("Add-Computer -WorkgroupName example -Force")
+ end
+ end
+
+ context "if password property is specified" do
+ it "constructs a command without credentials" do
+ resource.password("1234")
+ resource.user("admin")
+ expect(provider.join_command).to eql("$pswd = ConvertTo-SecureString '1234' -AsPlainText -Force;$credential = New-Object System.Management.Automation.PSCredential (\"admin\",$pswd);Add-Computer -WorkgroupName example -Credential $credential -Force")
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/yum_package_spec.rb b/spec/unit/resource/yum_package_spec.rb
index dd0d3ae928..54c3a65a98 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 (c) 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 properties (even though they really, really should be).
+ context "when passed immutable node property 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
@@ -49,22 +90,42 @@ describe Chef::Resource::YumPackage, "flush_cache" do
end
it "should default the flush timing to false" do
- flush_hash = { :before => false, :after => false }
+ flush_hash = { before: false, after: false }
expect(@resource.flush_cache).to eq(flush_hash)
end
it "should allow you to set the flush timing with an array" do
- flush_array = [ :before, :after ]
- flush_hash = { :before => true, :after => true }
+ flush_array = %i{before after}
+ flush_hash = { before: true, after: true }
@resource.flush_cache(flush_array)
expect(@resource.flush_cache).to eq(flush_hash)
end
it "should allow you to set the flush timing with a hash" do
- flush_hash = { :before => true, :after => true }
+ flush_hash = { before: true, after: true }
@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
index afd6c6739a..36d88450d1 100644
--- a/spec/unit/resource/yum_repository_spec.rb
+++ b/spec/unit/resource/yum_repository_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Thom May (<thom@chef.io>)
-# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,27 +22,130 @@ 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) }
+ let(:resource) { Chef::Resource::YumRepository.new("fakey_fakerton", run_context) }
- context "on linux", :linux_only do
- it "should create a new Chef::Resource::YumRepository" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_kind_of(Chef::Resource::YumRepository)
- end
+ 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 eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :add, :create, :delete, :makecache, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :makecache }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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 "clean_headers property defaults to false" do
+ expect(resource.clean_headers).to eql(false)
+ end
+
+ it "clean_metadata property defaults to true" do
+ expect(resource.clean_metadata).to eql(true)
+ end
+
+ it "description property defaults to 'Yum Repository'" do
+ expect(resource.description).to eql("Yum Repository")
+ end
+
+ it "enabled property defaults to true" do
+ expect(resource.enabled).to eql(true)
+ end
+
+ it "make_cache property defaults to true" do
+ expect(resource.make_cache).to eql(true)
+ end
- it "should resolve to a Noop class when yum is not found" do
+ it "mode property defaults to '0644'" do
+ expect(resource.mode).to eql("0644")
+ end
+
+ it "the timeout property expects numeric Strings" do
+ expect { resource.timeout "123" }.not_to raise_error
+ 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
+ expect { resource.priority "1" }.not_to raise_error
+ 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
+ expect { resource.failovermethod "roundrobin" }.not_to raise_error
+ 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
+ expect { resource.http_caching "all" }.not_to raise_error
+ expect { resource.http_caching "none" }.not_to raise_error
+ 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
+ expect { resource.metadata_expire "100d" }.not_to raise_error
+ expect { resource.metadata_expire "100h" }.not_to raise_error
+ expect { resource.metadata_expire "100m" }.not_to raise_error
+ expect { resource.metadata_expire "never" }.not_to raise_error
+ 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
+ expect { resource.mirror_expire "100d" }.not_to raise_error
+ expect { resource.mirror_expire "100h" }.not_to raise_error
+ expect { resource.mirror_expire "100m" }.not_to raise_error
+ 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
+ expect { resource.mirrorlist_expire "100d" }.not_to raise_error
+ expect { resource.mirrorlist_expire "100h" }.not_to raise_error
+ expect { resource.mirrorlist_expire "100m" }.not_to raise_error
+ expect { resource.mirrorlist_expire "never" }.to raise_error(ArgumentError)
+ end
+
+ it "accepts the legacy 'url' property" do
+ resource.url "foo"
+ expect(resource.baseurl).to eql("foo")
+ end
+
+ it "accepts the legacy 'keyurl' property" do
+ resource.keyurl "foo"
+ expect(resource.gpgkey).to eql("foo")
+ 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 "should resolve to a YumRepository class when yum is found" do
+ 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 "should resolve to a NoOp provider" do
+ it "resolves to a NoOp provider" do
expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
end
end
diff --git a/spec/unit/resource/zypper_package_spec.rb b/spec/unit/resource/zypper_package_spec.rb
new file mode 100644
index 0000000000..1dcd41fd2d
--- /dev/null
+++ b/spec/unit/resource/zypper_package_spec.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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::ZypperPackage, "initialize" do
+
+ static_provider_resolution(
+ resource: Chef::Resource::ZypperPackage,
+ provider: Chef::Provider::Package::Zypper,
+ name: :zypper_package,
+ action: :install,
+ os: "linux",
+ platform_family: "suse"
+ )
+
+end
+
+describe Chef::Resource::ZypperPackage, "defaults" do
+ let(:resource) { Chef::Resource::ZypperPackage.new("fakey_fakerton") }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :lock, :purge, :reconfig, :remove, :unlock, :upgrade actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :lock }.not_to raise_error
+ expect { resource.action :purge }.not_to raise_error
+ expect { resource.action :reconfig }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :unlock }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ 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..b1b9cdf874
--- /dev/null
+++ b/spec/unit/resource/zypper_repository_spec.rb
@@ -0,0 +1,115 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: Copyright (c) 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("fakey_fakerton", run_context) }
+
+ it "has a resource_name of :zypper_repository" do
+ expect(resource.resource_name).to eq(:zypper_repository)
+ end
+
+ it "the repo_name property is the name_property" do
+ expect(resource.repo_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :add, :create, :refresh, :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :refresh }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ 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 "type property defaults to 'NONE'" do
+ expect(resource.type).to eql("NONE")
+ end
+
+ it "enabled property defaults to true" do
+ expect(resource.enabled).to eql(true)
+ end
+
+ it "autorefresh property defaults to true" do
+ expect(resource.autorefresh).to eql(true)
+ end
+
+ it "gpgcheck property defaults to true" do
+ expect(resource.gpgcheck).to eql(true)
+ end
+
+ it "keeppackages property defaults to false" do
+ expect(resource.keeppackages).to eql(false)
+ end
+
+ it "priority property defaults to 99" do
+ expect(resource.priority).to eql(99)
+ end
+
+ it "mode property defaults to '0644'" do
+ expect(resource.mode).to eql("0644")
+ end
+
+ it "refresh_cache property defaults to true" do
+ expect(resource.refresh_cache).to eql(true)
+ end
+
+ it "gpgautoimportkeys property defaults to true" do
+ expect(resource.gpgautoimportkeys).to eql(true)
+ end
+
+ it "accepts the legacy 'key' property" do
+ resource.key "foo"
+ expect(resource.gpgkey).to eql("foo")
+ end
+
+ it "accepts the legacy 'uri' property" do
+ resource.uri "foo"
+ expect(resource.baseurl).to eql("foo")
+ 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_list_spec.rb b/spec/unit/resource_collection/resource_list_spec.rb
index dabb8f037d..289d12c603 100644
--- a/spec/unit/resource_collection/resource_list_spec.rb
+++ b/spec/unit/resource_collection/resource_list_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
describe Chef::ResourceCollection::ResourceList do
- let(:resource_list) { Chef::ResourceCollection::ResourceList.new() }
+ let(:resource_list) { Chef::ResourceCollection::ResourceList.new }
let(:resource) { Chef::Resource::ZenMaster.new("makoto") }
let(:second_resource) { Chef::Resource::ZenMaster.new("hattori") }
diff --git a/spec/unit/resource_collection/resource_set_spec.rb b/spec/unit/resource_collection/resource_set_spec.rb
index 20f6f70911..3af701324c 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 (c) 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
@@ -72,34 +73,34 @@ describe Chef::ResourceCollection::ResourceSet do
it "should find a resource by type symbol and name" do
collection.insert_as(zen_master)
- expect(collection.find(:zen_master => zen_master_name)).to equal(zen_master)
+ expect(collection.find(zen_master: zen_master_name)).to equal(zen_master)
end
it "should find a resource by type symbol and array of names" do
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)
+ check_by_names(collection.find(zen_master: [zen_master_name, zen_master2_name]), zen_master_name, zen_master2_name)
end
it "should find a resource by type symbol and array of names with custom names" do
collection.insert_as(zen_master, :zzz, "name1")
collection.insert_as(zen_master2, :zzz, "name2")
- check_by_names(collection.find( :zzz => %w{name1 name2}), zen_master_name, zen_master2_name)
+ check_by_names(collection.find( zzz: %w{name1 name2}), zen_master_name, zen_master2_name)
end
it "should find resources of multiple kinds (:zen_master => a, :zen_follower => b)" do
collection.insert_as(zen_master)
collection.insert_as(zen_follower)
- check_by_names(collection.find(:zen_master => [zen_master_name], :zen_follower => [zen_follower_name]),
- zen_master_name, zen_follower_name)
+ check_by_names(collection.find(zen_master: [zen_master_name], zen_follower: [zen_follower_name]),
+ zen_master_name, zen_follower_name)
end
it "should find resources of multiple kinds (:zen_master => a, :zen_follower => b with custom names)" do
collection.insert_as(zen_master, :zzz, "name1")
collection.insert_as(zen_master2, :zzz, "name2")
collection.insert_as(zen_follower, :yyy, "name3")
- check_by_names(collection.find(:zzz => %w{name1 name2}, :yyy => ["name3"]),
- zen_master_name, zen_follower_name, zen_master2_name)
+ check_by_names(collection.find(zzz: %w{name1 name2}, yyy: ["name3"]),
+ zen_master_name, zen_follower_name, zen_master2_name)
end
it "should find a resource by string zen_master[a]" do
@@ -113,32 +114,47 @@ 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)
+ 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]"),
- zen_master_name, zen_master2_name)
+ zen_master_name, zen_master2_name)
end
it "should find resources of multiple types by strings of zen_master[a]" do
collection.insert_as(zen_master)
collection.insert_as(zen_follower)
check_by_names(collection.find("zen_master[#{zen_master_name}]", "zen_follower[#{zen_follower_name}]"),
- zen_master_name, zen_follower_name)
+ zen_master_name, zen_follower_name)
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")
check_by_names(collection.find("zzz[name1,name2]", "yyy[name3]"),
- zen_master_name, zen_follower_name, zen_master2_name)
+ zen_master_name, zen_follower_name, zen_master2_name)
end
it "should only keep the last copy when multiple instances of a Resource are inserted" do
@@ -156,12 +172,41 @@ describe Chef::ResourceCollection::ResourceSet do
end
it "should raise an exception if you pass something other than a string or hash to resource" do
- expect { collection.find([Array.new]) }.to raise_error(ArgumentError)
+ expect { collection.find([[]]) }.to raise_error(ArgumentError)
end
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
@@ -170,7 +215,7 @@ describe Chef::ResourceCollection::ResourceSet do
end
it "accepts a single-element :resource_type => 'resource_name' Hash" do
- expect(collection.validate_lookup_spec!(:service => "apache2")).to be_truthy
+ expect(collection.validate_lookup_spec!(service: "apache2")).to be_truthy
end
it "accepts a chef resource object" do
diff --git a/spec/unit/resource_collection/stepable_iterator_spec.rb b/spec/unit/resource_collection/stepable_iterator_spec.rb
index 6354b1b7fb..ec831c9afe 100644
--- a/spec/unit/resource_collection/stepable_iterator_spec.rb
+++ b/spec/unit/resource_collection/stepable_iterator_spec.rb
@@ -72,7 +72,7 @@ describe Chef::ResourceCollection::StepableIterator do
@collection << lambda { @snitch_var = 42 }
@iterator = CRSI.for_collection(@collection)
- @iterator.each { |proc| proc.call }
+ @iterator.each(&:call)
end
it "allows the iteration to be paused" do
@@ -124,7 +124,7 @@ describe Chef::ResourceCollection::StepableIterator do
it "allows the iteration to start by being stepped" do
@snitch_var = nil
@iterator = CRSI.for_collection(@collection)
- @iterator.iterate_on(:element) { |proc| proc.call }
+ @iterator.iterate_on(:element, &:call)
@iterator.step
expect(@iterator.position).to eq(1)
expect(@snitch_var).to eq(23)
diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb
index 5feb34833a..b65d464f23 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 (c) 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::ResourceCollection do
- let(:rc) { Chef::ResourceCollection.new() }
+ let(:rc) { Chef::ResourceCollection.new }
let(:resource) { Chef::Resource::ZenMaster.new("makoto") }
it "should throw an error when calling a non-delegated method" do
@@ -65,7 +65,7 @@ describe Chef::ResourceCollection do
end
it "should accept named arguments in any order" do
- rc.insert(resource, :instance_name => "foo", :resource_type => "bar")
+ rc.insert(resource, instance_name: "foo", resource_type: "bar")
expect(rc[0]).to eq(resource)
end
@@ -97,7 +97,7 @@ describe Chef::ResourceCollection do
describe "each" do
it "should allow you to iterate over every resource in the collection" do
load_up_resources
- results = Array.new
+ results = []
expect do
rc.each do |r|
results << r.name
@@ -119,7 +119,7 @@ describe Chef::ResourceCollection do
describe "each_index" do
it "should allow you to iterate over every resource by index" do
load_up_resources
- results = Array.new
+ results = []
expect do
rc.each_index do |i|
results << rc[i].name
@@ -183,6 +183,14 @@ describe Chef::ResourceCollection do
expect(rc).to be_empty
end
+ it "should allow to delete resources with different providers" do
+ pkg = Chef::Resource::YumPackage.new("monkey")
+ rc.insert(pkg, instance_name: "monkey", resource_type: "package")
+ expect(rc).not_to be_empty
+ expect(rc.delete("package[monkey]")).to eql(pkg)
+ expect(rc).to be_empty
+ end
+
it "should raise an exception if you send something strange to delete" do
expect { rc.delete(:symbol) }.to raise_error(ArgumentError)
end
@@ -196,19 +204,19 @@ describe Chef::ResourceCollection do
it "should find a resource by symbol and name (:zen_master => monkey)" do
load_up_resources
- expect(rc.resources(:zen_master => "monkey").name).to eql("monkey")
+ expect(rc.resources(zen_master: "monkey").name).to eql("monkey")
end
it "should find a resource by symbol and array of names (:zen_master => [a,b])" do
load_up_resources
- results = rc.resources(:zen_master => %w{monkey dog})
+ results = rc.resources(zen_master: %w{monkey dog})
expect(results.length).to eql(2)
check_by_names(results, "monkey", "dog")
end
it "should find resources of multiple kinds (:zen_master => a, :file => b)" do
load_up_resources
- results = rc.resources(:zen_master => "monkey", :file => "something")
+ results = rc.resources(zen_master: "monkey", file: "something")
expect(results.length).to eql(2)
check_by_names(results, "monkey", "something")
end
@@ -219,6 +227,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)
@@ -237,7 +246,7 @@ describe Chef::ResourceCollection do
end
it "should raise an exception if you pass something other than a string or hash to resource" do
- expect { rc.resources([Array.new]) }.to raise_error(ArgumentError)
+ expect { rc.resources([[]]) }.to raise_error(ArgumentError)
end
it "raises an error when attempting to find a resource that does not exist" do
@@ -252,7 +261,7 @@ describe Chef::ResourceCollection do
end
it "accepts a single-element :resource_type => 'resource_name' Hash" do
- expect(rc.validate_lookup_spec!(:service => "apache2")).to be_truthy
+ expect(rc.validate_lookup_spec!(service: "apache2")).to be_truthy
end
it "accepts a chef resource object" do
@@ -291,10 +300,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 cc19cc7a01..8a75fe5d31 100644
--- a/spec/unit/resource_definition_spec.rb
+++ b/spec/unit/resource_definition_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +19,7 @@
require "spec_helper"
describe Chef::ResourceDefinition do
- let(:defn) { Chef::ResourceDefinition.new() }
+ let(:defn) { Chef::ResourceDefinition.new }
describe "initialize" do
it "should be a Chef::ResourceDefinition" do
@@ -66,13 +66,13 @@ describe Chef::ResourceDefinition do
it "should accept a new definition with a hash" do
expect do
- defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do
+ defn.define :smoke, cigar: "cuban", cigarette: "marlboro" do
end
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") {}
+ defn.define(:smoke, cigar: "cuban", cigarette: "marlboro") {}
expect(defn.params[:cigar]).to eql("cuban")
expect(defn.params[:cigarette]).to eql("marlboro")
end
@@ -92,7 +92,7 @@ describe Chef::ResourceDefinition do
it "should raise an exception if prototype_params is not a hash" do
expect do
- defn.define :monkey, Array.new do
+ defn.define :monkey, [] do
end
end.to raise_error(ArgumentError)
end
diff --git a/spec/unit/resource_inspector_spec.rb b/spec/unit/resource_inspector_spec.rb
new file mode 100644
index 0000000000..f3a4b2aa0a
--- /dev/null
+++ b/spec/unit/resource_inspector_spec.rb
@@ -0,0 +1,60 @@
+# Copyright:: Copyright (c) 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 Chef::ResourceInspector do
+ describe "inspecting a resource" do
+ subject { Chef::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 %i{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 { Chef::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 51075a7d44..69d5b28e7e 100644
--- a/spec/unit/resource_reporter_spec.rb
+++ b/spec/unit/resource_reporter_spec.rb
@@ -1,9 +1,9 @@
-#
+
# Author:: Daniel DeLeo (<dan@chef.io>)
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Tyler Cloke (<tyler@chef.io>)
#
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,62 +19,75 @@
# limitations under the License.
#
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
require "chef/resource_reporter"
require "socket"
describe Chef::ResourceReporter do
- before(:all) do
- @reporting_toggle_default = Chef::Config[:enable_reporting]
+ before(:each) do
Chef::Config[:enable_reporting] = true
end
- after(:all) do
- Chef::Config[:enable_reporting] = @reporting_toggle_default
- end
+ let(:node) { Chef::Node.new }
+
+ let(:rest_client) { double("Chef::ServerAPI (mock)") }
+
+ let(:resource_reporter) { Chef::ResourceReporter.new(rest_client) }
+
+ let(:new_resource) { Chef::Resource::File.new("/tmp/a-file.txt") }
+
+ let(:current_resource) { Chef::Resource::File.new("/tmp/a-file.txt") }
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:run_status) { Chef::RunStatus.new(node, events) }
+
+ let(:start_time) { Time.new }
+
+ let(:end_time) { Time.new + 20 }
+
+ let(:run_list) { Chef::RunList.new }
+
+ let(:run_id) { run_status.run_id }
+
+ let(:expansion) { Chef::RunList::RunListExpansion.new("_default", run_list.run_list_items) }
+
+ let(:cookbook_name) { "monkey" }
+
+ let(:cookbook_version) { double("Cookbook::Version", version: "1.2.3") }
+
+ let(:action_collection) { Chef::ActionCollection.new(events) }
before do
- @node = Chef::Node.new
- @node.name("spitfire")
- @rest_client = double("Chef::ServerAPI (mock)")
- allow(@rest_client).to receive(:post).and_return(true)
- @resource_reporter = Chef::ResourceReporter.new(@rest_client)
- @new_resource = Chef::Resource::File.new("/tmp/a-file.txt")
- @cookbook_name = "monkey"
- @new_resource.cookbook_name = @cookbook_name
- @cookbook_version = double("Cookbook::Version", :version => "1.2.3")
- allow(@new_resource).to receive(:cookbook_version).and_return(@cookbook_version)
- @current_resource = Chef::Resource::File.new("/tmp/a-file.txt")
- @start_time = Time.new
- @end_time = Time.new + 20
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @run_status = Chef::RunStatus.new(@node, @events)
- @run_list = Chef::RunList.new
- @run_list << "recipe[lobster]" << "role[rage]" << "recipe[fist]"
- @expansion = Chef::RunList::RunListExpansion.new("_default", @run_list.run_list_items)
- @run_id = @run_status.run_id
- allow(Time).to receive(:now).and_return(@start_time, @end_time)
+ node.name("spitfire")
+ allow(rest_client).to receive(:post).and_return(true)
+ new_resource.cookbook_name = cookbook_name
+ allow(new_resource).to receive(:cookbook_version).and_return(cookbook_version)
+ run_list << "recipe[lobster]" << "role[rage]" << "recipe[fist]"
+ allow(Time).to receive(:now).and_return(start_time, end_time)
+ events.register(action_collection)
+ events.register(resource_reporter)
+ events.cookbook_compilation_start(run_context)
end
context "when first created" do
it "has no updated resources" do
- expect(@resource_reporter.updated_resources.size).to eq(0)
+ expect(resource_reporter.updated_resources.count).to eq(0)
end
it "reports a successful run" do
- expect(@resource_reporter.status).to eq("success")
+ expect(resource_reporter.status).to eq("success")
end
it "assumes the resource history feature is supported" do
- expect(@resource_reporter.reporting_enabled?).to be_truthy
+ expect(resource_reporter.send(:reporting_enabled?)).to be_truthy
end
it "should have no error_descriptions" do
- expect(@resource_reporter.error_descriptions).to eq({})
- # @resource_reporter.error_descriptions.should be_empty
- # @resource_reporter.should have(0).error_descriptions
+ expect(resource_reporter.error_descriptions).to eq({})
end
end
@@ -86,49 +99,50 @@ describe Chef::ResourceReporter do
it "reports a successful run" do
skip "refactor how node gets set."
- expect(@resource_reporter.status).to eq("success")
+ expect(resource_reporter.status).to eq("success")
end
end
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
context "before converging any resources" do
+ let(:exception) { Exception.new }
+
before do
- @resource_reporter.run_started(@run_status)
- @exception = Exception.new
- @resource_reporter.run_failed(@exception)
+ resource_reporter.run_started(run_status)
+ resource_reporter.run_failed(exception)
end
it "sets the run status to 'failure'" do
- expect(@resource_reporter.status).to eq("failure")
+ expect(resource_reporter.status).to eq("failure")
end
it "keeps the exception data" do
- expect(@resource_reporter.exception).to eq(@exception)
+ expect(resource_reporter.exception).to eq(exception)
end
end
context "when a resource fails before loading current state" do
before do
- @exception = Exception.new
- @exception.set_backtrace(caller)
- @resource_reporter.resource_action_start(@new_resource, :create)
- @resource_reporter.resource_failed(@new_resource, :create, @exception)
- @resource_reporter.resource_completed(@new_resource)
+ exception = Exception.new
+ exception.set_backtrace(caller)
+ events.resource_action_start(new_resource, :create)
+ events.resource_failed(new_resource, :create, exception)
+ events.resource_completed(new_resource)
end
it "collects the resource as an updated resource" do
- expect(@resource_reporter.updated_resources.size).to eq(1)
+ expect(resource_reporter.updated_resources.count).to eq(1)
end
it "collects the desired state of the resource" do
- update_record = @resource_reporter.updated_resources.first
- expect(update_record.new_resource).to eq(@new_resource)
+ update_record = resource_reporter.updated_resources.first
+ expect(update_record.new_resource).to eq(new_resource)
end
end
@@ -137,58 +151,73 @@ describe Chef::ResourceReporter do
context "once the a resource's current state is loaded" do
before do
- @resource_reporter.resource_action_start(@new_resource, :create)
- @resource_reporter.resource_current_state_loaded(@new_resource, :create, @current_resource)
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
end
context "and the resource was not updated" do
before do
- @resource_reporter.resource_up_to_date(@new_resource, :create)
+ events.resource_up_to_date(new_resource, :create)
+ events.resource_completed(new_resource)
end
it "has no updated resources" do
- expect(@resource_reporter.updated_resources.size).to eq(0)
+ expect(resource_reporter.updated_resources.count).to eq(0)
+ end
+ end
+
+ context "and the resource was skipped" do
+ before do
+ conditional = nil
+ events.resource_skipped(new_resource, :create, conditional)
+ events.resource_completed(new_resource)
+ end
+
+ it "has no updated resources" do
+ expect(resource_reporter.updated_resources.count).to eq(0)
end
end
context "and the resource was updated" do
before do
- @new_resource.content("this is the old content")
- @current_resource.content("this is the new hotness")
- @resource_reporter.resource_updated(@new_resource, :create)
- @resource_reporter.resource_completed(@new_resource)
+ new_resource.content("this is the old content")
+ current_resource.content("this is the new hotness")
+ events.resource_updated(new_resource, :create)
+ events.resource_completed(new_resource)
end
it "collects the updated resource" do
- expect(@resource_reporter.updated_resources.size).to eq(1)
+ expect(resource_reporter.updated_resources.count).to eq(1)
end
it "collects the old state of the resource" do
- update_record = @resource_reporter.updated_resources.first
- expect(update_record.current_resource).to eq(@current_resource)
+ update_record = resource_reporter.updated_resources.first
+ expect(update_record.current_resource).to eq(current_resource)
end
it "collects the new state of the resource" do
- update_record = @resource_reporter.updated_resources.first
- expect(update_record.new_resource).to eq(@new_resource)
+ update_record = resource_reporter.updated_resources.first
+ expect(update_record.new_resource).to eq(new_resource)
end
context "and a subsequent resource fails before loading current resource" do
+ let(:next_new_resource) { Chef::Resource::Service.new("apache2") }
+
before do
- @next_new_resource = Chef::Resource::Service.new("apache2")
- @exception = Exception.new
- @exception.set_backtrace(caller)
- @resource_reporter.resource_failed(@next_new_resource, :create, @exception)
- @resource_reporter.resource_completed(@next_new_resource)
+ exception = Exception.new
+ exception.set_backtrace(caller)
+ events.resource_action_start(next_new_resource, :create)
+ events.resource_failed(next_new_resource, :create, exception)
+ events.resource_completed(next_new_resource)
end
it "collects the desired state of the failed resource" do
- failed_resource_update = @resource_reporter.updated_resources.last
- expect(failed_resource_update.new_resource).to eq(@next_new_resource)
+ failed_resource_update = resource_reporter.updated_resources.last
+ expect(failed_resource_update.new_resource).to eq(next_new_resource)
end
it "does not have the current state of the failed resource" do
- failed_resource_update = @resource_reporter.updated_resources.last
+ failed_resource_update = resource_reporter.updated_resources.last
expect(failed_resource_update.current_resource).to be_nil
end
end
@@ -200,56 +229,56 @@ describe Chef::ResourceReporter do
# used for implementation.
context "and a nested resource is updated" do
before do
- @implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt")
- @resource_reporter.resource_action_start(@implementation_resource , :create)
- @resource_reporter.resource_current_state_loaded(@implementation_resource, :create, @implementation_resource)
- @resource_reporter.resource_updated(@implementation_resource, :create)
- @resource_reporter.resource_completed(@implementation_resource)
- @resource_reporter.resource_updated(@new_resource, :create)
- @resource_reporter.resource_completed(@new_resource)
+ implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt")
+ events.resource_action_start(implementation_resource , :create)
+ events.resource_current_state_loaded(implementation_resource, :create, implementation_resource)
+ events.resource_updated(implementation_resource, :create)
+ events.resource_completed(implementation_resource)
+ events.resource_updated(new_resource, :create)
+ events.resource_completed(new_resource)
end
it "does not collect data about the nested resource" do
- expect(@resource_reporter.updated_resources.size).to eq(1)
+ expect(resource_reporter.updated_resources.count).to eq(1)
end
end
context "and a nested resource runs but is not updated" do
before do
- @implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt")
- @resource_reporter.resource_action_start(@implementation_resource , :create)
- @resource_reporter.resource_current_state_loaded(@implementation_resource, :create, @implementation_resource)
- @resource_reporter.resource_up_to_date(@implementation_resource, :create)
- @resource_reporter.resource_completed(@implementation_resource)
- @resource_reporter.resource_updated(@new_resource, :create)
- @resource_reporter.resource_completed(@new_resource)
+ implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt")
+ events.resource_action_start(implementation_resource , :create)
+ events.resource_current_state_loaded(implementation_resource, :create, implementation_resource)
+ events.resource_up_to_date(implementation_resource, :create)
+ events.resource_completed(implementation_resource)
+ events.resource_updated(new_resource, :create)
+ events.resource_completed(new_resource)
end
it "does not collect data about the nested resource" do
- expect(@resource_reporter.updated_resources.size).to eq(1)
+ expect(resource_reporter.updated_resources.count).to eq(1)
end
end
context "and the resource failed to converge" do
before do
- @exception = Exception.new
- @exception.set_backtrace(caller)
- @resource_reporter.resource_failed(@new_resource, :create, @exception)
- @resource_reporter.resource_completed(@new_resource)
+ exception = Exception.new
+ exception.set_backtrace(caller)
+ events.resource_failed(new_resource, :create, exception)
+ events.resource_completed(new_resource)
end
it "collects the resource as an updated resource" do
- expect(@resource_reporter.updated_resources.size).to eq(1)
+ expect(resource_reporter.updated_resources.count).to eq(1)
end
it "collects the desired state of the resource" do
- update_record = @resource_reporter.updated_resources.first
- expect(update_record.new_resource).to eq(@new_resource)
+ update_record = resource_reporter.updated_resources.first
+ expect(update_record.new_resource).to eq(new_resource)
end
it "collects the current state of the resource" do
- update_record = @resource_reporter.updated_resources.first
- expect(update_record.current_resource).to eq(@current_resource)
+ update_record = resource_reporter.updated_resources.first
+ expect(update_record.current_resource).to eq(current_resource)
end
end
@@ -259,10 +288,30 @@ 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 a sensitive execute resource" 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)
+ events.resource_action_start(@execute_resource, :run)
+ events.resource_current_state_loaded(@execute_resource, :run, current_resource)
+ events.resource_updated(@execute_resource, :run)
+ events.resource_completed(@execute_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
+ @first_update_report = @report["resources"].first
+ end
- @resource_reporter.run_started(@run_status)
+ it "resource_name in prepared_run_data should be the same" do
+ expect(@first_update_report["name"]).to eq("sensitive-resource")
+ end
end
context "when the new_resource does not have a string for name and identity" do
@@ -272,12 +321,12 @@ describe Chef::ResourceReporter do
allow(@bad_resource).to receive(:name).and_return(nil)
allow(@bad_resource).to receive(:identity).and_return(nil)
allow(@bad_resource).to receive(:path).and_return(nil)
- @resource_reporter.resource_action_start(@bad_resource, :create)
- @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource)
- @resource_reporter.resource_updated(@bad_resource, :create)
- @resource_reporter.resource_completed(@bad_resource)
- @run_status.stop_clock
- @report = @resource_reporter.prepare_run_data
+ events.resource_action_start(@bad_resource, :create)
+ events.resource_current_state_loaded(@bad_resource, :create, current_resource)
+ events.resource_updated(@bad_resource, :create)
+ events.resource_completed(@bad_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
@first_update_report = @report["resources"].first
end
@@ -293,15 +342,15 @@ describe Chef::ResourceReporter do
context "the new_resource name and id are hashes" do
before do
@bad_resource = Chef::Resource::File.new("/tmp/filename_as_hash.txt")
- allow(@bad_resource).to receive(:name).and_return({ :foo => :bar })
- allow(@bad_resource).to receive(:identity).and_return({ :foo => :bar })
- allow(@bad_resource).to receive(:path).and_return({ :foo => :bar })
- @resource_reporter.resource_action_start(@bad_resource, :create)
- @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource)
- @resource_reporter.resource_updated(@bad_resource, :create)
- @resource_reporter.resource_completed(@bad_resource)
- @run_status.stop_clock
- @report = @resource_reporter.prepare_run_data
+ allow(@bad_resource).to receive(:name).and_return({ foo: :bar })
+ allow(@bad_resource).to receive(:identity).and_return({ foo: :bar })
+ allow(@bad_resource).to receive(:path).and_return({ foo: :bar })
+ events.resource_action_start(@bad_resource, :create)
+ events.resource_current_state_loaded(@bad_resource, :create, current_resource)
+ events.resource_updated(@bad_resource, :create)
+ events.resource_completed(@bad_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
@first_update_report = @report["resources"].first
end
# Ruby 1.8.7 flattens out hash to string using join instead of inspect, resulting in
@@ -353,12 +402,12 @@ describe Chef::ResourceReporter do
# "status" : "success"
# "data" : ""
# }
- @resource_reporter.resource_action_start(new_resource, :create)
- @resource_reporter.resource_current_state_loaded(new_resource, :create, current_resource)
- @resource_reporter.resource_updated(new_resource, :create)
- @resource_reporter.resource_completed(new_resource)
- @run_status.stop_clock
- @report = @resource_reporter.prepare_run_data
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_updated(new_resource, :create)
+ events.resource_completed(new_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
@first_update_report = @report["resources"].first
end
@@ -375,18 +424,18 @@ 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
expect(@first_update_report["name"]).to eq(new_resource.name)
end
- it "includes the resource's id attribute" do
+ it "includes the resource's id property" do
expect(@first_update_report["id"]).to eq(new_resource.identity)
end
@@ -394,6 +443,7 @@ describe Chef::ResourceReporter do
# TODO: API takes integer number of milliseconds as a string. This
# should be an int.
expect(@first_update_report).to have_key("duration")
+ expect(@first_update_report["duration"]).to be_kind_of(String)
expect(@first_update_report["duration"].to_i).to be_within(100).of(0)
end
@@ -404,7 +454,7 @@ describe Chef::ResourceReporter do
it "includes the cookbook name of the resource" do
expect(@first_update_report).to have_key("cookbook_name")
- expect(@first_update_report["cookbook_name"]).to eq(@cookbook_name)
+ expect(@first_update_report["cookbook_name"]).to eq(cookbook_name)
end
it "includes the cookbook version of the resource" do
@@ -424,7 +474,7 @@ describe Chef::ResourceReporter do
it "includes the run_list" do
expect(@report).to have_key("run_list")
- expect(@report["run_list"]).to eq(Chef::JSONCompat.to_json(@run_status.node.run_list))
+ expect(@report["run_list"]).to eq(Chef::JSONCompat.to_json(run_status.node.run_list))
end
it "includes the expanded_run_list" do
@@ -433,30 +483,27 @@ describe Chef::ResourceReporter do
it "includes the end_time" do
expect(@report).to have_key("end_time")
- expect(@report["end_time"]).to eq(@run_status.end_time.to_s)
+ expect(@report["end_time"]).to eq(run_status.end_time.to_s)
end
end
context "when the resource is a File" do
- let(:new_resource) { @new_resource }
- let(:current_resource) { @current_resource }
-
it_should_behave_like "a successful client run"
end
context "when the resource is a RegistryKey with binary data" do
let(:new_resource) do
resource = Chef::Resource::RegistryKey.new('Wubba\Lubba\Dub\Dubs')
- resource.values([ { :name => "rick", :type => :binary, :data => 255.chr * 1 } ])
- allow(resource).to receive(:cookbook_name).and_return(@cookbook_name)
- allow(resource).to receive(:cookbook_version).and_return(@cookbook_version)
+ resource.values([ { name: "rick", type: :binary, data: 255.chr * 1 } ])
+ allow(resource).to receive(:cookbook_name).and_return(cookbook_name)
+ allow(resource).to receive(:cookbook_version).and_return(cookbook_version)
resource
end
let(:current_resource) do
resource = Chef::Resource::RegistryKey.new('Wubba\Lubba\Dub\Dubs')
- resource.values([ { :name => "rick", :type => :binary, :data => 255.chr * 1 } ])
+ resource.values([ { name: "rick", type: :binary, data: 255.chr * 1 } ])
resource
end
@@ -467,21 +514,21 @@ describe Chef::ResourceReporter do
before do
@backtrace = ["foo.rb:1 in `foo!'", "bar.rb:2 in `bar!", "'baz.rb:3 in `baz!'"]
- @node = Chef::Node.new
- @node.name("spitfire")
+ node = Chef::Node.new
+ node.name("spitfire")
@exception = ArgumentError.new
- allow(@exception).to receive(:inspect).and_return("Net::HTTPServerException")
+ allow(@exception).to receive(:inspect).and_return("Net::HTTPClientException")
allow(@exception).to receive(:message).and_return("Object not found")
allow(@exception).to receive(:backtrace).and_return(@backtrace)
- @resource_reporter.run_list_expand_failed(@node, @exception)
- @resource_reporter.run_failed(@exception)
- @report = @resource_reporter.prepare_run_data
+ resource_reporter.run_list_expand_failed(node, @exception)
+ resource_reporter.run_failed(@exception)
+ @report = resource_reporter.prepare_run_data
end
it "includes the exception type in the event data" do
expect(@report).to have_key("data")
expect(@report["data"]["exception"]).to have_key("class")
- expect(@report["data"]["exception"]["class"]).to eq("Net::HTTPServerException")
+ expect(@report["data"]["exception"]["class"]).to eq("Net::HTTPClientException")
end
it "includes the exception message in the event data" do
@@ -506,35 +553,36 @@ describe Chef::ResourceReporter do
@bad_resource = Chef::Resource::File.new("/tmp/a-file.txt")
@bad_resource.cookbook_name = nil
- @resource_reporter.resource_action_start(@bad_resource, :create)
- @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource)
- @resource_reporter.resource_updated(@bad_resource, :create)
- @resource_reporter.resource_completed(@bad_resource)
- @run_status.stop_clock
- @report = @resource_reporter.prepare_run_data
+ events.resource_action_start(@bad_resource, :create)
+ events.resource_current_state_loaded(@bad_resource, :create, current_resource)
+ events.resource_updated(@bad_resource, :create)
+ events.resource_completed(@bad_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
@first_update_report = @report["resources"].first
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
- expect(@first_update_report["name"]).to eq(@new_resource.name)
+ expect(@first_update_report["name"]).to eq(new_resource.name)
end
- it "includes the resource's id attribute" do
- expect(@first_update_report["id"]).to eq(@new_resource.identity)
+ it "includes the resource's id property" do
+ expect(@first_update_report["id"]).to eq(new_resource.identity)
end
it "includes the elapsed time for the resource to converge" do
# TODO: API takes integer number of milliseconds as a string. This
# should be an int.
expect(@first_update_report).to have_key("duration")
+ expect(@first_update_report["duration"]).to be_kind_of(String)
expect(@first_update_report["duration"].to_i).to be_within(100).of(0)
end
@@ -554,17 +602,17 @@ describe Chef::ResourceReporter do
context "when including a resource that overrides Resource#state" do
before do
- @current_state_resource = Chef::Resource::WithState.new("Stateful", @run_context)
+ @current_state_resource = Chef::Resource::WithState.new("Stateful", run_context)
@current_state_resource.state = nil
- @new_state_resource = Chef::Resource::WithState.new("Stateful", @run_context)
+ @new_state_resource = Chef::Resource::WithState.new("Stateful", run_context)
@new_state_resource.state = "Running"
- @resource_reporter.resource_action_start(@new_state_resource, :create)
- @resource_reporter.resource_current_state_loaded(@new_state_resource, :create, @current_state_resource)
- @resource_reporter.resource_updated(@new_state_resource, :create)
- @resource_reporter.resource_completed(@new_state_resource)
- @run_status.stop_clock
- @report = @resource_reporter.prepare_run_data
+ events.resource_action_start(@new_state_resource, :create)
+ events.resource_current_state_loaded(@new_state_resource, :create, @current_state_resource)
+ events.resource_updated(@new_state_resource, :create)
+ events.resource_completed(@new_state_resource)
+ run_status.stop_clock
+ @report = resource_reporter.prepare_run_data
@first_update_report = @report["resources"].first
end
@@ -583,36 +631,36 @@ describe Chef::ResourceReporter do
describe "when updating resource history on the server" do
before do
- @resource_reporter.run_started(@run_status)
- @run_status.start_clock
+ resource_reporter.run_started(run_status)
+ run_status.start_clock
end
context "when the server does not support storing resource history" do
before do
# 404 getting the run_id
@response = Net::HTTPNotFound.new("a response body", "404", "Not Found")
- @error = Net::HTTPServerException.new("404 message", @response)
- expect(@rest_client).to receive(:post).
- with("reports/nodes/spitfire/runs", { :action => :start, :run_id => @run_id,
- :start_time => @start_time.to_s },
- { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION }).
- and_raise(@error)
+ @error = Net::HTTPClientException.new("404 message", @response)
+ expect(rest_client).to receive(:post)
+ .with("reports/nodes/spitfire/runs", { action: :start, run_id: @run_id,
+ start_time: start_time.to_s },
+ { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION })
+ .and_raise(@error)
end
it "assumes the feature is not enabled" do
- @resource_reporter.run_started(@run_status)
- expect(@resource_reporter.reporting_enabled?).to be_falsey
+ resource_reporter.run_started(run_status)
+ expect(resource_reporter.send(:reporting_enabled?)).to be_falsey
end
it "does not send a resource report to the server" do
- @resource_reporter.run_started(@run_status)
- expect(@rest_client).not_to receive(:post)
- @resource_reporter.run_completed(@node)
+ resource_reporter.run_started(run_status)
+ expect(rest_client).not_to receive(:post)
+ resource_reporter.run_completed(node)
end
it "prints an error about the 404" do
- expect(Chef::Log).to receive(:debug).with(/404/)
- @resource_reporter.run_started(@run_status)
+ expect(Chef::Log).to receive(:trace).with(/404/)
+ resource_reporter.run_started(run_status)
end
end
@@ -621,27 +669,27 @@ describe Chef::ResourceReporter do
before do
# 500 getting the run_id
@response = Net::HTTPInternalServerError.new("a response body", "500", "Internal Server Error")
- @error = Net::HTTPServerException.new("500 message", @response)
- expect(@rest_client).to receive(:post).
- with("reports/nodes/spitfire/runs", { :action => :start, :run_id => @run_id, :start_time => @start_time.to_s },
- { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION }).
- and_raise(@error)
+ @error = Net::HTTPClientException.new("500 message", @response)
+ expect(rest_client).to receive(:post)
+ .with("reports/nodes/spitfire/runs", { action: :start, run_id: @run_id, start_time: start_time.to_s },
+ { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION })
+ .and_raise(@error)
end
it "assumes the feature is not enabled" do
- @resource_reporter.run_started(@run_status)
- expect(@resource_reporter.reporting_enabled?).to be_falsey
+ resource_reporter.run_started(run_status)
+ expect(resource_reporter.send(:reporting_enabled?)).to be_falsey
end
it "does not send a resource report to the server" do
- @resource_reporter.run_started(@run_status)
- expect(@rest_client).not_to receive(:post)
- @resource_reporter.run_completed(@node)
+ resource_reporter.run_started(run_status)
+ expect(rest_client).not_to receive(:post)
+ resource_reporter.run_completed(node)
end
it "prints an error about the error" do
expect(Chef::Log).to receive(:info).with(/500/)
- @resource_reporter.run_started(@run_status)
+ resource_reporter.run_started(run_status)
end
end
@@ -651,11 +699,11 @@ describe Chef::ResourceReporter do
Chef::Config[:enable_reporting_url_fatals] = true
# 500 getting the run_id
@response = Net::HTTPInternalServerError.new("a response body", "500", "Internal Server Error")
- @error = Net::HTTPServerException.new("500 message", @response)
- expect(@rest_client).to receive(:post).
- with("reports/nodes/spitfire/runs", { :action => :start, :run_id => @run_id, :start_time => @start_time.to_s },
- { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION }).
- and_raise(@error)
+ @error = Net::HTTPClientException.new("500 message", @response)
+ expect(rest_client).to receive(:post)
+ .with("reports/nodes/spitfire/runs", { action: :start, run_id: @run_id, start_time: start_time.to_s },
+ { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION })
+ .and_raise(@error)
end
after do
@@ -665,37 +713,37 @@ 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 do
- @resource_reporter.run_started(@run_status)
- end.to raise_error(Net::HTTPServerException)
+ resource_reporter.run_started(run_status)
+ end.to raise_error(Net::HTTPClientException)
end
end
context "after creating the run history document" do
before do
response = { "uri" => "https://example.com/reports/nodes/spitfire/runs/@run_id" }
- expect(@rest_client).to receive(:post).
- with("reports/nodes/spitfire/runs", { :action => :start, :run_id => @run_id, :start_time => @start_time.to_s },
- { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION }).
- and_return(response)
- @resource_reporter.run_started(@run_status)
+ expect(rest_client).to receive(:post)
+ .with("reports/nodes/spitfire/runs", { action: :start, run_id: @run_id, start_time: start_time.to_s },
+ { "X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION })
+ .and_return(response)
+ resource_reporter.run_started(run_status)
end
it "creates a run document on the server at the start of the run" do
- expect(@resource_reporter.run_id).to eq(@run_id)
+ expect(resource_reporter.run_id).to eq(@run_id)
end
it "updates the run document with resource updates at the end of the run" do
# update some resources...
- @resource_reporter.resource_action_start(@new_resource, :create)
- @resource_reporter.resource_current_state_loaded(@new_resource, :create, @current_resource)
- @resource_reporter.resource_updated(@new_resource, :create)
+ events.resource_action_start(new_resource, :create)
+ events.resource_current_state_loaded(new_resource, :create, current_resource)
+ events.resource_updated(new_resource, :create)
- allow(@resource_reporter).to receive(:end_time).and_return(@end_time)
- @expected_data = @resource_reporter.prepare_run_data
+ allow(resource_reporter).to receive(:end_time).and_return(end_time)
+ @expected_data = resource_reporter.prepare_run_data
response = { "result" => "ok" }
- expect(@rest_client).to receive(:raw_request).ordered do |method, url, headers, data|
+ expect(rest_client).to receive(:raw_request).ordered do |method, url, headers, data|
expect(method).to eq(:POST)
expect(headers).to eq({ "Content-Encoding" => "gzip",
"X-Ops-Reporting-Protocol-Version" => Chef::ResourceReporter::PROTOCOL_VERSION,
@@ -706,7 +754,7 @@ describe Chef::ResourceReporter do
response
end
- @resource_reporter.run_completed(@node)
+ resource_reporter.run_completed(node)
end
end
@@ -722,35 +770,35 @@ describe Chef::ResourceReporter do
it "should log 4xx errors" do
response = Net::HTTPClientError.new("forbidden", "403", "Forbidden")
- error = Net::HTTPServerException.new("403 message", response)
- allow(@rest_client).to receive(:raw_request).and_raise(error)
+ error = Net::HTTPClientException.new("403 message", response)
+ allow(rest_client).to receive(:raw_request).and_raise(error)
expect(Chef::Log).to receive(:error).with(/403/)
- @resource_reporter.post_reporting_data
+ resource_reporter.post_reporting_data
end
it "should log error 5xx errors" do
response = Net::HTTPServerError.new("internal error", "500", "Internal Server Error")
error = Net::HTTPFatalError.new("500 message", response)
- allow(@rest_client).to receive(:raw_request).and_raise(error)
+ allow(rest_client).to receive(:raw_request).and_raise(error)
expect(Chef::Log).to receive(:error).with(/500/)
- @resource_reporter.post_reporting_data
+ resource_reporter.post_reporting_data
end
it "should log if a socket error happens" do
- allow(@rest_client).to receive(:raw_request).and_raise(SocketError.new("test socket error"))
+ allow(rest_client).to receive(:raw_request).and_raise(SocketError.new("test socket error"))
expect(Chef::Log).to receive(:error).with(/test socket error/)
- @resource_reporter.post_reporting_data
+ resource_reporter.post_reporting_data
end
- it "should raise if an unkwown error happens" do
- allow(@rest_client).to receive(:raw_request).and_raise(Exception.new)
+ it "should raise if an unknown error happens" do
+ allow(rest_client).to receive(:raw_request).and_raise(Exception.new)
expect do
- @resource_reporter.post_reporting_data
+ resource_reporter.post_reporting_data
end.to raise_error(Exception)
end
end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 68fc675b37..7a19e0e8e1 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,10 +38,6 @@ describe Chef::Resource do
expect(resource.respond_to?(:shell_out!)).to be true
end
- it "should mixin shell_out_with_systems_locale" do
- expect(resource.respond_to?(:shell_out_with_systems_locale)).to be true
- end
-
describe "when inherited" do
it "adds an entry to a list of subclasses" do
@@ -119,14 +115,14 @@ describe Chef::Resource do
it "sets a list of state attributes" do
resource_class = Class.new(Chef::Resource)
resource_class.state_attrs(:checksum, :owner, :group, :mode)
- expect(resource_class.state_attrs).to match_array([:checksum, :owner, :group, :mode])
+ expect(resource_class.state_attrs).to match_array(%i{checksum owner group mode})
end
it "inherits state attributes from the superclass" do
resource_class = Class.new(Chef::Resource)
resource_subclass = Class.new(resource_class)
resource_class.state_attrs(:checksum, :owner, :group, :mode)
- expect(resource_subclass.state_attrs).to match_array([:checksum, :owner, :group, :mode])
+ expect(resource_subclass.state_attrs).to match_array(%i{checksum owner group mode})
end
it "combines inherited state attributes with non-inherited state attributes" do
@@ -134,7 +130,7 @@ describe Chef::Resource do
resource_subclass = Class.new(resource_class)
resource_class.state_attrs(:checksum, :owner)
resource_subclass.state_attrs(:group, :mode)
- expect(resource_subclass.state_attrs).to match_array([:checksum, :owner, :group, :mode])
+ expect(resource_subclass.state_attrs).to match_array(%i{checksum owner group mode})
end
end
@@ -160,8 +156,8 @@ describe Chef::Resource do
end
it "describes its state" do
- resource_state = file_resource.state
- expect(resource_state.keys).to match_array([:checksum, :owner, :group, :mode])
+ resource_state = file_resource.state_for_resource_reporter
+ expect(resource_state.keys).to match_array(%i{checksum owner group mode})
expect(resource_state[:checksum]).to eq("abc123")
expect(resource_state[:owner]).to eq("root")
expect(resource_state[:group]).to eq("wheel")
@@ -192,7 +188,6 @@ describe Chef::Resource do
describe "load_from" do
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)
@@ -205,7 +200,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
@@ -233,92 +227,101 @@ 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")
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee")
+ resource.notifies :reload, run_context.resource_collection.find(zen_master: "coffee")
expect(resource.delayed_notifications.detect { |e| e.resource.name == "coffee" && e.action == :reload }).not_to be_nil
end
it "should make notified resources be capable of acting immediately" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee"), :immediate
+ resource.notifies :reload, run_context.resource_collection.find(zen_master: "coffee"), :immediate
expect(resource.immediate_notifications.detect { |e| e.resource.name == "coffee" && e.action == :reload }).not_to be_nil
end
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 do
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee"), :someday
+ resource.notifies :reload, run_context.resource_collection.find(zen_master: "coffee"), :someday
end.to raise_error(ArgumentError)
end
it "should allow multiple notified resources appear in the actions hash" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee")
+ resource.notifies :reload, run_context.resource_collection.find(zen_master: "coffee")
expect(resource.delayed_notifications.detect { |e| e.resource.name == "coffee" && e.action == :reload }).not_to be_nil
run_context.resource_collection << Chef::Resource::ZenMaster.new("beans")
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "beans")
+ resource.notifies :reload, run_context.resource_collection.find(zen_master: "beans")
expect(resource.delayed_notifications.detect { |e| e.resource.name == "beans" && e.action == :reload }).not_to be_nil
end
it "creates a notification for a resource that is not yet in the resource collection" do
- resource.notifies(:restart, :service => "apache")
- expected_notification = Chef::Resource::Notification.new({ :service => "apache" }, :restart, resource)
+ resource.notifies(:restart, service: "apache")
+ expected_notification = Chef::Resource::Notification.new({ service: "apache" }, :restart, resource)
expect(resource.delayed_notifications).to include(expected_notification)
end
it "notifies another resource immediately" do
- resource.notifies_immediately(:restart, :service => "apache")
- expected_notification = Chef::Resource::Notification.new({ :service => "apache" }, :restart, resource)
+ resource.notifies_immediately(:restart, service: "apache")
+ expected_notification = Chef::Resource::Notification.new({ service: "apache" }, :restart, resource)
expect(resource.immediate_notifications).to include(expected_notification)
end
it "notifies a resource to take action at the end of the chef run" do
- resource.notifies_delayed(:restart, :service => "apache")
- expected_notification = Chef::Resource::Notification.new({ :service => "apache" }, :restart, resource)
+ resource.notifies_delayed(:restart, service: "apache")
+ expected_notification = Chef::Resource::Notification.new({ service: "apache" }, :restart, resource)
expect(resource.delayed_notifications).to include(expected_notification)
end
it "notifies a resource with an array for its name via its prettified string name" do
run_context.resource_collection << Chef::Resource::ZenMaster.new(%w{coffee tea})
- resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee, tea")
+ 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
it "should make resources appear in the actions hash of subscribed nodes" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- zr = run_context.resource_collection.find(:zen_master => "coffee")
+ zr = run_context.resource_collection.find(zen_master: "coffee")
resource.subscribes :reload, zr
expect(zr.delayed_notifications.detect { |e| e.resource.name == "funk" && e.action == :reload }).not_to be_nil
end
it "should make resources appear in the actions hash of subscribed nodes" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- zr = run_context.resource_collection.find(:zen_master => "coffee")
+ zr = run_context.resource_collection.find(zen_master: "coffee")
resource.subscribes :reload, zr
expect(zr.delayed_notifications.detect { |e| e.resource.name == resource.name && e.action == :reload }).not_to be_nil
run_context.resource_collection << Chef::Resource::ZenMaster.new("bean")
- zrb = run_context.resource_collection.find(:zen_master => "bean")
+ zrb = run_context.resource_collection.find(zen_master: "bean")
zrb.subscribes :reload, zr
expect(zr.delayed_notifications.detect { |e| e.resource.name == resource.name && e.action == :reload }).not_to be_nil
end
it "should make subscribed resources be capable of acting immediately" do
run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
- zr = run_context.resource_collection.find(:zen_master => "coffee")
+ zr = run_context.resource_collection.find(zen_master: "coffee")
resource.subscribes :reload, zr, :immediately
expect(zr.immediate_notifications.detect { |e| e.resource.name == resource.name && e.action == :reload }).not_to be_nil
end
@@ -354,6 +357,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 "suppresses 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 validation 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
+ 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
@@ -432,17 +492,19 @@ describe Chef::Resource do
expect(r.resource_name).to eq :blah
expect(r.declared_type).to eq :d
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)
+ # This tests some somewhat confusing behavior that used to occur due to the resource_name call
+ # automatically wiring up the old canonical provides line.
+ it "setting resoure_name does not override provides in prior resource" do
+ c1 = Class.new(Chef::Resource) do
+ resource_name :self_resource_name_test_4
+ provides :self_resource_name_test_4
+ end
+ c2 = Class.new(Chef::Resource) do
+ resource_name :self_resource_name_test_4
+ provides(:self_resource_name_test_4) { false } # simulates any filter that does not match
+ end
+ expect(Chef::Resource.resource_for_node(:self_resource_name_test_4, node)).to eql(c1)
end
end
@@ -462,11 +524,11 @@ describe Chef::Resource do
context "when the resource has a property with a default" 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)
+ expect(resource.to_hash.keys.sort).to eq(%i{a allowed_actions params provider updated
+ 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
@@ -474,11 +536,11 @@ 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 ]
+ expected_keys = %i{allowed_actions params provider updated
+ 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")
@@ -488,24 +550,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)
@@ -516,9 +566,14 @@ 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
@@ -565,31 +620,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 do
- class OverrideProviderBaseTest2 < Chef::Resource
- provider_base Chef::Provider::Package
- end
- 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
@@ -647,7 +689,7 @@ describe Chef::Resource do
snitch_variable = nil
resource.only_if { snitch_variable = true }
expect(resource.only_if.first.positivity).to eq(:only_if)
- #Chef::Mixin::Command.should_receive(:only_if).with(true, {}).and_return(false)
+ # Chef::Mixin::Command.should_receive(:only_if).with(true, {}).and_return(false)
resource.run_action(:purr)
expect(snitch_variable).to be_truthy
end
@@ -663,8 +705,8 @@ describe Chef::Resource do
it "accepts command options for only_if conditionals" do
expect_any_instance_of(Chef::Resource::Conditional).to receive(:evaluate_command).at_least(1).times
- resource.only_if("true", :cwd => "/tmp")
- expect(resource.only_if.first.command_opts).to eq({ :cwd => "/tmp" })
+ resource.only_if("true", cwd: "/tmp")
+ expect(resource.only_if.first.command_opts).to eq({ cwd: "/tmp" })
resource.run_action(:purr)
end
@@ -687,8 +729,8 @@ describe Chef::Resource do
end
it "accepts command options for not_if conditionals" do
- resource.not_if("pwd" , :cwd => "/tmp")
- expect(resource.not_if.first.command_opts).to eq({ :cwd => "/tmp" })
+ resource.not_if("pwd" , cwd: "/tmp")
+ expect(resource.not_if.first.command_opts).to eq({ cwd: "/tmp" })
end
it "accepts multiple not_if conditionals" do
@@ -810,7 +852,7 @@ describe Chef::Resource do
it "should print \"skipped due to action :nothing\" message for doc formatter when action is :nothing" do
fdoc = Chef::Formatters.new(:doc, STDOUT, STDERR)
allow(run_context).to receive(:events).and_return(fdoc)
- expect(fdoc).to receive(:puts).with(" (skipped due to action :nothing)", anything())
+ expect(fdoc).to receive(:puts).with(" (skipped due to action :nothing)", anything)
resource.should_skip?(:nothing)
end
@@ -838,10 +880,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 }
@@ -884,13 +924,20 @@ describe Chef::Resource do
klz.provides :energy, platform: %w{autobots decepticons}
end
- it "adds mappings for all platforms" do
+ it "adds mappings for all platforms", ruby: "< 2.7" do
expect(Chef.resource_handler_map).to receive(:set).with(
:tape_deck, Chef::Resource::Klz, {}
)
klz.provides :tape_deck
end
+ it "adds mappings for all platforms", ruby: ">= 2.7" do
+ expect(Chef.resource_handler_map).to receive(:set).with(
+ :tape_deck, Chef::Resource::Klz
+ )
+ klz.provides :tape_deck
+ end
+
end
describe "resource_for_node" do
@@ -925,7 +972,7 @@ describe Chef::Resource do
node.name("bumblebee")
node.automatic[:platform] = "autobots"
node.automatic[:platform_version] = "6.1"
- klz2.provides :dinobot, :platform => ["autobots"]
+ klz2.provides :dinobot, platform: ["autobots"]
Object.const_set("Grimlock", klz2)
klz2.provides :grimlock
end
@@ -940,6 +987,32 @@ describe Chef::Resource do
end
end
+ describe "chef_version constraints and the platform map" do
+ let(:klz3) { Class.new(Chef::Resource) }
+
+ it "doesn't wire up the provides when chef_version is < 1" do
+ klz3.provides :bulbasaur, chef_version: "< 1.0" # this should be false
+ expect { Chef::Resource.resource_for_node(:bulbasaur, node) }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "wires up the provides when chef_version is > 1" do
+ klz3.provides :bulbasaur, chef_version: "> 1.0" # this should be true
+ expect(Chef::Resource.resource_for_node(:bulbasaur, node)).to eql(klz3)
+ end
+
+ it "wires up the default when chef_version is < 1" do
+ klz3.chef_version_for_provides("< 1.0") # this should be false
+ klz3.provides :bulbasaur
+ expect { Chef::Resource.resource_for_node(:bulbasaur, node) }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "wires up the default when chef_version is > 1" do
+ klz3.chef_version_for_provides("> 1.0") # this should be true
+ klz3.provides :bulbasaur
+ expect(Chef::Resource.resource_for_node(:bulbasaur, node)).to eql(klz3)
+ end
+ end
+
end
describe "when creating notifications" do
@@ -968,7 +1041,7 @@ describe Chef::Resource do
describe "with a syntax error in the resource spec" do
- it "raises an exception immmediately" do
+ it "raises an exception immediately" do
expect do
resource.notifies(:run, "typo[missing-closing-bracket")
end.to raise_error(Chef::Exceptions::InvalidResourceSpecification)
@@ -1010,7 +1083,7 @@ describe Chef::Resource do
error_inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(resource, action, err)
description = Chef::Formatters::ErrorDescription.new("test")
error_inspector.add_explanation(description)
- Chef::Log.info("descrtiption: #{description.inspect},error_inspector: #{error_inspector}")
+ Chef::Log.info("description: #{description.inspect},error_inspector: #{error_inspector}")
description.sections[1]["Compiled Resource:"]
end
@@ -1066,8 +1139,8 @@ describe Chef::Resource do
end
context "with an array action" do
- before { resource.action([:two, :one]) }
- it { is_expected.to eq [:two, :one] }
+ before { resource.action(%i{two one}) }
+ it { is_expected.to eq %i{two one} }
end
context "with an assignment" do
@@ -1076,8 +1149,8 @@ describe Chef::Resource do
end
context "with an array assignment" do
- before { resource.action = [:two, :one] }
- it { is_expected.to eq [:two, :one] }
+ before { resource.action = %i{two one} }
+ it { is_expected.to eq %i{two one} }
end
context "with an invalid action" do
@@ -1114,8 +1187,122 @@ describe Chef::Resource do
end
context "with an array default action" do
- let(:default_action) { [:two, :one] }
- it { is_expected.to eq [:two, :one] }
+ let(:default_action) { %i{two one} }
+ it { is_expected.to eq %i{two one} }
+ end
+ end
+
+ describe ".preview_resource" do
+ let(:klass) { Class.new(Chef::Resource) }
+
+ before do
+ allow(Chef::DSL::Resources).to receive(:add_resource_dsl).with(:test_resource)
+ end
+
+ it "defaults to false" do
+ expect(klass.preview_resource).to eq false
+ end
+
+ it "can be set to true" do
+ klass.preview_resource(true)
+ expect(klass.preview_resource).to eq true
+ end
+
+ it "does not affect provides by default" do
+ expect(Chef.resource_handler_map).to receive(:set).with(:test_resource, klass, any_args)
+ klass.provides(:test_resource)
+ end
+ end
+
+ describe "tagged" do
+ let(:recipe) do
+ Chef::Recipe.new("hjk", "test", run_context)
+ end
+
+ describe "with the default node object" do
+ let(:node) { Chef::Node.new }
+
+ it "should return false for any tags" do
+ expect(resource.tagged?("foo")).to be(false)
+ end
+ end
+
+ it "should return true from tagged? if node is tagged" do
+ recipe.tag "foo"
+ expect(resource.tagged?("foo")).to be(true)
+ end
+
+ it "should return false from tagged? if node is not tagged" do
+ expect(resource.tagged?("foo")).to be(false)
+ end
+ end
+
+ describe "#with_umask" do
+ let(:resource) { Chef::Resource.new("testy testerson") }
+ let!(:original_umask) { ::File.umask }
+
+ after do
+ ::File.umask(original_umask)
+ end
+
+ it "does not affect the umask by default" do
+ block_value = nil
+
+ resource.with_umask do
+ block_value = ::File.umask
+ end
+
+ expect(block_value).to eq(original_umask)
+ end
+
+ if windows?
+ it "is a no-op on Windows" do
+ resource.umask = "0123"
+
+ block_value = nil
+
+ resource.with_umask do
+ block_value = ::File.umask
+ end
+
+ # Format the returned value so a potential error message is easier to understand.
+ actual_value = block_value.to_s(8).rjust(4, "0")
+
+ expect(actual_value).to eq("0000")
+ end
+ else
+ it "changes the umask in the block to the set value" do
+ resource.umask = "0123"
+
+ block_value = nil
+
+ resource.with_umask do
+ block_value = ::File.umask
+ end
+
+ # Format the returned value so a potential error message is easier to understand.
+ actual_value = block_value.to_s(8).rjust(4, "0")
+
+ expect(actual_value).to eq("0123")
+ end
+ end
+
+ it "resets the umask afterwards" do
+ resource.umask = "0123"
+
+ resource.with_umask do
+ "noop"
+ end
+
+ expect(::File.umask).to eq(original_umask)
+ end
+
+ it "resets the umask if the block raises an error" do
+ resource.umask = "0123"
+
+ expect { resource.with_umask { 1 / 0 } }.to raise_error(ZeroDivisionError)
+
+ expect(::File.umask).to eq(original_umask)
end
end
end
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/role_spec.rb b/spec/unit/role_spec.rb
index 1bbbc4c7b0..86589d1883 100644
--- a/spec/unit/role_spec.rb
+++ b/spec/unit/role_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "chef/role"
describe Chef::Role do
before(:each) do
- allow(ChefConfig).to receive(:windows?) { false }
+ allow(ChefUtils).to receive(:windows?) { false }
@role = Chef::Role.new
@role.name("ops_master")
end
@@ -104,31 +104,31 @@ describe Chef::Role do
describe "default_attributes" do
it "should let you set the default attributes hash explicitly" do
- expect(@role.default_attributes({ :one => "two" })).to eq({ :one => "two" })
+ expect(@role.default_attributes({ one: "two" })).to eq({ one: "two" })
end
it "should let you return the default attributes hash" do
- @role.default_attributes({ :one => "two" })
- expect(@role.default_attributes).to eq({ :one => "two" })
+ @role.default_attributes({ one: "two" })
+ expect(@role.default_attributes).to eq({ one: "two" })
end
it "should throw an ArgumentError if we aren't a kind of hash" do
- expect { @role.default_attributes(Array.new) }.to raise_error(ArgumentError)
+ expect { @role.default_attributes([]) }.to raise_error(ArgumentError)
end
end
describe "override_attributes" do
it "should let you set the override attributes hash explicitly" do
- expect(@role.override_attributes({ :one => "two" })).to eq({ :one => "two" })
+ expect(@role.override_attributes({ one: "two" })).to eq({ one: "two" })
end
it "should let you return the override attributes hash" do
- @role.override_attributes({ :one => "two" })
- expect(@role.override_attributes).to eq({ :one => "two" })
+ @role.override_attributes({ one: "two" })
+ expect(@role.override_attributes).to eq({ one: "two" })
end
it "should throw an ArgumentError if we aren't a kind of hash" do
- expect { @role.override_attributes(Array.new) }.to raise_error(ArgumentError)
+ expect { @role.override_attributes([]) }.to raise_error(ArgumentError)
end
end
@@ -137,15 +137,15 @@ describe Chef::Role do
@role.name("mars_volta")
@role.description("Great band!")
@role.run_list("one", "two", "role[a]")
- @role.default_attributes({ :el_groupo => "nuevo" })
- @role.override_attributes({ :deloused => "in the comatorium" })
+ @role.default_attributes({ el_groupo: "nuevo" })
+ @role.override_attributes({ deloused: "in the comatorium" })
@example = Chef::Role.new
@example.name("newname")
@example.description("Really Great band!")
@example.run_list("alpha", "bravo", "role[alpha]")
- @example.default_attributes({ :el_groupo => "nuevo dos" })
- @example.override_attributes({ :deloused => "in the comatorium XOXO" })
+ @example.default_attributes({ el_groupo: "nuevo dos" })
+ @example.override_attributes({ deloused: "in the comatorium XOXO" })
end
it "should update all fields except for name" do
@@ -158,13 +158,13 @@ describe Chef::Role do
end
end
- describe "when serialized as JSON", :json => true do
+ describe "when serialized as JSON", json: true do
before(:each) do
@role.name("mars_volta")
@role.description("Great band!")
@role.run_list("one", "two", "role[a]")
- @role.default_attributes({ :el_groupo => "nuevo" })
- @role.override_attributes({ :deloused => "in the comatorium" })
+ @role.default_attributes({ el_groupo: "nuevo" })
+ @role.override_attributes({ deloused: "in the comatorium" })
@serialized_role = Chef::JSONCompat.to_json(@role)
end
@@ -189,20 +189,20 @@ describe Chef::Role do
end
it "should include 'run_list'" do
- #Activesupport messes with Chef json formatting
- #This test should pass with and without activesupport
+ # Activesupport messes with Chef json formatting
+ # This test should pass with and without activesupport
expect(@serialized_role).to match(/"run_list":\["recipe\[one\]","recipe\[two\]","role\[a\]"\]/)
end
describe "and it has per-environment run lists" do
before do
@role.env_run_lists("_default" => ["one", "two", "role[a]"], "production" => ["role[monitoring]", "role[auditing]", "role[apache]"], "dev" => ["role[nginx]"])
- @serialized_role = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(@role), :create_additions => false)
+ @serialized_role = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(@role), create_additions: false)
end
it "includes the per-environment run lists" do
- #Activesupport messes with Chef json formatting
- #This test should pass with and without activesupport
+ # Activesupport messes with Chef json formatting
+ # This test should pass with and without activesupport
expect(@serialized_role["env_run_lists"]["production"]).to eq(["role[monitoring]", "role[auditing]", "role[apache]"])
expect(@serialized_role["env_run_lists"]["dev"]).to eq(["role[nginx]"])
end
@@ -218,7 +218,7 @@ describe Chef::Role do
end
end
- describe "when created from JSON", :json => true do
+ describe "when created from JSON", json: true do
before(:each) do
@role.name("mars_volta")
@role.description("Great band!")
@@ -245,10 +245,10 @@ describe Chef::Role do
end
end
- ROLE_DSL = <<-EOR
-name "ceiling_cat"
-description "like Aliens, but furry"
-EOR
+ ROLE_DSL = <<~EOR.freeze
+ name "ceiling_cat"
+ description "like Aliens, but furry"
+ EOR
describe "when loading from disk" do
before do
@@ -259,7 +259,7 @@ EOR
it "should return a Chef::Role object from JSON" do
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json"])
file_path = File.join(Chef::Config[:role_path], "memes/lolcat.json")
- expect(File).to receive(:exists?).with(file_path).exactly(1).times.and_return(true)
+ expect(File).to receive(:exist?).with(file_path).exactly(1).times.and_return(true)
expect(IO).to receive(:read).with(file_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
@@ -268,8 +268,9 @@ EOR
it "should return a Chef::Role object from a Ruby DSL" do
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.rb"])
rb_path = File.join(Chef::Config[:role_path], "memes/lolcat.rb")
- expect(File).to receive(:exists?).with(rb_path).exactly(2).times.and_return(true)
+ expect(File).to receive(:exist?).with(rb_path).exactly(1).times.and_return(true)
expect(File).to receive(:readable?).with(rb_path).exactly(1).times.and_return(true)
+ expect(File).to receive(:file?).with(rb_path).exactly(1).times.and_return(true)
expect(IO).to receive(:read).with(rb_path).and_return(ROLE_DSL)
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
@@ -279,8 +280,8 @@ EOR
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json", "#{Chef::Config[:role_path]}/memes/lolcat.rb"])
js_path = File.join(Chef::Config[:role_path], "memes/lolcat.json")
rb_path = File.join(Chef::Config[:role_path], "memes/lolcat.rb")
- expect(File).to receive(:exists?).with(js_path).exactly(1).times.and_return(true)
- expect(File).not_to receive(:exists?).with(rb_path)
+ expect(File).to receive(:exist?).with(js_path).exactly(1).times.and_return(true)
+ expect(File).not_to receive(:exist?).with(rb_path)
expect(IO).to receive(:read).with(js_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
@@ -288,19 +289,19 @@ EOR
it "should raise an exception if the file does not exist" do
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/meme.rb"])
- expect(File).not_to receive(:exists?)
+ expect(File).not_to receive(:exist?)
expect { @role.class.from_disk("lolcat") }.to raise_error(Chef::Exceptions::RoleNotFound)
end
it "should raise an exception if two files exist with the same name" do
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes/lolcat.rb", "#{Chef::Config[:role_path]}/lolcat.rb"])
- expect(File).not_to receive(:exists?)
+ expect(File).not_to receive(:exist?)
expect { @role.class.from_disk("lolcat") }.to raise_error(Chef::Exceptions::DuplicateRole)
end
it "should not raise an exception if two files exist with a similar name" do
expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes/lolcat.rb", "#{Chef::Config[:role_path]}/super_lolcat.rb"])
- expect(File).to receive(:exists?).with("#{Chef::Config[:role_path]}/memes/lolcat.rb").and_return(true)
+ expect(File).to receive(:exist?).with("#{Chef::Config[:role_path]}/memes/lolcat.rb").and_return(true)
allow_any_instance_of(Chef::Role).to receive(:from_file).with("#{Chef::Config[:role_path]}/memes/lolcat.rb")
expect { @role.class.from_disk("lolcat") }.not_to raise_error
end
@@ -309,48 +310,52 @@ EOR
describe "when loading from disk and role_path is an array" do
before(:each) do
- Chef::Config[:role_path] = ["/path1", "/path/path2"]
+ Chef::Config[:role_path] = ["/path1", "/path1/path2"]
end
+ let(:root) { windows? ? "C:/path1" : "/path1" }
+
it "should return a Chef::Role object from JSON" do
- expect(Dir).to receive(:glob).with(File.join("/path1", "**", "**")).exactly(1).times.and_return(["/path1/lolcat.json"])
- expect(File).to receive(:exists?).with("/path1/lolcat.json").exactly(1).times.and_return(true)
- expect(IO).to receive(:read).with("/path1/lolcat.json").and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
+ expect(Dir).to receive(:glob).with(File.join(root, "**", "**")).exactly(1).times.and_return(["#{root}/lolcat.json"])
+ expect(File).to receive(:exist?).with("#{root}/lolcat.json").exactly(1).times.and_return(true)
+ expect(IO).to receive(:read).with("#{root}/lolcat.json").and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should return a Chef::Role object from JSON when role is in the second path" do
- expect(Dir).to receive(:glob).with(File.join("/path1", "**", "**")).exactly(1).times.and_return([])
- expect(Dir).to receive(:glob).with(File.join("/path/path2", "**", "**")).exactly(1).times.and_return(["/path/path2/lolcat.json"])
- expect(File).to receive(:exists?).with("/path/path2/lolcat.json").exactly(1).times.and_return(true)
- expect(IO).to receive(:read).with("/path/path2/lolcat.json").and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
+ expect(Dir).to receive(:glob).with(File.join(root, "**", "**")).exactly(1).times.and_return([])
+ expect(Dir).to receive(:glob).with(File.join("#{root}/path2", "**", "**")).exactly(1).times.and_return(["#{root}/path2/lolcat.json"])
+ expect(File).to receive(:exist?).with("#{root}/path2/lolcat.json").exactly(1).times.and_return(true)
+ expect(IO).to receive(:read).with("#{root}/path2/lolcat.json").and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }')
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should return a Chef::Role object from a Ruby DSL" do
- expect(Dir).to receive(:glob).with(File.join("/path1", "**", "**")).exactly(1).times.and_return(["/path1/lolcat.rb"])
- expect(File).to receive(:exists?).with("/path1/lolcat.rb").exactly(2).times.and_return(true)
- expect(File).to receive(:readable?).with("/path1/lolcat.rb").and_return(true)
- expect(IO).to receive(:read).with("/path1/lolcat.rb").exactly(1).times.and_return(ROLE_DSL)
+ expect(Dir).to receive(:glob).with(File.join(root, "**", "**")).exactly(1).times.and_return(["#{root}/lolcat.rb"])
+ expect(File).to receive(:exist?).with("#{root}/lolcat.rb").exactly(1).times.and_return(true)
+ expect(File).to receive(:readable?).with("#{root}/lolcat.rb").and_return(true)
+ expect(File).to receive(:file?).with("#{root}/lolcat.rb").and_return(true)
+ expect(IO).to receive(:read).with("#{root}/lolcat.rb").exactly(1).times.and_return(ROLE_DSL)
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should return a Chef::Role object from a Ruby DSL when role is in the second path" do
- expect(Dir).to receive(:glob).with(File.join("/path1", "**", "**")).exactly(1).times.and_return([])
- expect(Dir).to receive(:glob).with(File.join("/path/path2", "**", "**")).exactly(1).times.and_return(["/path/path2/lolcat.rb"])
- expect(File).to receive(:exists?).with("/path/path2/lolcat.rb").exactly(2).times.and_return(true)
- expect(File).to receive(:readable?).with("/path/path2/lolcat.rb").and_return(true)
- expect(IO).to receive(:read).with("/path/path2/lolcat.rb").exactly(1).times.and_return(ROLE_DSL)
+ expect(Dir).to receive(:glob).with(File.join(root, "**", "**")).exactly(1).times.and_return([])
+ expect(Dir).to receive(:glob).with(File.join("#{root}/path2", "**", "**")).exactly(1).times.and_return(["#{root}/path2/lolcat.rb"])
+ expect(File).to receive(:exist?).with("#{root}/path2/lolcat.rb").exactly(1).times.and_return(true)
+ expect(File).to receive(:readable?).with("#{root}/path2/lolcat.rb").and_return(true)
+ expect(File).to receive(:file?).with("#{root}/path2/lolcat.rb").and_return(true)
+ expect(IO).to receive(:read).with("#{root}/path2/lolcat.rb").exactly(1).times.and_return(ROLE_DSL)
expect(@role).to be_a_kind_of(Chef::Role)
@role.class.from_disk("lolcat")
end
it "should raise an exception if the file does not exist" do
- expect(Dir).to receive(:glob).with(File.join("/path1", "**", "**")).exactly(1).times.and_return([])
- expect(Dir).to receive(:glob).with(File.join("/path/path2", "**", "**")).exactly(1).times.and_return([])
+ expect(Dir).to receive(:glob).with(File.join(root, "**", "**")).exactly(1).times.and_return([])
+ expect(Dir).to receive(:glob).with(File.join("#{root}/path2", "**", "**")).exactly(1).times.and_return([])
expect { @role.class.from_disk("lolcat") }.to raise_error(Chef::Exceptions::RoleNotFound)
end
diff --git a/spec/unit/run_context/child_run_context_spec.rb b/spec/unit/run_context/child_run_context_spec.rb
index 47a6c84f7a..da780a4055 100644
--- a/spec/unit/run_context/child_run_context_spec.rb
+++ b/spec/unit/run_context/child_run_context_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,19 +44,12 @@ describe Chef::RunContext::ChildRunContext do
expect(child.parent_run_context).to eq run_context
end
- it "audits is not the same as the parent" do
- expect(child.audits.object_id).not_to eq run_context.audits.object_id
- child.audits["hi"] = "lo"
- expect(child.audits["hi"]).to eq("lo")
- expect(run_context.audits["hi"]).not_to eq("lo")
- end
-
it "resource_collection is not the same as the parent" do
expect(child.resource_collection.object_id).not_to eq run_context.resource_collection.object_id
f = Chef::Resource::File.new("hi", child)
child.resource_collection.insert(f)
- expect(child.resource_collection).to include f
- expect(run_context.resource_collection).not_to include f
+ expect(child.resource_collection.include?(f)).to be true
+ expect(run_context.resource_collection.include?(f)).to be false
end
it "immediate_notification_collection is not the same as the parent" do
diff --git a/spec/unit/run_context/cookbook_compiler_spec.rb b/spec/unit/run_context/cookbook_compiler_spec.rb
index feb39615b6..4d744edc3a 100644
--- a/spec/unit/run_context/cookbook_compiler_spec.rb
+++ b/spec/unit/run_context/cookbook_compiler_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,6 +51,14 @@ describe Chef::RunContext::CookbookCompiler do
Chef::RunContext::CookbookCompiler.new(run_context, run_list_expansion, events)
end
+ describe "loading a cookbook fully" do
+ it "does not error" do
+ run_context.instance_variable_set(:@cookbook_compiler, compiler)
+ node.run_list("dependency1::default")
+ compiler.compile
+ end
+ end
+
describe "loading attribute files" do
# Attribute files in the fixture data will append their
@@ -107,7 +115,11 @@ describe Chef::RunContext::CookbookCompiler do
node.run_list("test-with-deps::default", "test-with-circular-deps::default")
compiler.compile_libraries
- expect(LibraryLoadOrder.load_order).to eq(["dependency1", "dependency2", "test-with-deps", "circular-dep2", "circular-dep1", "test-with-circular-deps"])
+ expect(LibraryLoadOrder.load_order).to eq(%w{dependency1 dependency2 test-with-deps circular-dep2 circular-dep1 test-with-circular-deps})
+
+ # additionally test that we only load them once
+ compiler.compile_libraries
+ expect(LibraryLoadOrder.load_order).to eq(%w{dependency1 dependency2 test-with-deps circular-dep2 circular-dep1 test-with-circular-deps})
end
end
@@ -121,18 +133,18 @@ describe Chef::RunContext::CookbookCompiler do
node.run_list("test-with-deps::default", "test-with-circular-deps::default")
compiler.compile_lwrps
- expect(LibraryLoadOrder.load_order).to eq(["dependency1-provider",
- "dependency1-resource",
- "dependency2-provider",
- "dependency2-resource",
- "test-with-deps-provider",
- "test-with-deps-resource",
- "circular-dep2-provider",
- "circular-dep2-resource",
- "circular-dep1-provider",
- "circular-dep1-resource",
- "test-with-circular-deps-provider",
- "test-with-circular-deps-resource"])
+ expect(LibraryLoadOrder.load_order).to eq(%w{dependency1-provider
+ dependency1-resource
+ dependency2-provider
+ dependency2-resource
+ test-with-deps-provider
+ test-with-deps-resource
+ circular-dep2-provider
+ circular-dep2-resource
+ circular-dep1-provider
+ circular-dep1-resource
+ test-with-circular-deps-provider
+ test-with-circular-deps-resource})
end
end
@@ -147,12 +159,12 @@ describe Chef::RunContext::CookbookCompiler do
node.run_list("test-with-deps::default", "test-with-circular-deps::default")
compiler.compile_resource_definitions
- expect(LibraryLoadOrder.load_order).to eq(["dependency1-definition",
- "dependency2-definition",
- "test-with-deps-definition",
- "circular-dep2-definition",
- "circular-dep1-definition",
- "test-with-circular-deps-definition"])
+ expect(LibraryLoadOrder.load_order).to eq(%w{dependency1-definition
+ dependency2-definition
+ test-with-deps-definition
+ circular-dep2-definition
+ circular-dep1-definition
+ test-with-circular-deps-definition})
end
end
@@ -163,9 +175,7 @@ describe Chef::RunContext::CookbookCompiler do
describe "event dispatch" do
let(:recipe) { "dependency1::default" }
let(:recipe_path) do
- File.expand_path("../../../data/run_context/cookbooks/dependency1/recipes/default.rb", __FILE__).tap do |path|
- path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
- end
+ File.expand_path("../../data/run_context/cookbooks/dependency1/recipes/default.rb", __dir__)
end
before do
node.run_list(recipe)
@@ -207,14 +217,14 @@ describe Chef::RunContext::CookbookCompiler do
it "should return an array of cookbook names as symbols without duplicates" do
node.run_list("test-with-circular-deps::default", "circular-dep1::default", "circular-dep2::default")
- expect(compiler.cookbook_order).to eq([:"circular-dep2",
- :"circular-dep1",
- :"test-with-circular-deps"])
+ expect(compiler.cookbook_order).to eq(%i{circular-dep2
+ circular-dep1
+ test-with-circular-deps})
end
it "determines if a cookbook is in the list of cookbooks reachable by dependency" do
node.run_list("test-with-deps::default", "test-with-deps::server")
- expect(compiler.cookbook_order).to eq([:dependency1, :dependency2, :"test-with-deps"])
+ expect(compiler.cookbook_order).to eq(%i{dependency1 dependency2 test-with-deps})
expect(compiler.unreachable_cookbook?(:dependency1)).to be_falsey
expect(compiler.unreachable_cookbook?(:dependency2)).to be_falsey
expect(compiler.unreachable_cookbook?(:'test-with-deps')).to be_falsey
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index f1c3072b8e..5bd7a360fb 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -138,7 +138,7 @@ describe Chef::RunContext do
expect(node).to receive(:loaded_recipe).with(:ancient, "aliens")
expect do
run_context.include_recipe("ancient::aliens")
- # In CHEF-5120, this becomes a Chef::Exceptions::MissingCookbookDependency error:
+ # In CHEF-5120, this becomes a Chef::Exceptions::MissingCookbookDependency error:
end.to raise_error(Chef::Exceptions::CookbookNotFound)
end
@@ -184,7 +184,7 @@ describe Chef::RunContext do
describe "handling reboot requests" do
let(:expected) do
- { :reason => "spec tests require a reboot" }
+ { reason: "spec tests require a reboot" }
end
it "stores and deletes the reboot request" do
diff --git a/spec/unit/run_list/run_list_expansion_spec.rb b/spec/unit/run_list/run_list_expansion_spec.rb
index 3a39bc79cc..2d56cd8532 100644
--- a/spec/unit/run_list/run_list_expansion_spec.rb
+++ b/spec/unit/run_list/run_list_expansion_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -94,12 +94,15 @@ describe Chef::RunList::RunListExpansion do
allow(@expansion).to receive(:fetch_role).and_return(@first_role, @second_role)
@expansion.expand
@json = '{"id":"_default","run_list":[{"type":"recipe","name":"lobster::mastercookbook","version":"0.1.0",'
- .concat(
-'"skipped":false},{"type":"role","name":"rage","children":[{"type":"role","name":"mollusk","children":[],"missing":null,'
- .concat(
-'"error":null,"skipped":null},{"type":"recipe","name":"crabrevenge","version":null,"skipped":false}],"missing":null,'
- .concat(
-'"error":null,"skipped":null},{"type":"recipe","name":"fist","version":"0.1","skipped":false}]}')))
+ .concat(
+ '"skipped":false},{"type":"role","name":"rage","children":[{"type":"role","name":"mollusk","children":[],"missing":null,'
+ .concat(
+ '"error":null,"skipped":null},{"type":"recipe","name":"crabrevenge","version":null,"skipped":false}],"missing":null,'
+ .concat(
+ '"error":null,"skipped":null},{"type":"recipe","name":"fist","version":"0.1","skipped":false}]}'
+ )
+ )
+ )
end
diff --git a/spec/unit/run_list/run_list_item_spec.rb b/spec/unit/run_list/run_list_item_spec.rb
index 3f8fd2ab3b..56017e96da 100644
--- a/spec/unit/run_list/run_list_item_spec.rb
+++ b/spec/unit/run_list/run_list_item_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,15 +22,15 @@ describe Chef::RunList::RunListItem do
describe "when creating from a Hash" do
it "raises an exception when the hash doesn't have a :type key" do
- expect { Chef::RunList::RunListItem.new(:name => "tatft") }.to raise_error(ArgumentError)
+ expect { Chef::RunList::RunListItem.new(name: "tatft") }.to raise_error(ArgumentError)
end
it "raises an exception when the hash doesn't have an :name key" do
- expect { Chef::RunList::RunListItem.new(:type => "R") }.to raise_error(ArgumentError)
+ expect { Chef::RunList::RunListItem.new(type: "R") }.to raise_error(ArgumentError)
end
it "sets the name and type as given in the hash" do
- item = Chef::RunList::RunListItem.new(:type => "fuuu", :name => "uuuu")
+ item = Chef::RunList::RunListItem.new(type: "fuuu", name: "uuuu")
expect(item.to_s).to eq("fuuu[uuuu]")
end
diff --git a/spec/unit/run_list/versioned_recipe_list_spec.rb b/spec/unit/run_list/versioned_recipe_list_spec.rb
index 91c601b294..751b7ab403 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -54,25 +54,25 @@ describe Chef::RunList::VersionedRecipeList do
it "should allow you to specify a version" do
list.add_recipe "rails", "1.0.0"
expect(list).to eq(%w{apt god apache2 rails})
- expect(list.with_versions).to include({ :name => "rails", :version => "1.0.0" })
+ expect(list.with_versions).to include({ name: "rails", version: "1.0.0" })
end
it "should allow you to specify a version for a recipe that already exists" do
list.add_recipe "apt", "1.2.3"
expect(list).to eq(%w{apt god apache2})
- expect(list.with_versions).to include({ :name => "apt", :version => "1.2.3" })
+ expect(list.with_versions).to include({ name: "apt", version: "1.2.3" })
end
it "should allow you to specify the same version of a recipe twice" do
list.add_recipe "rails", "1.0.0"
list.add_recipe "rails", "1.0.0"
- expect(list.with_versions).to include({ :name => "rails", :version => "1.0.0" })
+ expect(list.with_versions).to include({ name: "rails", version: "1.0.0" })
end
- it "should allow you to spcify no version, even when a version already exists" do
+ it "should allow you to specify no version, even when a version already exists" do
list.add_recipe "rails", "1.0.0"
list.add_recipe "rails"
- expect(list.with_versions).to include({ :name => "rails", :version => "1.0.0" })
+ expect(list.with_versions).to include({ name: "rails", version: "1.0.0" })
end
it "should not allow multiple versions of the same recipe" do
@@ -85,9 +85,9 @@ describe Chef::RunList::VersionedRecipeList do
let(:versioned_recipes) do
[
- { :name => "apt", :version => "1.0.0" },
- { :name => "god", :version => nil },
- { :name => "apache2", :version => "0.0.1" },
+ { name: "apt", version: "1.0.0" },
+ { name: "god", version: nil },
+ { name: "apache2", version: "0.0.1" },
]
end
it "should return an array of hashes with :name and :version" do
@@ -106,9 +106,9 @@ describe Chef::RunList::VersionedRecipeList do
let(:versioned_recipes) do
[
- { :name => "apt", :version => "~> 1.2.0" },
- { :name => "god", :version => nil },
- { :name => "apache2", :version => "0.0.1" },
+ { name: "apt", version: "~> 1.2.0" },
+ { name: "god", version: nil },
+ { name: "apache2", version: "0.0.1" },
]
end
@@ -151,7 +151,7 @@ describe Chef::RunList::VersionedRecipeList do
let(:versioned_recipes) do
[
- { :name => "apt", :version => "~> 1.2.0" },
+ { name: "apt", version: "~> 1.2.0" },
]
end
@@ -170,7 +170,7 @@ describe Chef::RunList::VersionedRecipeList do
let(:versioned_recipes) do
[
- { :name => "apt::cacher", :version => "~> 1.2.0" },
+ { name: "apt::cacher", version: "~> 1.2.0" },
]
end
@@ -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_list_spec.rb b/spec/unit/run_list_spec.rb
index 0dcd349ced..c2f21ea5fe 100644
--- a/spec/unit/run_list_spec.rb
+++ b/spec/unit/run_list_spec.rb
@@ -2,7 +2,7 @@
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Seth Falcon (<seth@chef.io>)
# Author:: Christopher Walters (<cw@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -133,7 +133,7 @@ describe Chef::RunList do
it "should yield each member to your block" do
@run_list << "foo"
@run_list << "bar"
- seen = Array.new
+ seen = []
@run_list.each { |r| seen << r }
expect(seen).to be_include("recipe[foo]")
expect(seen).to be_include("recipe[bar]")
@@ -170,12 +170,12 @@ describe Chef::RunList do
@role = Chef::Role.new
@role.name "stubby"
@role.run_list "one", "two"
- @role.default_attributes :one => :two
- @role.override_attributes :three => :four
+ @role.default_attributes one: :two
+ @role.override_attributes three: :four
@role.env_run_list["production"] = Chef::RunList.new( "one", "two", "five")
allow(Chef::Role).to receive(:load).and_return(@role)
- @rest = double("Chef::ServerAPI", { :get => @role.to_hash, :url => "/" })
+ @rest = double("Chef::ServerAPI", { get: @role.to_hash, url: "/" })
allow(Chef::ServerAPI).to receive(:new).and_return(@rest)
@run_list << "role[stubby]"
@@ -197,7 +197,7 @@ describe Chef::RunList do
describe "from the chef server" do
it "should load the role from the chef server" do
- #@rest.should_receive(:get).with("roles/stubby")
+ # @rest.should_receive(:get).with("roles/stubby")
expansion = @run_list.expand("_default", "server")
expect(expansion.recipes).to eq(%w{one two kitty})
end
@@ -263,7 +263,7 @@ describe Chef::RunList do
it "should recurse into a child role" do
dog = Chef::Role.new
dog.name "dog"
- dog.default_attributes :seven => :nine
+ dog.default_attributes seven: :nine
dog.run_list "three"
@role.run_list << "role[dog]"
allow(Chef::Role).to receive(:from_disk).with("stubby").and_return(@role)
@@ -277,7 +277,7 @@ describe Chef::RunList do
it "should not recurse infinitely" do
dog = Chef::Role.new
dog.name "dog"
- dog.default_attributes :seven => :nine
+ dog.default_attributes seven: :nine
dog.run_list "role[dog]", "three"
@role.run_list << "role[dog]"
allow(Chef::Role).to receive(:from_disk).with("stubby").and_return(@role)
diff --git a/spec/unit/run_lock_spec.rb b/spec/unit/run_lock_spec.rb
index 350a0b2c66..3459f86f68 100644
--- a/spec/unit/run_lock_spec.rb
+++ b/spec/unit/run_lock_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,10 +15,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
require "chef/client"
-describe Chef::RunLock do
+# FIXME: these are disabled on MacOS due to timing issues in our anka build cluster
+# these issues should be fixed and the tests should be re-eenabled. If we are getting
+# omnibus test phases on mac tests which are reasonable and not ~3 hours long, then the
+# condition to avoid this testing on macs can be deleted
+describe Chef::RunLock, :not_supported_on_macos do
default_cache_path = windows? ? 'C:\chef' : "/var/chef"
default_pid_location = windows? ? 'C:\chef\cache\chef-client-running.pid' : "/var/chef/cache/chef-client-running.pid"
diff --git a/spec/unit/run_status_spec.rb b/spec/unit/run_status_spec.rb
index 6305b7497b..c31891e8be 100644
--- a/spec/unit/run_status_spec.rb
+++ b/spec/unit/run_status_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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 c1e10a78f4..500ec13f7b 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 (c) 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
@@ -125,8 +111,7 @@ describe Chef::Runner do
it "should use the provider specified by the resource (if it has one)" do
provider = Chef::Provider::Easy.new(run_context.resource_collection[0], run_context)
- # Expect provider to be called twice, because will fall back to old provider lookup
- expect(run_context.resource_collection[0]).to receive(:provider).twice.and_return(Chef::Provider::Easy)
+ expect(run_context.resource_collection[0]).to receive(:provider).once.and_return(Chef::Provider::Easy)
expect(Chef::Provider::Easy).to receive(:new).once.and_return(provider)
runner.converge
end
@@ -140,25 +125,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
@@ -271,10 +257,10 @@ describe Chef::Runner do
end
expect(exception).to be_a(Chef::Exceptions::MultipleFailures)
- expected_message = <<-E
-Multiple failures occurred:
-* FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
-* FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
+ expected_message = <<~E
+ Multiple failures occurred:
+ * FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
+ * FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
E
expect(exception.message).to eq(expected_message)
@@ -308,7 +294,7 @@ Multiple failures occurred:
# execution, and schedule delayed actions :second and :third on the first
# resource. The duplicate actions should "collapse" to a single notification
# and order should be preserved.
- expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third])
+ expect(SnitchyProvider.all_actions_called).to eq(%i{first first second third})
end
it "executes delayed notifications in the order they were declared" do
@@ -334,7 +320,7 @@ Multiple failures occurred:
third_resource.notifies(:third_action, first_resource, :delayed)
runner.converge
- expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third])
+ expect(SnitchyProvider.all_actions_called).to eq(%i{first first second third})
end
it "does not fire notifications if the resource was not updated by the last action executed" do
@@ -360,7 +346,7 @@ Multiple failures occurred:
runner.converge
# All of the resources should only fire once:
- expect(SnitchyProvider.all_actions_called).to eq([:first, :second, :third])
+ expect(SnitchyProvider.all_actions_called).to eq(%i{first second third})
# all of the resources should be marked as updated for reporting purposes
expect(first_resource).to be_updated
diff --git a/spec/unit/scan_access_control_spec.rb b/spec/unit/scan_access_control_spec.rb
index c747f6cc4a..5406201891 100644
--- a/spec/unit/scan_access_control_spec.rb
+++ b/spec/unit/scan_access_control_spec.rb
@@ -1,5 +1,5 @@
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +15,7 @@
# limitations under the License.
#
-require File.expand_path("../../spec_helper", __FILE__)
+require "spec_helper"
require "chef/scan_access_control"
describe Chef::ScanAccessControl do
@@ -49,7 +49,7 @@ describe Chef::ScanAccessControl do
describe "when the fs entity exists" do
before do
- @stat = double("File::Stat for #{@new_resource.path}", :uid => 0, :gid => 0, :mode => 00100644)
+ @stat = double("File::Stat for #{@new_resource.path}", uid: 0, gid: 0, mode: 00100644)
expect(File).to receive(:realpath).with(@new_resource.path).and_return(@real_file)
expect(File).to receive(:stat).with(@real_file).and_return(@stat)
expect(File).to receive(:exist?).with(@new_resource.path).and_return(true)
@@ -128,7 +128,7 @@ describe Chef::ScanAccessControl do
end
it "sets the owner of current_resource to the username of the current owner" do
- @root_passwd = double("Struct::Passwd for uid 0", :name => "root")
+ @root_passwd = double("Struct::Passwd for uid 0", name: "root")
expect(Etc).to receive(:getpwuid).with(0).and_return(@root_passwd)
@scanner.set_all!
@@ -163,7 +163,7 @@ describe Chef::ScanAccessControl do
end
it "sets the group of the current resource to the group name" do
- @group_entry = double("Struct::Group for wheel", :name => "wheel")
+ @group_entry = double("Struct::Group for wheel", name: "wheel")
expect(Etc).to receive(:getgrgid).with(0).and_return(@group_entry)
@scanner.set_all!
diff --git a/spec/unit/search/query_spec.rb b/spec/unit/search/query_spec.rb
index 51667784fb..403c0592a6 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,9 +22,10 @@ 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) do
@@ -81,10 +82,10 @@ 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) do
{
@@ -148,12 +149,7 @@ describe Chef::Search::Query do
],
"start" => 0,
"total" => 4,
- } end
-
- let(:big_response) do
- r = response.dup
- r["total"] = 8
- r
+ }
end
let(:big_response_empty) do
@@ -174,37 +170,32 @@ describe Chef::Search::Query do
it "accepts a type as the first argument" do
expect { query.search("node") }.not_to raise_error
expect { query.search(:node) }.not_to raise_error
- expect { query.search(Hash.new) }.to raise_error(Chef::Exceptions::InvalidSearchQuery, /(Hash)/)
+ expect { query.search({}) }.to raise_error(Chef::Exceptions::InvalidSearchQuery, /(Hash)/)
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
it "throws an exception if you pass an incorrect option" do
expect { query.search(:node, "platform:rhel", total: 10) }
- .to raise_error(ArgumentError, /unknown keyword: total/)
+ .to raise_error(ArgumentError, /unknown keyword: :?total/)
end
it "returns the raw rows, start, and total if no block is passed" do
@@ -223,12 +214,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
@@ -242,6 +233,34 @@ describe Chef::Search::Query do
end
end
+ it "fuzzifies node searches when fuzz is set and type is a symbol" 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 "fuzzifies node searches when fuzz is set and type is a string" 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 }
@@ -269,22 +288,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..5d78c4bd9b
--- /dev/null
+++ b/spec/unit/server_api_spec.rb
@@ -0,0 +1,151 @@
+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
+ let(:version_class) do
+ Class.new do
+ extend Chef::Mixin::VersionedAPIFactory
+
+ version_class_v0 = Class.new do
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 0
+ end
+ add_versioned_api_class version_class_v0
+
+ version_class_v2 = Class.new do
+ extend Chef::Mixin::VersionedAPI
+ minimum_api_version 2
+ end
+ add_versioned_api_class version_class_v2
+ end
+ end
+
+ before do
+ Chef::ServerAPIVersions.instance.reset!
+ end
+
+ let(:versioned_client) do
+ Chef::ServerAPI.new(url, version_class: version_class)
+ 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!
+ 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
+
+ it "406 on a post does protocol negotiation" 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: {})
+ client.post("foo", post_body)
+ end
+
+ it "406 on a put does protocol negotiation" do
+ WebMock.disable_net_connect!
+ put_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(:put, "http://chef.example.com:4000/foo").with(body: put_body.to_json, headers: { "X-Ops-Server-Api-Version" => "2" }).to_return(status: [406, "Not Acceptable"], body: body_406 )
+ stub_request(:put, "http://chef.example.com:4000/foo").with(body: put_body.to_json, headers: { "X-Ops-Server-Api-Version" => "0" }).to_return(status: 200, body: "", headers: {})
+ client.put("foo", put_body)
+ end
+
+ it "406 on a get does protocol negotiation" do
+ WebMock.disable_net_connect!
+ body_406 = '{"error":"invalid-x-ops-server-api-version","message":"Specified version 2 not supported","min_version":0,"max_version":1}'
+ stub_request(:get, "http://chef.example.com:4000/foo").with(headers: { "X-Ops-Server-Api-Version" => "2" }).to_return(status: [406, "Not Acceptable"], body: body_406 )
+ stub_request(:get, "http://chef.example.com:4000/foo").with(headers: { "X-Ops-Server-Api-Version" => "0" }).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..27c6ab6c81
--- /dev/null
+++ b/spec/unit/server_api_versions_spec.rb
@@ -0,0 +1,66 @@
+#
+# Copyright:: Copyright (c) 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/model_wrapper_spec.rb b/spec/unit/shell/model_wrapper_spec.rb
index 00425f2a01..1c96554a8b 100644
--- a/spec/unit/shell/model_wrapper_spec.rb
+++ b/spec/unit/shell/model_wrapper_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,13 +21,13 @@ require "ostruct"
describe Shell::ModelWrapper do
before do
- @model = OpenStruct.new(:name => "Chef::Node")
+ @model = OpenStruct.new(name: "Chef::Node")
@wrapper = Shell::ModelWrapper.new(@model)
end
describe "when created with an explicit model_symbol" do
before do
- @model = OpenStruct.new(:name => "Chef::ApiClient")
+ @model = OpenStruct.new(name: "Chef::ApiClient")
@wrapper = Shell::ModelWrapper.new(@model, :client)
end
@@ -46,7 +46,7 @@ describe Shell::ModelWrapper do
@node_1.name("sammich")
@node_2 = Chef::Node.new
@node_2.name("yummy")
- @server_response = { :node_1 => @node_1, :node_2 => @node_2 }
+ @server_response = { node_1: @node_1, node_2: @node_2 }
@wrapper = Shell::ModelWrapper.new(Chef::Node)
allow(Chef::Node).to receive(:list).and_return(@server_response)
end
@@ -57,7 +57,7 @@ describe Shell::ModelWrapper do
end
it "maps the listed nodes when given a block" do
- expect(@wrapper.all { |n| n.name }.sort.reverse).to eq(%w{yummy sammich})
+ expect(@wrapper.all(&:name).sort.reverse).to eq(%w{yummy sammich})
end
end
@@ -67,7 +67,7 @@ describe Shell::ModelWrapper do
@node_1.name("sammich")
@node_2 = Chef::Node.new
@node_2.name("yummy")
- @server_response = { :node_1 => @node_1, :node_2 => @node_2 }
+ @server_response = { node_1: @node_1, node_2: @node_2 }
@wrapper = Shell::ModelWrapper.new(Chef::Node)
# Creating a Chef::Search::Query object tries to read the private key...
diff --git a/spec/unit/shell/shell_ext_spec.rb b/spec/unit/shell/shell_ext_spec.rb
index 0afa1f6390..b2f32efb6a 100644
--- a/spec/unit/shell/shell_ext_spec.rb
+++ b/spec/unit/shell/shell_ext_spec.rb
@@ -1,6 +1,6 @@
# Author:: Daniel DeLeo (<dan@kallistec.com>)
# Copyright:: Copyright 2009-2016, Daniel DeLeo
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,14 +19,57 @@
require "spec_helper"
describe Shell::Extensions do
+ let(:test_shell_session) do
+ Class.new(Shell::ShellSession) do
+ def rebuild_node
+ nil
+ end
+
+ def rebuild_collection
+ nil
+ end
+
+ def loading
+ nil
+ end
+
+ def loading_complete
+ nil
+ end
+ end
+ end
+
+ let(:test_job_manager) do
+ Class.new do
+ attr_accessor :jobs
+ end
+ end
+
+ let(:object_test_harness) do
+ Proc.new do
+ extend Shell::Extensions::ObjectCoreExtensions
+
+ def conf=(new_conf)
+ @conf = new_conf
+ end
+
+ def conf
+ @conf
+ end
+
+ desc "rspecin'"
+ def rspec_method; end
+ end
+ end
+
describe "extending object for top level methods" do
before do
- @shell_client = TestableShellSession.instance
+ @shell_client = test_shell_session.instance
allow(Shell).to receive(:session).and_return(@shell_client)
- @job_manager = TestJobManager.new
+ @job_manager = test_job_manager.new
@root_context = Object.new
- @root_context.instance_eval(&ObjectTestHarness)
+ @root_context.instance_eval(&object_test_harness)
Shell::Extensions.extend_context_object(@root_context)
@root_context.conf = double("irbconf")
end
@@ -34,8 +77,8 @@ describe Shell::Extensions do
it "finds a subsession in irb for an object" do
target_context_obj = Chef::Node.new
- irb_context = double("context", :main => target_context_obj)
- irb_session = double("irb session", :context => irb_context)
+ irb_context = double("context", main: target_context_obj)
+ irb_session = double("irb session", context: irb_context)
@job_manager.jobs = [[:thread, irb_session]]
allow(@root_context).to receive(:jobs).and_return(@job_manager)
@root_context.ensure_session_select_defined
@@ -92,9 +135,9 @@ describe Shell::Extensions do
end
it "prints node attributes" do
- node = double("node", :attribute => { :foo => :bar })
+ node = double("node", attribute: { foo: :bar })
@shell_client.node = node
- expect(@root_context).to receive(:pp).with({ :foo => :bar })
+ expect(@root_context).to receive(:pp).with({ foo: :bar })
@root_context.ohai
expect(@root_context).to receive(:pp).with(:bar)
@root_context.ohai(:foo)
diff --git a/spec/unit/shell/shell_session_spec.rb b/spec/unit/shell/shell_session_spec.rb
index 259e6096a4..b71cc6fb33 100644
--- a/spec/unit/shell/shell_session_spec.rb
+++ b/spec/unit/shell/shell_session_spec.rb
@@ -18,178 +18,201 @@
require "spec_helper"
require "ostruct"
-class TestableShellSession < Shell::ShellSession
+describe Shell::ShellSession do
+ it "is a singleton object" do
+ expect(Shell::ShellSession).to include(Singleton)
+ end
+end
- def rebuild_node
- nil
+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
- def rebuild_collection
- nil
+ before do
+ Chef::Config[:shell_config] = { override_runlist: [Chef::RunList::RunListItem.new("shell::override")] }
+ session.node = node
+ session.json_configuration = json_attribs
end
- def loading
- nil
+ 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
- def loading_complete
- nil
+ 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::ShellSession do
-
- it "is a singleton object" do
- expect(Shell::ShellSession).to include(Singleton)
+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
-end
-
-describe Shell::ClientSession do
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 => {})
+ 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, [])
+ 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::StandAloneSession do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:node) { Chef::Node.new }
+ let(:session) { Shell::StandAloneSession.instance }
+ let(:recipe) { Chef::Recipe.new(nil, nil, run_context) }
+ let(:run_context) { Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new) }
+
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)
+ Chef::Config[:shell_config] = { override_runlist: [Chef::RunList::RunListItem.new("shell::override")] }
+ 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)
+ 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)
+ 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(json_attribs, Chef::Config[:shell_config]).and_return(client)
+ session.send(:rebuild_node)
end
end
-describe Shell::SoloSession do
- before do
- Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new("shell::override")] }
- Chef::Config[:shell_solo] = true
- @session = Shell::SoloSession.instance
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = @session.run_context = Chef::RunContext.new(@node, {}, @events)
- @session.node = @node
- @recipe = @session.recipe = Chef::Recipe.new(nil, nil, @run_context)
- Shell::Extensions.extend_context_recipe(@recipe)
- end
+describe Shell::SoloLegacySession do
+ let(:json_attribs) { { "a" => "b" } }
+ let(:node) { Chef::Node.new }
+ let(:session) { Shell::SoloLegacySession.instance }
+ let(:recipe) { Chef::Recipe.new(nil, nil, run_context) }
+ let(:run_context) { Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new) }
- after do
- Chef::Config[:shell_solo] = nil
+ before do
+ Chef::Config[:shell_config] = { override_runlist: [Chef::RunList::RunListItem.new("shell::override")] }
+ Chef::Config[:solo_legacy_shell] = true
+ session.node = node
+ session.json_configuration = json_attribs
+ session.run_context = run_context
+ session.recipe = recipe
+ Shell::Extensions.extend_context_recipe(recipe)
end
it "returns a collection based on it's compilation object and the extra recipe provided by chef-shell" do
- allow(@session).to receive(:node_built?).and_return(true)
+ allow(session).to receive(:node_built?).and_return(true)
kitteh = Chef::Resource::Cat.new("keyboard")
- @recipe.run_context.resource_collection << kitteh
- expect(@session.resource_collection).to include(kitteh)
+ recipe.run_context.resource_collection << kitteh
+ expect(session.resource_collection.include?(kitteh)).to be true
end
it "returns definitions from its compilation object" do
- expect(@session.definitions).to eq(@run_context.definitions)
+ expect(session.definitions).to eq(run_context.definitions)
end
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")
- #pending "1) keep attribs in an ivar 2) pass them to the node 3) feed them to the node on reset"
+ session.node_attributes = { "besnard_lakes" => "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
it "generates its resource collection from the compiled cookbooks and the ad hoc recipe" do
- allow(@session).to receive(:node_built?).and_return(true)
+ allow(session).to receive(:node_built?).and_return(true)
kitteh_cat = Chef::Resource::Cat.new("kitteh")
- @run_context.resource_collection << kitteh_cat
+ run_context.resource_collection << kitteh_cat
keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @recipe.run_context.resource_collection << keyboard_cat
- #@session.rebuild_collection
- expect(@session.resource_collection).to include(kitteh_cat, keyboard_cat)
+ recipe.run_context.resource_collection << keyboard_cat
+ # session.rebuild_collection
+ expect(session.resource_collection.include?(kitteh_cat)).to be true
+ expect(session.resource_collection.include?(keyboard_cat)).to be true
end
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)
+ allow(session).to receive(:node_built?).and_return(true)
+ chef_runner = double("Chef::Runner.new", converge: :converged)
+ expect(Chef::Runner).to receive(:new).with(session.recipe.run_context).and_return(chef_runner)
- expect(@recipe.run_chef).to eq(:converged)
+ 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)
+ 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(json_attribs, Chef::Config[:shell_config]).and_return(client)
+ 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/shell_spec.rb b/spec/unit/shell_spec.rb
index 8ba1afa72a..9c1a0c8e3d 100644
--- a/spec/unit/shell_spec.rb
+++ b/spec/unit/shell_spec.rb
@@ -18,32 +18,28 @@
require "spec_helper"
require "ostruct"
-ObjectTestHarness = Proc.new do
- extend Shell::Extensions::ObjectCoreExtensions
+describe Shell do
+ let(:object_test_harness) do
+ Proc.new do
+ extend Shell::Extensions::ObjectCoreExtensions
- def conf=(new_conf)
- @conf = new_conf
- end
+ def conf=(new_conf)
+ @conf = new_conf
+ end
- def conf
- @conf
- end
+ def conf
+ @conf
+ end
- desc "rspecin'"
- def rspec_method
+ desc "rspecin'"
+ def rspec_method; end
+ end
end
-end
-
-class TestJobManager
- attr_accessor :jobs
-end
-
-describe Shell do
before do
Shell.irb_conf = {}
allow(Shell::ShellSession.instance).to receive(:reset!)
- allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(ChefUtils).to receive(:windows?).and_return(false)
allow(Chef::Util::PathHelper).to receive(:home).and_return("/home/foo")
end
@@ -67,7 +63,7 @@ describe Shell do
conf = OpenStruct.new
conf.main = Object.new
- conf.main.instance_eval(&ObjectTestHarness)
+ conf.main.instance_eval(&object_test_harness)
Shell.irb_conf[:IRB_RC].call(conf)
expect(conf.prompt_c).to eq("chef > ")
expect(conf.return_format).to eq(" => %s \n")
@@ -108,7 +104,7 @@ describe Shell do
before do
@chef_object = Object.new
- @chef_object.instance_eval(&ObjectTestHarness)
+ @chef_object.instance_eval(&object_test_harness)
end
it "creates help text for methods with descriptions" do
@@ -154,8 +150,8 @@ describe Shell do
end
it "creates a help banner with the command descriptions" do
- expect(@chef_object.help_banner).to match(/^\|\ Command[\s]+\|\ Description[\s]*$/)
- expect(@chef_object.help_banner).to match(/^\|\ rspec_method[\s]+\|\ rspecin\'[\s]*$/)
+ expect(@chef_object.help_banner).to match(/^\|\ Command\s+\|\ Description\s*$/)
+ expect(@chef_object.help_banner).to match(/^\|\ rspec_method\s+\|\ rspecin\'\s*$/)
end
end
diff --git a/spec/unit/train_transport_spec.rb b/spec/unit/train_transport_spec.rb
new file mode 100644
index 0000000000..d0724911cb
--- /dev/null
+++ b/spec/unit/train_transport_spec.rb
@@ -0,0 +1,85 @@
+#
+# Author:: Bryan McLellan (<btm@loftninjas.org>)
+# Copyright:: Copyright (c) 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::TrainTransport do
+ let(:transport) { Chef::TrainTransport.new(Chef::Log) }
+
+ describe "load_credentials" do
+ let(:good_credentials) { { "switch.cisco.com" => { "user" => "cisco", "password" => "cisco", "enable_password" => "secret" } } }
+
+ before do
+ allow(transport).to receive(:parse_credentials_file).and_return(good_credentials)
+ end
+
+ it "matches credentials when they exist" do
+ expect(transport.load_credentials("switch.cisco.com")[:user]).to eq("cisco")
+ expect(transport.load_credentials("switch.cisco.com")[:password]).to eq("cisco")
+ expect(transport.load_credentials("switch.cisco.com")[:enable_password]).to eq("secret")
+ end
+
+ it "returns nil if there is no match" do
+ expect(transport.load_credentials("router.unicorns.com")).to be_nil
+ end
+
+ # [foo.example.org] => {"foo"=>{"example"=>{"org"=>{}}}}
+ # ['foo.example.org'] => {"foo.example.org"=>{}}
+ it "warns if the host has been split by toml" do
+ allow(transport).to receive(:credentials_file_path).and_return("/Users/scotthourglass/.chef/credentials")
+ allow(transport).to receive(:parse_credentials_file).and_return({ "foo" => { "example" => { "org" => {} } } })
+ expect(Chef::Log).to receive(:warn).with(/as a Hash/)
+ expect(Chef::Log).to receive(:warn).with(/Hostnames must be surrounded by single quotes/)
+ expect(transport.load_credentials("foo.example.org")).to be_nil
+ end
+ end
+
+ describe "credentials_file_path" do
+ let(:config_cred_file_path) { "/somewhere/credentials" }
+ let(:host_cred_file_path) { Chef::Platform.windows? ? "C:\\chef\\foo.example.org\\credentials" : "/etc/chef/foo.example.org/credentials" }
+
+ context "when a file path is specified by a config" do
+ before do
+ tm_config = double("Config Context", host: "foo.example.org", credentials_file: config_cred_file_path)
+ allow(Chef::Config).to receive(:target_mode).and_return(tm_config)
+ end
+
+ it "returns the path if it exists" do
+ allow(File).to receive(:exist?).with(config_cred_file_path).and_return(true)
+ expect(transport.credentials_file_path).to eq(config_cred_file_path)
+ end
+
+ it "raises an error if it does not exist" do
+ allow(File).to receive(:exist?).and_return(false)
+ expect { transport.credentials_file_path }.to raise_error(ArgumentError, /does not exist/)
+ end
+ end
+
+ it "raises an error if the default creds files do not exist" do
+ allow(File).to receive(:exist?).and_return(false)
+ expect { transport.credentials_file_path }.to raise_error(ArgumentError, /does not exist/)
+ end
+
+ it "returns the path to the default config file if it exists" do
+ tm_config = double("Config Context", host: "foo.example.org", credentials_file: nil)
+ allow(Chef::Config).to receive(:target_mode).and_return(tm_config)
+ allow(File).to receive(:exist?).with(host_cred_file_path).and_return(true)
+ expect(transport.credentials_file_path).to eq(host_cred_file_path)
+ end
+ end
+end
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index a1806db987..a52272799c 100644
--- a/spec/unit/user_spec.rb
+++ b/spec/unit/user_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -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.
@@ -65,7 +65,7 @@ describe Chef::User do
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
+ expect { @user.name({}) }.to raise_error(ArgumentError)
end
end
@@ -84,7 +84,7 @@ describe Chef::User do
end
it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
+ expect { @user.name({}) }.to raise_error(ArgumentError)
end
end
@@ -99,7 +99,7 @@ describe Chef::User do
end
it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
+ expect { @user.public_key({}) }.to raise_error(ArgumentError)
end
end
@@ -114,7 +114,7 @@ describe Chef::User do
end
it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
+ expect { @user.private_key({}) }.to raise_error(ArgumentError)
end
end
@@ -201,7 +201,7 @@ describe Chef::User do
end
describe "API Interactions" do
- before (:each) do
+ before(:each) do
@user = Chef::User.new
@user.name "foobar"
@http_client = double("Chef::ServerAPI mock")
@@ -243,7 +243,7 @@ describe Chef::User do
describe "create" do
it "creates a new user via the API" do
@user.password "password"
- expect(@http_client).to receive(:post).with("users", { :name => "foobar", :admin => false, :password => "password" }).and_return({})
+ expect(@http_client).to receive(:post).with("users", { name: "foobar", admin: false, password: "password" }).and_return({})
@user.create
end
end
@@ -260,7 +260,7 @@ describe Chef::User do
describe "update" do
it "updates an existing user on via the API" do
- expect(@http_client).to receive(:put).with("users/foobar", { :name => "foobar", :admin => false }).and_return({})
+ expect(@http_client).to receive(:put).with("users/foobar", { name: "foobar", admin: false }).and_return({})
@user.update
end
end
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
index 16f0d0158b..17bd4f3094 100644
--- a/spec/unit/user_v1_spec.rb
+++ b/spec/unit/user_v1_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Steven Danna (steve@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,7 @@ describe Chef::UserV1 do
@user = Chef::UserV1.new
end
- shared_examples_for "string fields with no contraints" do
+ shared_examples_for "string fields with no constraints" do
it "should let you set the public key" do
expect(@user.send(method, "some_string")).to eq("some_string")
end
@@ -37,7 +37,7 @@ describe Chef::UserV1 do
end
it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ expect { @user.send(method, {}) }.to raise_error(ArgumentError)
end
end
@@ -57,7 +57,7 @@ describe Chef::UserV1 do
end
it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ expect { @user.send(method, {}) }.to raise_error(ArgumentError)
end
end
@@ -95,7 +95,7 @@ describe Chef::UserV1 do
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ expect { @user.username({}) }.to raise_error(ArgumentError)
end
end
@@ -109,49 +109,49 @@ describe Chef::UserV1 do
describe "string fields" do
describe "public_key" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :public_key }
end
end
describe "private_key" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :private_key }
end
end
describe "display_name" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :display_name }
end
end
describe "first_name" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :first_name }
end
end
describe "middle_name" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :middle_name }
end
end
describe "last_name" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :last_name }
end
end
describe "email" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :email }
end
end
describe "password" do
- it_should_behave_like "string fields with no contraints" do
+ it_should_behave_like "string fields with no constraints" do
let(:method) { :password }
end
end
@@ -311,10 +311,10 @@ describe Chef::UserV1 do
end
describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => "406") }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:response_406) { OpenStruct.new(code: "406") }
+ let(:exception_406) { Net::HTTPClientException.new("406 Not Acceptable", response_406) }
- before (:each) do
+ before(:each) do
@user = Chef::UserV1.new
allow(@user).to receive(:chef_root_rest_v0).and_return(double("chef rest root v0 object"))
allow(@user).to receive(:chef_root_rest_v1).and_return(double("chef rest root v1 object"))
@@ -334,13 +334,13 @@ describe Chef::UserV1 do
let(:payload) do
{
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password",
+ username: "some_username",
+ display_name: "some_display_name",
+ first_name: "some_first_name",
+ middle_name: "some_middle_name",
+ last_name: "some_last_name",
+ email: "some_email",
+ password: "some_password",
}
end
@@ -356,14 +356,14 @@ describe Chef::UserV1 do
context "when server API V1 is not valid on the Chef Server receiving the request" do
let(:payload) do
{
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password",
- :public_key => "some_public_key",
+ username: "some_username",
+ display_name: "some_display_name",
+ first_name: "some_first_name",
+ middle_name: "some_middle_name",
+ last_name: "some_last_name",
+ email: "some_email",
+ password: "some_password",
+ public_key: "some_public_key",
}
end
@@ -373,8 +373,8 @@ describe Chef::UserV1 do
end
context "when the server returns a 400" do
- let(:response_400) { OpenStruct.new(:code => "400") }
- let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
+ let(:response_400) { OpenStruct.new(code: "400") }
+ let(:exception_400) { Net::HTTPClientException.new("400 Bad Request", response_400) }
context "when the 400 was due to public / private key fields no longer being supported" do
let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
@@ -444,12 +444,12 @@ describe Chef::UserV1 do
describe "create" do
let(:payload) do
{
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password",
+ username: "some_username",
+ display_name: "some_display_name",
+ first_name: "some_first_name",
+ last_name: "some_last_name",
+ email: "some_email",
+ password: "some_password",
}
end
before do
@@ -473,7 +473,7 @@ describe Chef::UserV1 do
context "when handling API V1" do
it "creates a new user via the API with a middle_name when it exists" do
@user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({ :middle_name => "some_middle_name" })).and_return({})
+ expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({ middle_name: "some_middle_name" })).and_return({})
@user.create
end
end # when server API V1 is valid on the Chef Server receiving the request
@@ -496,7 +496,7 @@ describe Chef::UserV1 do
it "creates a new user via the API with a middle_name when it exists" do
@user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({ :middle_name => "some_middle_name" })).and_return({})
+ expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({ middle_name: "some_middle_name" })).and_return({})
@user.create
end
end # when server API V1 is not valid on the Chef Server receiving the request
@@ -535,7 +535,7 @@ describe Chef::UserV1 do
end # Versioned API Interactions
describe "API Interactions" do
- before (:each) do
+ before(:each) do
@user = Chef::UserV1.new
@user.username "foobar"
@http_client = double("Chef::ServerAPI mock")
diff --git a/spec/unit/util/backup_spec.rb b/spec/unit/util/backup_spec.rb
index 8bc68ec160..1db64d3029 100644
--- a/spec/unit/util/backup_spec.rb
+++ b/spec/unit/util/backup_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@ require "tmpdir"
describe Chef::Util::Backup do
- let (:tempfile) do
+ let(:tempfile) do
Tempfile.new("chef-util-backup-spec-test")
end
@@ -110,7 +110,7 @@ describe Chef::Util::Backup do
end
it "should strip the drive letter off for windows" do
expect(@backup).to receive(:path).and_return('c:\a\b\c.txt')
- expect(@backup.send(:backup_filename)).to match(%r|^\\a\\b\\c.txt.chef-\d{14}.\d{6}$|)
+ expect(@backup.send(:backup_filename)).to match(/^\\a\\b\\c.txt.chef-\d{14}.\d{6}$/)
end
it "should strip the drive letter off for windows (with forwardslashes)" do
expect(@backup).to receive(:path).and_return("c:/a/b/c.txt")
diff --git a/spec/unit/util/diff_spec.rb b/spec/unit/util/diff_spec.rb
index 4bb0abd087..d1440dbaaa 100644
--- a/spec/unit/util/diff_spec.rb
+++ b/spec/unit/util/diff_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -61,10 +61,6 @@ shared_examples_for "a diff util" do
Chef::Config[:diff_disabled] = true
end
- after do
- Chef::Config[:diff_disabled] = false
- end
-
it "calling for_output should return the error message" do
expect(differ.for_output).to eql( [ "(diff output suppressed by config)" ] )
end
@@ -300,14 +296,9 @@ shared_examples_for "a diff util" do
describe "when testing the diff_filesize_threshold" do
before do
- @diff_filesize_threshold_saved = Chef::Config[:diff_filesize_threshold]
Chef::Config[:diff_filesize_threshold] = 10
end
- after do
- Chef::Config[:diff_filesize_threshold] = @diff_filesize_threshold_saved
- end
-
describe "when the old_file goes over the threshold" do
before do
old_tempfile.write("But thats what you get when Wu-Tang raised you")
@@ -359,14 +350,9 @@ shared_examples_for "a diff util" do
describe "when the diff output is too long" do
before do
- @diff_output_threshold_saved = Chef::Config[:diff_output_threshold]
Chef::Config[:diff_output_threshold] = 10
end
- after do
- Chef::Config[:diff_output_threshold] = @diff_output_threshold_saved
- end
-
it "calling for_output should return the error message" do
expect(differ.for_output).to eql(["(long diff of over 10 characters, diff output suppressed)"])
end
@@ -402,7 +388,7 @@ shared_examples_for "a diff util" do
end
end
- it "should identify null-teriminated multi-line string files as binary" do
+ it "should identify null-terminated multi-line string files as binary" do
Tempfile.open("chef-util-diff-spec") do |file|
file.write("This is a binary file.\nNo Really\nit is\0")
file.close
@@ -546,7 +532,7 @@ shared_examples_for "a diff util" do
end
-describe Chef::Util::Diff, :uses_diff => true do
+describe Chef::Util::Diff do
let!(:old_file) { old_tempfile.path }
let!(:new_file) { new_tempfile.path }
diff --git a/spec/unit/util/dsc/configuration_generator_spec.rb b/spec/unit/util/dsc/configuration_generator_spec.rb
index cfa7a4e264..3f85f3189d 100644
--- a/spec/unit/util/dsc/configuration_generator_spec.rb
+++ b/spec/unit/util/dsc/configuration_generator_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala <jmundrawala@chef.io>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,6 +25,85 @@ describe Chef::Util::DSC::ConfigurationGenerator do
Chef::Util::DSC::ConfigurationGenerator.new(node, "tmp")
end
+ describe "#validate_switch_name!" do
+ it "should not raise an error if a name contains all upper case letters" do
+ conf_man.send(:validate_switch_name!, "HELLO")
+ end
+
+ it "should not raise an error if the name contains all lower case letters" do
+ conf_man.send(:validate_switch_name!, "hello")
+ end
+
+ it "should not raise an error if no special characters are used except _" do
+ conf_man.send(:validate_switch_name!, "hello_world")
+ end
+
+ %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
+ it "raises an ArgumentError if configuration name contains #{sym}" do
+ expect do
+ conf_man.send(:validate_switch_name!, "Hello#{sym}")
+ end.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "#escape_parameter_value" do
+ # Is this list really complete?
+ %w{` " # '}.each do |c|
+ it "escapes #{c}" do
+ expect(conf_man.send(:escape_parameter_value, "stuff #{c}")).to eql("stuff `#{c}")
+ end
+ end
+
+ it "does not do anything to a string without special characters" do
+ expect(conf_man.send(:escape_parameter_value, "stuff")).to eql("stuff")
+ end
+ end
+
+ describe "#escape_string_parameter_value" do
+ it "surrounds a string with ''" do
+ expect(conf_man.send(:escape_string_parameter_value, "stuff")).to eql("'stuff'")
+ end
+ end
+
+ describe "#command_switches_string" do
+ it "raises an ArgumentError if the key is not a symbol" do
+ expect do
+ conf_man.send(:command_switches_string, { "foo" => "bar" })
+ end.to raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid switch names" do
+ expect do
+ conf_man.send(:command_switches_string, { foo!: "bar" })
+ end.to raise_error(ArgumentError)
+ end
+
+ it "ignores switches with a false value" do
+ expect(conf_man.send(:command_switches_string, { foo: false })).to eql("")
+ end
+
+ it "should correctly handle a value type of string" do
+ expect(conf_man.send(:command_switches_string, { foo: "bar" })).to eql("-foo 'bar'")
+ end
+
+ it "should correctly handle a value type of string even when it is 0 length" do
+ expect(conf_man.send(:command_switches_string, { foo: "" })).to eql("-foo ''")
+ end
+
+ it "should not quote integers" do
+ expect(conf_man.send(:command_switches_string, { foo: 1 })).to eql("-foo 1")
+ end
+
+ it "should not quote floats" do
+ expect(conf_man.send(:command_switches_string, { foo: 1.0 })).to eql("-foo 1.0")
+ end
+
+ it "has just the switch when the value is true" do
+ expect(conf_man.send(:command_switches_string, { foo: true })).to eql("-foo")
+ end
+ end
+
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")
@@ -76,7 +155,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
context "when symbols are used as switches" do
it "should merge the hash if there are no restricted switches" do
- merged = conf_man.send(:get_merged_configuration_flags!, { :flag => "a" }, "hello")
+ merged = conf_man.send(:get_merged_configuration_flags!, { flag: "a" }, "hello")
expect(merged).to include(:flag)
expect(merged[:flag]).to eql("a")
expect(merged).to include(:outputpath)
@@ -84,18 +163,18 @@ describe Chef::Util::DSC::ConfigurationGenerator do
it "should raise an ArgumentError if you try to override outputpath" do
expect do
- conf_man.send(:get_merged_configuration_flags!, { :outputpath => "a" }, "hello")
+ conf_man.send(:get_merged_configuration_flags!, { outputpath: "a" }, "hello")
end.to raise_error(ArgumentError)
end
it "should be case insensitive for switches that are not allowed" do
expect do
- conf_man.send(:get_merged_configuration_flags!, { :OutputPath => "a" }, "hello")
+ conf_man.send(:get_merged_configuration_flags!, { OutputPath: "a" }, "hello")
end.to raise_error(ArgumentError)
end
it "should be case insensitive to switches that are allowed" do
- merged = conf_man.send(:get_merged_configuration_flags!, { :FLAG => "a" }, "hello")
+ merged = conf_man.send(:get_merged_configuration_flags!, { FLAG: "a" }, "hello")
expect(merged).to include(:flag)
end
end
@@ -161,7 +240,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
dsc = conf_man.send(:configuration_code, "archive{}", "hello", {})
found_configuration = false
dsc.split(";").each do |command|
- if command.downcase =~ /\s*configuration\s+'hello'\s*\{\s*node\s+'localhost'\s*\{\s*archive\s*\{\s*\}\s*\}\s*\}\s*/
+ if /\s*configuration\s+'hello'\s*\{\s*node\s+'localhost'\s*\{\s*archive\s*\{\s*\}\s*\}\s*\}\s*/.match?(command.downcase)
found_configuration = true
end
end
diff --git a/spec/unit/util/dsc/lcm_output_parser_spec.rb b/spec/unit/util/dsc/lcm_output_parser_spec.rb
index d59497de6f..5d5cc79f15 100644
--- a/spec/unit/util/dsc/lcm_output_parser_spec.rb
+++ b/spec/unit/util/dsc/lcm_output_parser_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,146 +19,230 @@
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 ]
-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)
+ str = <<~EOF
+ logtype: [machinename]: LCM: [ Start Set ]
+ 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, false)
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
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-logtype: [machinename]: LCM: [ Start Set ] [name]
-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)
+ str = <<~EOF
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name]
+ logtype: [machinename]: LCM: [ Start Set ] [name]
+ 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, false)
expect(resources[0].changes_state?).to be_truthy
end
it "preserves the log provided for how the system changed the state" do
- str = <<EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-logtype: [machinename]: LCM: [ Start Set ] [name]
-logtype: [machinename]: [message]
-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)
+ str = <<~EOF
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name]
+ logtype: [machinename]: LCM: [ Start Set ] [name]
+ logtype: [machinename]: [message]
+ 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, false)
expect(resources[0].change_log).to match_array(["[name]", "[message]", "[name]"])
end
- it "should return false for changes_state?" do
- str = <<EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-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)
+ it "returns false for changes_state?" do
+ str = <<~EOF
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name]
+ 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, 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
- str = <<EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-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)
+ 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]
+ 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, 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
- str = <<-EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Skip Set ]
-logtype: [machinename]: LCM: [ End Resource ]
-logtype: [machinename]: LCM: [ Start Resource ] [name2]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ End Set ]
-logtype: [machinename]: LCM: [ End Set ]
-EOF
-
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ 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]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Skip Set ]
+ logtype: [machinename]: LCM: [ End Resource ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name2]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ End Set ]
+ logtype: [machinename]: LCM: [ End Set ]
+ EOF
+
+ 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
- str = <<-EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Skip Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name2]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ End Set ]
-logtype: [machinename]: LCM: [ End Resource ]
-logtype: [machinename]: LCM: [ End Set ]
-EOF
-
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ 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]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Skip Set ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name2]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ End Set ]
+ logtype: [machinename]: LCM: [ End Resource ]
+ logtype: [machinename]: LCM: [ End Set ]
+ EOF
+
+ 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
- str = <<-EOF
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ Start Resource ] [name]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Start Resource ] [name2]
-logtype: [machinename]: LCM: [ Start Test ]
-logtype: [machinename]: LCM: [ End Test ]
-logtype: [machinename]: LCM: [ Start Set ]
-logtype: [machinename]: LCM: [ End Set ]
-logtype: [machinename]: LCM: [ End Resource ]
-logtype: [machinename]: LCM: [ End Set ]
-EOF
- resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str)
+ 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]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Start Resource ] [name2]
+ logtype: [machinename]: LCM: [ Start Test ]
+ logtype: [machinename]: LCM: [ End Test ]
+ logtype: [machinename]: LCM: [ Start Set ]
+ logtype: [machinename]: LCM: [ End Set ]
+ logtype: [machinename]: LCM: [ End Resource ]
+ logtype: [machinename]: LCM: [ End Set ]
+ EOF
+ 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 45fe8df40d..8adf778949 100644
--- a/spec/unit/util/dsc/local_configuration_manager_spec.rb
+++ b/spec/unit/util/dsc/local_configuration_manager_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards <adamed@chef.io>
-# Copyright:: Copyright 2014-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,118 +24,169 @@ describe Chef::Util::DSC::LocalConfigurationManager do
let(:lcm) { Chef::Util::DSC::LocalConfigurationManager.new(nil, "tmp") }
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
+ <<~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) do
- <<-EOH
-Start-DscConfiguration : A parameter cannot be found\r\n that matches parameter name 'whatif'.
-At line:1 char:123
-+ run-somecommand -whatif
-+ ~~~~~~~~
- + CategoryInfo : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException
- + FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand
-EOH
+ <<~EOH
+ Start-DscConfiguration : A parameter cannot be found\r\n that matches parameter name 'whatif'.
+ At line:1 char:123
+ + run-somecommand -whatif
+ + ~~~~~~~~
+ + CategoryInfo : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException
+ + FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand
+ EOH
end
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
+ <<~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) do
- double("LCM cmdlet status", :stderr => lcm_standard_error, :return_value => lcm_standard_output, :succeeded? => lcm_cmdlet_success)
+ let(:powershell) do
+ double("Chef::PowerShell", errors: lcm_errors, error?: !lcm_errors.empty?, result: lcm_result)
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(:run_configuration_cmdlet).and_return(powershell)
+ 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_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
+
+ 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_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
+
+ 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
+ expect(Chef::Log).not_to receive(:warn)
end
+ end
- let(:lcm_standard_output) { normal_lcm_output }
- let(:lcm_standard_error) { nil }
- let(:lcm_cmdlet_success) { true }
+ context "when running on PowerShell version less than 5" do
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
- it "should successfully return resource information for normally formatted output when cmdlet the cmdlet succeeds" do
- test_configuration_result = lcm.test_configuration("config", {})
+ 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 | Out-String" }
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
+
+ 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 }
+ let(:lcm_result) { "" }
+ let(:lcm_errors) { [no_whatif_lcm_output] }
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
- expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
+ expect { test_configuration_result = lcm.test_configuration("config") }.not_to raise_error
expect(test_configuration_result.class).to be(Array)
end
end
context "that fails due to a DSC resource not being imported before StartDSCConfiguration -whatif is executed" do
- let(:lcm_standard_output) { "" }
- let(:lcm_standard_error) { dsc_resource_import_failure_output }
- let(:lcm_cmdlet_success) { false }
+ let(:lcm_result) { "" }
+ let(:lcm_errors) { [dsc_resource_import_failure_output] }
- 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
+ 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
+ expect { test_configuration_result = lcm.test_configuration("config") }.not_to raise_error
expect(test_configuration_result.class).to be(Array)
end
end
context "that fails due to an unknown PowerShell cmdlet error" do
- let(:lcm_standard_output) { "some output" }
- let(:lcm_standard_error) { "Abort, Retry, Fail?" }
- let(:lcm_cmdlet_success) { false }
+ let(:lcm_result) { "some output" }
+ let(:lcm_errors) { ["Abort, Retry, Fail?"] }
- 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
+ expect { lcm.test_configuration("config") }.not_to raise_error
end
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", :windows_powershell_dsc_only do
+ context "when invalid dsc script is given" do
+ it "raises exception" do
+ configuration_document = "invalid-config"
+ expect { lcm.send(:run_configuration_cmdlet, configuration_document, true) }.to raise_error(Chef::PowerShell::CommandFailed)
+ end
+ end
+ end
end
diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb
index a864a2c1da..540a570cd3 100644
--- a/spec/unit/util/dsc/resource_store.rb
+++ b/spec/unit/util/dsc/resource_store.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala <jdm@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,7 +55,7 @@ describe Chef::Util::DSC::ResourceStore do
expect(resource_store.find("foo")).to eql([resource_a])
end
- it "returns multiple resoures if they are found" do
+ it "returns multiple resources if they are found" do
expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_b])
expect(resource_store.find("foo")).to include(resource_a, resource_b)
end
diff --git a/spec/unit/util/editor_spec.rb b/spec/unit/util/editor_spec.rb
index e53bc9662a..ac851a835e 100644
--- a/spec/unit/util/editor_spec.rb
+++ b/spec/unit/util/editor_spec.rb
@@ -9,7 +9,7 @@ describe Chef::Util::Editor do
end
it "makes a copy of an Array" do
- array = Array.new
+ array = []
editor = described_class.new(array)
expect(editor.lines).to_not be(array)
end
diff --git a/spec/unit/util/file_edit_spec.rb b/spec/unit/util/file_edit_spec.rb
index 1a71d1d856..5bf8580368 100644
--- a/spec/unit/util/file_edit_spec.rb
+++ b/spec/unit/util/file_edit_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,29 +22,29 @@ require "tempfile"
describe Chef::Util::FileEdit do
let(:starting_content) do
- <<-EOF
-127.0.0.1 localhost
-255.255.255.255 broadcasthost
-::1 localhost
-fe80::1%lo0 localhost
+ <<~EOF
+ 127.0.0.1 localhost
+ 255.255.255.255 broadcasthost
+ ::1 localhost
+ fe80::1%lo0 localhost
EOF
end
let(:localhost_replaced) do
- <<-EOF
-127.0.0.1 replacement
-255.255.255.255 broadcasthost
-::1 replacement
-fe80::1%lo0 replacement
+ <<~EOF
+ 127.0.0.1 replacement
+ 255.255.255.255 broadcasthost
+ ::1 replacement
+ fe80::1%lo0 replacement
EOF
end
let(:localhost_line_replaced) do
- <<-EOF
-replacement line
-255.255.255.255 broadcasthost
-replacement line
-replacement line
+ <<~EOF
+ replacement line
+ 255.255.255.255 broadcasthost
+ replacement line
+ replacement line
EOF
end
@@ -54,41 +54,41 @@ replacement line
end
let(:localhost_line_deleted) do
- <<-EOF
-255.255.255.255 broadcasthost
+ <<~EOF
+ 255.255.255.255 broadcasthost
EOF
end
let(:append_after_all_localhost) do
- <<-EOF
-127.0.0.1 localhost
-new line inserted
-255.255.255.255 broadcasthost
-::1 localhost
-new line inserted
-fe80::1%lo0 localhost
-new line inserted
+ <<~EOF
+ 127.0.0.1 localhost
+ new line inserted
+ 255.255.255.255 broadcasthost
+ ::1 localhost
+ new line inserted
+ fe80::1%lo0 localhost
+ new line inserted
EOF
end
let(:append_after_content) do
- <<-EOF
-127.0.0.1 localhost
-255.255.255.255 broadcasthost
-::1 localhost
-fe80::1%lo0 localhost
-new line inserted
+ <<~EOF
+ 127.0.0.1 localhost
+ 255.255.255.255 broadcasthost
+ ::1 localhost
+ fe80::1%lo0 localhost
+ new line inserted
EOF
end
let(:append_twice) do
- <<-EOF
-127.0.0.1 localhost
-255.255.255.255 broadcasthost
-::1 localhost
-fe80::1%lo0 localhost
-once
-twice
+ <<~EOF
+ 127.0.0.1 localhost
+ 255.255.255.255 broadcasthost
+ ::1 localhost
+ fe80::1%lo0 localhost
+ once
+ twice
EOF
end
@@ -105,7 +105,7 @@ twice
target_file.close!
end
- describe "initialiize" do
+ describe "initialize" do
it "should create a new Chef::Util::FileEdit object" do
expect(fedit).to be_instance_of(Chef::Util::FileEdit)
end
diff --git a/spec/unit/util/powershell/cmdlet_spec.rb b/spec/unit/util/powershell/cmdlet_spec.rb
deleted file mode 100644
index 800e4cc9c0..0000000000
--- a/spec/unit/util/powershell/cmdlet_spec.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Author:: Jay Mundrawala <jdm@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 "chef"
-require "chef/util/powershell/cmdlet"
-
-describe Chef::Util::Powershell::Cmdlet do
- before (:all) do
- @node = Chef::Node.new
- @cmdlet = Chef::Util::Powershell::Cmdlet.new(@node, "Some-Commandlet")
- end
-
- 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
-
- it "should not raise an error if the name contains all lower case letters" do
- @cmdlet.send(:validate_switch_name!, "hello")
- end
-
- it "should not raise an error if no special characters are used except _" do
- @cmdlet.send(:validate_switch_name!, "hello_world")
- end
-
- %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
- it "raises an Argument error if it configuration name contains #{sym}" do
- expect do
- @cmdlet.send(:validate_switch_name!, "Hello#{sym}")
- end.to raise_error(ArgumentError)
- end
- end
- end
-
- describe "#escape_parameter_value" do
- # Is this list really complete?
- %w{` " # '}.each do |c|
- it "escapse #{c}" do
- expect(@cmdlet.send(:escape_parameter_value, "stuff #{c}")).to eql("stuff `#{c}")
- end
- end
-
- it "does not do anything to a string without special characters" do
- expect(@cmdlet.send(:escape_parameter_value, "stuff")).to eql("stuff")
- end
- end
-
- 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
- it "raises an ArgumentError if the key is not a symbol" do
- expect do
- @cmdlet.send(:command_switches_string, { "foo" => "bar" })
- end.to raise_error(ArgumentError)
- end
-
- it "does not allow invalid switch names" do
- expect do
- @cmdlet.send(:command_switches_string, { :foo! => "bar" })
- end.to raise_error(ArgumentError)
- end
-
- it "ignores switches with a false value" do
- expect(@cmdlet.send(:command_switches_string, { foo: false })).to eql("")
- end
-
- it "should correctly handle a value type of string" do
- expect(@cmdlet.send(:command_switches_string, { foo: "bar" })).to eql("-foo 'bar'")
- end
-
- it "should correctly handle a value type of string even when it is 0 length" do
- expect(@cmdlet.send(:command_switches_string, { foo: "" })).to eql("-foo ''")
- end
-
- it "should not quote integers" do
- expect(@cmdlet.send(:command_switches_string, { foo: 1 })).to eql("-foo 1")
- end
-
- it "should not quote floats" do
- expect(@cmdlet.send(:command_switches_string, { foo: 1.0 })).to eql("-foo 1.0")
- end
-
- it "has just the switch when the value is true" do
- expect(@cmdlet.send(:command_switches_string, { foo: true })).to eql("-foo")
- end
- end
-end
diff --git a/spec/unit/util/powershell/ps_credential_spec.rb b/spec/unit/util/powershell/ps_credential_spec.rb
index 6f65174d15..73abe41de8 100644
--- a/spec/unit/util/powershell/ps_credential_spec.rb
+++ b/spec/unit/util/powershell/ps_credential_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala <jdm@chef.io>
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,8 +20,8 @@ require "chef"
require "chef/util/powershell/ps_credential"
describe Chef::Util::Powershell::PSCredential do
- let (:username) { "foo" }
- let (:password) { "ThIsIsThEpAsSwOrD" }
+ let(:username) { "foo" }
+ let(:password) { "ThIsIsThEpAsSwOrD" }
context "when username and password are provided" do
let(:ps_credential) { Chef::Util::Powershell::PSCredential.new(username, password) }
@@ -29,8 +29,9 @@ describe Chef::Util::Powershell::PSCredential do
it "should create the script to create a PSCredential when calling" do
allow(ps_credential).to receive(:encrypt).with(password).and_return("encrypted")
expect(ps_credential.to_psobject).to eq(
- "New-Object System.Management.Automation.PSCredential("\
- "'#{username}',('encrypted' | ConvertTo-SecureString))")
+ "New-Object System.Management.Automation.PSCredential("\
+ "'#{username}',('encrypted' | ConvertTo-SecureString))"
+ )
end
end
diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb
index 609ff02215..72a0824895 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 (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,6 +30,9 @@ describe Chef::Util::Selinux do
end
before do
+ allow(ChefUtils).to receive(:windows?).and_return(false)
+ allow(ENV).to receive(:[]).with("PATHEXT").and_return(nil)
+ allow(ENV).to receive(:[]).with("PATH").and_call_original
TestClass.reset_state
@test_instance = TestClass.new
end
@@ -39,9 +42,9 @@ describe Chef::Util::Selinux do
end
it "each part of ENV['PATH'] should be checked" do
- expected_paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
+ expected_paths = ENV["PATH"].split(File::PATH_SEPARATOR) + %w{/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin}
- expected_paths.each do |bin_path|
+ expected_paths.uniq.each do |bin_path|
selinux_path = File.join(bin_path, "selinuxenabled")
expect(File).to receive(:executable?).with(selinux_path).and_return(false)
end
@@ -60,8 +63,8 @@ describe Chef::Util::Selinux do
describe "when selinux is enabled" do
before do
- cmd_result = double("Cmd Result", :exitstatus => 0)
- expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { :returns => [0, 1] }).and_return(cmd_result)
+ cmd_result = double("Cmd Result", exitstatus: 0)
+ expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { returns: [0, 1] }).and_return(cmd_result)
end
it "should report selinux is enabled" do
@@ -73,8 +76,8 @@ describe Chef::Util::Selinux do
describe "when selinux is disabled" do
before do
- cmd_result = double("Cmd Result", :exitstatus => 1)
- expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { :returns => [0, 1] }).and_return(cmd_result)
+ cmd_result = double("Cmd Result", exitstatus: 1)
+ expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { returns: [0, 1] }).and_return(cmd_result)
end
it "should report selinux is disabled" do
@@ -86,8 +89,8 @@ describe Chef::Util::Selinux do
describe "when selinux gives an unexpected status" do
before do
- cmd_result = double("Cmd Result", :exitstatus => 101)
- expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { :returns => [0, 1] }).and_return(cmd_result)
+ cmd_result = double("Cmd Result", exitstatus: 101)
+ expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, { returns: [0, 1] }).and_return(cmd_result)
end
it "should throw an error" do
@@ -113,7 +116,7 @@ describe Chef::Util::Selinux do
end
describe "when restorecon binary exists on the system" do
- let (:path) { "/path/to/awesome directory" }
+ let(:path) { "/path/to/awesome directory" }
before do
@restorecon_enabled_path = File.join("/sbin", "restorecon")
@@ -124,45 +127,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_compacted!).with(@restorecon_enabled_path, "-R", path).twice
@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_compacted!).with(@restorecon_enabled_path, "-R", "-r", path).twice
@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_compacted!).with(@restorecon_enabled_path, "-R", path).twice
@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/threaded_job_queue_spec.rb b/spec/unit/util/threaded_job_queue_spec.rb
index 8a89943a8a..6925cb5dda 100644
--- a/spec/unit/util/threaded_job_queue_spec.rb
+++ b/spec/unit/util/threaded_job_queue_spec.rb
@@ -1,4 +1,4 @@
-# Copyright:: Copyright 2014-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,8 +21,17 @@ end
describe Chef::Util::ThreadedJobQueue do
let(:queue) { Chef::Util::ThreadedJobQueue.new }
+ around(:example) do |example|
+ old_value = Thread.report_on_exception
+ Thread.report_on_exception = false
+
+ example.run
+
+ Thread.report_on_exception = old_value
+ end
+
it "should pass mutex to jobs with an arity of 1" do
- job = double()
+ job = double
expect(job).to receive(:arity).at_least(:once).and_return(1)
expect(job).to receive(:call).exactly(5).times.with(an_instance_of(Mutex))
@@ -31,7 +40,7 @@ describe Chef::Util::ThreadedJobQueue do
end
it "should pass nothing to jobs with an arity of 0" do
- job = double()
+ job = double
expect(job).to receive(:arity).at_least(:once).and_return(0)
expect(job).to receive(:call).exactly(5).times.with(no_args)
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..b484aed6c6
--- /dev/null
+++ b/spec/unit/util/windows/logon_session_spec.rb
@@ -0,0 +1,285 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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 raises 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_class_spec.rb b/spec/unit/version_class_spec.rb
index 5543fbe3c7..cb90f13df7 100644
--- a/spec/unit/version_class_spec.rb
+++ b/spec/unit/version_class_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -105,7 +105,7 @@ describe Chef::Version do
a = %w{0.0.0 0.0.1 0.1.0 0.1.1 1.0.0 1.1.0 1.1.1}.map do |s|
Chef::Version.new(s)
end
- got = a.sort.map { |v| v.to_s }
+ got = a.sort.map(&:to_s)
expect(got).to eq(%w{0.0.0 0.0.1 0.1.0 0.1.1 1.0.0 1.1.0 1.1.1})
end
@@ -113,7 +113,7 @@ describe Chef::Version do
a = %w{9.8.7 1.0.0 1.2.3 4.4.6 4.5.6 0.8.6 4.5.5 5.9.8 3.5.7}.map do |s|
Chef::Version.new(s)
end
- got = a.sort.map { |v| v.to_s }
+ got = a.sort.map(&:to_s)
expect(got).to eq(%w{0.8.6 1.0.0 1.2.3 3.5.7 4.4.6 4.5.5 4.5.6 5.9.8 9.8.7})
end
@@ -160,9 +160,9 @@ describe Chef::Version do
[ "1.2.2", :<=, "1.2.1", false ],
[ "1.2.2", :<, "1.2.1", false ],
].each do |spec|
- it "(#{spec.first(3).join(' ')}) should be #{spec[3]}" do
+ it "(#{spec.first(3).join(" ")}) should be #{spec[3]}" do
got = Chef::Version.new(spec[0]).send(spec[1],
- Chef::Version.new(spec[2]))
+ Chef::Version.new(spec[2]))
expect(got).to eq(spec[3])
end
end
diff --git a/spec/unit/version_constraint_spec.rb b/spec/unit/version_constraint_spec.rb
index f83af03fb7..67078cffca 100644
--- a/spec/unit/version_constraint_spec.rb
+++ b/spec/unit/version_constraint_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2010-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
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..d4873f537a
--- /dev/null
+++ b/spec/unit/win32/error_spec.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Aliasgar Batterywala (aliasgar.batterywala@msystechnologies.com)
+# Copyright:: Copyright (c) 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 ChefUtils.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 with code 2202" 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
+
+ context "last error received is logon failure" do
+ it "raises a Win32ApiError exception with code 1326" do
+ expect { Chef::ReservedNames::Win32::Error.raise! nil, Chef::ReservedNames::Win32::API::Error::ERROR_LOGON_FAILURE }.to raise_error { |error|
+ expect(error).to be_a(Chef::Exceptions::Win32APIError)
+ # This is not localized but neither is the exception.
+ expect(error.to_s).to match(/System Error Code: 1326/)
+ }
+ 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..69864ccd70
--- /dev/null
+++ b/spec/unit/win32/link_spec.rb
@@ -0,0 +1,73 @@
+#
+# Copyright:: Copyright (c) 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 ChefUtils.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 unprivileged 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 unprivileged 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 unprivileged 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/registry_spec.rb b/spec/unit/win32/registry_spec.rb
index 1523ac1919..124765129b 100644
--- a/spec/unit/win32/registry_spec.rb
+++ b/spec/unit/win32/registry_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prajakta Purohit (prajakta@chef.io)
-# Copyright:: Copyright 2012-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,16 +21,16 @@ require "spec_helper"
describe Chef::Win32::Registry do
include_context "Win32"
- let(:value1) { { :name => "one", :type => :string, :data => "1" } }
- let(:value1_upcase_name) { { :name => "ONE", :type => :string, :data => "1" } }
+ let(:value1) { { name: "one", type: :string, data: "1" } }
+ let(:value1_upcase_name) { { name: "ONE", type: :string, data: "1" } }
let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
let(:key) { 'Software\OpscodeNumbers' }
let(:key_parent) { "Software" }
let(:key_to_delete) { "OpscodeNumbers" }
let(:sub_key) { "OpscodePrimes" }
let(:missing_key_path) { 'HKCU\Software' }
- let(:registry) { Chef::Win32::Registry.new() }
- let(:hive_mock) { double("::Win32::Registry::KHKEY_CURRENT_USER") }
+ let(:registry) { Chef::Win32::Registry.new }
+ let(:hive_mock) { double("::Win32::Registry::HKEY_CURRENT_USER") }
let(:reg_mock) { double("reg") }
before(:all) do
@@ -41,7 +41,7 @@ describe Chef::Win32::Registry do
before(:each) do
allow_any_instance_of(Chef::Win32::Registry).to receive(:machine_architecture).and_return(:x86_64)
- #Making the values for registry constants available on unix
+ # Making the values for registry constants available on unix
Win32::Registry::KEY_SET_VALUE = 0x0002
Win32::Registry::KEY_QUERY_VALUE = 0x0001
Win32::Registry::KEY_WRITE = 0x00020000 | 0x0002 | 0x0004
@@ -252,7 +252,7 @@ describe Chef::Win32::Registry do
expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
- expect(reg_mock).to receive(:each_key).and_return(no_args())
+ expect(reg_mock).to receive(:each_key).and_return(no_args)
expect(registry.has_subkeys?(key_path)).to eq(false)
end
@@ -290,7 +290,7 @@ describe Chef::Win32::Registry do
expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
- expect(reg_mock).to receive(:any?).and_yield(no_args())
+ expect(reg_mock).to receive(:any?).and_yield(no_args)
registry.value_exists?(key_path, value1) == false
end
end
@@ -305,7 +305,7 @@ describe Chef::Win32::Registry do
expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "1")
+ expect(reg_mock).to receive(:each).with(no_args).and_yield("one", 1, "1")
expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
expect(registry.data_exists?(key_path, value1)).to eq(true)
end
@@ -315,7 +315,7 @@ describe Chef::Win32::Registry do
expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "2")
+ expect(reg_mock).to receive(:each).with(no_args).and_yield("one", 1, "2")
expect(registry.data_exists?(key_path, value1)).to eq(false)
end
end
diff --git a/spec/unit/win32/security_spec.rb b/spec/unit/win32/security_spec.rb
new file mode 100644
index 0000000000..ed4c033aa3
--- /dev/null
+++ b/spec/unit/win32/security_spec.rb
@@ -0,0 +1,136 @@
+#
+# Author:: Aliasgar Batterywala (aliasgar.batterywala@msystechnologies.com)
+# Copyright:: Copyright (c) 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 ChefUtils.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
+ token = double(:process_token)
+ allow(token).to receive_message_chain(:handle, :handle)
+
+ allow(Chef::ReservedNames::Win32::Security).to receive(:open_current_process_token).and_return(token)
+ 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
+
+ describe "self.lookup_account_name" do
+ let(:security_class) { Chef::ReservedNames::Win32::Security }
+
+ context "when FFI::LastError.error result is ERROR_INSUFFICIENT_BUFFER" do
+ it "does not raise the exception" do
+ expect(FFI::LastError).to receive(:error).and_return(122)
+ expect { security_class.lookup_account_name "system" }.to_not raise_error
+ end
+ end
+
+ context "when operation completed successfully and FFI::LastError.error result is NO_ERROR" do
+ it "does not raise the exception" do
+ expect(FFI::LastError).to receive(:error).and_return(0)
+ expect { security_class.lookup_account_name "system" }.to_not raise_error
+ end
+ end
+
+ context "when FFI::LastError.error result is not ERROR_INSUFFICIENT_BUFFER and not NO_ERROR" do
+ it "raises Chef::ReservedNames::Win32::Error.raise! exception" do
+ expect(FFI::LastError).to receive(:error).and_return(123).at_least(:once)
+ expect { security_class.lookup_account_name "system" }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+ end
+end
diff --git a/spec/unit/windows_service_spec.rb b/spec/unit/windows_service_spec.rb
index 3555b70f1b..02a795426d 100644
--- a/spec/unit/windows_service_spec.rb
+++ b/spec/unit/windows_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
-# Copyright:: Copyright 2013-2016, Chef Software, Inc.
+# Copyright:: Copyright (c) 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.
#
require "spec_helper"
-if Chef::Platform.windows?
+if ChefUtils.windows?
require "chef/application/windows_service"
end
@@ -32,16 +32,18 @@ describe "Chef::Application::WindowsService", :windows_only do
let(:timeout) { 7200 }
let(:shellout_options) do
{
- :timeout => timeout,
- :logger => Chef::Log,
+ timeout: timeout,
+ logger: Chef::Log,
}
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/bin/bundle-platform b/tasks/bin/bundle-platform
deleted file mode 100755
index aa8443e74c..0000000000
--- a/tasks/bin/bundle-platform
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env ruby
-require_relative "bundler_patch"
-
-platforms = ARGV.shift
-platforms = platforms.split(" ").map { |p| Gem::Platform.new(p) }
-Gem::Platform.instance_eval { @local = platforms.last }
-old_platforms = Gem.platforms
-Gem.platforms = platforms
-puts "bundle-platform set Gem.platforms to #{Gem.platforms.map { |p| p.to_s }} (was #{old_platforms.map { |p| p.to_s } })"
-
-desired_version = ARGV.shift.delete("_")
-
-# 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", desired_version)
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/bundler_patch.rb b/tasks/bin/bundler_patch.rb
deleted file mode 100644
index 5665e44eca..0000000000
--- a/tasks/bin/bundler_patch.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# This is a temporary monkey patch to address https://github.com/bundler/bundler/issues/4896
-# the heart of the fix is line #18 with the addition of:
-# && (possibility.activated - existing_node.payload.activated).empty?
-# This ensures we do not mis linux platform gems in some scenarios like ffi in kitchen-test.
-# There is a permanent fix to bundler (See https://github.com/bundler/bundler/pull/4836) which
-# is due to ship in v1.14. Once we adopt that version, we can remove this file
-
-require "bundler"
-require "bundler/vendor/molinillo/lib/molinillo/resolution"
-
-module Bundler::Molinillo
- class Resolver
- # A specific resolution from a given {Resolver}
- class Resolution
- def attempt_to_activate
- debug(depth) { "Attempting to activate " + possibility.to_s }
- existing_node = activated.vertex_named(name)
- if existing_node.payload && (possibility.activated - existing_node.payload.activated).empty?
- debug(depth) { "Found existing spec (#{existing_node.payload})" }
- attempt_to_activate_existing_spec(existing_node)
- else
- attempt_to_activate_new_spec
- end
- end
- end
- end
-end
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/gem-version-diff b/tasks/bin/gem-version-diff
deleted file mode 100755
index 617402d4e6..0000000000
--- a/tasks/bin/gem-version-diff
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env ruby
-
-require_relative "../../version_policy"
-
-old_version, new_version = ARGV[0..1]
-
-require "set"
-ENV["BUNDLE_WITHOUT"] = INSTALL_WITHOUT_GROUPS.join(":")
-relevant_gems = Set.new
-`bundle list`.each_line do |line|
- next unless line =~ /^ \* (\S+)/
- relevant_gems.add($1)
-end
-
-old_gems = {}
-old_file = `git show #{old_version}:Gemfile.lock`
-old_file.each_line do |line|
- next unless line =~ /^ (\S+) \(([^\)]+)\)$/
- next unless relevant_gems.include?($1)
- old_gems[$1] = $2
-end
-
-new_gems = {}
-new_file = `git show #{new_version}:Gemfile.lock`
-new_file.each_line do |line|
- next unless line =~ /^ (\S+) \(([^\)]+)\)$/
- next unless relevant_gems.include?($1)
- new_gems[$1] = $2
-end
-
-modified_gems = (old_gems.keys & new_gems.keys).sort.select { |name| new_gems[name] != old_gems[name] }.map { |name| "#{name} - #{new_gems[name]} (was #{old_gems[name]})" }
-removed_gems = (old_gems.keys - new_gems.keys).sort.map { |name| "#{name} - #{old_gems[name]}" }
-added_gems = (new_gems.keys - old_gems.keys).sort.map { |name| "#{name} - #{new_gems[name]}" }
-
-puts "MODIFIED:\n#{modified_gems.join("\n")}" if modified_gems.any?
-puts "\nADDED:\n#{added_gems.join("\n")}" if added_gems.any?
-puts "\nREMOVED:\n#{removed_gems.join("\n")}" if removed_gems.any?
diff --git a/tasks/bin/run_external_test b/tasks/bin/run_external_test
index 74f76d3229..e4d984e9ee 100755
--- a/tasks/bin/run_external_test
+++ b/tasks/bin/run_external_test
@@ -1,47 +1,39 @@
-#!/bin/bash
-
-# Fail fast (e) and echo commands (vx)
-set -evx
-
-# Arguments
-TEST_GEM=$1
-shift
-
-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
-
-TEST_GEM_ROOT=$(bundle show $TEST_GEM)
-
-# 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
-
-# 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
-
-bundle config
-bundle exec $@
+#!/usr/bin/env ruby
+
+# This script helps to test external gems in the content of the current
+# Chef install. We want to make sure that the external gems will still function
+# once we release Chef so we run *their* specs against the current contents
+# of the chef / ohai repos. It let's us know if we need to update downstream
+# gems or fix regressions in chef *before* we release.
+
+$:.unshift(File.expand_path("../../lib", __dir__))
+
+require "tmpdir"
+require "bundler"
+require "chef/mixin/shell_out"
+
+include Chef::Mixin::ShellOut
+
+github_repo = ARGV.shift
+git_thing = ARGV.shift
+
+build_dir = Dir.pwd
+
+env = {
+ "GEMFILE_MOD" => "gem 'chef', path: '#{build_dir}'; " \
+ "gem 'ohai', git: 'https://github.com/chef/ohai.git', branch: 'master'",
+ "CHEF_LICENSE" => "accept-no-persist",
+}
+
+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_unbundled_env do
+ shell_out!("bundle install --jobs=3 --retry=3", 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 0176fe209e..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 "update #{args}"
- platforms.each do |platform|
- bundle "update #{args}", 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 67647dd4f0..0000000000
--- a/tasks/bundle_util.rb
+++ /dev/null
@@ -1,110 +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,
- "_#{desired_bundler_version}_",
- *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, "_#{desired_bundler_version}_", *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
-
- def desired_bundler_version
- @desired_bundler_version ||= begin
- omnibus_overrides = File.join(project_root, "omnibus_overrides.rb")
- File.readlines(omnibus_overrides).each do |line|
- return $1 if line =~ /^override :bundler, version: "(.+)"$/
- end
- end
- end
-end
diff --git a/tasks/cbgb.rb b/tasks/cbgb.rb
deleted file mode 100644
index 70ca1036e8..0000000000
--- a/tasks/cbgb.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# Author:: Thom May (tmay@chef.io)
-# Author:: Nathen Harvey (nharvey@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 "rake"
-
-CBGB_SOURCE = File.join(File.dirname(__FILE__), "..", "CBGB.toml")
-CBGB_TARGET = File.join(File.dirname(__FILE__), "..", "CBGB.md")
-
-begin
- require "tomlrb"
-
- task :default => :generate
-
- namespace :cbgb do
- desc "Generate MarkDown version of CBGB file"
- task :generate do
- cbgb = Tomlrb.load_file CBGB_SOURCE
- out = "<!-- This is a generated file. Please do not edit directly -->\n"
- out << "<!-- Modify CBGB.toml file and run `rake cbgb:generate` to regenerate -->\n\n"
- out << "# " + cbgb["Preamble"]["title"] + "\n\n"
- out << cbgb["Preamble"]["text"] + "\n"
- out << "# Board of Governors\n\n"
- out << "## " + cbgb["Org"]["Lead"]["title"] + "\n\n"
- out << person(cbgb["people"], cbgb["Org"]["Lead"]["person"]) + "\n\n"
- out << "### " + cbgb["Org"]["Contributors"]["title"] + "\n\n"
- out << cbgb(cbgb["people"], cbgb["Org"]["Contributors"]["governers"]) + "\n\n"
- out << "### " + cbgb["Org"]["Corporate-Contributors"]["title"] + "\n\n"
- 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") do |fn|
- fn.write out
- end
- end
- end
-
- def components(list, cmp)
- out = ""
- cmp.each do |k, v|
- out << "\n#### #{v['title'].gsub('#', '\\#')}\n"
- out << cbgb(list, v["cbgb"])
- end
- out
- end
-
- def cbgb(list, people)
- o = ""
- people.each do |p|
- o << person(list, p) + "\n"
- end
- o
- end
-
- def person(list, person)
- if list[person].has_key?("GitHub")
- out = "* [#{list[person]["Name"]}](https://github.com/#{list[person]["GitHub"]})"
- else
- out = "* #{list[person]["Name"]}"
- end
- if list[person].has_key?("Person")
- out << " - #{list[person]["Person"]}"
- end
- out
- end
-
-rescue LoadError
- STDERR.puts "\n*** TomlRb not available.\n\n"
-end
diff --git a/tasks/changelog.rb b/tasks/changelog.rb
deleted file mode 100644
index 15f56c53ee..0000000000
--- a/tasks/changelog.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-begin
- require "github_changelog_generator/task"
-
- GitHubChangelogGenerator::RakeTask.new :changelog do |config|
- config.issues = false
- 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 0b216f8e52..f83b1c356c 100644
--- a/tasks/dependencies.rb
+++ b/tasks/dependencies.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright (c) 2016 Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,121 +15,42 @@
# 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
+
# Update all dependencies to the latest constraint-matching version
- desc "Update all dependencies. dependencies:update to update as little as possible."
- task :update => %w{
+ desc "Update all dependencies."
+ task update: %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
}
- desc "Update Gemfile.lock and all Gemfile.<platform>.locks."
- task :update_gemfile_lock do |t, rake_args|
- Rake::Task["bundle:update"].invoke
- end
-
- def gemfile_lock_task(task_name, dirs: [], other_platforms: true, leave_frozen: true)
- dirs.each do |dir|
- desc "Update #{dir}/Gemfile.lock."
- task task_name do |t, rake_args|
- extend BundleUtil
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating #{dir}/Gemfile.lock ..."
- puts "-------------------------------------------------------------------"
- with_bundle_unfrozen(cwd: dir, leave_frozen: leave_frozen) do
- bundle "install", cwd: dir, delete_gemfile_lock: true
- 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_unbundled_env do
+ rm_f "#{dir}/Gemfile.lock"
+ sh "bundle lock --update --add-platform ruby x64-mingw32 x86-mingw32"
end
end
end
end
- def berksfile_lock_task(task_name, dirs: [])
- dirs.each do |dir|
- desc "Update #{dir}/Berksfile.lock."
- task task_name do |t, rake_args|
- extend BundleUtil
- puts ""
- puts "-------------------------------------------------------------------"
- puts "Updating #{dir}/Berksfile.lock ..."
- puts "-------------------------------------------------------------------"
- if 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_unbundled_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
- }
- berksfile_lock_task :update_kitchen_tests_berksfile_lock, dirs: %w{
- kitchen-tests
- kitchen-tests/cookbooks/audit_test
- }
-
- desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}."
- task :update_omnibus_overrides do |t, rake_args|
- 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}"
- end
-
- # Emit it
- puts "Latest version of #{gem_name} is #{$1}"
- overrides << "override #{override_name.inspect}, version: #{$1.inspect}\n"
- end
+ bundle_update_locked_multiplatform_task :update_gemfile_lock, "."
+ bundle_update_locked_multiplatform_task :update_omnibus_gemfile_lock, "omnibus"
- # 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
-desc "Update all dependencies and check for outdated gems."
-task :dependencies => [ "dependencies:update", "bundle:outdated" ]
-task :update => [ "dependencies:update", "bundle:outdated"]
diff --git a/tasks/docs.rb b/tasks/docs.rb
new file mode 100755
index 0000000000..b815988b8b
--- /dev/null
+++ b/tasks/docs.rb
@@ -0,0 +1,303 @@
+RESOURCES_TO_SKIP = ["whyrun_safe_ruby_block", "l_w_r_p_base", "user_resource_abstract_base_class", "linux_user", "pw_user", "aix_user", "dscl_user", "solaris_user", "windows_user", "mac_user", ""].freeze
+
+namespace :docs_site do
+
+ desc "Generate resource documentation pages in a docs_site directory"
+
+ task :resources do
+ Encoding.default_external = Encoding::UTF_8
+
+ $:.unshift(File.expand_path(File.join(__dir__, "lib")))
+
+ require "chef/resource_inspector"
+ require "fileutils"
+ require "yaml"
+
+ # @return [String, nil] a pretty default value string or nil if we want to skip it
+ def pretty_default(default)
+ return nil if default.nil? || default == "" || default == "lazy default"
+
+ if default.is_a?(String)
+
+ # .inspect wraps the value in quotes which we want for strings, but not sentences or symbols as strings
+ return default.inspect unless default[0] == ":" || default.end_with?(".")
+ end
+ default
+ end
+
+ # generate the top example resource block example text
+ # @param properties Array<Hash>
+ # @return String
+ def generate_resource_block(resource_name, properties, default_action)
+ padding_size = largest_property_name(properties) + 6
+
+ # build the resource string with property spacing between property names and comments
+ text = ""
+ text << "#{resource_name} 'name' do\n"
+ properties.each do |p|
+ pretty_default = pretty_default(p["default"])
+
+ text << " #{p["name"].ljust(padding_size)}"
+ text << friendly_types_list(p["is"])
+ text << " # default value: 'name' unless specified" if p["name_property"]
+ text << " # default value: #{pretty_default}" unless pretty_default.nil? || (pretty_default.is_a?(String) && pretty_default.length > 45) # 45 chars is too long for these example blocks
+ text << "\n"
+ end
+ text << " #{"action".ljust(padding_size)}Symbol # defaults to :#{default_action.first} if not specified\n"
+ text << "end"
+ text
+ end
+
+ # we need to know how much space to leave so columns line up
+ # @return String
+ def largest_property_name(properties)
+ if properties.empty?
+ 6 # we'll include "action" even without properties and it's 6 chars long
+ else
+ properties.max_by { |x| x["name"].size }["name"].size
+ end
+ end
+
+ def friendly_full_property_list(name, properties)
+ prop_list = [
+ "`#{name}` is the resource.",
+ "`name` is the name given to the resource block.",
+ "`action` identifies which steps Chef Infra Client will take to bring the node into the desired state.",
+ ]
+
+ # handle the case where we have no properties
+ prop_list << friendly_property_list(properties) unless properties.empty?
+
+ prop_list
+ end
+
+ # given an array of properties print out a single comma separated string
+ # handling commas and plural vs. singular wording depending
+ # on the number of properties
+ # @return String
+ def friendly_property_list(arr)
+ return nil if arr.empty? # resources w/o properties
+
+ props = arr.map { |x| "`#{x["name"]}`" }
+
+ # build the text string containing all properties bolded w/ punctuation
+ if props.size > 1
+ props[-1] = "and #{props[-1]}"
+ end
+ text = props.size == 2 ? props.join(" ") : props.join(", ")
+ text << ( props.size > 1 ? " are the properties" : " is the property" )
+ text << " available to this resource."
+ text
+ end
+
+ # given an array of types print out a single comma separated string
+ # handling TrueClass/FalseClass which needs to be "true" and "false"
+ # and removing any nil values since those are less types in properties
+ # and more side effects of legacy design
+ # @return String
+ # TODO:
+ # - still does not include nil (?)
+ def friendly_types_list(arr)
+ fixed_arr = Array(arr).map do |x|
+ case x
+ when "TrueClass"
+ "true"
+ when "FalseClass"
+ "false"
+ when "NilClass"
+ nil
+ else
+ x
+ end
+ end
+ # compact to remove the nil values
+ fixed_arr.compact.join(", ")
+ end
+
+ # print out the human readable form of the default
+ def friendly_default_value(property)
+ return "The resource block's name" if property["name_property"]
+
+ return nil if property["default"].nil? || property["default"] == ""
+
+ # this way we properly print out a string of a hash or an array instead of just the values
+ property["default"].to_s
+ end
+
+ #
+ # Build the actions section of the resource yaml
+ #
+ # @return [Hash]
+ #
+ def action_list(actions)
+ list = {}
+ actions.sort.each do |action|
+ # nothing is a special case that sources the content from the docs site
+ list[action.to_sym] = (action == "nothing" ? { "shortcode" => "resources_common_actions_nothing.md" } : { "markdown" => nil })
+ end
+
+ list
+ end
+
+ # TODO:
+ # - what to do about "lazy default" for default?
+ def properties_list(properties)
+ properties.map do |property|
+ default_val = friendly_default_value(property)
+
+ values = {}
+ values["property"] = property["name"]
+ values["ruby_type"] = friendly_types_list(property["is"])
+ values["required"] = !!property["required"] # right now we just want a boolean value here since the docs doesn't know what to do with an array of actions
+ values["default_value"] = default_val unless default_val.nil?
+ values["new_in"] = property["introduced"] unless property["introduced"].nil?
+ values["allowed_values"] = property["equal_to"].join(", ") unless property["equal_to"].empty?
+ values["description_list"] = [{ "markdown" => property["description"] }]
+ values
+ end
+ end
+
+ def special_properties(name)
+ properties = {}
+
+ # these package properties support passing arrays for the package name
+ properties["multi_package_resource"] = true if %w{snap_package dpkg_package yum_package apt_package zypper_package homebrew_package dnf_package pacman_package homebrew_package}.include?(name)
+
+ properties["common_resource_functionality_resources_common_windows_security"] = true if name == "remote_directory"
+
+ properties["cookbook_file_specificity"] = true if name == "cookbook_file"
+
+ properties["debug_recipes_chef_shell"] = true if name == "breakpoint"
+
+ properties["handler_custom"] = true if name == "chef_handler"
+
+ properties["handler_types"] = true if name == "chef_handler"
+
+ properties["nameless_apt_update"] = true if name == "apt_update"
+
+ properties["nameless_build_essential"] = true if name == "build_essential"
+
+ properties["properties_resources_common_windows_security"] = true if %w{cookbook_file file template remote_file directory}.include?(name)
+
+ properties["properties_shortcode"] = "resource_log_properties.md" if name == "log"
+
+ properties["ps_credential_helper"] = true if name == "dsc_script"
+
+ properties["registry_key"] = true if name == "registry_key"
+
+ properties["remote_directory_recursive_directories"] = true if name == "remote_directory"
+
+ properties["remote_file_prevent_re_downloads"] = true if name == "remote_file"
+
+ properties["remote_file_unc_path"] = true if name == "remote_file"
+
+ properties["resource_directory_recursive_directories"] = true if %w{directory remote_directory}.include?(name)
+
+ properties["resource_package_options"] = true if name == "package"
+
+ properties["resources_common_atomic_update"] = true if %w{cookbook_file file template remote_file}.include?(name)
+
+ properties["resources_common_guard_interpreter"] = true if name == "script"
+
+ properties["resources_common_guards"] = true unless %w{ruby_block chef_acl chef_environment chef_data_bag chef_mirror chef_container chef_client chef_organization remote_file chef_node chef_group breakpoint chef_role registry_key chef_data_bag_item chef_user package}.include?(name)
+
+ properties["resources_common_notification"] = true unless %w{ruby_block chef_acl python chef_environment chef_data_bag chef_mirror perl chef_container chef_client chef_organization remote_file chef_node chef_group breakpoint chef_role registry_key chef_data_bag_item chef_user ruby package}.include?(name)
+
+ properties["resources_common_properties"] = true unless %w{ruby_block chef_acl python chef_environment chef_data_bag chef_mirror perl chef_container chef_client chef_organization remote_file chef_node chef_group breakpoint chef_role registry_key chef_data_bag_item chef_user ruby package}.include?(name)
+
+ properties["ruby_style_basics_chef_log"] = true if name == "log"
+
+ properties["syntax_shortcode"] = "resource_log_syntax.md" if name == "log"
+
+ properties["template_requirements"] = true if name == "template"
+
+ properties["unit_file_verification"] = true if name == "systemd_unit"
+
+ properties
+ end
+
+ # Breaks a block of text into the different sections expected for the description,
+ # using the markers "Note:" for "note" sections and "Warning:" for "warning" sections.
+ # TODO: has the limitation that the plain description section is assumed to come first,
+ # and is followed by one or more "note"s or "warning"s sections.
+ def build_description(name, text)
+ return [{ "markdown" => nil }] if text.nil?
+
+ description_pattern = /(Note:|Warning:)?((?:(?!Note:|Warning:).)*)/m
+
+ description = []
+
+ text.scan(description_pattern) do |preface, body|
+ body.strip!
+ next if body.empty?
+
+ element = { "markdown" => body }
+
+ case preface
+ when "Note:"
+ description << { "note" => element }
+ when "Warning:"
+ description << { "warning" => element }
+ when nil
+ description << element
+ else
+ raise "Unexpected thing happened! preface: '#{preface}', body: '#{body}'"
+ end
+ end
+
+ # if we're on a package resource, depending on the OS we want to inject a warning / note that you can just use 'package' instead
+ description << { "notes_resource_based_on_package" => true } if %w{apt_package bff_package dnf_package homebrew_package ips_package openbsd_package pacman_package portage_package smartos_package windows_package yum_package zypper_package pacman_package freebsd_package}.include?(name)
+
+ description
+ end
+
+ # the main method that builds what will become the yaml file
+ def build_resource_data(name, data)
+ properties = data["properties"].reject { |v| v["name"] == "name" || v["deprecated"] }.sort_by! { |v| v["name"] }
+
+ r = {}
+
+ # We want all our resources to show up in the main resource reference page
+ r["resource_reference"] = true
+
+ # These properties are set to special values for only a few resources.
+ r.merge!(special_properties(name))
+
+ r["resource"] = name
+ r["resource_description_list"] = build_description(name, data["description"])
+ r["resource_new_in"] = data["introduced"] unless data["introduced"].nil?
+ r["syntax_full_code_block"] = generate_resource_block(name, properties, data["default_action"])
+ r["syntax_properties_list"] = nil
+ r["syntax_full_properties_list"] = friendly_full_property_list(name, properties)
+ r["actions_list"] = action_list(data["actions"])
+ r["properties_list"] = properties_list(properties)
+ r["examples"] = data["examples"]
+
+ r
+ end
+
+ FileUtils.mkdir_p "docs_site"
+ resources = Chef::JSONCompat.parse(Chef::ResourceInspector.inspect)
+
+ resources.each do |resource, data|
+ # skip some resources we don't directly document
+ next if RESOURCES_TO_SKIP.include?(resource)
+
+ next if ENV["DEBUG"] && !(resource == ENV["DEBUG"])
+
+ resource_data = build_resource_data(resource, data)
+
+ if ENV["DEBUG"]
+ require "pp"
+ pp resource
+ puts "=========="
+ pp data
+ puts "=========="
+ pp resource_data
+ else
+ puts "Writing out #{resource}."
+ File.open("docs_site/#{resource}.yaml", "w") { |f| f.write(YAML.dump(resource_data)) }
+ end
+ end
+ end
+end
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
deleted file mode 100644
index e13d4724b0..0000000000
--- a/tasks/maintainers.rb
+++ /dev/null
@@ -1,211 +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 "rake"
-
-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",
- "chef/client-docs", "chef/ffi-yajl", "chef/libyajl2-gem",
- "chef/mixlib-authentication", "chef/mixlib-cli",
- "chef/mixlib-config", "chef/mixlib-install", "chef/mixlib-log",
- "chef/mixlib-shellout", "chef/ohai", "chef/omnibus-chef"]
-
-begin
- require "tomlrb"
- require "octokit"
- require "pp"
-
- namespace :maintainers do
- task :default => :generate
-
- desc "Generate MarkDown version of MAINTAINERS file"
- task :generate do
- out = "<!-- This is a generated file. Please do not edit directly -->\n\n"
- out << "# " + source["Preamble"]["title"] + "\n\n"
- out << source["Preamble"]["text"] + "\n"
-
- # The project lead is a special case
- out << "# " + source["Org"]["Lead"]["title"] + "\n\n"
- out << format_person(source["Org"]["Lead"]["person"]) + "\n\n"
-
- out << format_components(source["Org"]["Components"])
- File.open(TARGET, "w") do |fn|
- fn.write out
- end
- end
-
- desc "Synchronize GitHub teams"
- # there's a special @chef/client-maintainers team that's everyone
- # and then there's a team per component
- task :synchronize do
- Octokit.auto_paginate = true
- get_github_teams
- prepare_teams(source["Org"]["Components"].dup)
- sync_teams!
- end
- end
-
- def github
- @github ||= Octokit::Client.new(:netrc => true)
- end
-
- def source
- @source ||= Tomlrb.load_file SOURCE
- end
-
- def teams
- @teams ||= { "client-maintainers" => { "title" => "Client Maintainers" } }
- end
-
- def add_members(team, name)
- teams["client-maintainers"]["members"] ||= []
- teams["client-maintainers"]["members"] << name
- teams[team] ||= {}
- teams[team]["members"] ||= []
- teams[team]["members"] << name
- end
-
- def set_team_title(team, title)
- teams[team] ||= {}
- teams[team]["title"] = title
- end
-
- def gh_teams
- @gh_teams ||= {}
- end
-
- # we have to resolve team names to ids. While we're at it, we can get the privacy
- # setting, so we know whether we need to update it
- def get_github_teams
- github.org_teams("chef").each do |team|
- gh_teams[team[:slug]] = { "id" => team[:id], "privacy" => team[:privacy] }
- end
- end
-
- def get_github_team(team)
- github.team_members(gh_teams[team]["id"]).map do |member|
- member[:login]
- end.sort.uniq.map(&:downcase)
- rescue
- []
- end
-
- def create_team(team)
- puts "creating new github team: #{team} with title: #{teams[team]["title"]} "
- t = github.create_team("chef", name: team, description: teams[team]["title"],
- privacy: "closed", repo_names: REPOSITORIES,
- accept: "application/vnd.github.ironman-preview+json")
- gh_teams[team] = { "id" => t[:id], "privacy" => t[:privacy] }
- end
-
- def compare_teams(current, desired)
- # additions are the subtraction of the current state from the desired state
- # deletions are the subtraction of the desired state from the current state
- [desired - current, current - desired]
- end
-
- def prepare_teams(cmp)
- %w{text paths}.each { |k| cmp.delete(k) }
- if cmp.key?("team")
- team = cmp.delete("team")
- add_members(team, cmp.delete("lieutenant")) if cmp.key?("lieutenant")
- add_members(team, cmp.delete("maintainers")) if cmp.key?("maintainers")
- set_team_title(team, cmp.delete("title"))
- else
- %w{maintainers lieutenant title}.each { |k| cmp.delete(k) }
- end
- cmp.each { |_k, v| prepare_teams(v) }
- end
-
- def update_team(team, additions, deletions)
- create_team(team) unless gh_teams.key?(team)
- update_team_privacy(team)
- add_team_members(team, additions)
- remove_team_members(team, deletions)
- rescue
- puts "failed for #{team}"
- end
-
- def update_team_privacy(team)
- return if gh_teams[team]["privacy"] == "closed"
- puts "Setting #{team} privacy to closed from #{gh_teams[team]["privacy"]}"
- github.update_team(gh_teams[team]["id"], privacy: "closed",
- accept: "application/vnd.github.ironman-preview+json")
- end
-
- def add_team_members(team, additions)
- additions.each do |member|
- puts "Adding #{member} to #{team}"
- github.add_team_membership(gh_teams[team]["id"], member, role: "member",
- accept: "application/vnd.github.ironman-preview+json")
- end
- end
-
- def remove_team_members(team, deletions)
- deletions.each do |member|
- puts "Removing #{member} from #{team}"
- github.remove_team_membership(gh_teams[team]["id"], member,
- accept: "application/vnd.github.ironman-preview+json")
- end
- end
-
- def sync_teams!
- teams.each do |name, details|
- current = get_github_team(name)
- desired = details["members"].flatten.sort.uniq.map(&:downcase)
- additions, deletions = compare_teams(current, desired)
- update_team(name, additions, deletions)
- end
- end
-
- def get_person(person)
- source["people"][person]
- end
-
- def format_components(cmp)
- out = "## " + cmp.delete("title") + "\n\n"
- out << cmp.delete("text") + "\n" if cmp.has_key?("text")
- out << "To mention the team, use @chef/#{cmp.delete("team")}\n\n" if cmp.has_key?("team")
- if cmp.has_key?("lieutenant")
- out << "### Lieutenant\n\n"
- out << format_person(cmp.delete("lieutenant")) + "\n\n"
- end
- out << format_maintainers(cmp.delete("maintainers")) + "\n" if cmp.has_key?("maintainers")
- cmp.delete("paths")
- cmp.each { |k, v| out << format_components(v) }
- out
- end
-
- def format_maintainers(people)
- o = "### Maintainers\n\n"
- people.each do |p|
- o << format_person(p) + "\n"
- end
- o
- end
-
- def format_person(person)
- mnt = get_person(person)
- "* [#{mnt["Name"]}](https://github.com/#{mnt["GitHub"]})"
- end
-
-rescue LoadError
- STDERR.puts "\n*** TomlRb not available.\n\n"
-end
diff --git a/tasks/rspec.rb b/tasks/rspec.rb
index 616a68f09e..929e0f91b0 100644
--- a/tasks/rspec.rb
+++ b/tasks/rspec.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,66 +20,59 @@
require "rubygems"
require "rake"
-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 Gem Components"
task :component_specs do
- Dir.chdir("chef-config") do
- Bundler.with_clean_env do
- sh("bundle install")
- sh("bundle exec rake spec")
+ %w{chef-utils chef-config}.each do |gem|
+ Dir.chdir(gem) do
+ Bundler.with_unbundled_env do
+ sh("bundle install --jobs=3 --retry=3")
+ sh("bundle exec rake spec")
+ end
end
end
end
- task :default => :spec
+ task default: :spec
- task :spec => :component_specs
+ task spec: :component_specs
- desc "Run standard specs (minus long running specs)"
+ desc "Run all specs in spec directory"
RSpec::Core::RakeTask.new(:spec) do |t|
+ t.verbose = false
t.rspec_opts = %w{--profile}
- # right now this just limits to functional + unit, but could also remove
- # individual tests marked long-running
t.pattern = FileList["spec/**/*_spec.rb"]
end
namespace :spec do
- desc "Run all specs in spec directory with RCov"
- RSpec::Core::RakeTask.new(:rcov) do |t|
- t.rspec_opts = %w{--profile}
- t.pattern = FileList["spec/**/*_spec.rb"]
- t.rcov = true
- t.rcov_opts = lambda do
- IO.readlines("#{CHEF_ROOT}/spec/rcov.opts").map { |l| l.chomp.split " " }.flatten
- end
- end
-
desc "Run all specs in spec directory"
RSpec::Core::RakeTask.new(:all) do |t|
+ t.verbose = false
t.rspec_opts = %w{--profile}
t.pattern = FileList["spec/**/*_spec.rb"]
end
desc "Print Specdoc for all specs"
RSpec::Core::RakeTask.new(:doc) do |t|
+ t.verbose = false
t.rspec_opts = %w{--format specdoc --dry-run --profile}
t.pattern = FileList["spec/**/*_spec.rb"]
end
desc "Run the specs under spec/unit with activesupport loaded"
RSpec::Core::RakeTask.new(:activesupport) do |t|
+ t.verbose = false
t.rspec_opts = %w{--require active_support/core_ext --profile}
# Only node_spec and role_spec specifically have issues, target those tests
t.pattern = FileList["spec/unit/node_spec.rb", "spec/unit/role_spec.rb"]
end
- [:unit, :functional, :integration, :stress].each do |sub|
+ %i{unit functional integration stress}.each do |sub|
desc "Run the specs under spec/#{sub}"
RSpec::Core::RakeTask.new(sub) do |t|
+ t.verbose = false
t.rspec_opts = %w{--profile}
t.pattern = FileList["spec/#{sub}/**/*_spec.rb"]
end
diff --git a/tasks/spellcheck.rb b/tasks/spellcheck.rb
new file mode 100644
index 0000000000..0eb8924a81
--- /dev/null
+++ b/tasks/spellcheck.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright (c) 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.
+#
+
+namespace :spellcheck do
+ task run: :prereqs do
+ sh 'cspell lint --no-progress "**/*"'
+ end
+
+ task prereqs: %i{cspell_check config_check fetch_common}
+
+ task :fetch_common do
+ sh "wget -q https://raw.githubusercontent.com/chef/chef_dictionary/master/chef.txt -O chef_dictionary.txt"
+ end
+
+ task :config_check do
+ require "json"
+
+ config_file = "cspell.json"
+
+ unless File.readable?(config_file)
+ abort "Spellcheck config file '#{config_file}' not found, skipping spellcheck"
+ end
+
+ unless (JSON.parse(File.read(config_file)) rescue false)
+ abort "Failed to parse config file '#{config_file}', skipping spellcheck"
+ end
+ end
+
+ task :cspell_check do
+ cspell_version = begin
+ `cspell --version`
+ rescue
+ nil
+ end
+
+ cspell_version.is_a?(String) || abort(<<~INSTALL_CSPELL)
+ cspell is needed to run the spellcheck tasks. Run `npm install -g cspell` to install.
+ For more information: https://www.npmjs.com/package/cspell
+ INSTALL_CSPELL
+ end
+end
+
+desc "Run spellcheck on the project."
+task spellcheck: "spellcheck:run"
diff --git a/vendor/bundle/bundler/gems/bundler-audit-4e32fca89d75 b/vendor/bundle/bundler/gems/bundler-audit-4e32fca89d75
deleted file mode 160000
-Subproject 4e32fca89d75f0e249671431ff38aadc02bfb28
diff --git a/vendor/bundle/bundler/gems/chefstyle-52a0d55a9e8f b/vendor/bundle/bundler/gems/chefstyle-52a0d55a9e8f
deleted file mode 160000
-Subproject 52a0d55a9e8fb30d067c0a66371db4ae781a026
diff --git a/version_policy.rb b/version_policy.rb
deleted file mode 100644
index bfe08e91e4..0000000000
--- a/version_policy.rb
+++ /dev/null
@@ -1,115 +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
- :bundler => "1.12.5", # until we figure out how to work with 1.13.0
- "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.4",
- "libxslt" => "1.1.29",
- "libyaml" => "0.1.6",
- "makedepend" => "1.0.5",
- "ncurses" => "5.9",
- "pkg-config-lite" => "0.28-1",
- "ruby" => "2.3.1",
- # 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" =>"???",
- "openssl" => "1.0.2h",
-}
-
-#
-# 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", # until we get working with 1.13.0
-}
-
-#
-# 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
-# stove - halite pins to ~> 3.2 in 1.2.1
-# rubocop - chef-style pins to 0.39.0 in 0.3.1
-#
-ACCEPTABLE_OUTDATED_GEMS = [
- "json", # aws-sdk-v1 pins this because Ruby 2.0; chef-provisioning fix to abandon v1 TBD
- "rubocop", # chefstyle pins this, will often be somewhat behind
- "slop", # expected to disappear with pry 0.11
- "typhoeus", # Until the travis gem updates to 1.0.
- "cucumber-core", # Until cucumber 2.0
-]
-
-#
-# 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
-}